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: (149 commits)
  USB: ohci-pnx4008: Remove unnecessary cast of return value of kzalloc
  USB: additions to the quirk list
  usb-storage: implement autosuspend
  USB: cdc-acm: add new device id to option driver
  USB: goku_udc trivial cleanups
  USB: usb gadget stack can now -DDEBUG with Kconfig
  usb gadget stack: remove usb_ep_*_buffer(), part 2
  usb gadget stack: remove usb_ep_*_buffer(), part 1
  USB: pxa2xx_udc -- cleanups, mostly removing dma hooks
  USB: pxa2xx_udc: use generic gpio layer
  USB: quirk for samsung printer
  USB: usb/dma doc updates
  USB: drivers/usb/storage/unusual_devs.h whitespace cleanup
  USB: remove Makefile reference to obsolete OHCI_AT91
  USB: io_*: remove bogus termios no change checks
  USB: mos7720: remove bogus no termios change check
  USB: visor and whiteheat: remove bogus termios change checks
  USB: pl2303: remove bogus checks and fix speed support to use tty_get_baud_rate()
  USB: mos7840.c: turn this into a serial driver
  USB: make the usb_device numa_node get assigned from controller
  ...
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index f9937ad..9734577 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -39,3 +39,16 @@
 		If you want to suspend a device immediately but leave it
 		free to wake up in response to I/O requests, you should
 		write "0" to power/autosuspend.
+
+What:		/sys/bus/usb/devices/.../power/persist
+Date:		May 2007
+KernelVersion:	2.6.23
+Contact:	Alan Stern <stern@rowland.harvard.edu>
+Description:
+		If CONFIG_USB_PERSIST is set, then each USB device directory
+		will contain a file named power/persist.  The file holds a
+		boolean value (0 or 1) indicating whether or not the
+		"USB-Persist" facility is enabled for the device.  Since the
+		facility is inherently dangerous, it is disabled by default
+		for all devices except hubs.  For more information, see
+		Documentation/usb/persist.txt.
diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt
index 5b8d695..152b510 100644
--- a/Documentation/power/swsusp.txt
+++ b/Documentation/power/swsusp.txt
@@ -393,6 +393,9 @@
 Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays)
 before suspending; then remount them after resuming.
 
+There is a work-around for this problem.  For more information, see
+Documentation/usb/persist.txt.
+
 Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were
 compiled with the similar configuration files. Anyway I found that
 suspend to disk (and resume) is much slower on 2.6.16 compared to
diff --git a/Documentation/usb/dma.txt b/Documentation/usb/dma.txt
index 62844ae..e8b50b7 100644
--- a/Documentation/usb/dma.txt
+++ b/Documentation/usb/dma.txt
@@ -32,12 +32,15 @@
 It's good to avoid making CPUs copy data needlessly.  The costs can add up,
 and effects like cache-trashing can impose subtle penalties.
 
-- When you're allocating a buffer for DMA purposes anyway, use the buffer
-  primitives.  Think of them as kmalloc and kfree that give you the right
-  kind of addresses to store in urb->transfer_buffer and urb->transfer_dma,
-  while guaranteeing that no hidden copies through DMA "bounce" buffers will
-  slow things down.  You'd also set URB_NO_TRANSFER_DMA_MAP in
-  urb->transfer_flags:
+- If you're doing lots of small data transfers from the same buffer all
+  the time, that can really burn up resources on systems which use an
+  IOMMU to manage the DMA mappings.  It can cost MUCH more to set up and
+  tear down the IOMMU mappings with each request than perform the I/O!
+
+  For those specific cases, USB has primitives to allocate less expensive
+  memory.  They work like kmalloc and kfree versions that give you the right
+  kind of addresses to store in urb->transfer_buffer and urb->transfer_dma.
+  You'd also set URB_NO_TRANSFER_DMA_MAP in urb->transfer_flags:
 
 	void *usb_buffer_alloc (struct usb_device *dev, size_t size,
 		int mem_flags, dma_addr_t *dma);
@@ -45,6 +48,10 @@
 	void usb_buffer_free (struct usb_device *dev, size_t size,
 		void *addr, dma_addr_t dma);
 
+  Most drivers should *NOT* be using these primitives; they don't need
+  to use this type of memory ("dma-coherent"), and memory returned from
+  kmalloc() will work just fine.
+
   For control transfers you can use the buffer primitives or not for each
   of the transfer buffer and setup buffer independently.  Set the flag bits
   URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP to indicate which
@@ -54,29 +61,39 @@
   The memory buffer returned is "dma-coherent"; sometimes you might need to
   force a consistent memory access ordering by using memory barriers.  It's
   not using a streaming DMA mapping, so it's good for small transfers on
-  systems where the I/O would otherwise tie up an IOMMU mapping.  (See
+  systems where the I/O would otherwise thrash an IOMMU mapping.  (See
   Documentation/DMA-mapping.txt for definitions of "coherent" and "streaming"
   DMA mappings.)
 
   Asking for 1/Nth of a page (as well as asking for N pages) is reasonably
   space-efficient.
 
+  On most systems the memory returned will be uncached, because the
+  semantics of dma-coherent memory require either bypassing CPU caches
+  or using cache hardware with bus-snooping support.  While x86 hardware
+  has such bus-snooping, many other systems use software to flush cache
+  lines to prevent DMA conflicts.
+
 - Devices on some EHCI controllers could handle DMA to/from high memory.
-  Driver probe() routines can notice this using a generic DMA call, then
-  tell higher level code (network, scsi, etc) about it like this:
 
-	if (dma_supported (&intf->dev, 0xffffffffffffffffULL))
-		net->features |= NETIF_F_HIGHDMA;
+  Unfortunately, the current Linux DMA infrastructure doesn't have a sane
+  way to expose these capabilities ... and in any case, HIGHMEM is mostly a
+  design wart specific to x86_32.  So your best bet is to ensure you never
+  pass a highmem buffer into a USB driver.  That's easy; it's the default
+  behavior.  Just don't override it; e.g. with NETIF_F_HIGHDMA.
 
-  That can eliminate dma bounce buffering of requests that originate (or
-  terminate) in high memory, in cases where the buffers aren't allocated
-  with usb_buffer_alloc() but instead are dma-mapped.
+  This may force your callers to do some bounce buffering, copying from
+  high memory to "normal" DMA memory.  If you can come up with a good way
+  to fix this issue (for x86_32 machines with over 1 GByte of memory),
+  feel free to submit patches.
 
 
 WORKING WITH EXISTING BUFFERS
 
 Existing buffers aren't usable for DMA without first being mapped into the
-DMA address space of the device.
+DMA address space of the device.  However, most buffers passed to your
+driver can safely be used with such DMA mapping.  (See the first section
+of DMA-mapping.txt, titled "What memory is DMA-able?")
 
 - When you're using scatterlists, you can map everything at once.  On some
   systems, this kicks in an IOMMU and turns the scatterlists into single
@@ -114,3 +131,8 @@
   The calls manage urb->transfer_dma for you, and set URB_NO_TRANSFER_DMA_MAP
   so that usbcore won't map or unmap the buffer.  The same goes for
   urb->setup_dma and URB_NO_SETUP_DMA_MAP for control requests.
+
+Note that several of those interfaces are currently commented out, since
+they don't have current users.  See the source code.  Other than the dmasync
+calls (where the underlying DMA primitives have changed), most of them can
+easily be commented back in if you want to use them.
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt
new file mode 100644
index 0000000..df54d64
--- /dev/null
+++ b/Documentation/usb/persist.txt
@@ -0,0 +1,156 @@
+		USB device persistence during system suspend
+
+		   Alan Stern <stern@rowland.harvard.edu>
+
+		 September 2, 2006 (Updated May 29, 2007)
+
+
+	What is the problem?
+
+According to the USB specification, when a USB bus is suspended the
+bus must continue to supply suspend current (around 1-5 mA).  This
+is so that devices can maintain their internal state and hubs can
+detect connect-change events (devices being plugged in or unplugged).
+The technical term is "power session".
+
+If a USB device's power session is interrupted then the system is
+required to behave as though the device has been unplugged.  It's a
+conservative approach; in the absence of suspend current the computer
+has no way to know what has actually happened.  Perhaps the same
+device is still attached or perhaps it was removed and a different
+device plugged into the port.  The system must assume the worst.
+
+By default, Linux behaves according to the spec.  If a USB host
+controller loses power during a system suspend, then when the system
+wakes up all the devices attached to that controller are treated as
+though they had disconnected.  This is always safe and it is the
+"officially correct" thing to do.
+
+For many sorts of devices this behavior doesn't matter in the least.
+If the kernel wants to believe that your USB keyboard was unplugged
+while the system was asleep and a new keyboard was plugged in when the
+system woke up, who cares?  It'll still work the same when you type on
+it.
+
+Unfortunately problems _can_ arise, particularly with mass-storage
+devices.  The effect is exactly the same as if the device really had
+been unplugged while the system was suspended.  If you had a mounted
+filesystem on the device, you're out of luck -- everything in that
+filesystem is now inaccessible.  This is especially annoying if your
+root filesystem was located on the device, since your system will
+instantly crash.
+
+Loss of power isn't the only mechanism to worry about.  Anything that
+interrupts a power session will have the same effect.  For example,
+even though suspend current may have been maintained while the system
+was asleep, on many systems during the initial stages of wakeup the
+firmware (i.e., the BIOS) resets the motherboard's USB host
+controllers.  Result: all the power sessions are destroyed and again
+it's as though you had unplugged all the USB devices.  Yes, it's
+entirely the BIOS's fault, but that doesn't do _you_ any good unless
+you can convince the BIOS supplier to fix the problem (lots of luck!).
+
+On many systems the USB host controllers will get reset after a
+suspend-to-RAM.  On almost all systems, no suspend current is
+available during hibernation (also known as swsusp or suspend-to-disk).
+You can check the kernel log after resuming to see if either of these
+has happened; look for lines saying "root hub lost power or was reset".
+
+In practice, people are forced to unmount any filesystems on a USB
+device before suspending.  If the root filesystem is on a USB device,
+the system can't be suspended at all.  (All right, it _can_ be
+suspended -- but it will crash as soon as it wakes up, which isn't
+much better.)
+
+
+	What is the solution?
+
+Setting CONFIG_USB_PERSIST will cause the kernel to work around these
+issues.  It enables a mode in which the core USB device data
+structures are allowed to persist across a power-session disruption.
+It works like this.  If the kernel sees that a USB host controller is
+not in the expected state during resume (i.e., if the controller was
+reset or otherwise had lost power) then it applies a persistence check
+to each of the USB devices below that controller for which the
+"persist" attribute is set.  It doesn't try to resume the device; that
+can't work once the power session is gone.  Instead it issues a USB
+port reset and then re-enumerates the device.  (This is exactly the
+same thing that happens whenever a USB device is reset.)  If the
+re-enumeration shows that the device now attached to that port has the
+same descriptors as before, including the Vendor and Product IDs, then
+the kernel continues to use the same device structure.  In effect, the
+kernel treats the device as though it had merely been reset instead of
+unplugged.
+
+If no device is now attached to the port, or if the descriptors are
+different from what the kernel remembers, then the treatment is what
+you would expect.  The kernel destroys the old device structure and
+behaves as though the old device had been unplugged and a new device
+plugged in, just as it would without the CONFIG_USB_PERSIST option.
+
+The end result is that the USB device remains available and usable.
+Filesystem mounts and memory mappings are unaffected, and the world is
+now a good and happy place.
+
+Note that even when CONFIG_USB_PERSIST is set, the "persist" feature
+will be applied only to those devices for which it is enabled.  You
+can enable the feature by doing (as root):
+
+	echo 1 >/sys/bus/usb/devices/.../power/persist
+
+where the "..." should be filled in the with the device's ID.  Disable
+the feature by writing 0 instead of 1.  For hubs the feature is
+automatically and permanently enabled, so you only have to worry about
+setting it for devices where it really matters.
+
+
+	Is this the best solution?
+
+Perhaps not.  Arguably, keeping track of mounted filesystems and
+memory mappings across device disconnects should be handled by a
+centralized Logical Volume Manager.  Such a solution would allow you
+to plug in a USB flash device, create a persistent volume associated
+with it, unplug the flash device, plug it back in later, and still
+have the same persistent volume associated with the device.  As such
+it would be more far-reaching than CONFIG_USB_PERSIST.
+
+On the other hand, writing a persistent volume manager would be a big
+job and using it would require significant input from the user.  This
+solution is much quicker and easier -- and it exists now, a giant
+point in its favor!
+
+Furthermore, the USB_PERSIST option applies to _all_ USB devices, not
+just mass-storage devices.  It might turn out to be equally useful for
+other device types, such as network interfaces.
+
+
+	WARNING: Using CONFIG_USB_PERSIST can be dangerous!!
+
+When recovering an interrupted power session the kernel does its best
+to make sure the USB device hasn't been changed; that is, the same
+device is still plugged into the port as before.  But the checks
+aren't guaranteed to be 100% accurate.
+
+If you replace one USB device with another of the same type (same
+manufacturer, same IDs, and so on) there's an excellent chance the
+kernel won't detect the change.  Serial numbers and other strings are
+not compared.  In many cases it wouldn't help if they were, because
+manufacturers frequently omit serial numbers entirely in their
+devices.
+
+Furthermore it's quite possible to leave a USB device exactly the same
+while changing its media.  If you replace the flash memory card in a
+USB card reader while the system is asleep, the kernel will have no
+way to know you did it.  The kernel will assume that nothing has
+happened and will continue to use the partition tables, inodes, and
+memory mappings for the old card.
+
+If the kernel gets fooled in this way, it's almost certain to cause
+data corruption and to crash your system.  You'll have no one to blame
+but yourself.
+
+YOU HAVE BEEN WARNED!  USE AT YOUR OWN RISK!
+
+That having been said, most of the time there shouldn't be any trouble
+at all.  The "persist" feature can be extremely useful.  Make the most
+of it.
diff --git a/MAINTAINERS b/MAINTAINERS
index 05a1f1a..845fbf4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3724,12 +3724,12 @@
 W:	http://pegasus2.sourceforge.net/
 S:	Maintained
 
-USB PRINTER DRIVER
-P:	Vojtech Pavlik
-M:	vojtech@suse.cz
+USB PRINTER DRIVER (usblp)
+P:	Pete Zaitcev
+M:	zaitcev@redhat.com
 L:	linux-usb-users@lists.sourceforge.net
 L:	linux-usb-devel@lists.sourceforge.net
-S:	Maintained
+S:	Supported
 
 USB RTL8150 DRIVER
 P:	Petko Manolov
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index 746a118..18c8b6c 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -1547,10 +1547,8 @@
 #endif
 
 #if 0 /* We let them stop themselves. */
-	struct list_head *p;
 	struct ub_lun *lun;
-	list_for_each(p, &sc->luns) {
-		lun = list_entry(p, struct ub_lun, link);
+	list_for_each_entry(lun, &sc->luns, link) {
 		blk_stop_queue(lun->disk->queue);
 	}
 #endif
@@ -1562,7 +1560,6 @@
 {
 	struct ub_dev *sc = container_of(work, struct ub_dev, reset_work);
 	unsigned long flags;
-	struct list_head *p;
 	struct ub_lun *lun;
 	int lkr, rc;
 
@@ -1608,8 +1605,7 @@
 	spin_lock_irqsave(sc->lock, flags);
 	sc->reset = 0;
 	tasklet_schedule(&sc->tasklet);
-	list_for_each(p, &sc->luns) {
-		lun = list_entry(p, struct ub_lun, link);
+	list_for_each_entry(lun, &sc->luns, link) {
 		blk_start_queue(lun->disk->queue);
 	}
 	wake_up(&sc->reset_wait);
@@ -2348,7 +2344,6 @@
 static void ub_disconnect(struct usb_interface *intf)
 {
 	struct ub_dev *sc = usb_get_intfdata(intf);
-	struct list_head *p;
 	struct ub_lun *lun;
 	unsigned long flags;
 
@@ -2403,8 +2398,7 @@
 	/*
 	 * Unregister the upper layer.
 	 */
-	list_for_each (p, &sc->luns) {
-		lun = list_entry(p, struct ub_lun, link);
+	list_for_each_entry(lun, &sc->luns, link) {
 		del_gendisk(lun->disk);
 		/*
 		 * I wish I could do:
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 3afa4a5..b2baeae 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1009,20 +1009,22 @@
 }
 
 /* Treat USB reset pretty much the same as suspend/resume */
-static void hid_pre_reset(struct usb_interface *intf)
+static int hid_pre_reset(struct usb_interface *intf)
 {
 	/* FIXME: What if the interface is already suspended? */
 	hid_suspend(intf, PMSG_ON);
+	return 0;
 }
 
-static void hid_post_reset(struct usb_interface *intf)
+/* Same routine used for post_reset and reset_resume */
+static int hid_post_reset(struct usb_interface *intf)
 {
 	struct usb_device *dev = interface_to_usbdev (intf);
 
 	hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
 	/* FIXME: Any more reinitialization needed? */
 
-	hid_resume(intf);
+	return hid_resume(intf);
 }
 
 static struct usb_device_id hid_usb_ids [] = {
@@ -1039,6 +1041,7 @@
 	.disconnect =	hid_disconnect,
 	.suspend =	hid_suspend,
 	.resume =	hid_resume,
+	.reset_resume =	hid_post_reset,
 	.pre_reset =	hid_pre_reset,
 	.post_reset =	hid_post_reset,
 	.id_table =	hid_usb_ids,
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 15499b7..172a606 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -2,9 +2,12 @@
 # USB device configuration
 #
 
-menu "USB support"
+menuconfig USB_SUPPORT
+	bool "USB support"
 	depends on HAS_IOMEM
 
+if USB_SUPPORT
+
 # Host-side USB depends on having a host controller
 # NOTE:  dummy_hcd is always an option, but it's ignored here ...
 # NOTE:  SL-811 option should be board-specific ...
@@ -12,6 +15,7 @@
 	boolean
 	default y if USB_ARCH_HAS_OHCI
 	default y if USB_ARCH_HAS_EHCI
+	default y if PCMCIA				# sl811_cs
 	default y if ARM				# SL-811
 	default PCI
 
@@ -130,5 +134,4 @@
 
 source "drivers/usb/gadget/Kconfig"
 
-endmenu
-
+endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 72464b5..befff5f 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -15,7 +15,7 @@
 obj-$(CONFIG_USB_UHCI_HCD)	+= host/
 obj-$(CONFIG_USB_SL811_HCD)	+= host/
 obj-$(CONFIG_USB_U132_HCD)	+= host/
-obj-$(CONFIG_USB_OHCI_AT91)	+= host/
+obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
 
 obj-$(CONFIG_USB_ACM)		+= class/
 obj-$(CONFIG_USB_PRINTER)	+= class/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 8bcf7fe..1bc8840 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -171,7 +171,7 @@
 	struct delayed_work poll_work;
 	u32 card_info[CXINF_MAX];
 	struct mutex poll_state_serialize;
-	int poll_state;
+	enum cxacru_poll_state poll_state;
 
 	/* contol handles */
 	struct mutex cm_serialize;
@@ -226,58 +226,48 @@
 
 static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
 {
-	if (unlikely(value < 0)) {
-		return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
-						value / 100, -value % 100);
-	} else {
-		return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
-						value / 100, value % 100);
-	}
+	return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
+					value / 100, abs(value) % 100);
 }
 
 static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
 {
-	switch (value) {
-	case 0: return snprintf(buf, PAGE_SIZE, "no\n");
-	case 1: return snprintf(buf, PAGE_SIZE, "yes\n");
-	default: return 0;
-	}
+	static char *str[] = { "no", "yes" };
+	if (unlikely(value >= ARRAY_SIZE(str)))
+		return snprintf(buf, PAGE_SIZE, "%u\n", value);
+	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
 }
 
 static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
 {
-	switch (value) {
-	case 1: return snprintf(buf, PAGE_SIZE, "not connected\n");
-	case 2: return snprintf(buf, PAGE_SIZE, "connected\n");
-	case 3: return snprintf(buf, PAGE_SIZE, "lost\n");
-	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
-	}
+	static char *str[] = { NULL, "not connected", "connected", "lost" };
+	if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
+		return snprintf(buf, PAGE_SIZE, "%u\n", value);
+	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
 }
 
 static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
 {
-	switch (value) {
-	case 0: return snprintf(buf, PAGE_SIZE, "down\n");
-	case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n");
-	case 2: return snprintf(buf, PAGE_SIZE, "training\n");
-	case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n");
-	case 4: return snprintf(buf, PAGE_SIZE, "exchange\n");
-	case 5: return snprintf(buf, PAGE_SIZE, "up\n");
-	case 6: return snprintf(buf, PAGE_SIZE, "waiting\n");
-	case 7: return snprintf(buf, PAGE_SIZE, "initialising\n");
-	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
-	}
+	static char *str[] = { "down", "attempting to activate",
+		"training", "channel analysis", "exchange", "up",
+		"waiting", "initialising"
+	};
+	if (unlikely(value >= ARRAY_SIZE(str)))
+		return snprintf(buf, PAGE_SIZE, "%u\n", value);
+	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
 }
 
 static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
 {
-	switch (value) {
-	case 0: return 0;
-	case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n");
-	case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n");
-	case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n");
-	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
-	}
+	static char *str[] = {
+			NULL,
+			"ANSI T1.413",
+			"ITU-T G.992.1 (G.DMT)",
+			"ITU-T G.992.2 (G.LITE)"
+	};
+	if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
+		return snprintf(buf, PAGE_SIZE, "%u\n", value);
+	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
 }
 
 /*
@@ -308,11 +298,10 @@
 	struct cxacru_data *instance = usbatm_instance->driver_data;
 	u32 value = instance->card_info[CXINF_LINE_STARTABLE];
 
-	switch (value) {
-	case 0: return snprintf(buf, PAGE_SIZE, "running\n");
-	case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
-	default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
-	}
+	static char *str[] = { "running", "stopped" };
+	if (unlikely(value >= ARRAY_SIZE(str)))
+		return snprintf(buf, PAGE_SIZE, "%u\n", value);
+	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
 }
 
 static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 0081c1d..cd51520c 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1157,6 +1157,9 @@
 	{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
 	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
 	},
+	{ USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */
+	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
+	},
 	{ USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */
 	.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
 	},
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 6778f9a..9a14789 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -1,5 +1,5 @@
 /*
- * usblp.c  Version 0.13
+ * usblp.c
  *
  * Copyright (c) 1999 Michael Gee	<michael@linuxspecific.com>
  * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
@@ -61,11 +61,11 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.13"
 #define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap, Pete Zaitcev, David Paschal"
 #define DRIVER_DESC "USB Printer Device Class driver"
 
 #define USBLP_BUF_SIZE		8192
+#define USBLP_BUF_SIZE_IN	1024
 #define USBLP_DEVICE_ID_SIZE	1024
 
 /* ioctls: */
@@ -127,14 +127,22 @@
  */
 #define STATUS_BUF_SIZE		8
 
+/*
+ * Locks down the locking order:
+ * ->wmut locks wstatus.
+ * ->mut locks the whole usblp, except [rw]complete, and thus, by indirection,
+ * [rw]status. We only touch status when we know the side idle.
+ * ->lock locks what interrupt accesses.
+ */
 struct usblp {
 	struct usb_device 	*dev;			/* USB device */
-	struct mutex		mut;			/* locks this struct, especially "dev" */
-	char			*writebuf;		/* write transfer_buffer */
+	struct mutex		wmut;
+	struct mutex		mut;
+	spinlock_t		lock;		/* locks rcomplete, wcomplete */
 	char			*readbuf;		/* read transfer_buffer */
 	char			*statusbuf;		/* status transfer_buffer */
-	struct urb		*readurb, *writeurb;	/* The urbs */
-	wait_queue_head_t	wait;			/* Zzzzz ... */
+	struct usb_anchor	urbs;
+	wait_queue_head_t	rwait, wwait;
 	int			readcount;		/* Counter for reads */
 	int			ifnum;			/* Interface number */
 	struct usb_interface	*intf;			/* The interface */
@@ -147,8 +155,9 @@
 	}			protocol[USBLP_MAX_PROTOCOLS];
 	int			current_protocol;
 	int			minor;			/* minor number of device */
-	int			wcomplete;		/* writing is completed */
-	int			rcomplete;		/* reading is completed */
+	int			wcomplete, rcomplete;
+	int			wstatus;	/* bytes written or error */
+	int			rstatus;	/* bytes ready or error */
 	unsigned int		quirks;			/* quirks flags */
 	unsigned char		used;			/* True if open */
 	unsigned char		present;		/* True if not disconnected */
@@ -166,9 +175,6 @@
 	dbg("dev=0x%p", usblp->dev);
 	dbg("present=%d", usblp->present);
 	dbg("readbuf=0x%p", usblp->readbuf);
-	dbg("writebuf=0x%p", usblp->writebuf);
-	dbg("readurb=0x%p", usblp->readurb);
-	dbg("writeurb=0x%p", usblp->writeurb);
 	dbg("readcount=%d", usblp->readcount);
 	dbg("ifnum=%d", usblp->ifnum);
     for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) {
@@ -178,8 +184,8 @@
     }
 	dbg("current_protocol=%d", usblp->current_protocol);
 	dbg("minor=%d", usblp->minor);
-	dbg("wcomplete=%d", usblp->wcomplete);
-	dbg("rcomplete=%d", usblp->rcomplete);
+	dbg("wstatus=%d", usblp->wstatus);
+	dbg("rstatus=%d", usblp->rstatus);
 	dbg("quirks=%d", usblp->quirks);
 	dbg("used=%d", usblp->used);
 	dbg("bidir=%d", usblp->bidir);
@@ -222,6 +228,11 @@
 	{ 0, 0 }
 };
 
+static int usblp_wwait(struct usblp *usblp, int nonblock);
+static int usblp_wtest(struct usblp *usblp, int nonblock);
+static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock);
+static int usblp_rtest(struct usblp *usblp, int nonblock);
+static int usblp_submit_read(struct usblp *usblp);
 static int usblp_select_alts(struct usblp *usblp);
 static int usblp_set_protocol(struct usblp *usblp, int protocol);
 static int usblp_cache_device_id_string(struct usblp *usblp);
@@ -279,33 +290,47 @@
 {
 	struct usblp *usblp = urb->context;
 
-	if (unlikely(!usblp || !usblp->dev || !usblp->used))
-		return;
-
-	if (unlikely(!usblp->present))
-		goto unplug;
-	if (unlikely(urb->status))
-		warn("usblp%d: nonzero read/write bulk status received: %d",
-			usblp->minor, urb->status);
+	if (usblp->present && usblp->used) {
+		if (urb->status)
+			printk(KERN_WARNING "usblp%d: "
+			    "nonzero read bulk status received: %d\n",
+			    usblp->minor, urb->status);
+	}
+	spin_lock(&usblp->lock);
+	if (urb->status < 0)
+		usblp->rstatus = urb->status;
+	else
+		usblp->rstatus = urb->actual_length;
 	usblp->rcomplete = 1;
-unplug:
-	wake_up_interruptible(&usblp->wait);
+	wake_up(&usblp->rwait);
+	spin_unlock(&usblp->lock);
+
+	usb_free_urb(urb);
 }
 
 static void usblp_bulk_write(struct urb *urb)
 {
 	struct usblp *usblp = urb->context;
 
-	if (unlikely(!usblp || !usblp->dev || !usblp->used))
-		return;
-	if (unlikely(!usblp->present))
-		goto unplug;
-	if (unlikely(urb->status))
-		warn("usblp%d: nonzero read/write bulk status received: %d",
-			usblp->minor, urb->status);
+	if (usblp->present && usblp->used) {
+		if (urb->status)
+			printk(KERN_WARNING "usblp%d: "
+			    "nonzero write bulk status received: %d\n",
+			    usblp->minor, urb->status);
+	}
+	spin_lock(&usblp->lock);
+	if (urb->status < 0)
+		usblp->wstatus = urb->status;
+	else
+		usblp->wstatus = urb->actual_length;
 	usblp->wcomplete = 1;
-unplug:
-	wake_up_interruptible(&usblp->wait);
+	wake_up(&usblp->wwait);
+	spin_unlock(&usblp->lock);
+
+	/* XXX Use usb_setup_bulk_urb when available. Talk to Marcel. */
+	kfree(urb->transfer_buffer);
+	urb->transfer_buffer = NULL;	/* Not refcounted, so to be safe... */
+	usb_free_urb(urb);
 }
 
 /*
@@ -322,7 +347,8 @@
 	error = usblp_read_status (usblp, usblp->statusbuf);
 	if (error < 0) {
 		if (printk_ratelimit())
-			err("usblp%d: error %d reading printer status",
+			printk(KERN_ERR
+				"usblp%d: error %d reading printer status\n",
 				usblp->minor, error);
 		return 0;
 	}
@@ -336,8 +362,10 @@
 	if (~status & LP_PSELECD)
 		newerr = 2;
 
-	if (newerr != err)
-		info("usblp%d: %s", usblp->minor, usblp_messages[newerr]);
+	if (newerr != err) {
+		printk(KERN_INFO "usblp%d: %s\n",
+		   usblp->minor, usblp_messages[newerr]);
+	}
 
 	return newerr;
 }
@@ -345,12 +373,9 @@
 static int handle_bidir (struct usblp *usblp)
 {
 	if (usblp->bidir && usblp->used && !usblp->sleeping) {
-		usblp->readcount = 0;
-		usblp->readurb->dev = usblp->dev;
-		if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0)
+		if (usblp_submit_read(usblp) < 0)
 			return -EIO;
 	}
-
 	return 0;
 }
 
@@ -403,11 +428,9 @@
 	usblp->used = 1;
 	file->private_data = usblp;
 
-	usblp->writeurb->transfer_buffer_length = 0;
 	usblp->wcomplete = 1; /* we begin writeable */
+	usblp->wstatus = 0;
 	usblp->rcomplete = 0;
-	usblp->writeurb->status = 0;
-	usblp->readurb->status = 0;
 
 	if (handle_bidir(usblp) < 0) {
 		usblp->used = 0;
@@ -421,20 +444,17 @@
 
 static void usblp_cleanup (struct usblp *usblp)
 {
-	info("usblp%d: removed", usblp->minor);
+	printk(KERN_INFO "usblp%d: removed\n", usblp->minor);
 
+	kfree(usblp->readbuf);
 	kfree (usblp->device_id_string);
 	kfree (usblp->statusbuf);
-	usb_free_urb(usblp->writeurb);
-	usb_free_urb(usblp->readurb);
 	kfree (usblp);
 }
 
 static void usblp_unlink_urbs(struct usblp *usblp)
 {
-	usb_kill_urb(usblp->writeurb);
-	if (usblp->bidir)
-		usb_kill_urb(usblp->readurb);
+	usb_kill_anchored_urbs(&usblp->urbs);
 }
 
 static int usblp_release(struct inode *inode, struct file *file)
@@ -455,10 +475,18 @@
 /* No kernel lock - fine */
 static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait)
 {
+	int ret;
+	unsigned long flags;
+
 	struct usblp *usblp = file->private_data;
-	poll_wait(file, &usblp->wait, wait);
- 	return ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN  | POLLRDNORM)
+	/* Should we check file->f_mode & FMODE_WRITE before poll_wait()? */
+	poll_wait(file, &usblp->rwait, wait);
+	poll_wait(file, &usblp->wwait, wait);
+	spin_lock_irqsave(&usblp->lock, flags);
+	ret = ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN  | POLLRDNORM)
  			       | (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM);
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	return ret;
 }
 
 static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -632,10 +660,11 @@
 		switch (cmd) {
 
 			case LPGETSTATUS:
-				if (usblp_read_status(usblp, usblp->statusbuf)) {
+				if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
 					if (printk_ratelimit())
-						err("usblp%d: failed reading printer status",
-							usblp->minor);
+						printk(KERN_ERR "usblp%d:"
+						    "failed reading printer status (%d)\n",
+						    usblp->minor, retval);
 					retval = -EIO;
 					goto done;
 				}
@@ -656,161 +685,137 @@
 static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
 {
 	struct usblp *usblp = file->private_data;
-	int timeout, intr, rv, err = 0, transfer_length = 0;
-	size_t writecount = 0;
+	char *writebuf;
+	struct urb *writeurb;
+	int rv;
+	int transfer_length;
+	ssize_t writecount = 0;
+
+	if (mutex_lock_interruptible(&usblp->wmut)) {
+		rv = -EINTR;
+		goto raise_biglock;
+	}
+	if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0)
+		goto raise_wait;
 
 	while (writecount < count) {
-		if (!usblp->wcomplete) {
-			barrier();
-			if (file->f_flags & O_NONBLOCK) {
-				writecount += transfer_length;
-				return writecount ? writecount : -EAGAIN;
-			}
-
-			timeout = USBLP_WRITE_TIMEOUT;
-
-			rv = wait_event_interruptible_timeout(usblp->wait, usblp->wcomplete || !usblp->present , timeout);
-			if (rv < 0)
-				return writecount ? writecount : -EINTR;
-		}
-		intr = mutex_lock_interruptible (&usblp->mut);
-		if (intr)
-			return writecount ? writecount : -EINTR;
-		if (!usblp->present) {
-			mutex_unlock (&usblp->mut);
-			return -ENODEV;
-		}
-
-		if (usblp->sleeping) {
-			mutex_unlock (&usblp->mut);
-			return writecount ? writecount : -ENODEV;
-		}
-
-		if (usblp->writeurb->status != 0) {
-			if (usblp->quirks & USBLP_QUIRK_BIDIR) {
-				if (!usblp->wcomplete)
-					err("usblp%d: error %d writing to printer",
-						usblp->minor, usblp->writeurb->status);
-				err = usblp->writeurb->status;
-			} else
-				err = usblp_check_status(usblp, err);
-			mutex_unlock (&usblp->mut);
-
-			/* if the fault was due to disconnect, let khubd's
-			 * call to usblp_disconnect() grab usblp->mut ...
-			 */
-			schedule ();
-			continue;
-		}
-
-		/* We must increment writecount here, and not at the
-		 * end of the loop. Otherwise, the final loop iteration may
-		 * be skipped, leading to incomplete printer output.
+		/*
+		 * Step 1: Submit next block.
 		 */
-		writecount += transfer_length;
-		if (writecount == count) {
-			mutex_unlock(&usblp->mut);
-			break;
-		}
-
-		transfer_length=(count - writecount);
-		if (transfer_length > USBLP_BUF_SIZE)
+		if ((transfer_length = count - writecount) > USBLP_BUF_SIZE)
 			transfer_length = USBLP_BUF_SIZE;
 
-		usblp->writeurb->transfer_buffer_length = transfer_length;
+		rv = -ENOMEM;
+		if ((writebuf = kmalloc(USBLP_BUF_SIZE, GFP_KERNEL)) == NULL)
+			goto raise_buf;
+		if ((writeurb = usb_alloc_urb(0, GFP_KERNEL)) == NULL)
+			goto raise_urb;
+		usb_fill_bulk_urb(writeurb, usblp->dev,
+			usb_sndbulkpipe(usblp->dev,
+			  usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress),
+			writebuf, transfer_length, usblp_bulk_write, usblp);
+		usb_anchor_urb(writeurb, &usblp->urbs);
 
-		if (copy_from_user(usblp->writeurb->transfer_buffer, 
+		if (copy_from_user(writebuf,
 				   buffer + writecount, transfer_length)) {
-			mutex_unlock(&usblp->mut);
-			return writecount ? writecount : -EFAULT;
+			rv = -EFAULT;
+			goto raise_badaddr;
 		}
 
-		usblp->writeurb->dev = usblp->dev;
+		spin_lock_irq(&usblp->lock);
 		usblp->wcomplete = 0;
-		err = usb_submit_urb(usblp->writeurb, GFP_KERNEL);
-		if (err) {
+		spin_unlock_irq(&usblp->lock);
+		if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
+			usblp->wstatus = 0;
+			spin_lock_irq(&usblp->lock);
 			usblp->wcomplete = 1;
-			if (err != -ENOMEM)
-				count = -EIO;
-			else
-				count = writecount ? writecount : -ENOMEM;
-			mutex_unlock (&usblp->mut);
-			break;
+			wake_up(&usblp->wwait);
+			spin_unlock_irq(&usblp->lock);
+			if (rv != -ENOMEM)
+				rv = -EIO;
+			goto raise_submit;
 		}
-		mutex_unlock (&usblp->mut);
+
+		/*
+		 * Step 2: Wait for transfer to end, collect results.
+		 */
+		rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK));
+		if (rv < 0) {
+			/*
+			 * If interrupted, we simply leave the URB to dangle,
+			 * so the ->release will call usb_kill_urb().
+			 */
+			goto collect_error;
+		}
+
+		if (usblp->wstatus < 0) {
+			usblp_check_status(usblp, 0);
+			rv = -EIO;
+			goto collect_error;
+		}
+		/*
+		 * This is critical: it must be our URB, not other writer's.
+		 * The wmut exists mainly to cover us here.
+		 */
+		writecount += usblp->wstatus;
 	}
 
-	return count;
+	mutex_unlock(&usblp->wmut);
+	return writecount;
+
+raise_submit:
+raise_badaddr:
+	usb_unanchor_urb(writeurb);
+	usb_free_urb(writeurb);
+raise_urb:
+	kfree(writebuf);
+raise_buf:
+raise_wait:
+collect_error:		/* Out of raise sequence */
+	mutex_unlock(&usblp->wmut);
+raise_biglock:
+	return writecount ? writecount : rv;
 }
 
-static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+/*
+ * Notice that we fail to restart in a few cases: on EFAULT, on restart
+ * error, etc. This is the historical behaviour. In all such cases we return
+ * EIO, and applications loop in order to get the new read going.
+ */
+static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, loff_t *ppos)
 {
 	struct usblp *usblp = file->private_data;
-	int rv, intr;
+	ssize_t count;
+	ssize_t avail;
+	int rv;
 
 	if (!usblp->bidir)
 		return -EINVAL;
 
-	intr = mutex_lock_interruptible (&usblp->mut);
-	if (intr)
-		return -EINTR;
-	if (!usblp->present) {
-		count = -ENODEV;
-		goto done;
-	}
+	rv = usblp_rwait_and_lock(usblp, !!(file->f_flags & O_NONBLOCK));
+	if (rv < 0)
+		return rv;
 
-	if (!usblp->rcomplete) {
-		barrier();
-
-		if (file->f_flags & O_NONBLOCK) {
-			count = -EAGAIN;
-			goto done;
-		}
-		mutex_unlock(&usblp->mut);
-		rv = wait_event_interruptible(usblp->wait, usblp->rcomplete || !usblp->present);
-		mutex_lock(&usblp->mut);
-		if (rv < 0) {
-			count = -EINTR;
-			goto done;
-		}
-	}
-
-	if (!usblp->present) {
-		count = -ENODEV;
-		goto done;
-	}
-
-	if (usblp->sleeping) {
-		count = -ENODEV;
-		goto done;
-	}
-
-	if (usblp->readurb->status) {
-		err("usblp%d: error %d reading from printer",
-			usblp->minor, usblp->readurb->status);
-		usblp->readurb->dev = usblp->dev;
- 		usblp->readcount = 0;
-		usblp->rcomplete = 0;
-		if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0)
-			dbg("error submitting urb");
+	if ((avail = usblp->rstatus) < 0) {
+		printk(KERN_ERR "usblp%d: error %d reading from printer\n",
+		    usblp->minor, (int)avail);
+		usblp_submit_read(usblp);
 		count = -EIO;
 		goto done;
 	}
 
-	count = count < usblp->readurb->actual_length - usblp->readcount ?
-		count :	usblp->readurb->actual_length - usblp->readcount;
-
-	if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) {
+	count = len < avail - usblp->readcount ? len : avail - usblp->readcount;
+	if (count != 0 &&
+	    copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) {
 		count = -EFAULT;
 		goto done;
 	}
 
-	if ((usblp->readcount += count) == usblp->readurb->actual_length) {
-		usblp->readcount = 0;
-		usblp->readurb->dev = usblp->dev;
-		usblp->rcomplete = 0;
-		if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) {
-			count = -EIO;
+	if ((usblp->readcount += count) == avail) {
+		if (usblp_submit_read(usblp) < 0) {
+			/* We don't want to leak USB return codes into errno. */
+			if (count == 0)
+				count = -EIO;
 			goto done;
 		}
 	}
@@ -821,6 +826,165 @@
 }
 
 /*
+ * Wait for the write path to come idle.
+ * This is called under the ->wmut, so the idle path stays idle.
+ *
+ * Our write path has a peculiar property: it does not buffer like a tty,
+ * but waits for the write to succeed. This allows our ->release to bug out
+ * without waiting for writes to drain. But it obviously does not work
+ * when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use
+ * select(2) or poll(2) to wait for the buffer to drain before closing.
+ * Alternatively, set blocking mode with fcntl and issue a zero-size write.
+ *
+ * Old v0.13 code had a non-functional timeout for wait_event(). Someone forgot
+ * to check the return code for timeout expiration, so it had no effect.
+ * Apparently, it was intended to check for error conditons, such as out
+ * of paper. It is going to return when we settle things with CUPS. XXX
+ */
+static int usblp_wwait(struct usblp *usblp, int nonblock)
+{
+	DECLARE_WAITQUEUE(waita, current);
+	int rc;
+
+	add_wait_queue(&usblp->wwait, &waita);
+	for (;;) {
+		if (mutex_lock_interruptible(&usblp->mut)) {
+			rc = -EINTR;
+			break;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		if ((rc = usblp_wtest(usblp, nonblock)) < 0) {
+			mutex_unlock(&usblp->mut);
+			break;
+		}
+		mutex_unlock(&usblp->mut);
+		if (rc == 0)
+			break;
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&usblp->wwait, &waita);
+	return rc;
+}
+
+static int usblp_wtest(struct usblp *usblp, int nonblock)
+{
+	unsigned long flags;
+
+	if (!usblp->present)
+		return -ENODEV;
+	if (signal_pending(current))
+		return -EINTR;
+	spin_lock_irqsave(&usblp->lock, flags);
+	if (usblp->wcomplete) {
+		spin_unlock_irqrestore(&usblp->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	if (usblp->sleeping)
+		return -ENODEV;
+	if (nonblock)
+		return -EAGAIN;
+	return 1;
+}
+
+/*
+ * Wait for read bytes to become available. This probably should have been
+ * called usblp_r_lock_and_wait(), because we lock first. But it's a traditional
+ * name for functions which lock and return.
+ *
+ * We do not use wait_event_interruptible because it makes locking iffy.
+ */
+static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock)
+{
+	DECLARE_WAITQUEUE(waita, current);
+	int rc;
+
+	add_wait_queue(&usblp->rwait, &waita);
+	for (;;) {
+		if (mutex_lock_interruptible(&usblp->mut)) {
+			rc = -EINTR;
+			break;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		if ((rc = usblp_rtest(usblp, nonblock)) < 0) {
+			mutex_unlock(&usblp->mut);
+			break;
+		}
+		if (rc == 0)	/* Keep it locked */
+			break;
+		mutex_unlock(&usblp->mut);
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&usblp->rwait, &waita);
+	return rc;
+}
+
+static int usblp_rtest(struct usblp *usblp, int nonblock)
+{
+	unsigned long flags;
+
+	if (!usblp->present)
+		return -ENODEV;
+	if (signal_pending(current))
+		return -EINTR;
+	spin_lock_irqsave(&usblp->lock, flags);
+	if (usblp->rcomplete) {
+		spin_unlock_irqrestore(&usblp->lock, flags);
+		return 0;
+	}
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	if (usblp->sleeping)
+		return -ENODEV;
+	if (nonblock)
+		return -EAGAIN;
+	return 1;
+}
+
+/*
+ * Please check ->bidir and other such things outside for now.
+ */
+static int usblp_submit_read(struct usblp *usblp)
+{
+	struct urb *urb;
+	unsigned long flags;
+	int rc;
+
+	rc = -ENOMEM;
+	if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL)
+		goto raise_urb;
+
+	usb_fill_bulk_urb(urb, usblp->dev,
+		usb_rcvbulkpipe(usblp->dev,
+		  usblp->protocol[usblp->current_protocol].epread->bEndpointAddress),
+		usblp->readbuf, USBLP_BUF_SIZE_IN,
+		usblp_bulk_read, usblp);
+	usb_anchor_urb(urb, &usblp->urbs);
+
+	spin_lock_irqsave(&usblp->lock, flags);
+	usblp->readcount = 0; /* XXX Why here? */
+	usblp->rcomplete = 0;
+	spin_unlock_irqrestore(&usblp->lock, flags);
+	if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
+		dbg("error submitting urb (%d)", rc);
+		spin_lock_irqsave(&usblp->lock, flags);
+		usblp->rstatus = rc;
+		usblp->rcomplete = 1;
+		spin_unlock_irqrestore(&usblp->lock, flags);
+		goto raise_submit;
+	}
+
+	return 0;
+
+raise_submit:
+	usb_unanchor_urb(urb);
+	usb_free_urb(urb);
+raise_urb:
+	return rc;
+}
+
+/*
  * Checks for printers that have quirks, such as requiring unidirectional
  * communication but reporting bidirectional; currently some HP printers
  * have this flaw (HP 810, 880, 895, etc.), or needing an init string
@@ -891,55 +1055,41 @@
 	/* Malloc and start initializing usblp structure so we can use it
 	 * directly. */
 	if (!(usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL))) {
-		err("out of memory for usblp");
+		retval = -ENOMEM;
 		goto abort;
 	}
 	usblp->dev = dev;
+	mutex_init(&usblp->wmut);
 	mutex_init (&usblp->mut);
-	init_waitqueue_head(&usblp->wait);
+	spin_lock_init(&usblp->lock);
+	init_waitqueue_head(&usblp->rwait);
+	init_waitqueue_head(&usblp->wwait);
+	init_usb_anchor(&usblp->urbs);
 	usblp->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
 	usblp->intf = intf;
 
-	usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!usblp->writeurb) {
-		err("out of memory");
-		goto abort;
-	}
-	usblp->readurb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!usblp->readurb) {
-		err("out of memory");
-		goto abort;
-	}
-
 	/* Malloc device ID string buffer to the largest expected length,
 	 * since we can re-query it on an ioctl and a dynamic string
 	 * could change in length. */
 	if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) {
-		err("out of memory for device_id_string");
+		retval = -ENOMEM;
 		goto abort;
 	}
 
-	usblp->writebuf = usblp->readbuf = NULL;
-	usblp->writeurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
-	usblp->readurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
-	/* Malloc write & read buffers.  We somewhat wastefully
+	/*
+	 * Allocate read buffer. We somewhat wastefully
 	 * malloc both regardless of bidirectionality, because the
-	 * alternate setting can be changed later via an ioctl. */
-	if (!(usblp->writebuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE,
-				GFP_KERNEL, &usblp->writeurb->transfer_dma))) {
-		err("out of memory for write buf");
-		goto abort;
-	}
-	if (!(usblp->readbuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE,
-				GFP_KERNEL, &usblp->readurb->transfer_dma))) {
-		err("out of memory for read buf");
+	 * alternate setting can be changed later via an ioctl.
+	 */
+	if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) {
+		retval = -ENOMEM;
 		goto abort;
 	}
 
 	/* Allocate buffer for printer status */
 	usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL);
 	if (!usblp->statusbuf) {
-		err("out of memory for statusbuf");
+		retval = -ENOMEM;
 		goto abort;
 	}
 
@@ -954,12 +1104,15 @@
 		dbg("incompatible printer-class device 0x%4.4X/0x%4.4X",
 			le16_to_cpu(dev->descriptor.idVendor),
 			le16_to_cpu(dev->descriptor.idProduct));
+		retval = -ENODEV;
 		goto abort;
 	}
 
 	/* Setup the selected alternate setting and endpoints. */
-	if (usblp_set_protocol(usblp, protocol) < 0)
+	if (usblp_set_protocol(usblp, protocol) < 0) {
+		retval = -ENODEV;	/* ->probe isn't ->ioctl */
 		goto abort;
+	}
 
 	/* Retrieve and store the device ID string. */
 	usblp_cache_device_id_string(usblp);
@@ -977,12 +1130,14 @@
 
 	retval = usb_register_dev(intf, &usblp_class);
 	if (retval) {
-		err("Not able to get a minor for this device.");
+		printk(KERN_ERR "usblp: Not able to get a minor"
+		    " (base %u, slice default): %d\n",
+		    USBLP_MINOR_BASE, retval);
 		goto abort_intfdata;
 	}
 	usblp->minor = intf->minor;
-	info("usblp%d: USB %sdirectional printer dev %d "
-		"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X",
+	printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d "
+		"if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n",
 		usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum,
 		usblp->ifnum,
 		usblp->protocol[usblp->current_protocol].alt_setting,
@@ -997,19 +1152,12 @@
 	device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
 abort:
 	if (usblp) {
-		if (usblp->writebuf)
-			usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
-				usblp->writebuf, usblp->writeurb->transfer_dma);
-		if (usblp->readbuf)
-			usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
-				usblp->readbuf, usblp->readurb->transfer_dma);
+		kfree(usblp->readbuf);
 		kfree(usblp->statusbuf);
 		kfree(usblp->device_id_string);
-		usb_free_urb(usblp->writeurb);
-		usb_free_urb(usblp->readurb);
 		kfree(usblp);
 	}
-	return -EIO;
+	return retval;
 }
 
 /*
@@ -1078,8 +1226,9 @@
 		if (ifd->desc.bInterfaceProtocol == 1) {
 			epread = NULL;
 		} else if (usblp->quirks & USBLP_QUIRK_BIDIR) {
-			info("Disabling reads from problem bidirectional "
-				"printer on usblp%d", usblp->minor);
+			printk(KERN_INFO "usblp%d: Disabling reads from "
+			    "problematic bidirectional printer\n",
+			    usblp->minor);
 			epread = NULL;
 		}
 
@@ -1119,25 +1268,12 @@
 		return -EINVAL;
 	r = usb_set_interface(usblp->dev, usblp->ifnum, alts);
 	if (r < 0) {
-		err("can't set desired altsetting %d on interface %d",
+		printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n",
 			alts, usblp->ifnum);
 		return r;
 	}
 
-	usb_fill_bulk_urb(usblp->writeurb, usblp->dev,
-		usb_sndbulkpipe(usblp->dev,
-		  usblp->protocol[protocol].epwrite->bEndpointAddress),
-		usblp->writebuf, 0,
-		usblp_bulk_write, usblp);
-
 	usblp->bidir = (usblp->protocol[protocol].epread != NULL);
-	if (usblp->bidir)
-		usb_fill_bulk_urb(usblp->readurb, usblp->dev,
-			usb_rcvbulkpipe(usblp->dev,
-			  usblp->protocol[protocol].epread->bEndpointAddress),
-			usblp->readbuf, USBLP_BUF_SIZE,
-			usblp_bulk_read, usblp);
-
 	usblp->current_protocol = protocol;
 	dbg("usblp%d set protocol %d", usblp->minor, protocol);
 	return 0;
@@ -1190,13 +1326,11 @@
 	mutex_lock (&usblp_mutex);
 	mutex_lock (&usblp->mut);
 	usblp->present = 0;
+	wake_up(&usblp->wwait);
+	wake_up(&usblp->rwait);
 	usb_set_intfdata (intf, NULL);
 
 	usblp_unlink_urbs(usblp);
-	usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
-			usblp->writebuf, usblp->writeurb->transfer_dma);
-	usb_buffer_free (usblp->dev, USBLP_BUF_SIZE,
-			usblp->readbuf, usblp->readurb->transfer_dma);
 	mutex_unlock (&usblp->mut);
 
 	if (!usblp->used)
@@ -1211,6 +1345,11 @@
 	/* we take no more IO */
 	usblp->sleeping = 1;
 	usblp_unlink_urbs(usblp);
+#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */
+	/* not strictly necessary, but just in case */
+	wake_up(&usblp->wwait);
+	wake_up(&usblp->rwait);
+#endif
 
 	return 0;
 }
@@ -1251,12 +1390,7 @@
 
 static int __init usblp_init(void)
 {
-	int retval;
-	retval = usb_register(&usblp_driver);
-	if (!retval)
-		info(DRIVER_VERSION ": " DRIVER_DESC);
-
-	return retval;
+	return usb_register(&usblp_driver);
 }
 
 static void __exit usblp_exit(void)
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 346fc03..97b09f28 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -86,6 +86,31 @@
 
 	  If you are unsure about this, say N here.
 
+config USB_PERSIST
+	bool "USB device persistence during system suspend (DANGEROUS)"
+	depends on USB && PM && EXPERIMENTAL
+	default n
+	help
+
+	  If you say Y here and enable the "power/persist" attribute
+	  for a USB device, the device's data structures will remain
+	  persistent across system suspend, even if the USB bus loses
+	  power.  (This includes hibernation, also known as swsusp or
+	  suspend-to-disk.)  The devices will reappear as if by magic
+	  when the system wakes up, with no need to unmount USB
+	  filesystems, rmmod host-controller drivers, or do anything
+	  else.
+
+	  	WARNING: This option can be dangerous!
+
+	  If a USB device is replaced by another of the same type while
+	  the system is asleep, there's a good chance the kernel won't
+	  detect the change.  Likewise if the media in a USB storage
+	  device is replaced.  When this happens it's almost certain to
+	  cause data corruption and maybe even crash your system.
+
+	  If you are unsure, say N here.
+
 config USB_OTG
 	bool
 	depends on USB && EXPERIMENTAL
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index dd34823..cb69aa1 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -85,15 +85,21 @@
 	memcpy(&endpoint->desc, d, n);
 	INIT_LIST_HEAD(&endpoint->urb_list);
 
-	/* If the bInterval value is outside the legal range,
-	 * set it to a default value: 32 ms */
+	/* Fix up bInterval values outside the legal range. Use 32 ms if no
+	 * proper value can be guessed. */
 	i = 0;		/* i = min, j = max, n = default */
 	j = 255;
 	if (usb_endpoint_xfer_int(d)) {
 		i = 1;
 		switch (to_usb_device(ddev)->speed) {
 		case USB_SPEED_HIGH:
-			n = 9;		/* 32 ms = 2^(9-1) uframes */
+			/* Many device manufacturers are using full-speed
+			 * bInterval values in high-speed interrupt endpoint
+			 * descriptors. Try to fix those and fall back to a
+			 * 32 ms default value otherwise. */
+			n = fls(d->bInterval*8);
+			if (n == 0)
+				n = 9;	/* 32 ms = 2^(9-1) uframes */
 			j = 16;
 			break;
 		default:		/* USB_SPEED_FULL or _LOW */
@@ -124,6 +130,21 @@
 		endpoint->desc.bInterval = n;
 	}
 
+	/* Some buggy low-speed devices have Bulk endpoints, which is
+	 * explicitly forbidden by the USB spec.  In an attempt to make
+	 * them usable, we will try treating them as Interrupt endpoints.
+	 */
+	if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&
+			usb_endpoint_xfer_bulk(d)) {
+		dev_warn(ddev, "config %d interface %d altsetting %d "
+		    "endpoint 0x%X is Bulk; changing to Interrupt\n",
+		    cfgno, inum, asnum, d->bEndpointAddress);
+		endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
+		endpoint->desc.bInterval = 1;
+		if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
+			endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
+	}
+
 	/* Skip over any Class Specific or Vendor Specific descriptors;
 	 * find the next endpoint or interface descriptor */
 	endpoint->extra = buffer;
@@ -274,6 +295,7 @@
 	struct usb_descriptor_header *header;
 	int len, retval;
 	u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
+	unsigned iad_num = 0;
 
 	memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
 	if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -351,6 +373,20 @@
 				++n;
 			}
 
+		} else if (header->bDescriptorType ==
+				USB_DT_INTERFACE_ASSOCIATION) {
+			if (iad_num == USB_MAXIADS) {
+				dev_warn(ddev, "found more Interface "
+					       "Association Descriptors "
+					       "than allocated for in "
+					       "configuration %d\n", cfgno);
+			} else {
+				config->intf_assoc[iad_num] =
+					(struct usb_interface_assoc_descriptor
+					*)header;
+				iad_num++;
+			}
+
 		} else if (header->bDescriptorType == USB_DT_DEVICE ||
 			    header->bDescriptorType == USB_DT_CONFIG)
 			dev_warn(ddev, "config %d contains an unexpected "
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 6753ca0..87c794d6 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -102,6 +102,10 @@
 /* C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
   "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
   
+static const char *format_iad =
+/* A:  FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
+  "A:  FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";
+
 static const char *format_iface =
 /* I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
   "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
@@ -146,6 +150,7 @@
 	{USB_CLASS_STILL_IMAGE,		"still"},
 	{USB_CLASS_CSCID,		"scard"},
 	{USB_CLASS_CONTENT_SEC,		"c-sec"},
+	{USB_CLASS_VIDEO,		"video"},
 	{-1,				"unk."}		/* leave as last */
 };
 
@@ -286,6 +291,21 @@
 	return start;
 }
 
+static char *usb_dump_iad_descriptor(char *start, char *end,
+	const struct usb_interface_assoc_descriptor *iad)
+{
+	if (start > end)
+		return start;
+	start += sprintf(start, format_iad,
+			 iad->bFirstInterface,
+			 iad->bInterfaceCount,
+			 iad->bFunctionClass,
+			 class_decode(iad->bFunctionClass),
+			 iad->bFunctionSubClass,
+			 iad->bFunctionProtocol);
+	return start;
+}
+
 /* TBD:
  * 0. TBDs
  * 1. marking active interface altsettings (code lists all, but should mark
@@ -322,6 +342,12 @@
 	if (!config)		/* getting these some in 2.3.7; none in 2.3.6 */
 		return start + sprintf(start, "(null Cfg. desc.)\n");
 	start = usb_dump_config_descriptor(start, end, &config->desc, active);
+	for (i = 0; i < USB_MAXIADS; i++) {
+		if (config->intf_assoc[i] == NULL)
+			break;
+		start = usb_dump_iad_descriptor(start, end,
+					config->intf_assoc[i]);
+	}
 	for (i = 0; i < config->desc.bNumInterfaces; i++) {
 		intfc = config->intf_cache[i];
 		interface = config->interface[i];
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 2619986..73c4936 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -24,10 +24,19 @@
 
 #include <linux/device.h>
 #include <linux/usb.h>
+#include <linux/usb/quirks.h>
 #include <linux/workqueue.h>
 #include "hcd.h"
 #include "usb.h"
 
+#define VERBOSE_DEBUG	0
+
+#if VERBOSE_DEBUG
+#define dev_vdbg	dev_dbg
+#else
+#define dev_vdbg(dev, fmt, args...)	do { } while (0)
+#endif
+
 #ifdef CONFIG_HOTPLUG
 
 /*
@@ -802,18 +811,17 @@
 			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) {
+	/* For devices that don't have a driver, we do a generic suspend. */
+	if (udev->dev.driver)
+		udriver = to_usb_device_driver(udev->dev.driver);
+	else {
 		udev->do_remote_wakeup = 0;
-		status = usb_port_suspend(udev);
-		goto done;
+		udriver = &usb_generic_driver;
 	}
-
-	udriver = to_usb_device_driver(udev->dev.driver);
 	status = udriver->suspend(udev, msg);
 
-done:
-	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ done:
+	dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
 	if (status == 0)
 		udev->dev.power.power_state.event = msg.event;
 	return status;
@@ -825,8 +833,9 @@
 	struct usb_device_driver	*udriver;
 	int				status = 0;
 
-	if (udev->state == USB_STATE_NOTATTACHED ||
-			udev->state != USB_STATE_SUSPENDED)
+	if (udev->state == USB_STATE_NOTATTACHED)
+		goto done;
+	if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
 		goto done;
 
 	/* Can't resume it if it doesn't have a driver. */
@@ -835,11 +844,14 @@
 		goto done;
 	}
 
+	if (udev->quirks & USB_QUIRK_RESET_RESUME)
+		udev->reset_resume = 1;
+
 	udriver = to_usb_device_driver(udev->dev.driver);
 	status = udriver->resume(udev);
 
-done:
-	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ done:
+	dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
 	if (status == 0) {
 		udev->autoresume_disabled = 0;
 		udev->dev.power.power_state.event = PM_EVENT_ON;
@@ -877,15 +889,13 @@
 		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;
+ done:
+	dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
 	return status;
 }
 
 /* Caller has locked intf's usb_device's pm_mutex */
-static int usb_resume_interface(struct usb_interface *intf)
+static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
 {
 	struct usb_driver	*driver;
 	int			status = 0;
@@ -905,23 +915,37 @@
 	}
 	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);
+	if (reset_resume) {
+		if (driver->reset_resume) {
+			status = driver->reset_resume(intf);
+			if (status)
+				dev_err(&intf->dev, "%s error %d\n",
+						"reset_resume", status);
+		} else {
+			// status = -EOPNOTSUPP;
+			dev_warn(&intf->dev, "no %s for driver %s?\n",
+					"reset_resume", driver->name);
+		}
 	} else {
-		dev_warn(&intf->dev, "no resume for driver %s?\n",
-				driver->name);
-		mark_active(intf);
+		if (driver->resume) {
+			status = driver->resume(intf);
+			if (status)
+				dev_err(&intf->dev, "%s error %d\n",
+						"resume", status);
+		} else {
+			// status = -EOPNOTSUPP;
+			dev_warn(&intf->dev, "no %s for driver %s?\n",
+					"resume", driver->name);
+		}
 	}
 
 done:
-	// dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+	dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
 	if (status == 0)
-		intf->dev.power.power_state.event = PM_EVENT_ON;
+		mark_active(intf);
+
+	/* FIXME: Unbind the driver and reprobe if the resume failed
+	 * (not possible if auto_pm is set) */
 	return status;
 }
 
@@ -958,6 +982,18 @@
 						"for autosuspend\n");
 				return -EOPNOTSUPP;
 			}
+
+			/* Don't allow autosuspend if the device will need
+			 * a reset-resume and any of its interface drivers
+			 * doesn't include support.
+			 */
+			if (udev->quirks & USB_QUIRK_RESET_RESUME) {
+				struct usb_driver *driver;
+
+				driver = to_usb_driver(intf->dev.driver);
+				if (!driver->reset_resume)
+					return -EOPNOTSUPP;
+			}
 		}
 	}
 
@@ -974,7 +1010,7 @@
 			 * or for the past.
 			 */
 			queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-					suspend_time - jiffies);
+				round_jiffies_relative(suspend_time - jiffies));
 			}
 		return -EAGAIN;
 	}
@@ -1054,14 +1090,21 @@
 				break;
 		}
 	}
-	if (status == 0)
+	if (status == 0) {
+
+		/* Non-root devices don't need to do anything for FREEZE
+		 * or PRETHAW. */
+		if (udev->parent && (msg.event == PM_EVENT_FREEZE ||
+				msg.event == PM_EVENT_PRETHAW))
+			goto done;
 		status = usb_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];
-			usb_resume_interface(intf);
+			usb_resume_interface(intf, 0);
 		}
 
 		/* Try another autosuspend when the interfaces aren't busy */
@@ -1076,7 +1119,7 @@
 	}
 
  done:
-	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
 	return status;
 }
 
@@ -1131,7 +1174,8 @@
 			status = usb_autoresume_device(parent);
 			if (status == 0) {
 				status = usb_resume_device(udev);
-				if (status) {
+				if (status || udev->state ==
+						USB_STATE_NOTATTACHED) {
 					usb_autosuspend_device(parent);
 
 					/* It's possible usb_resume_device()
@@ -1152,28 +1196,25 @@
 			/* 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;
-			else
-				status = usb_resume_device(udev);
+			status = usb_resume_device(udev);
  		}
 	} else {
 
-		/* Needed only for setting udev->dev.power.power_state.event
-		 * and for possible debugging message. */
+		/* Needed for setting udev->dev.power.power_state.event,
+		 * for possible debugging message, and for reset_resume. */
 		status = usb_resume_device(udev);
 	}
 
 	if (status == 0 && udev->actconfig) {
 		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
 			intf = udev->actconfig->interface[i];
-			usb_resume_interface(intf);
+			usb_resume_interface(intf, udev->reset_resume);
 		}
 	}
 
  done:
-	// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+	udev->reset_resume = 0;
 	return status;
 }
 
@@ -1240,8 +1281,8 @@
 	int	status;
 
 	status = usb_autopm_do_device(udev, -1);
-	// dev_dbg(&udev->dev, "%s: cnt %d\n",
-	//		__FUNCTION__, udev->pm_usage_cnt);
+	dev_vdbg(&udev->dev, "%s: cnt %d\n",
+			__FUNCTION__, udev->pm_usage_cnt);
 }
 
 /**
@@ -1260,8 +1301,8 @@
 void usb_try_autosuspend_device(struct usb_device *udev)
 {
 	usb_autopm_do_device(udev, 0);
-	// dev_dbg(&udev->dev, "%s: cnt %d\n",
-	// 		__FUNCTION__, udev->pm_usage_cnt);
+	dev_vdbg(&udev->dev, "%s: cnt %d\n",
+			__FUNCTION__, udev->pm_usage_cnt);
 }
 
 /**
@@ -1288,8 +1329,8 @@
 	int	status;
 
 	status = usb_autopm_do_device(udev, 1);
-	// dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
-	//		__FUNCTION__, status, udev->pm_usage_cnt);
+	dev_vdbg(&udev->dev, "%s: status %d cnt %d\n",
+			__FUNCTION__, status, udev->pm_usage_cnt);
 	return status;
 }
 
@@ -1361,8 +1402,8 @@
 	int	status;
 
 	status = usb_autopm_do_interface(intf, -1);
-	// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
-	//		__FUNCTION__, status, intf->pm_usage_cnt);
+	dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
+			__FUNCTION__, status, intf->pm_usage_cnt);
 }
 EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
 
@@ -1405,8 +1446,8 @@
 	int	status;
 
 	status = usb_autopm_do_interface(intf, 1);
-	// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
-	//		__FUNCTION__, status, intf->pm_usage_cnt);
+	dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
+			__FUNCTION__, status, intf->pm_usage_cnt);
 	return status;
 }
 EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
@@ -1427,8 +1468,8 @@
 	int	status;
 
 	status = usb_autopm_do_interface(intf, 0);
-	// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
-	//		__FUNCTION__, status, intf->pm_usage_cnt);
+	dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
+			__FUNCTION__, status, intf->pm_usage_cnt);
 	return status;
 }
 EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
@@ -1508,8 +1549,15 @@
 	if (!is_usb_device(dev))	/* Ignore PM for interfaces */
 		return 0;
 	udev = to_usb_device(dev);
-	if (udev->autoresume_disabled)
-		return -EPERM;
+
+	/* If autoresume is disabled then we also want to prevent resume
+	 * during system wakeup.  However, a "persistent-device" reset-resume
+	 * after power loss counts as a wakeup event.  So allow a
+	 * reset-resume to occur if remote wakeup is enabled. */
+	if (udev->autoresume_disabled) {
+		if (!(udev->reset_resume && udev->do_remote_wakeup))
+			return -EPERM;
+	}
 	return usb_external_resume_device(udev);
 }
 
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 01c857a..5d860bc 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -16,15 +16,15 @@
  */
 
 #include <linux/module.h>
-#include <linux/spinlock.h>
 #include <linux/errno.h>
+#include <linux/rwsem.h>
 #include <linux/usb.h>
 
 #include "usb.h"
 
 #define MAX_USB_MINORS	256
 static const struct file_operations *usb_minors[MAX_USB_MINORS];
-static DEFINE_SPINLOCK(minor_lock);
+static DECLARE_RWSEM(minor_rwsem);
 
 static int usb_open(struct inode * inode, struct file * file)
 {
@@ -33,14 +33,11 @@
 	int err = -ENODEV;
 	const struct file_operations *old_fops, *new_fops = NULL;
 
-	spin_lock (&minor_lock);
+	down_read(&minor_rwsem);
 	c = usb_minors[minor];
 
-	if (!c || !(new_fops = fops_get(c))) {
-		spin_unlock(&minor_lock);
-		return err;
-	}
-	spin_unlock(&minor_lock);
+	if (!c || !(new_fops = fops_get(c)))
+		goto done;
 
 	old_fops = file->f_op;
 	file->f_op = new_fops;
@@ -52,6 +49,8 @@
 		file->f_op = fops_get(old_fops);
 	}
 	fops_put(old_fops);
+ done:
+	up_read(&minor_rwsem);
 	return err;
 }
 
@@ -166,7 +165,7 @@
 	if (class_driver->fops == NULL)
 		goto exit;
 
-	spin_lock (&minor_lock);
+	down_write(&minor_rwsem);
 	for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
 		if (usb_minors[minor])
 			continue;
@@ -176,7 +175,7 @@
 		retval = 0;
 		break;
 	}
-	spin_unlock (&minor_lock);
+	up_write(&minor_rwsem);
 
 	if (retval)
 		goto exit;
@@ -197,9 +196,9 @@
 	intf->usb_dev = device_create(usb_class->class, &intf->dev,
 				      MKDEV(USB_MAJOR, minor), "%s", temp);
 	if (IS_ERR(intf->usb_dev)) {
-		spin_lock (&minor_lock);
+		down_write(&minor_rwsem);
 		usb_minors[intf->minor] = NULL;
-		spin_unlock (&minor_lock);
+		up_write(&minor_rwsem);
 		retval = PTR_ERR(intf->usb_dev);
 	}
 exit:
@@ -236,9 +235,9 @@
 
 	dbg ("removing %d minor", intf->minor);
 
-	spin_lock (&minor_lock);
+	down_write(&minor_rwsem);
 	usb_minors[intf->minor] = NULL;
-	spin_unlock (&minor_lock);
+	up_write(&minor_rwsem);
 
 	snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
 	device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
@@ -247,5 +246,3 @@
 	destroy_usb_class();
 }
 EXPORT_SYMBOL(usb_deregister_dev);
-
-
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 9bbcb20..b2fc2b1 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -19,6 +19,7 @@
 
 #include <linux/usb.h>
 #include "usb.h"
+#include "hcd.h"
 
 static inline const char *plural(int n)
 {
@@ -193,16 +194,34 @@
 
 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).
+	int rc;
+
+	/* Normal USB devices suspend through their upstream port.
+	 * Root hubs don't have upstream ports to suspend,
+	 * so we have to shut down their downstream HC-to-USB
+	 * interfaces manually by doing a bus (or "global") suspend.
 	 */
-	return usb_port_suspend(udev);
+	if (!udev->parent)
+		rc = hcd_bus_suspend(udev);
+	else
+		rc = usb_port_suspend(udev);
+	return rc;
 }
 
 static int generic_resume(struct usb_device *udev)
 {
-	return usb_port_resume(udev);
+	int rc;
+
+	/* Normal USB devices resume/reset through their upstream port.
+	 * Root hubs don't have upstream ports to resume or reset,
+	 * so we have to start up their downstream HC-to-USB
+	 * interfaces manually by doing a bus (or "global") resume.
+	 */
+	if (!udev->parent)
+		rc = hcd_bus_resume(udev);
+	else
+		rc = usb_port_resume(udev);
+	return rc;
 }
 
 #endif	/* CONFIG_PM */
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index edf4300..5cf6d5f 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -207,7 +207,8 @@
 	 * We must ignore the FREEZE vs SUSPEND distinction here, because
 	 * otherwise the swsusp will save (and restore) garbage state.
 	 */
-	if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON)
+	if (!(hcd->state == HC_STATE_SUSPENDED ||
+			hcd->state == HC_STATE_HALT))
 		return -EBUSY;
 
 	if (hcd->driver->suspend) {
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 8969e42..963520f 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -582,10 +582,12 @@
 	}
 
 	/* The USB 2.0 spec says 256 ms.  This is close enough and won't
-	 * exceed that limit if HZ is 100. */
+	 * exceed that limit if HZ is 100. The math is more clunky than
+	 * maybe expected, this is to make sure that all timers for USB devices
+	 * fire at the same time to give the CPU a break inbetween */
 	if (hcd->uses_new_polling ? hcd->poll_rh :
 			(length == 0 && hcd->status_urb != NULL))
-		mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250));
+		mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
 }
 EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
 
@@ -614,8 +616,8 @@
 		urb->hcpriv = hcd;	/* indicate it's queued */
 
 		if (!hcd->uses_new_polling)
-			mod_timer (&hcd->rh_timer, jiffies +
-					msecs_to_jiffies(250));
+			mod_timer (&hcd->rh_timer,
+				(jiffies/(HZ/4) + 1) * (HZ/4));
 
 		/* If a status change has already occurred, report it ASAP */
 		else if (hcd->poll_pending)
@@ -901,17 +903,32 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void urb_unlink (struct urb *urb)
+static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
 {
 	unsigned long		flags;
+	int at_root_hub = (urb->dev == hcd->self.root_hub);
 
 	/* clear all state linking urb to this dev (and hcd) */
-
 	spin_lock_irqsave (&hcd_data_lock, flags);
 	list_del_init (&urb->urb_list);
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
-}
 
+	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,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+		if (urb->transfer_buffer_length != 0
+			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+			dma_unmap_single (hcd->self.controller,
+					urb->transfer_dma,
+					urb->transfer_buffer_length,
+					usb_pipein (urb->pipe)
+					    ? DMA_FROM_DEVICE
+					    : DMA_TO_DEVICE);
+	}
+}
 
 /* may be called in any context with a valid urb->dev usecount
  * caller surrenders "ownership" of urb
@@ -948,19 +965,9 @@
 	else switch (hcd->state) {
 	case HC_STATE_RUNNING:
 	case HC_STATE_RESUMING:
-doit:
 		list_add_tail (&urb->urb_list, &ep->urb_list);
 		status = 0;
 		break;
-	case HC_STATE_SUSPENDED:
-		/* HC upstream links (register access, wakeup signaling) can work
-		 * even when the downstream links (and DMA etc) are quiesced; let
-		 * usbcore talk to the root hub.
-		 */
-		if (hcd->self.controller->power.power_state.event == PM_EVENT_ON
-				&& urb->dev->parent == NULL)
-			goto doit;
-		/* FALL THROUGH */
 	default:
 		status = -ESHUTDOWN;
 		break;
@@ -1014,7 +1021,7 @@
 	status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
 done:
 	if (unlikely (status)) {
-		urb_unlink (urb);
+		urb_unlink(hcd, urb);
 		atomic_dec (&urb->use_count);
 		if (urb->reject)
 			wake_up (&usb_kill_urb_queue);
@@ -1255,42 +1262,59 @@
 
 #ifdef	CONFIG_PM
 
-int hcd_bus_suspend (struct usb_bus *bus)
+int hcd_bus_suspend(struct usb_device *rhdev)
 {
-	struct usb_hcd		*hcd;
-	int			status;
+	struct usb_hcd	*hcd = container_of(rhdev->bus, struct usb_hcd, self);
+	int		status;
+	int		old_state = hcd->state;
 
-	hcd = container_of (bus, struct usb_hcd, self);
-	if (!hcd->driver->bus_suspend)
-		return -ENOENT;
-	hcd->state = HC_STATE_QUIESCING;
-	status = hcd->driver->bus_suspend (hcd);
-	if (status == 0)
+	dev_dbg(&rhdev->dev, "bus %s%s\n",
+			rhdev->auto_pm ? "auto-" : "", "suspend");
+	if (!hcd->driver->bus_suspend) {
+		status = -ENOENT;
+	} else {
+		hcd->state = HC_STATE_QUIESCING;
+		status = hcd->driver->bus_suspend(hcd);
+	}
+	if (status == 0) {
+		usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
 		hcd->state = HC_STATE_SUSPENDED;
-	else
-		dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
+	} else {
+		hcd->state = old_state;
+		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
 				"suspend", status);
+	}
 	return status;
 }
 
-int hcd_bus_resume (struct usb_bus *bus)
+int hcd_bus_resume(struct usb_device *rhdev)
 {
-	struct usb_hcd		*hcd;
-	int			status;
+	struct usb_hcd	*hcd = container_of(rhdev->bus, struct usb_hcd, self);
+	int		status;
+	int		old_state = hcd->state;
 
-	hcd = container_of (bus, struct usb_hcd, self);
+	dev_dbg(&rhdev->dev, "usb %s%s\n",
+			rhdev->auto_pm ? "auto-" : "", "resume");
 	if (!hcd->driver->bus_resume)
 		return -ENOENT;
 	if (hcd->state == HC_STATE_RUNNING)
 		return 0;
+
 	hcd->state = HC_STATE_RESUMING;
-	status = hcd->driver->bus_resume (hcd);
-	if (status == 0)
+	status = hcd->driver->bus_resume(hcd);
+	if (status == 0) {
+		/* TRSMRCY = 10 msec */
+		msleep(10);
+		usb_set_device_state(rhdev, rhdev->actconfig
+				? USB_STATE_CONFIGURED
+				: USB_STATE_ADDRESS);
 		hcd->state = HC_STATE_RUNNING;
-	else {
-		dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n",
+	} else {
+		hcd->state = old_state;
+		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
 				"resume", status);
-		usb_hc_died(hcd);
+		if (status != -ESHUTDOWN)
+			usb_hc_died(hcd);
 	}
 	return status;
 }
@@ -1384,30 +1408,10 @@
  */
 void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
 {
-	int at_root_hub;
-
-	at_root_hub = (urb->dev == hcd->self.root_hub);
-	urb_unlink (urb);
-
-	/* 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,
-					sizeof (struct usb_ctrlrequest),
-					DMA_TO_DEVICE);
-		if (urb->transfer_buffer_length != 0
-			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
-			dma_unmap_single (hcd->self.controller, 
-					urb->transfer_dma,
-					urb->transfer_buffer_length,
-					usb_pipein (urb->pipe)
-					    ? DMA_FROM_DEVICE
-					    : DMA_TO_DEVICE);
-	}
-
+	urb_unlink(hcd, urb);
 	usbmon_urb_complete (&hcd->self, urb);
+	usb_unanchor_urb(urb);
+
 	/* pass ownership to the completion handler */
 	urb->complete (urb);
 	atomic_dec (&urb->use_count);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index ef50fa4..b5ebb73 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -364,23 +364,13 @@
 #ifdef CONFIG_PM
 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);
+extern int hcd_bus_suspend(struct usb_device *rhdev);
+extern int hcd_bus_resume(struct usb_device *rhdev);
 #else
 static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
 {
 	return;
 }
-
-static inline int hcd_bus_suspend(struct usb_bus *bus)
-{
-	return 0;
-}
-
-static inline int hcd_bus_resume (struct usb_bus *bus)
-{
-	return 0;
-}
 #endif /* CONFIG_PM */
 
 /*
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a9cf8b3..50e7901 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -31,9 +31,16 @@
 #include "hcd.h"
 #include "hub.h"
 
+#ifdef	CONFIG_USB_PERSIST
+#define	USB_PERSIST	1
+#else
+#define	USB_PERSIST	0
+#endif
+
 struct usb_hub {
 	struct device		*intfdev;	/* the "interface" device */
 	struct usb_device	*hdev;
+	struct kref		kref;
 	struct urb		*urb;		/* for interrupt polling pipe */
 
 	/* buffer for urb ... with extra space in case of babble */
@@ -66,6 +73,7 @@
 	unsigned		limited_power:1;
 	unsigned		quiescing:1;
 	unsigned		activating:1;
+	unsigned		disconnected:1;
 
 	unsigned		has_indicators:1;
 	u8			indicator[USB_MAXCHILDREN];
@@ -321,7 +329,7 @@
 	to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
 
 	spin_lock_irqsave(&hub_event_lock, flags);
-	if (list_empty(&hub->event_list)) {
+	if (!hub->disconnected & list_empty(&hub->event_list)) {
 		list_add_tail(&hub->event_list, &hub_event_list);
 		wake_up(&khubd_wait);
 	}
@@ -330,6 +338,7 @@
 
 void usb_kick_khubd(struct usb_device *hdev)
 {
+	/* FIXME: What if hdev isn't bound to the hub driver? */
 	kick_khubd(hdev_to_hub(hdev));
 }
 
@@ -400,9 +409,10 @@
 	struct usb_hub		*hub =
 		container_of(work, struct usb_hub, tt.kevent);
 	unsigned long		flags;
+	int			limit = 100;
 
 	spin_lock_irqsave (&hub->tt.lock, flags);
-	while (!list_empty (&hub->tt.clear_list)) {
+	while (--limit && !list_empty (&hub->tt.clear_list)) {
 		struct list_head	*temp;
 		struct usb_tt_clear	*clear;
 		struct usb_device	*hdev = hub->hdev;
@@ -550,48 +560,68 @@
 static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 {
 	struct usb_device *hdev = hub->hdev;
-	int ret;
+	int ret = 0;
 
-	if (hdev->children[port1-1] && set_state) {
+	if (hdev->children[port1-1] && set_state)
 		usb_set_device_state(hdev->children[port1-1],
 				USB_STATE_NOTATTACHED);
-	}
-	ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+	if (!hub->error)
+		ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
 	if (ret)
 		dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
-			port1, ret);
-
+				port1, ret);
 	return ret;
 }
 
+/*
+ * Disable a port and mark a logical connnect-change event, so that some
+ * time later khubd will disconnect() any existing usb_device on the port
+ * and will re-enumerate if there actually is a device attached.
+ */
+static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
+{
+	dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
+	hub_port_disable(hub, port1, 1);
+
+	/* FIXME let caller ask to power down the port:
+	 *  - some devices won't enumerate without a VBUS power cycle
+	 *  - SRP saves power that way
+	 *  - ... new call, TBD ...
+	 * That's easy if this hub can switch power per-port, and
+	 * khubd reactivates the port later (timer, SRP, etc).
+	 * Powerdown must be optional, because of reset/DFU.
+	 */
+
+	set_bit(port1, hub->change_bits);
+ 	kick_khubd(hub);
+}
 
 /* caller has locked the hub device */
-static void hub_pre_reset(struct usb_interface *intf)
+static int hub_pre_reset(struct usb_interface *intf)
 {
 	struct usb_hub *hub = usb_get_intfdata(intf);
 	struct usb_device *hdev = hub->hdev;
-	int port1;
+	int i;
 
-	for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
-		if (hdev->children[port1 - 1]) {
-			usb_disconnect(&hdev->children[port1 - 1]);
-			if (hub->error == 0)
-				hub_port_disable(hub, port1, 0);
-		}
+	/* Disconnect all the children */
+	for (i = 0; i < hdev->maxchild; ++i) {
+		if (hdev->children[i])
+			usb_disconnect(&hdev->children[i]);
 	}
 	hub_quiesce(hub);
+	return 0;
 }
 
 /* caller has locked the hub device */
-static void hub_post_reset(struct usb_interface *intf)
+static int hub_post_reset(struct usb_interface *intf)
 {
 	struct usb_hub *hub = usb_get_intfdata(intf);
 
-	hub_activate(hub);
 	hub_power_on(hub);
+	hub_activate(hub);
+	return 0;
 }
 
-
 static int hub_configure(struct usb_hub *hub,
 	struct usb_endpoint_descriptor *endpoint)
 {
@@ -845,43 +875,42 @@
 	return ret;
 }
 
+static void hub_release(struct kref *kref)
+{
+	struct usb_hub *hub = container_of(kref, struct usb_hub, kref);
+
+	usb_put_intf(to_usb_interface(hub->intfdev));
+	kfree(hub);
+}
+
 static unsigned highspeed_hubs;
 
 static void hub_disconnect(struct usb_interface *intf)
 {
 	struct usb_hub *hub = usb_get_intfdata (intf);
-	struct usb_device *hdev;
+
+	/* Take the hub off the event list and don't let it be added again */
+	spin_lock_irq(&hub_event_lock);
+	list_del_init(&hub->event_list);
+	hub->disconnected = 1;
+	spin_unlock_irq(&hub_event_lock);
 
 	/* Disconnect all children and quiesce the hub */
 	hub->error = 0;
 	hub_pre_reset(intf);
 
 	usb_set_intfdata (intf, NULL);
-	hdev = hub->hdev;
 
-	if (hdev->speed == USB_SPEED_HIGH)
+	if (hub->hdev->speed == USB_SPEED_HIGH)
 		highspeed_hubs--;
 
 	usb_free_urb(hub->urb);
-	hub->urb = NULL;
-
-	spin_lock_irq(&hub_event_lock);
-	list_del_init(&hub->event_list);
-	spin_unlock_irq(&hub_event_lock);
-
 	kfree(hub->descriptor);
-	hub->descriptor = NULL;
-
 	kfree(hub->status);
-	hub->status = NULL;
+	usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
+			hub->buffer_dma);
 
-	if (hub->buffer) {
-		usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer,
-				hub->buffer_dma);
-		hub->buffer = NULL;
-	}
-
-	kfree(hub);
+	kref_put(&hub->kref, hub_release);
 }
 
 static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -929,10 +958,12 @@
 		return -ENOMEM;
 	}
 
+	kref_init(&hub->kref);
 	INIT_LIST_HEAD(&hub->event_list);
 	hub->intfdev = &intf->dev;
 	hub->hdev = hdev;
 	INIT_DELAYED_WORK(&hub->leds, led_work);
+	usb_get_intf(intf);
 
 	usb_set_intfdata (intf, hub);
 	intf->needs_remote_wakeup = 1;
@@ -982,49 +1013,6 @@
 }
 
 
-/* grab device/port lock, returning index of that port (zero based).
- * protects the upstream link used by this device from concurrent
- * tree operations like suspend, resume, reset, and disconnect, which
- * apply to everything downstream of a given port.
- */
-static int locktree(struct usb_device *udev)
-{
-	int			t;
-	struct usb_device	*hdev;
-
-	if (!udev)
-		return -ENODEV;
-
-	/* root hub is always the first lock in the series */
-	hdev = udev->parent;
-	if (!hdev) {
-		usb_lock_device(udev);
-		return 0;
-	}
-
-	/* on the path from root to us, lock everything from
-	 * top down, dropping parent locks when not needed
-	 */
-	t = locktree(hdev);
-	if (t < 0)
-		return t;
-
-	/* everything is fail-fast once disconnect
-	 * processing starts
-	 */
-	if (udev->state == USB_STATE_NOTATTACHED) {
-		usb_unlock_device(hdev);
-		return -ENODEV;
-	}
-
-	/* when everyone grabs locks top->bottom,
-	 * non-overlapping work may be concurrent
-	 */
-	usb_lock_device(udev);
-	usb_unlock_device(hdev);
-	return udev->portnum;
-}
-
 static void recursively_mark_NOTATTACHED(struct usb_device *udev)
 {
 	int i;
@@ -1089,41 +1077,6 @@
 	spin_unlock_irqrestore(&device_state_lock, flags);
 }
 
-
-#ifdef	CONFIG_PM
-
-/**
- * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
- * @rhdev: struct usb_device for the root hub
- *
- * The USB host controller driver calls this function when its root hub
- * is resumed and Vbus power has been interrupted or the controller
- * has been reset.  The routine marks all the children of the root hub
- * as NOTATTACHED and marks logical connect-change events on their ports.
- */
-void usb_root_hub_lost_power(struct usb_device *rhdev)
-{
-	struct usb_hub *hub;
-	int port1;
-	unsigned long flags;
-
-	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
-
-	spin_lock_irqsave(&device_state_lock, flags);
-	hub = hdev_to_hub(rhdev);
-	for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
-		if (rhdev->children[port1 - 1]) {
-			recursively_mark_NOTATTACHED(
-					rhdev->children[port1 - 1]);
-			set_bit(port1, hub->change_bits);
-		}
-	}
-	spin_unlock_irqrestore(&device_state_lock, flags);
-}
-EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
-
-#endif	/* CONFIG_PM */
-
 static void choose_address(struct usb_device *udev)
 {
 	int		devnum;
@@ -1264,7 +1217,6 @@
 
 #ifdef	CONFIG_USB_OTG
 #include "otg_whitelist.h"
-static int __usb_port_suspend(struct usb_device *, int port1);
 #endif
 
 /**
@@ -1370,11 +1322,11 @@
 		 * (Includes HNP test device.)
 		 */
 		if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-			err = __usb_port_suspend(udev, udev->bus->otg_port);
+			err = usb_port_suspend(udev);
 			if (err < 0)
 				dev_dbg(&udev->dev, "HNP fail, %d\n", err);
 		}
-		err = -ENODEV;
+		err = -ENOTSUPP;
 		goto fail;
 	}
 #endif
@@ -1471,9 +1423,9 @@
 		if (!(portstatus & USB_PORT_STAT_CONNECTION))
 			return -ENOTCONN;
 
-		/* bomb out completely if something weird happened */
+		/* bomb out completely if the connection bounced */
 		if ((portchange & USB_PORT_STAT_C_CONNECTION))
-			return -EINVAL;
+			return -ENOTCONN;
 
 		/* if we`ve finished resetting, then break out of the loop */
 		if (!(portstatus & USB_PORT_STAT_RESET) &&
@@ -1552,34 +1504,24 @@
 	return status;
 }
 
-/*
- * Disable a port and mark a logical connnect-change event, so that some
- * time later khubd will disconnect() any existing usb_device on the port
- * and will re-enumerate if there actually is a device attached.
- */
-static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
-{
-	dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
-	hub_port_disable(hub, port1, 1);
-
-	/* FIXME let caller ask to power down the port:
-	 *  - some devices won't enumerate without a VBUS power cycle
-	 *  - SRP saves power that way
-	 *  - ... new call, TBD ...
-	 * That's easy if this hub can switch power per-port, and
-	 * khubd reactivates the port later (timer, SRP, etc).
-	 * Powerdown must be optional, because of reset/DFU.
-	 */
-
-	set_bit(port1, hub->change_bits);
- 	kick_khubd(hub);
-}
-
 #ifdef	CONFIG_PM
 
 #ifdef	CONFIG_USB_SUSPEND
 
 /*
+ * usb_port_suspend - suspend a usb device's upstream port
+ * @udev: device that's no longer in active use, not a root hub
+ * 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_port_resume().  It's also routine
+ * to disconnect devices while they are suspended.
+ *
+ * This only affects the USB hardware for a device; its interfaces
+ * (and, for hubs, child devices) must already have been suspended.
+ *
  * Selective port suspend reduces power; most suspended devices draw
  * less than 500 uA.  It's also used in OTG, along with remote wakeup.
  * All devices below the suspended port are also suspended.
@@ -1588,11 +1530,35 @@
  * also support "remote wakeup", where the device can activate the USB
  * tree above them to deliver data, such as a keypress or packet.  In
  * some cases, this wakes the USB host.
+ *
+ * Suspending OTG devices may trigger HNP, if that's been enabled
+ * between a pair of dual-role devices.  That will change roles, such
+ * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
+ *
+ * Devices on USB hub ports have only one "suspend" state, corresponding
+ * to ACPI D2, "may cause the device to lose some context".
+ * State transitions include:
+ *
+ *   - suspend, resume ... when the VBUS power link stays live
+ *   - suspend, disconnect ... VBUS lost
+ *
+ * Once VBUS drop breaks the circuit, the port it's using has to go through
+ * normal re-enumeration procedures, starting with enabling VBUS power.
+ * Other than re-initializing the hub (plug/unplug, except for root hubs),
+ * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
+ * timer, no SRP, no requests through sysfs.
+ *
+ * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
+ * the root hub for their bus goes into global suspend ... so we don't
+ * (falsely) update the device power state to say it suspended.
+ *
+ * Returns 0 on success, else negative errno.
  */
-static int hub_port_suspend(struct usb_hub *hub, int port1,
-		struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev)
 {
-	int	status;
+	struct usb_hub	*hub = hdev_to_hub(udev->parent);
+	int		port1 = udev->portnum;
+	int		status;
 
 	// dev_dbg(hub->intfdev, "suspend port %d\n", port1);
 
@@ -1609,17 +1575,15 @@
 				NULL, 0,
 				USB_CTRL_SET_TIMEOUT);
 		if (status)
-			dev_dbg(&udev->dev,
-				"won't remote wakeup, status %d\n",
-				status);
+			dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
+					status);
 	}
 
 	/* see 7.1.7.6 */
 	status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
 	if (status) {
-		dev_dbg(hub->intfdev,
-			"can't suspend port %d, status %d\n",
-			port1, status);
+		dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
+				port1, status);
 		/* paranoia:  "should not happen" */
 		(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
 				USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
@@ -1637,85 +1601,24 @@
 }
 
 /*
- * Devices on USB hub ports have only one "suspend" state, corresponding
- * to ACPI D2, "may cause the device to lose some context".
- * State transitions include:
- *
- *   - suspend, resume ... when the VBUS power link stays live
- *   - suspend, disconnect ... VBUS lost
- *
- * Once VBUS drop breaks the circuit, the port it's using has to go through
- * normal re-enumeration procedures, starting with enabling VBUS power.
- * Other than re-initializing the hub (plug/unplug, except for root hubs),
- * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
- * timer, no SRP, no requests through sysfs.
- *
- * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
- * 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_port_suspend (struct usb_device *udev, int port1)
-{
-	int	status = 0;
-
-	/* caller owns the udev device lock */
-	if (port1 < 0)
-		return port1;
-
-	/* 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);
-	else {
-		dev_dbg(&udev->dev, "usb %ssuspend\n",
-				udev->auto_pm ? "auto-" : "");
-		usb_set_device_state(udev, USB_STATE_SUSPENDED);
-	}
-	return status;
-}
-
-/*
- * 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_port_resume().  It's also routine
- * to disconnect devices while they are suspended.
- *
- * This only affects the USB hardware for a device; its interfaces
- * (and, for hubs, child devices) must already have been suspended.
- *
- * Suspending OTG devices may trigger HNP, if that's been enabled
- * between a pair of dual-role devices.  That will change roles, such
- * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
- *
- * Returns 0 on success, else negative errno.
- */
-int usb_port_suspend(struct usb_device *udev)
-{
-	return __usb_port_suspend(udev, udev->portnum);
-}
-
-/*
  * If the USB "suspend" state is in use (rather than "global suspend"),
  * many devices will be individually taken out of suspend state using
- * special" resume" signaling.  These routines kick in shortly after
+ * special "resume" signaling.  This routine kicks in shortly after
  * hardware resume signaling is finished, either because of selective
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
+ *
+ * If @udev->reset_resume is set then the device is reset before the
+ * status check is done.
  */
 static int finish_port_resume(struct usb_device *udev)
 {
-	int	status;
+	int	status = 0;
 	u16	devstatus;
 
 	/* caller owns the udev device lock */
-	dev_dbg(&udev->dev, "finish resume\n");
+	dev_dbg(&udev->dev, "finish %sresume\n",
+			udev->reset_resume ? "reset-" : "");
 
 	/* usb ch9 identifies four variants of SUSPENDED, based on what
 	 * state the device resumes to.  Linux currently won't see the
@@ -1726,22 +1629,30 @@
 			? USB_STATE_CONFIGURED
 			: USB_STATE_ADDRESS);
 
+	/* 10.5.4.5 says not to reset a suspended port if the attached
+	 * device is enabled for remote wakeup.  Hence the reset
+	 * operation is carried out here, after the port has been
+	 * resumed.
+	 */
+	if (udev->reset_resume)
+		status = usb_reset_device(udev);
+
  	/* 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,
 	 * and device drivers will know about any resume quirks.
 	 */
-	status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
-	if (status >= 0)
-		status = (status == 2 ? 0 : -ENODEV);
+	if (status == 0) {
+		status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+		if (status >= 0)
+			status = (status == 2 ? 0 : -ENODEV);
+	}
 
-	if (status)
-		dev_dbg(&udev->dev,
-			"gone after usb resume? status %d\n",
-			status);
-	else if (udev->actconfig) {
+	if (status) {
+		dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
+				status);
+	} else if (udev->actconfig) {
 		le16_to_cpus(&devstatus);
-		if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
-				&& udev->parent) {
+		if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
 			status = usb_control_msg(udev,
 					usb_sndctrlpipe(udev, 0),
 					USB_REQ_CLEAR_FEATURE,
@@ -1754,19 +1665,52 @@
 					"wakeup, status %d\n", status);
 		}
 		status = 0;
-
-	} else if (udev->devnum <= 0) {
-		dev_dbg(&udev->dev, "bogus resume!\n");
-		status = -EINVAL;
 	}
 	return status;
 }
 
-static int
-hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+/*
+ * usb_port_resume - re-activate a suspended usb device's upstream port
+ * @udev: device to re-activate, not a root hub
+ * Context: must be able to sleep; device not locked; pm locks held
+ *
+ * This will re-activate the suspended device, increasing power usage
+ * while letting drivers communicate again with its endpoints.
+ * USB resume explicitly guarantees that the power session between
+ * the host and the device is the same as it was when the device
+ * suspended.
+ *
+ * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this
+ * routine won't check that the port is still enabled.  Furthermore,
+ * if @udev->reset_resume is set then finish_port_resume() above will
+ * reset @udev.  The end result is that a broken power session can be
+ * recovered and @udev will appear to persist across a loss of VBUS power.
+ *
+ * For example, if a host controller doesn't maintain VBUS suspend current
+ * during a system sleep or is reset when the system wakes up, all the USB
+ * power sessions below it will be broken.  This is especially troublesome
+ * for mass-storage devices containing mounted filesystems, since the
+ * device will appear to have disconnected and all the memory mappings
+ * to it will be lost.  Using the USB_PERSIST facility, the device can be
+ * made to appear as if it had not disconnected.
+ *
+ * This facility is inherently dangerous.  Although usb_reset_device()
+ * makes every effort to insure that the same device is present after the
+ * reset as before, it cannot provide a 100% guarantee.  Furthermore it's
+ * quite possible for a device to remain unaltered but its media to be
+ * changed.  If the user replaces a flash memory card while the system is
+ * asleep, he will have only himself to blame when the filesystem on the
+ * new card is corrupted and the system crashes.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_port_resume(struct usb_device *udev)
 {
-	int	status;
-	u16	portchange, portstatus;
+	struct usb_hub	*hub = hdev_to_hub(udev->parent);
+	int		port1 = udev->portnum;
+	int		status;
+	u16		portchange, portstatus;
+	unsigned	mask_flags, want_flags;
 
 	/* Skip the initial Clear-Suspend step for a remote wakeup */
 	status = hub_port_status(hub, port1, &portstatus, &portchange);
@@ -1781,30 +1725,31 @@
 	status = clear_port_feature(hub->hdev,
 			port1, USB_PORT_FEAT_SUSPEND);
 	if (status) {
-		dev_dbg(hub->intfdev,
-			"can't resume port %d, status %d\n",
-			port1, status);
+		dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
+				port1, status);
 	} else {
 		/* drive resume for at least 20 msec */
-		if (udev)
-			dev_dbg(&udev->dev, "usb %sresume\n",
-					udev->auto_pm ? "auto-" : "");
+		dev_dbg(&udev->dev, "usb %sresume\n",
+				udev->auto_pm ? "auto-" : "");
 		msleep(25);
 
-#define LIVE_FLAGS	( USB_PORT_STAT_POWER \
-			| USB_PORT_STAT_ENABLE \
-			| USB_PORT_STAT_CONNECTION)
-
 		/* Virtual root hubs can trigger on GET_PORT_STATUS to
 		 * stop resume signaling.  Then finish the resume
 		 * sequence.
 		 */
 		status = hub_port_status(hub, port1, &portstatus, &portchange);
-SuspendCleared:
-		if (status < 0
-				|| (portstatus & LIVE_FLAGS) != LIVE_FLAGS
-				|| (portstatus & USB_PORT_STAT_SUSPEND) != 0
-				) {
+
+ SuspendCleared:
+		if (USB_PERSIST && udev->reset_resume)
+			want_flags = USB_PORT_STAT_POWER
+					| USB_PORT_STAT_CONNECTION;
+		else
+			want_flags = USB_PORT_STAT_POWER
+					| USB_PORT_STAT_CONNECTION
+					| USB_PORT_STAT_ENABLE;
+		mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
+
+		if (status < 0 || (portstatus & mask_flags) != want_flags) {
 			dev_dbg(hub->intfdev,
 				"port %d status %04x.%04x after resume, %d\n",
 				port1, portchange, portstatus, status);
@@ -1816,51 +1761,19 @@
 						USB_PORT_FEAT_C_SUSPEND);
 			/* TRSMRCY = 10 msec */
 			msleep(10);
-			if (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;
-}
-
-/*
- * 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
- *
- * This will re-activate the suspended device, increasing power usage
- * while letting drivers communicate again with its endpoints.
- * USB resume explicitly guarantees that the power session between
- * the host and the device is the same as it was when the device
- * suspended.
- *
- * Returns 0 on success, else negative errno.
- */
-int usb_port_resume(struct usb_device *udev)
-{
-	int	status;
-
-	/* we change the device's upstream USB link,
-	 * but root hubs have no upstream USB link.
-	 */
-	if (udev->parent) {
-		// 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-" : "");
+	if (status == 0)
 		status = finish_port_resume(udev);
-	}
-	if (status < 0)
+	if (status < 0) {
 		dev_dbg(&udev->dev, "can't resume, status %d\n", status);
+		hub_port_logical_disconnect(hub, port1);
+	}
 	return status;
 }
 
@@ -1887,21 +1800,16 @@
 	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;
+	int status = 0;
+
+	/* However we may need to do a reset-resume */
+	if (udev->reset_resume) {
+		dev_dbg(&udev->dev, "reset-resume\n");
+		status = usb_reset_device(udev);
+	}
+	return status;
 }
 
 static inline int remote_wakeup(struct usb_device *udev)
@@ -1916,7 +1824,6 @@
 	struct usb_hub		*hub = usb_get_intfdata (intf);
 	struct usb_device	*hdev = hub->hdev;
 	unsigned		port1;
-	int			status = 0;
 
 	/* fail if children aren't already suspended */
 	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
@@ -1942,49 +1849,75 @@
 
 	/* stop khubd and related activity */
 	hub_quiesce(hub);
-
-	/* "global suspend" of the downstream HC-to-USB interface */
-	if (!hdev->parent) {
-		status = hcd_bus_suspend(hdev->bus);
-		if (status != 0) {
-			dev_dbg(&hdev->dev, "'global' suspend %d\n", status);
-			hub_activate(hub);
-		}
-	}
-	return status;
+	return 0;
 }
 
 static int hub_resume(struct usb_interface *intf)
 {
 	struct usb_hub		*hub = usb_get_intfdata (intf);
-	struct usb_device	*hdev = hub->hdev;
-	int			status;
 
 	dev_dbg(&intf->dev, "%s\n", __FUNCTION__);
 
-	/* "global resume" of the downstream HC-to-USB interface */
-	if (!hdev->parent) {
-		struct usb_bus	*bus = hdev->bus;
-		if (bus) {
-			status = hcd_bus_resume (bus);
-			if (status) {
-				dev_dbg(&intf->dev, "'global' resume %d\n",
-					status);
-				return status;
-			}
-		} else
-			return -EOPNOTSUPP;
-		if (status == 0) {
-			/* TRSMRCY = 10 msec */
-			msleep(10);
-		}
-	}
-
 	/* tell khubd to look for changes on this hub */
 	hub_activate(hub);
 	return 0;
 }
 
+static int hub_reset_resume(struct usb_interface *intf)
+{
+	struct usb_hub *hub = usb_get_intfdata(intf);
+	struct usb_device *hdev = hub->hdev;
+	int port1;
+
+	hub_power_on(hub);
+
+	for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+		struct usb_device *child = hdev->children[port1-1];
+
+		if (child) {
+
+			/* For "USB_PERSIST"-enabled children we must
+			 * mark the child device for reset-resume and
+			 * turn off the connect-change status to prevent
+			 * khubd from disconnecting it later.
+			 */
+			if (USB_PERSIST && child->persist_enabled) {
+				child->reset_resume = 1;
+				clear_port_feature(hdev, port1,
+						USB_PORT_FEAT_C_CONNECTION);
+
+			/* Otherwise we must disconnect the child,
+			 * but as we may not lock the child device here
+			 * we have to do a "logical" disconnect.
+			 */
+			} else {
+				hub_port_logical_disconnect(hub, port1);
+			}
+		}
+	}
+
+	hub_activate(hub);
+	return 0;
+}
+
+/**
+ * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
+ * @rhdev: struct usb_device for the root hub
+ *
+ * The USB host controller driver calls this function when its root hub
+ * is resumed and Vbus power has been interrupted or the controller
+ * has been reset.  The routine marks @rhdev as having lost power.  When
+ * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST
+ * is enabled then it will carry out power-session recovery, otherwise
+ * it will disconnect all the child devices.
+ */
+void usb_root_hub_lost_power(struct usb_device *rhdev)
+{
+	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+	rhdev->reset_resume = 1;
+}
+EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
+
 #else	/* CONFIG_PM */
 
 static inline int remote_wakeup(struct usb_device *udev)
@@ -1992,8 +1925,9 @@
 	return 0;
 }
 
-#define hub_suspend NULL
-#define hub_resume NULL
+#define hub_suspend		NULL
+#define hub_resume		NULL
+#define hub_reset_resume	NULL
 #endif
 
 
@@ -2456,19 +2390,6 @@
 		return;
 	}
 
-#ifdef  CONFIG_USB_SUSPEND
-	/* If something is connected, but the port is suspended, wake it up. */
-	if (portstatus & USB_PORT_STAT_SUSPEND) {
-		status = hub_port_resume(hub, port1, NULL);
-		if (status < 0) {
-			dev_dbg(hub_dev,
-				"can't clear suspend on port %d; %d\n",
-				port1, status);
-			goto done;
-		}
-	}
-#endif
-
 	for (i = 0; i < SET_CONFIG_TRIES; i++) {
 		struct usb_device *udev;
 
@@ -2579,7 +2500,7 @@
 		ep0_reinit(udev);
 		release_address(udev);
 		usb_put_dev(udev);
-		if (status == -ENOTCONN)
+		if ((status == -ENOTCONN) || (status == -ENOTSUPP))
 			break;
 	}
  
@@ -2620,10 +2541,12 @@
 		list_del_init(tmp);
 
 		hub = list_entry(tmp, struct usb_hub, event_list);
-		hdev = hub->hdev;
-		intf = to_usb_interface(hub->intfdev);
-		hub_dev = &intf->dev;
+		kref_get(&hub->kref);
+		spin_unlock_irq(&hub_event_lock);
 
+		hdev = hub->hdev;
+		hub_dev = hub->intfdev;
+		intf = to_usb_interface(hub_dev);
 		dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
 				hdev->state, hub->descriptor
 					? hub->descriptor->bNbrPorts
@@ -2632,16 +2555,10 @@
 				(u16) hub->change_bits[0],
 				(u16) hub->event_bits[0]);
 
-		usb_get_intf(intf);
-		spin_unlock_irq(&hub_event_lock);
-
 		/* Lock the device, then check to see if we were
 		 * disconnected while waiting for the lock to succeed. */
-		if (locktree(hdev) < 0) {
-			usb_put_intf(intf);
-			continue;
-		}
-		if (hub != usb_get_intfdata(intf))
+		usb_lock_device(hdev);
+		if (unlikely(hub->disconnected))
 			goto loop;
 
 		/* If the hub has died, clean up after it */
@@ -2804,7 +2721,7 @@
 			usb_autopm_enable(intf);
 loop:
 		usb_unlock_device(hdev);
-		usb_put_intf(intf);
+		kref_put(&hub->kref, hub_release);
 
         } /* end while (1) */
 }
@@ -2839,6 +2756,7 @@
 	.disconnect =	hub_disconnect,
 	.suspend =	hub_suspend,
 	.resume =	hub_resume,
+	.reset_resume =	hub_reset_resume,
 	.pre_reset =	hub_pre_reset,
 	.post_reset =	hub_post_reset,
 	.ioctl =	hub_ioctl,
@@ -2941,6 +2859,11 @@
  * this from a driver probe() routine after downloading new firmware.
  * For calls that might not occur during probe(), drivers should lock
  * the device using usb_lock_device_for_reset().
+ *
+ * Locking exception: This routine may also be called from within an
+ * autoresume handler.  Such usage won't conflict with other tasks
+ * holding the device lock because these tasks should always call
+ * usb_autopm_resume_device(), thereby preventing any unwanted autoresume.
  */
 int usb_reset_device(struct usb_device *udev)
 {
@@ -2971,7 +2894,7 @@
 		 * Other endpoints will be handled by re-enumeration. */
 		ep0_reinit(udev);
 		ret = hub_port_init(parent_hub, udev, port1, i);
-		if (ret >= 0)
+		if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
 			break;
 	}
 	clear_bit(port1, parent_hub->busy_bits);
@@ -3087,6 +3010,7 @@
 				drv = to_usb_driver(cintf->dev.driver);
 				if (drv->pre_reset)
 					(drv->pre_reset)(cintf);
+	/* FIXME: Unbind if pre_reset returns an error or isn't defined */
 			}
 		}
 	}
@@ -3105,6 +3029,7 @@
 				drv = to_usb_driver(cintf->dev.driver);
 				if (drv->post_reset)
 					(drv->post_reset)(cintf);
+	/* FIXME: Unbind if post_reset returns an error or isn't defined */
 			}
 			if (cintf != iface)
 				up(&cintf->dev.sem);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index f9fed34..530e854 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -404,8 +404,6 @@
 
 		io->urbs [i]->complete = sg_complete;
 		io->urbs [i]->context = io;
-		io->urbs [i]->status = -EINPROGRESS;
-		io->urbs [i]->actual_length = 0;
 
 		/*
 		 * Some systems need to revert to PIO when DMA is temporarily
@@ -499,7 +497,8 @@
 
 	/* queue the urbs.  */
 	spin_lock_irq (&io->lock);
-	for (i = 0; i < entries && !io->status; i++) {
+	i = 0;
+	while (i < entries && !io->status) {
 		int	retval;
 
 		io->urbs [i]->dev = io->dev;
@@ -516,7 +515,6 @@
 		case -ENOMEM:
 			io->urbs[i]->dev = NULL;
 			retval = 0;
-			i--;
 			yield ();
 			break;
 
@@ -527,6 +525,7 @@
 			 * URBs are queued at once; N milliseconds?
 			 */
 		case 0:
+			++i;
 			cpu_relax ();
 			break;
 
@@ -1385,6 +1384,36 @@
 	.uevent =	usb_if_uevent,
 };
 
+static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
+						       struct usb_host_config *config,
+						       u8 inum)
+{
+	struct usb_interface_assoc_descriptor *retval = NULL;
+	struct usb_interface_assoc_descriptor *intf_assoc;
+	int first_intf;
+	int last_intf;
+	int i;
+
+	for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) {
+		intf_assoc = config->intf_assoc[i];
+		if (intf_assoc->bInterfaceCount == 0)
+			continue;
+
+		first_intf = intf_assoc->bFirstInterface;
+		last_intf = first_intf + (intf_assoc->bInterfaceCount - 1);
+		if (inum >= first_intf && inum <= last_intf) {
+			if (!retval)
+				retval = intf_assoc;
+			else
+				dev_err(&dev->dev, "Interface #%d referenced"
+					" by multiple IADs\n", inum);
+		}
+	}
+
+	return retval;
+}
+
+
 /*
  * usb_set_configuration - Makes a particular device setting be current
  * @dev: the device whose configuration is being updated
@@ -1531,6 +1560,7 @@
 		intfc = cp->intf_cache[i];
 		intf->altsetting = intfc->altsetting;
 		intf->num_altsetting = intfc->num_altsetting;
+		intf->intf_assoc = find_iad(dev, cp, i);
 		kref_get(&intfc->ref);
 
 		alt = usb_altnum_to_altsetting(intf, 0);
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 739f520..aa21b38 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -30,10 +30,28 @@
 static const struct usb_device_id usb_quirk_list[] = {
 	/* HP 5300/5370C scanner */
 	{ USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 },
+	/* Benq S2W 3300U */
+	{ USB_DEVICE(0x04a5, 0x20b0), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+	/* Seiko Epson Corp. Perfection 1200 */
+	{ USB_DEVICE(0x04b8, 0x0104), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
 	/* Seiko Epson Corp - Perfection 1670 */
 	{ USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+	/* Samsung ML-2510 Series printer */
+	{ USB_DEVICE(0x04e8, 0x327e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
 	/* Elsa MicroLink 56k (V.250) */
 	{ USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+	/* Ultima Electronics Corp.*/
+	{ USB_DEVICE(0x05d8, 0x4005), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+	/* Umax [hex] Astra 3400U */
+	{ USB_DEVICE(0x1606, 0x0060), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+
+	/* Philips PSC805 audio device */
+	{ USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
+
+	/* RIM Blackberry */
+	{ USB_DEVICE(0x0fca, 0x0001), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+	{ USB_DEVICE(0x0fca, 0x0004), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
+	{ USB_DEVICE(0x0fca, 0x0006), .driver_info = USB_QUIRK_NO_AUTOSUSPEND },
 
 	{ }  /* terminating entry must be last */
 };
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index be37c86..d47ae89 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -169,6 +169,73 @@
 }
 static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
 
+
+#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND)
+static const char power_group[] = "power";
+#endif
+
+#ifdef	CONFIG_USB_PERSIST
+
+static ssize_t
+show_persist(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_device *udev = to_usb_device(dev);
+
+	return sprintf(buf, "%d\n", udev->persist_enabled);
+}
+
+static ssize_t
+set_persist(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	int value;
+
+	/* Hubs are always enabled for USB_PERSIST */
+	if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+		return -EPERM;
+
+	if (sscanf(buf, "%d", &value) != 1)
+		return -EINVAL;
+	usb_pm_lock(udev);
+	udev->persist_enabled = !!value;
+	usb_pm_unlock(udev);
+	return count;
+}
+
+static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist);
+
+static int add_persist_attributes(struct device *dev)
+{
+	int rc = 0;
+
+	if (is_usb_device(dev)) {
+		struct usb_device *udev = to_usb_device(dev);
+
+		/* Hubs are automatically enabled for USB_PERSIST */
+		if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+			udev->persist_enabled = 1;
+		rc = sysfs_add_file_to_group(&dev->kobj,
+				&dev_attr_persist.attr,
+				power_group);
+	}
+	return rc;
+}
+
+static void remove_persist_attributes(struct device *dev)
+{
+	sysfs_remove_file_from_group(&dev->kobj,
+			&dev_attr_persist.attr,
+			power_group);
+}
+
+#else
+
+#define add_persist_attributes(dev)	0
+#define remove_persist_attributes(dev)	do {} while (0)
+
+#endif	/* CONFIG_USB_PERSIST */
+
 #ifdef	CONFIG_USB_SUSPEND
 
 static ssize_t
@@ -276,8 +343,6 @@
 
 static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
 
-static char power_group[] = "power";
-
 static int add_power_attributes(struct device *dev)
 {
 	int rc = 0;
@@ -311,6 +376,7 @@
 
 #endif	/* CONFIG_USB_SUSPEND */
 
+
 /* Descriptor fields */
 #define usb_descriptor_attr_le16(field, format_string)			\
 static ssize_t								\
@@ -384,6 +450,10 @@
 	if (retval)
 		return retval;
 
+	retval = add_persist_attributes(dev);
+	if (retval)
+		goto error;
+
 	retval = add_power_attributes(dev);
 	if (retval)
 		goto error;
@@ -421,9 +491,29 @@
 	device_remove_file(dev, &dev_attr_product);
 	device_remove_file(dev, &dev_attr_serial);
 	remove_power_attributes(dev);
+	remove_persist_attributes(dev);
 	sysfs_remove_group(&dev->kobj, &dev_attr_grp);
 }
 
+/* Interface Accociation Descriptor fields */
+#define usb_intf_assoc_attr(field, format_string)			\
+static ssize_t								\
+show_iad_##field (struct device *dev, struct device_attribute *attr,	\
+		char *buf)						\
+{									\
+	struct usb_interface *intf = to_usb_interface (dev);		\
+									\
+	return sprintf (buf, format_string,				\
+			intf->intf_assoc->field); 		\
+}									\
+static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL);
+
+usb_intf_assoc_attr (bFirstInterface, "%02x\n")
+usb_intf_assoc_attr (bInterfaceCount, "%02d\n")
+usb_intf_assoc_attr (bFunctionClass, "%02x\n")
+usb_intf_assoc_attr (bFunctionSubClass, "%02x\n")
+usb_intf_assoc_attr (bFunctionProtocol, "%02x\n")
+
 /* Interface fields */
 #define usb_intf_attr(field, format_string)				\
 static ssize_t								\
@@ -487,6 +577,18 @@
 }
 static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
 
+static struct attribute *intf_assoc_attrs[] = {
+	&dev_attr_iad_bFirstInterface.attr,
+	&dev_attr_iad_bInterfaceCount.attr,
+	&dev_attr_iad_bFunctionClass.attr,
+	&dev_attr_iad_bFunctionSubClass.attr,
+	&dev_attr_iad_bFunctionProtocol.attr,
+	NULL,
+};
+static struct attribute_group intf_assoc_attr_grp = {
+	.attrs = intf_assoc_attrs,
+};
+
 static struct attribute *intf_attrs[] = {
 	&dev_attr_bInterfaceNumber.attr,
 	&dev_attr_bAlternateSetting.attr,
@@ -538,6 +640,8 @@
 		alt->string = usb_cache_string(udev, alt->desc.iInterface);
 	if (alt->string)
 		retval = device_create_file(dev, &dev_attr_interface);
+	if (intf->intf_assoc)
+		retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp);
 	usb_create_intf_ep_files(intf, udev);
 	return 0;
 }
@@ -549,4 +653,5 @@
 	usb_remove_intf_ep_files(intf);
 	device_remove_file(dev, &dev_attr_interface);
 	sysfs_remove_group(&dev->kobj, &intf_attr_grp);
+	sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp);
 }
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 94ea972..52ec44b 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -4,6 +4,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/usb.h>
+#include <linux/wait.h>
 #include "hcd.h"
 
 #define to_urb(d) container_of(d, struct urb, kref)
@@ -11,6 +12,10 @@
 static void urb_destroy(struct kref *kref)
 {
 	struct urb *urb = to_urb(kref);
+
+	if (urb->transfer_flags & URB_FREE_BUFFER)
+		kfree(urb->transfer_buffer);
+
 	kfree(urb);
 }
 
@@ -34,6 +39,7 @@
 		memset(urb, 0, sizeof(*urb));
 		kref_init(&urb->kref);
 		spin_lock_init(&urb->lock);
+		INIT_LIST_HEAD(&urb->anchor_list);
 	}
 }
 
@@ -100,8 +106,60 @@
 		kref_get(&urb->kref);
 	return urb;
 }
-		
-		
+
+/**
+ * usb_anchor_urb - anchors an URB while it is processed
+ * @urb: pointer to the urb to anchor
+ * @anchor: pointer to the anchor
+ *
+ * This can be called to have access to URBs which are to be executed
+ * without bothering to track them
+ */
+void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	usb_get_urb(urb);
+	list_add_tail(&urb->anchor_list, &anchor->urb_list);
+	urb->anchor = anchor;
+	spin_unlock_irqrestore(&anchor->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_urb);
+
+/**
+ * usb_unanchor_urb - unanchors an URB
+ * @urb: pointer to the urb to anchor
+ *
+ * Call this to stop the system keeping track of this URB
+ */
+void usb_unanchor_urb(struct urb *urb)
+{
+	unsigned long flags;
+	struct usb_anchor *anchor;
+
+	if (!urb)
+		return;
+
+	anchor = urb->anchor;
+	if (!anchor)
+		return;
+
+	spin_lock_irqsave(&anchor->lock, flags);
+	if (unlikely(anchor != urb->anchor)) {
+		/* we've lost the race to another thread */
+		spin_unlock_irqrestore(&anchor->lock, flags);
+		return;
+	}
+	urb->anchor = NULL;
+	list_del(&urb->anchor_list);
+	spin_unlock_irqrestore(&anchor->lock, flags);
+	usb_put_urb(urb);
+	if (list_empty(&anchor->urb_list))
+		wake_up(&anchor->wait);
+}
+EXPORT_SYMBOL_GPL(usb_unanchor_urb);
+
 /*-------------------------------------------------------------------*/
 
 /**
@@ -478,6 +536,48 @@
 	spin_unlock_irq(&urb->lock);
 }
 
+/**
+ * usb_kill_anchored_urbs - cancel transfer requests en masse
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be killed starting
+ * from the back of the queue
+ */
+void usb_kill_anchored_urbs(struct usb_anchor *anchor)
+{
+	struct urb *victim;
+
+	spin_lock_irq(&anchor->lock);
+	while (!list_empty(&anchor->urb_list)) {
+		victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list);
+		/* we must make sure the URB isn't freed before we kill it*/
+		usb_get_urb(victim);
+		spin_unlock_irq(&anchor->lock);
+		/* this will unanchor the URB */
+		usb_kill_urb(victim);
+		usb_put_urb(victim);
+		spin_lock_irq(&anchor->lock);
+	}
+	spin_unlock_irq(&anchor->lock);
+}
+EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
+
+/**
+ * usb_wait_anchor_empty_timeout - wait for an anchor to be unused
+ * @anchor: the anchor you want to become unused
+ * @timeout: how long you are willing to wait in milliseconds
+ *
+ * Call this is you want to be sure all an anchor's
+ * URBs have finished
+ */
+int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
+				  unsigned int timeout)
+{
+	return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+				  msecs_to_jiffies(timeout));
+}
+EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
 EXPORT_SYMBOL(usb_init_urb);
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
@@ -485,4 +585,3 @@
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 EXPORT_SYMBOL(usb_kill_urb);
-
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 4a6299b..0fee5c6 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -253,6 +253,7 @@
 	dev->dev.bus = &usb_bus_type;
 	dev->dev.type = &usb_device_type;
 	dev->dev.dma_mask = bus->controller->dma_mask;
+	set_dev_node(&dev->dev, dev_to_node(bus->controller));
 	dev->state = USB_STATE_ATTACHED;
 
 	INIT_LIST_HEAD(&dev->ep0.urb_list);
@@ -578,11 +579,12 @@
  * address (through the pointer provided).
  *
  * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags
- * to avoid behaviors like using "DMA bounce buffers", or tying down I/O
- * mapping hardware for long idle periods.  The implementation varies between
+ * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU
+ * hardware during URB completion/resubmit.  The implementation varies between
  * platforms, depending on details of how DMA will work to this device.
- * Using these buffers also helps prevent cacheline sharing problems on
- * architectures where CPU caches are not DMA-coherent.
+ * Using these buffers also eliminates cacheline sharing problems on
+ * architectures where CPU caches are not DMA-coherent.  On systems without
+ * bus-snooping caches, these buffers are uncached.
  *
  * When the buffer is no longer used, free it with usb_buffer_free().
  */
@@ -607,7 +609,7 @@
  *
  * This reclaims an I/O buffer, letting it be reused.  The memory must have
  * been allocated using usb_buffer_alloc(), and the parameters must match
- * those provided in that allocation request. 
+ * those provided in that allocation request.
  */
 void usb_buffer_free(
 	struct usb_device *dev,
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index bf2eb0d..ad5fa03 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -52,8 +52,16 @@
 
 #else
 
-#define usb_port_suspend(dev)		0
-#define usb_port_resume(dev)		0
+static inline int usb_port_suspend(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline int usb_port_resume(struct usb_device *udev)
+{
+	return 0;
+}
+
 static inline void usb_pm_lock(struct usb_device *udev) {}
 static inline void usb_pm_unlock(struct usb_device *udev) {}
 
@@ -100,11 +108,13 @@
 static inline void mark_active(struct usb_interface *f)
 {
 	f->is_active = 1;
+	f->dev.power.power_state.event = PM_EVENT_ON;
 }
 
 static inline void mark_quiesced(struct usb_interface *f)
 {
 	f->is_active = 0;
+	f->dev.power.power_state.event = PM_EVENT_SUSPEND;
 }
 
 static inline int is_active(const struct usb_interface *f)
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f771a7c..45e01e2 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -42,6 +42,20 @@
 	   For more information, see <http://www.linux-usb.org/gadget> and
 	   the kernel DocBook documentation for this API.
 
+config USB_GADGET_DEBUG
+	boolean "Debugging messages"
+	depends on USB_GADGET && DEBUG_KERNEL && EXPERIMENTAL
+	help
+	   Many controller and gadget drivers will print some debugging
+	   messages if you use this option to ask for those messages.
+
+	   Avoid enabling these messages, even if you're actively
+	   debugging such a driver.  Many drivers will emit so many
+	   messages that the driver timings are affected, which will
+	   either create new failure modes or remove the one you're
+	   trying to track down.  Never enable these messages for a
+	   production build.
+
 config USB_GADGET_DEBUG_FILES
 	boolean "Debugging information files"
 	depends on USB_GADGET && PROC_FS
@@ -208,6 +222,27 @@
 
 	   Select this only if your OMAP board has a Mini-AB connector.
 
+config USB_GADGET_S3C2410
+	boolean "S3C2410 USB Device Controller"
+	depends on ARCH_S3C2410
+	help
+	  Samsung's S3C2410 is an ARM-4 processor with an integrated
+	  full speed USB 1.1 device controller.  It has 4 configurable
+	  endpoints, as well as endpoint zero (for control transfers).
+
+	  This driver has been tested on the S3C2410, S3C2412, and
+	  S3C2440 processors.
+
+config USB_S3C2410
+	tristate
+	depends on USB_GADGET_S3C2410
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
+config USB_S3C2410_DEBUG
+	boolean "S3C2410 udc debug messages"
+	depends on USB_GADGET_S3C2410
+
 config USB_GADGET_AT91
 	boolean "AT91 USB Device Port"
 	depends on ARCH_AT91 && !ARCH_AT91SAM9RL
@@ -226,6 +261,24 @@
 	depends on USB_GADGET_AT91
 	default USB_GADGET
 
+config USB_GADGET_M66592
+	boolean "M66592 driver"
+	select USB_GADGET_DUALSPEED
+	help
+	   M66592 is a USB 2.0 peripheral controller.
+
+	   It has seven configurable endpoints, and endpoint zero.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "m66592_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_M66592
+	tristate
+	depends on USB_GADGET_M66592
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_DUMMY_HCD
 	boolean "Dummy HCD (DEVELOPMENT)"
 	depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5db1939..8ae76f7 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -1,14 +1,20 @@
 #
 # USB peripheral controller drivers
 #
+ifeq ($(CONFIG_USB_GADGET_DEBUG),y)
+	EXTRA_CFLAGS		+= -DDEBUG
+endif
+
 obj-$(CONFIG_USB_DUMMY_HCD)	+= dummy_hcd.o
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
 obj-$(CONFIG_USB_PXA2XX)	+= pxa2xx_udc.o
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
 obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
+obj-$(CONFIG_USB_S3C2410)	+= s3c2410_udc.o
 obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
+obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index ba163f3..63d7d65 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -601,25 +601,6 @@
 	kfree(req);
 }
 
-static void *at91_ep_alloc_buffer(
-	struct usb_ep *_ep,
-	unsigned bytes,
-	dma_addr_t *dma,
-	gfp_t gfp_flags)
-{
-	*dma = ~0;
-	return kmalloc(bytes, gfp_flags);
-}
-
-static void at91_ep_free_buffer(
-	struct usb_ep *ep,
-	void *buf,
-	dma_addr_t dma,
-	unsigned bytes)
-{
-	kfree(buf);
-}
-
 static int at91_ep_queue(struct usb_ep *_ep,
 			struct usb_request *_req, gfp_t gfp_flags)
 {
@@ -788,8 +769,6 @@
 	.disable	= at91_ep_disable,
 	.alloc_request	= at91_ep_alloc_request,
 	.free_request	= at91_ep_free_request,
-	.alloc_buffer	= at91_ep_alloc_buffer,
-	.free_buffer	= at91_ep_free_buffer,
 	.queue		= at91_ep_queue,
 	.dequeue	= at91_ep_dequeue,
 	.set_halt	= at91_ep_set_halt,
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index fcb5526..f2fbdc7 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -497,38 +497,6 @@
 	kfree (req);
 }
 
-static void *
-dummy_alloc_buffer (
-	struct usb_ep *_ep,
-	unsigned bytes,
-	dma_addr_t *dma,
-	gfp_t mem_flags
-) {
-	char			*retval;
-	struct dummy_ep		*ep;
-	struct dummy		*dum;
-
-	ep = usb_ep_to_dummy_ep (_ep);
-	dum = ep_to_dummy (ep);
-
-	if (!dum->driver)
-		return NULL;
-	retval = kmalloc (bytes, mem_flags);
-	*dma = (dma_addr_t) retval;
-	return retval;
-}
-
-static void
-dummy_free_buffer (
-	struct usb_ep *_ep,
-	void *buf,
-	dma_addr_t dma,
-	unsigned bytes
-) {
-	if (bytes)
-		kfree (buf);
-}
-
 static void
 fifo_complete (struct usb_ep *ep, struct usb_request *req)
 {
@@ -659,10 +627,6 @@
 	.alloc_request	= dummy_alloc_request,
 	.free_request	= dummy_free_request,
 
-	.alloc_buffer	= dummy_alloc_buffer,
-	.free_buffer	= dummy_free_buffer,
-	/* map, unmap, ... eventually hook the "generic" dma calls */
-
 	.queue		= dummy_queue,
 	.dequeue	= dummy_dequeue,
 
@@ -1784,8 +1748,7 @@
 
 	spin_lock_irq (&dum->lock);
 	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
-		dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n");
-		rc = -ENODEV;
+		rc = -ESHUTDOWN;
 	} else {
 		dum->rh_state = DUMMY_RH_RUNNING;
 		set_link_state (dum);
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 325bf7c..dbaf867 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -277,7 +277,7 @@
 #define DEV_CONFIG_CDC
 #endif
 
-#ifdef CONFIG_USB_GADGET_HUSB2DEV
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
 #define DEV_CONFIG_CDC
 #endif
 
@@ -292,7 +292,7 @@
 #define	DEV_CONFIG_SUBSET
 #endif
 
-#ifdef CONFIG_USB_GADGET_SH
+#ifdef CONFIG_USB_GADGET_SUPERH
 #define	DEV_CONFIG_SUBSET
 #endif
 
@@ -301,6 +301,10 @@
 #define	DEV_CONFIG_SUBSET
 #endif
 
+#ifdef CONFIG_USB_GADGET_M66592
+#define DEV_CONFIG_CDC
+#endif
+
 
 /*-------------------------------------------------------------------------*/
 
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 4639b62..8712ef9 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3733,19 +3733,12 @@
 	}
 
 	/* Free the data buffers */
-	for (i = 0; i < NUM_BUFFERS; ++i) {
-		struct fsg_buffhd	*bh = &fsg->buffhds[i];
-
-		if (bh->buf)
-			usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma,
-					mod_data.buflen);
-	}
+	for (i = 0; i < NUM_BUFFERS; ++i)
+		kfree(fsg->buffhds[i].buf);
 
 	/* Free the request and buffer for endpoint 0 */
 	if (req) {
-		if (req->buf)
-			usb_ep_free_buffer(fsg->ep0, req->buf,
-					req->dma, EP0_BUFSIZE);
+		kfree(req->buf);
 		usb_ep_free_request(fsg->ep0, req);
 	}
 
@@ -3963,8 +3956,7 @@
 #endif
 
 	if (gadget->is_otg) {
-		otg_desc.bmAttributes |= USB_OTG_HNP,
-		config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		otg_desc.bmAttributes |= USB_OTG_HNP;
 	}
 
 	rc = -ENOMEM;
@@ -3973,8 +3965,7 @@
 	fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
 	if (!req)
 		goto out;
-	req->buf = usb_ep_alloc_buffer(fsg->ep0, EP0_BUFSIZE,
-			&req->dma, GFP_KERNEL);
+	req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
 	if (!req->buf)
 		goto out;
 	req->complete = ep0_complete;
@@ -3986,8 +3977,7 @@
 		/* Allocate for the bulk-in endpoint.  We assume that
 		 * the buffer will also work with the bulk-out (and
 		 * interrupt-in) endpoint. */
-		bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen,
-				&bh->dma, GFP_KERNEL);
+		bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL);
 		if (!bh->buf)
 			goto out;
 		bh->next = bh + 1;
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
index 3ca2b31..10b2b33 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.c
+++ b/drivers/usb/gadget/fsl_usb2_udc.c
@@ -228,7 +228,7 @@
 
 	/* Config PHY interface */
 	portctrl = fsl_readl(&dr_regs->portsc1);
-	portctrl &= ~(PORTSCX_PHY_TYPE_SEL & PORTSCX_PORT_WIDTH);
+	portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
 	switch (udc->phy_mode) {
 	case FSL_USB2_PHY_ULPI:
 		portctrl |= PORTSCX_PTS_ULPI;
@@ -601,39 +601,6 @@
 		kfree(req);
 }
 
-/*------------------------------------------------------------------
- * Allocate an I/O buffer
-*---------------------------------------------------------------------*/
-static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
-		dma_addr_t *dma, gfp_t gfp_flags)
-{
-	struct fsl_ep *ep;
-
-	if (!_ep)
-		return NULL;
-
-	ep = container_of(_ep, struct fsl_ep, ep);
-
-	return dma_alloc_coherent(ep->udc->gadget.dev.parent,
-			bytes, dma, gfp_flags);
-}
-
-/*------------------------------------------------------------------
- * frees an i/o buffer
-*---------------------------------------------------------------------*/
-static void fsl_free_buffer(struct usb_ep *_ep, void *buf,
-		dma_addr_t dma, unsigned bytes)
-{
-	struct fsl_ep *ep;
-
-	if (!_ep)
-		return;
-
-	ep = container_of(_ep, struct fsl_ep, ep);
-
-	dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
-}
-
 /*-------------------------------------------------------------------------*/
 static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
 {
@@ -1047,9 +1014,6 @@
 	.alloc_request = fsl_alloc_request,
 	.free_request = fsl_free_request,
 
-	.alloc_buffer = fsl_alloc_buffer,
-	.free_buffer = fsl_free_buffer,
-
 	.queue = fsl_ep_queue,
 	.dequeue = fsl_ep_dequeue,
 
@@ -2189,27 +2153,19 @@
  * init resource for globle controller
  * Return the udc handle on success or NULL on failure
  ------------------------------------------------------------------*/
-static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
+static int __init struct_udc_setup(struct fsl_udc *udc,
+		struct platform_device *pdev)
 {
-	struct fsl_udc *udc;
 	struct fsl_usb2_platform_data *pdata;
 	size_t size;
 
-	udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
-	if (udc == NULL) {
-		ERR("malloc udc failed\n");
-		return NULL;
-	}
-
 	pdata = pdev->dev.platform_data;
 	udc->phy_mode = pdata->phy_mode;
-	/* max_ep_nr is bidirectional ep number, max_ep doubles the number */
-	udc->max_ep = pdata->max_ep_nr * 2;
 
 	udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
 	if (!udc->eps) {
 		ERR("malloc fsl_ep failed\n");
-		goto cleanup;
+		return -1;
 	}
 
 	/* initialized QHs, take care of alignment */
@@ -2225,7 +2181,7 @@
 	if (!udc->ep_qh) {
 		ERR("malloc QHs for udc failed\n");
 		kfree(udc->eps);
-		goto cleanup;
+		return -1;
 	}
 
 	udc->ep_qh_size = size;
@@ -2244,11 +2200,7 @@
 	udc->remote_wakeup = 0;	/* default to 0 on reset */
 	spin_lock_init(&udc->lock);
 
-	return udc;
-
-cleanup:
-	kfree(udc);
-	return NULL;
+	return 0;
 }
 
 /*----------------------------------------------------------------
@@ -2287,35 +2239,37 @@
 }
 
 /* Driver probe function
- * all intialize operations implemented here except enabling usb_intr reg
+ * all intialization operations implemented here except enabling usb_intr reg
+ * board setup should have been done in the platform code
  */
 static int __init fsl_udc_probe(struct platform_device *pdev)
 {
 	struct resource *res;
 	int ret = -ENODEV;
 	unsigned int i;
+	u32 dccparams;
 
 	if (strcmp(pdev->name, driver_name)) {
 		VDBG("Wrong device\n");
 		return -ENODEV;
 	}
 
-	/* board setup should have been done in the platform code */
-
-	/* Initialize the udc structure including QH member and other member */
-	udc_controller = struct_udc_setup(pdev);
-	if (!udc_controller) {
-		VDBG("udc_controller is NULL \n");
+	udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+	if (udc_controller == NULL) {
+		ERR("malloc udc failed\n");
 		return -ENOMEM;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res)
+	if (!res) {
+		kfree(udc_controller);
 		return -ENXIO;
+	}
 
 	if (!request_mem_region(res->start, res->end - res->start + 1,
 				driver_name)) {
 		ERR("request mem region for %s failed \n", pdev->name);
+		kfree(udc_controller);
 		return -EBUSY;
 	}
 
@@ -2328,13 +2282,24 @@
 	usb_sys_regs = (struct usb_sys_interface *)
 			((u32)dr_regs + USB_DR_SYS_OFFSET);
 
+	/* Read Device Controller Capability Parameters register */
+	dccparams = fsl_readl(&dr_regs->dccparams);
+	if (!(dccparams & DCCPARAMS_DC)) {
+		ERR("This SOC doesn't support device role\n");
+		ret = -ENODEV;
+		goto err2;
+	}
+	/* Get max device endpoints */
+	/* DEN is bidirectional ep number, max_ep doubles the number */
+	udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
+
 	udc_controller->irq = platform_get_irq(pdev, 0);
 	if (!udc_controller->irq) {
 		ret = -ENODEV;
 		goto err2;
 	}
 
-	ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ,
+	ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
 			driver_name, udc_controller);
 	if (ret != 0) {
 		ERR("cannot request irq %d err %d \n",
@@ -2342,6 +2307,13 @@
 		goto err2;
 	}
 
+	/* Initialize the udc structure including QH member and other member */
+	if (struct_udc_setup(udc_controller, pdev)) {
+		ERR("Can't initialize udc data structure\n");
+		ret = -ENOMEM;
+		goto err3;
+	}
+
 	/* initialize usb hw reg except for regs for EP,
 	 * leave usbintr reg untouched */
 	dr_controller_setup(udc_controller);
@@ -2403,6 +2375,7 @@
 	iounmap(dr_regs);
 err1:
 	release_mem_region(res->start, res->end - res->start + 1);
+	kfree(udc_controller);
 	return ret;
 }
 
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
index c6291e0..832ab82 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -101,6 +101,10 @@
 #define WAIT_FOR_OUT_STATUS     3
 #define DATA_STATE_RECV         4
 
+/* Device Controller Capability Parameter register */
+#define DCCPARAMS_DC				0x00000080
+#define DCCPARAMS_DEN_MASK			0x0000001f
+
 /* Frame Index Register Bit Masks */
 #define	USB_FRINDEX_MASKS			0x3fff
 /* USB CMD  Register Bit Masks */
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index d041b91..53e9139 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -8,6 +8,8 @@
  * (And avoiding all runtime comparisons in typical one-choice configs!)
  *
  * NOTE:  some of these controller drivers may not be available yet.
+ * Some are available on 2.4 kernels; several are available, but not
+ * yet pushed in the 2.6 mainline tree.
  */
 #ifdef CONFIG_USB_GADGET_NET2280
 #define	gadget_is_net2280(g)	!strcmp("net2280", (g)->name)
@@ -33,12 +35,14 @@
 #define	gadget_is_goku(g)	0
 #endif
 
+/* SH3 UDC -- not yet ported 2.4 --> 2.6 */
 #ifdef CONFIG_USB_GADGET_SUPERH
 #define	gadget_is_sh(g)		!strcmp("sh_udc", (g)->name)
 #else
 #define	gadget_is_sh(g)		0
 #endif
 
+/* not yet stable on 2.6 (would help "original Zaurus") */
 #ifdef CONFIG_USB_GADGET_SA1100
 #define	gadget_is_sa1100(g)	!strcmp("sa1100_udc", (g)->name)
 #else
@@ -51,6 +55,7 @@
 #define	gadget_is_lh7a40x(g)	0
 #endif
 
+/* handhelds.org tree (?) */
 #ifdef CONFIG_USB_GADGET_MQ11XX
 #define	gadget_is_mq11xx(g)	!strcmp("mq11xx_udc", (g)->name)
 #else
@@ -63,22 +68,24 @@
 #define	gadget_is_omap(g)	0
 #endif
 
+/* not yet ported 2.4 --> 2.6 */
 #ifdef CONFIG_USB_GADGET_N9604
 #define	gadget_is_n9604(g)	!strcmp("n9604_udc", (g)->name)
 #else
 #define	gadget_is_n9604(g)	0
 #endif
 
+/* various unstable versions available */
 #ifdef CONFIG_USB_GADGET_PXA27X
 #define	gadget_is_pxa27x(g)	!strcmp("pxa27x_udc", (g)->name)
 #else
 #define	gadget_is_pxa27x(g)	0
 #endif
 
-#ifdef CONFIG_USB_GADGET_HUSB2DEV
-#define gadget_is_husb2dev(g)	!strcmp("husb2_udc", (g)->name)
+#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+#define gadget_is_atmel_usba(g)	!strcmp("atmel_usba_udc", (g)->name)
 #else
-#define gadget_is_husb2dev(g)	0
+#define gadget_is_atmel_usba(g)	0
 #endif
 
 #ifdef CONFIG_USB_GADGET_S3C2410
@@ -93,6 +100,7 @@
 #define gadget_is_at91(g)	0
 #endif
 
+/* status unclear */
 #ifdef CONFIG_USB_GADGET_IMX
 #define gadget_is_imx(g)	!strcmp("imx_udc", (g)->name)
 #else
@@ -106,6 +114,7 @@
 #endif
 
 /* Mentor high speed function controller */
+/* from Montavista kernel (?) */
 #ifdef CONFIG_USB_GADGET_MUSBHSFC
 #define gadget_is_musbhsfc(g)	!strcmp("musbhsfc_udc", (g)->name)
 #else
@@ -119,12 +128,20 @@
 #define gadget_is_musbhdrc(g)	0
 #endif
 
+/* from Montavista kernel (?) */
 #ifdef CONFIG_USB_GADGET_MPC8272
 #define gadget_is_mpc8272(g)	!strcmp("mpc8272_udc", (g)->name)
 #else
 #define gadget_is_mpc8272(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_M66592
+#define	gadget_is_m66592(g)	!strcmp("m66592_udc", (g)->name)
+#else
+#define	gadget_is_m66592(g)	0
+#endif
+
+
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
 // ...
@@ -181,9 +198,11 @@
 		return 0x16;
 	else if (gadget_is_mpc8272(gadget))
 		return 0x17;
-	else if (gadget_is_husb2dev(gadget))
+	else if (gadget_is_atmel_usba(gadget))
 		return 0x18;
 	else if (gadget_is_fsl_usb2(gadget))
 		return 0x19;
+	else if (gadget_is_m66592(gadget))
+		return 0x20;
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index d08a8d0..1c5aa49 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -1248,17 +1248,11 @@
 	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);
+	dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ);
 	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;
 
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index ae931af..d6c5f11 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -20,7 +20,6 @@
  *  - DMA works with ep1 (OUT transfers) and ep2 (IN transfers).
  */
 
-#undef DEBUG
 // #define	VERBOSE		/* extra debug messages (success too) */
 // #define	USB_TRACE	/* packet-level success messages */
 
@@ -296,51 +295,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-/* 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 *
-goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
-			dma_addr_t *dma, gfp_t gfp_flags)
-{
-	void		*retval;
-	struct goku_ep	*ep;
-
-	ep = container_of(_ep, struct goku_ep, ep);
-	if (!_ep)
-		return NULL;
-	*dma = DMA_ADDR_INVALID;
-
-	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.
-		retval = dma_alloc_coherent(&ep->dev->pdev->dev,
-				bytes, dma, gfp_flags);
-	} else
-		retval = kmalloc(bytes, gfp_flags);
-	return retval;
-}
-
-static void
-goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes)
-{
-	/* free memory into the right allocator */
-	if (dma != DMA_ADDR_INVALID) {
-		struct goku_ep	*ep;
-
-		ep = container_of(_ep, struct goku_ep, ep);
-		if (!_ep)
-			return;
-		dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
-	} else
-		kfree (buf);
-}
-
-/*-------------------------------------------------------------------------*/
-
 static void
 done(struct goku_ep *ep, struct goku_request *req, int status)
 {
@@ -485,7 +439,7 @@
 			/* use ep1/ep2 double-buffering for OUT */
 			if (!(size & PACKET_ACTIVE))
 				size = readl(&regs->EPxSizeLB[ep->num]);
-			if (!(size & PACKET_ACTIVE)) 	// "can't happen"
+			if (!(size & PACKET_ACTIVE))	/* "can't happen" */
 				break;
 			size &= DATASIZE;	/* EPxSizeH == 0 */
 
@@ -1026,9 +980,6 @@
 	.alloc_request	= goku_alloc_request,
 	.free_request	= goku_free_request,
 
-	.alloc_buffer	= goku_alloc_buffer,
-	.free_buffer	= goku_free_buffer,
-
 	.queue		= goku_queue,
 	.dequeue	= goku_dequeue,
 
@@ -1140,17 +1091,17 @@
 		is_usb_connected
 			? ((tmp & PW_PULLUP) ? "full speed" : "powered")
 			: "disconnected",
-		({char *tmp;
+		({char *state;
 		switch(dev->ep0state){
-		case EP0_DISCONNECT:	tmp = "ep0_disconnect"; break;
-		case EP0_IDLE:		tmp = "ep0_idle"; break;
-		case EP0_IN:		tmp = "ep0_in"; break;
-		case EP0_OUT:		tmp = "ep0_out"; break;
-		case EP0_STATUS:	tmp = "ep0_status"; break;
-		case EP0_STALL:		tmp = "ep0_stall"; break;
-		case EP0_SUSPEND:	tmp = "ep0_suspend"; break;
-		default:		tmp = "ep0_?"; break;
-		} tmp; })
+		case EP0_DISCONNECT:	state = "ep0_disconnect"; break;
+		case EP0_IDLE:		state = "ep0_idle"; break;
+		case EP0_IN:		state = "ep0_in"; break;
+		case EP0_OUT:		state = "ep0_out"; break;
+		case EP0_STATUS:	state = "ep0_status"; break;
+		case EP0_STALL:		state = "ep0_stall"; break;
+		case EP0_SUSPEND:	state = "ep0_suspend"; break;
+		default:		state = "ep0_?"; break;
+		} state; })
 		);
 	size -= t;
 	next += t;
@@ -1195,7 +1146,6 @@
 	for (i = 0; i < 4; i++) {
 		struct goku_ep		*ep = &dev->ep [i];
 		struct goku_request	*req;
-		int			t;
 
 		if (i && !ep->desc)
 			continue;
@@ -1283,7 +1233,7 @@
 static void udc_reinit (struct goku_udc *dev)
 {
 	static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" };
-	
+
 	unsigned i;
 
 	INIT_LIST_HEAD (&dev->gadget.ep_list);
@@ -1896,9 +1846,9 @@
 
 	/* done */
 	the_controller = dev;
-	device_register(&dev->gadget.dev);
-
-	return 0;
+	retval = device_register(&dev->gadget.dev);
+	if (retval == 0)
+		return 0;
 
 done:
 	if (dev)
@@ -1910,8 +1860,8 @@
 /*-------------------------------------------------------------------------*/
 
 static struct pci_device_id pci_ids [] = { {
-	.class = 	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
-	.class_mask = 	~0,
+	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+	.class_mask =	~0,
 	.vendor =	0x102f,		/* Toshiba */
 	.device =	0x0107,		/* this UDC */
 	.subvendor =	PCI_ANY_ID,
diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h
index ea8c8e5..bc4eb1e 100644
--- a/drivers/usb/gadget/goku_udc.h
+++ b/drivers/usb/gadget/goku_udc.h
@@ -41,8 +41,10 @@
 #define INT_SYSERROR		0x40000
 #define INT_PWRDETECT		0x80000
 
-#define	INT_DEVWIDE		(INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
-#define	INT_EP0 		(INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
+#define	INT_DEVWIDE \
+	(INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND)
+#define	INT_EP0 \
+	(INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK)
 
 	u32	dma_master;
 #define MST_EOPB_DIS		0x0800
@@ -231,7 +233,7 @@
 enum ep0state {
 	EP0_DISCONNECT,		/* no host */
 	EP0_IDLE,		/* between STATUS ack and SETUP report */
-	EP0_IN, EP0_OUT, 	/* data stage */
+	EP0_IN, EP0_OUT,	/* data stage */
 	EP0_STATUS,		/* status stage */
 	EP0_STALL,		/* data or status stages */
 	EP0_SUSPEND,		/* usb suspend */
@@ -242,7 +244,7 @@
 	struct usb_gadget		gadget;
 	spinlock_t			lock;
 	struct goku_ep			ep[4];
-	struct usb_gadget_driver 	*driver;
+	struct usb_gadget_driver	*driver;
 
 	enum ep0state			ep0state;
 	unsigned			got_irq:1,
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 46d0e52..e60745ff 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -37,7 +37,7 @@
 #include <linux/device.h>
 #include <linux/moduleparam.h>
 
-#include <linux/usb_gadgetfs.h>
+#include <linux/usb/gadgetfs.h>
 #include <linux/usb_gadget.h>
 
 
@@ -923,7 +923,7 @@
 	struct dev_data		*dev = ep->driver_data;
 
 	if (req->buf != dev->rbuf) {
-		usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
+		kfree(req->buf);
 		req->buf = dev->rbuf;
 		req->dma = DMA_ADDR_INVALID;
 	}
@@ -963,7 +963,7 @@
 		return -EBUSY;
 	}
 	if (len > sizeof (dev->rbuf))
-		req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC);
+		req->buf = kmalloc(len, GFP_ATOMIC);
 	if (req->buf == 0) {
 		req->buf = dev->rbuf;
 		return -ENOMEM;
@@ -1505,7 +1505,7 @@
 		}
 		break;
 
-#ifndef	CONFIG_USB_GADGETFS_PXA2XX
+#ifndef	CONFIG_USB_GADGET_PXA2XX
 	/* PXA automagically handles this request too */
 	case USB_REQ_GET_CONFIGURATION:
 		if (ctrl->bRequestType != 0x80)
diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c
index a0a73c0..e78c2dd 100644
--- a/drivers/usb/gadget/lh7a40x_udc.c
+++ b/drivers/usb/gadget/lh7a40x_udc.c
@@ -75,10 +75,6 @@
 static int lh7a40x_ep_disable(struct usb_ep *ep);
 static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t);
 static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *);
-static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *,
-				  gfp_t);
-static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t,
-				unsigned);
 static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t);
 static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *);
 static int lh7a40x_set_halt(struct usb_ep *ep, int);
@@ -104,9 +100,6 @@
 	.alloc_request = lh7a40x_alloc_request,
 	.free_request = lh7a40x_free_request,
 
-	.alloc_buffer = lh7a40x_alloc_buffer,
-	.free_buffer = lh7a40x_free_buffer,
-
 	.queue = lh7a40x_queue,
 	.dequeue = lh7a40x_dequeue,
 
@@ -1134,26 +1127,6 @@
 	kfree(req);
 }
 
-static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes,
-				  dma_addr_t * dma, gfp_t gfp_flags)
-{
-	char *retval;
-
-	DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags);
-
-	retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
-	if (retval)
-		*dma = virt_to_bus(retval);
-	return retval;
-}
-
-static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma,
-				unsigned bytes)
-{
-	DEBUG("%s, %p\n", __FUNCTION__, ep);
-	kfree(buf);
-}
-
 /** Queue one request
  *  Kickstart transfer if needed
  *  NOTE: Sets INDEX register
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
new file mode 100644
index 0000000..c0a962b
--- /dev/null
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -0,0 +1,1653 @@
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "m66592-udc.h"
+
+MODULE_DESCRIPTION("M66592 USB gadget driiver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+
+#define DRIVER_VERSION	"29 May 2007"
+
+/* module parameters */
+static unsigned short clock = M66592_XTAL24;
+module_param(clock, ushort, 0644);
+MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0(default=16384)");
+static unsigned short vif = M66592_LDRV;
+module_param(vif, ushort, 0644);
+MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)");
+static unsigned short endian = 0;
+module_param(endian, ushort, 0644);
+MODULE_PARM_DESC(endian, "data endian: big=256, little=0(default=0)");
+static unsigned short irq_sense = M66592_INTL;
+module_param(irq_sense, ushort, 0644);
+MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0(default=2)");
+
+static const char udc_name[] = "m66592_udc";
+static const char *m66592_ep_name[] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"
+};
+
+static void disable_controller(struct m66592 *m66592);
+static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req);
+static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req);
+static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags);
+
+static void transfer_complete(struct m66592_ep *ep,
+			      struct m66592_request *req,
+			      int status);
+/*-------------------------------------------------------------------------*/
+static inline u16 get_usb_speed(struct m66592 *m66592)
+{
+	return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST);
+}
+
+static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum,
+			    unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
+		    M66592_INTENB0);
+	m66592_bset(m66592, (1 << pipenum), reg);
+	m66592_write(m66592, tmp, M66592_INTENB0);
+}
+
+static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum,
+			     unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
+		    M66592_INTENB0);
+	m66592_bclr(m66592, (1 << pipenum), reg);
+	m66592_write(m66592, tmp, M66592_INTENB0);
+}
+
+static void m66592_usb_connect(struct m66592 *m66592)
+{
+	m66592_bset(m66592, M66592_CTRE, M66592_INTENB0);
+	m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
+		    M66592_INTENB0);
+	m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
+
+	m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
+}
+
+static void m66592_usb_disconnect(struct m66592 *m66592)
+{
+	m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
+		    M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+
+	m66592->gadget.speed = USB_SPEED_UNKNOWN;
+	spin_unlock(&m66592->lock);
+	m66592->driver->disconnect(&m66592->gadget);
+	spin_lock(&m66592->lock);
+
+	disable_controller(m66592);
+	INIT_LIST_HEAD(&m66592->ep[0].queue);
+}
+
+static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum)
+{
+	u16 pid = 0;
+	unsigned long offset;
+
+	if (pipenum == 0)
+		pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID;
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		pid = m66592_read(m66592, offset) & M66592_PID;
+	} else
+		printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+
+	return pid;
+}
+
+static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum,
+				       u16 pid)
+{
+	unsigned long offset;
+
+	if (pipenum == 0)
+		m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		m66592_mdfy(m66592, pid, M66592_PID, offset);
+	} else
+		printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+}
+
+static inline void pipe_start(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_BUF);
+}
+
+static inline void pipe_stop(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_NAK);
+}
+
+static inline void pipe_stall(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_STALL);
+}
+
+static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum)
+{
+	u16 ret = 0;
+	unsigned long offset;
+
+	if (pipenum == 0)
+		ret = m66592_read(m66592, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		ret = m66592_read(m66592, offset);
+	} else
+		printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+
+	return ret;
+}
+
+static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum)
+{
+	unsigned long offset;
+
+	pipe_stop(m66592, pipenum);
+
+	if (pipenum == 0)
+		m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		m66592_bset(m66592, M66592_SQCLR, offset);
+	} else
+		printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum);
+}
+
+static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum)
+{
+	u16 tmp;
+	int size;
+
+	if (pipenum == 0) {
+		tmp = m66592_read(m66592, M66592_DCPCFG);
+		if ((tmp & M66592_CNTMD) != 0)
+			size = 256;
+		else {
+			tmp = m66592_read(m66592, M66592_DCPMAXP);
+			size = tmp & M66592_MAXP;
+		}
+	} else {
+		m66592_write(m66592, pipenum, M66592_PIPESEL);
+		tmp = m66592_read(m66592, M66592_PIPECFG);
+		if ((tmp & M66592_CNTMD) != 0) {
+			tmp = m66592_read(m66592, M66592_PIPEBUF);
+			size = ((tmp >> 10) + 1) * 64;
+		} else {
+			tmp = m66592_read(m66592, M66592_PIPEMAXP);
+			size = tmp & M66592_MXPS;
+		}
+	}
+
+	return size;
+}
+
+static inline void pipe_change(struct m66592 *m66592, u16 pipenum)
+{
+	struct m66592_ep *ep = m66592->pipenum2ep[pipenum];
+
+	if (ep->use_dma)
+		return;
+
+	m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel);
+
+	ndelay(450);
+
+	m66592_bset(m66592, M66592_MBW, ep->fifosel);
+}
+
+static int pipe_buffer_setting(struct m66592 *m66592,
+			       struct m66592_pipe_info *info)
+{
+	u16 bufnum = 0, buf_bsize = 0;
+	u16 pipecfg = 0;
+
+	if (info->pipe == 0)
+		return -EINVAL;
+
+	m66592_write(m66592, info->pipe, M66592_PIPESEL);
+
+	if (info->dir_in)
+		pipecfg |= M66592_DIR;
+	pipecfg |= info->type;
+	pipecfg |= info->epnum;
+	switch (info->type) {
+	case M66592_INT:
+		bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT);
+		buf_bsize = 0;
+		break;
+	case M66592_BULK:
+		bufnum = m66592->bi_bufnum +
+			 (info->pipe - M66592_BASE_PIPENUM_BULK) * 16;
+		m66592->bi_bufnum += 16;
+		buf_bsize = 7;
+		pipecfg |= M66592_DBLB;
+		if (!info->dir_in)
+			pipecfg |= M66592_SHTNAK;
+		break;
+	case M66592_ISO:
+		bufnum = m66592->bi_bufnum +
+			 (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16;
+		m66592->bi_bufnum += 16;
+		buf_bsize = 7;
+		break;
+	}
+	if (m66592->bi_bufnum > M66592_MAX_BUFNUM) {
+		printk(KERN_ERR "m66592 pipe memory is insufficient(%d)\n",
+		       m66592->bi_bufnum);
+		return -ENOMEM;
+	}
+
+	m66592_write(m66592, pipecfg, M66592_PIPECFG);
+	m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF);
+	m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP);
+	if (info->interval)
+		info->interval--;
+	m66592_write(m66592, info->interval, M66592_PIPEPERI);
+
+	return 0;
+}
+
+static void pipe_buffer_release(struct m66592 *m66592,
+				struct m66592_pipe_info *info)
+{
+	if (info->pipe == 0)
+		return;
+
+	switch (info->type) {
+	case M66592_BULK:
+		if (is_bulk_pipe(info->pipe))
+			m66592->bi_bufnum -= 16;
+		break;
+	case M66592_ISO:
+		if (is_isoc_pipe(info->pipe))
+			m66592->bi_bufnum -= 16;
+		break;
+	}
+
+	if (is_bulk_pipe(info->pipe)) {
+		m66592->bulk--;
+	} else if (is_interrupt_pipe(info->pipe))
+		m66592->interrupt--;
+	else if (is_isoc_pipe(info->pipe)) {
+		m66592->isochronous--;
+		if (info->type == M66592_BULK)
+			m66592->bulk--;
+	} else
+		printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n",
+		       info->pipe);
+}
+
+static void pipe_initialize(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+
+	m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel);
+
+	m66592_write(m66592, M66592_ACLRM, ep->pipectr);
+	m66592_write(m66592, 0, ep->pipectr);
+	m66592_write(m66592, M66592_SQCLR, ep->pipectr);
+	if (ep->use_dma) {
+		m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel);
+
+		ndelay(450);
+
+		m66592_bset(m66592, M66592_MBW, ep->fifosel);
+	}
+}
+
+static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
+			      const struct usb_endpoint_descriptor *desc,
+			      u16 pipenum, int dma)
+{
+	if ((pipenum != 0) && dma) {
+		if (m66592->num_dma == 0) {
+			m66592->num_dma++;
+			ep->use_dma = 1;
+			ep->fifoaddr = M66592_D0FIFO;
+			ep->fifosel = M66592_D0FIFOSEL;
+			ep->fifoctr = M66592_D0FIFOCTR;
+			ep->fifotrn = M66592_D0FIFOTRN;
+		} else if (m66592->num_dma == 1) {
+			m66592->num_dma++;
+			ep->use_dma = 1;
+			ep->fifoaddr = M66592_D1FIFO;
+			ep->fifosel = M66592_D1FIFOSEL;
+			ep->fifoctr = M66592_D1FIFOCTR;
+			ep->fifotrn = M66592_D1FIFOTRN;
+		} else {
+			ep->use_dma = 0;
+			ep->fifoaddr = M66592_CFIFO;
+			ep->fifosel = M66592_CFIFOSEL;
+			ep->fifoctr = M66592_CFIFOCTR;
+			ep->fifotrn = 0;
+		}
+	} else {
+		ep->use_dma = 0;
+		ep->fifoaddr = M66592_CFIFO;
+		ep->fifosel = M66592_CFIFOSEL;
+		ep->fifoctr = M66592_CFIFOCTR;
+		ep->fifotrn = 0;
+	}
+
+	ep->pipectr = get_pipectr_addr(pipenum);
+	ep->pipenum = pipenum;
+	ep->ep.maxpacket = desc->wMaxPacketSize;
+	m66592->pipenum2ep[pipenum] = ep;
+	m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
+	INIT_LIST_HEAD(&ep->queue);
+}
+
+static void m66592_ep_release(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 pipenum = ep->pipenum;
+
+	if (pipenum == 0)
+		return;
+
+	if (ep->use_dma)
+		m66592->num_dma--;
+	ep->pipenum = 0;
+	ep->busy = 0;
+	ep->use_dma = 0;
+}
+
+static int alloc_pipe_config(struct m66592_ep *ep,
+			     const struct usb_endpoint_descriptor *desc)
+{
+	struct m66592 *m66592 = ep->m66592;
+	struct m66592_pipe_info info;
+	int dma = 0;
+	int *counter;
+	int ret;
+
+	ep->desc = desc;
+
+	BUG_ON(ep->pipenum);
+
+	switch(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (m66592->bulk >= M66592_MAX_NUM_BULK) {
+			if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
+				printk(KERN_ERR "bulk pipe is insufficient\n");
+				return -ENODEV;
+			} else {
+				info.pipe = M66592_BASE_PIPENUM_ISOC +
+					    m66592->isochronous;
+				counter = &m66592->isochronous;
+			}
+		} else {
+			info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk;
+			counter = &m66592->bulk;
+		}
+		info.type = M66592_BULK;
+		dma = 1;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (m66592->interrupt >= M66592_MAX_NUM_INT) {
+			printk(KERN_ERR "interrupt pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt;
+		info.type = M66592_INT;
+		counter = &m66592->interrupt;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
+			printk(KERN_ERR "isochronous pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous;
+		info.type = M66592_ISO;
+		counter = &m66592->isochronous;
+		break;
+	default:
+		printk(KERN_ERR "unexpect xfer type\n");
+		return -EINVAL;
+	}
+	ep->type = info.type;
+
+	info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	info.maxpacket = desc->wMaxPacketSize;
+	info.interval = desc->bInterval;
+	if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		info.dir_in = 1;
+	else
+		info.dir_in = 0;
+
+	ret = pipe_buffer_setting(m66592, &info);
+	if (ret < 0) {
+		printk(KERN_ERR "pipe_buffer_setting fail\n");
+		return ret;
+	}
+
+	(*counter)++;
+	if ((counter == &m66592->isochronous) && info.type == M66592_BULK)
+		m66592->bulk++;
+
+	m66592_ep_setting(m66592, ep, desc, info.pipe, dma);
+	pipe_initialize(ep);
+
+	return 0;
+}
+
+static int free_pipe_config(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	struct m66592_pipe_info info;
+
+	info.pipe = ep->pipenum;
+	info.type = ep->type;
+	pipe_buffer_release(m66592, &info);
+	m66592_ep_release(ep);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum)
+{
+	enable_irq_ready(m66592, pipenum);
+	enable_irq_nrdy(m66592, pipenum);
+}
+
+static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum)
+{
+	disable_irq_ready(m66592, pipenum);
+	disable_irq_nrdy(m66592, pipenum);
+}
+
+/* if complete is true, gadget driver complete function is not call */
+static void control_end(struct m66592 *m66592, unsigned ccpl)
+{
+	m66592->ep[0].internal_ccpl = ccpl;
+	pipe_start(m66592, 0);
+	m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR);
+}
+
+static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, ep->pipenum);
+	m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0,
+		    (M66592_ISEL | M66592_CURPIPE),
+		    M66592_CFIFOSEL);
+	m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+	if (req->req.length == 0) {
+		m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+		pipe_start(m66592, 0);
+		transfer_complete(ep, req, 0);
+	} else {
+		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
+		irq_ep0_write(ep, req);
+	}
+}
+
+static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 tmp;
+
+	pipe_change(m66592, ep->pipenum);
+	disable_irq_empty(m66592, ep->pipenum);
+	pipe_start(m66592, ep->pipenum);
+
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0))
+		pipe_irq_enable(m66592, ep->pipenum);
+	else
+		irq_packet_write(ep, req);
+}
+
+static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 pipenum = ep->pipenum;
+
+	if (ep->pipenum == 0) {
+		m66592_mdfy(m66592, M66592_PIPE0,
+			    (M66592_ISEL | M66592_CURPIPE),
+			    M66592_CFIFOSEL);
+		m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+		pipe_start(m66592, pipenum);
+		pipe_irq_enable(m66592, pipenum);
+	} else {
+		if (ep->use_dma) {
+			m66592_bset(m66592, M66592_TRCLR, ep->fifosel);
+			pipe_change(m66592, pipenum);
+			m66592_bset(m66592, M66592_TRENB, ep->fifosel);
+			m66592_write(m66592,
+				     (req->req.length + ep->ep.maxpacket - 1) /
+				     ep->ep.maxpacket, ep->fifotrn);
+		}
+		pipe_start(m66592, pipenum);	/* trigger once */
+		pipe_irq_enable(m66592, pipenum);
+	}
+}
+
+static void start_packet(struct m66592_ep *ep, struct m66592_request *req)
+{
+	if (ep->desc->bEndpointAddress & USB_DIR_IN)
+		start_packet_write(ep, req);
+	else
+		start_packet_read(ep, req);
+}
+
+static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 ctsq;
+
+	ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ;
+
+	switch (ctsq) {
+	case M66592_CS_RDDS:
+		start_ep0_write(ep, req);
+		break;
+	case M66592_CS_WRDS:
+		start_packet_read(ep, req);
+		break;
+
+	case M66592_CS_WRND:
+		control_end(ep->m66592, 0);
+		break;
+	default:
+		printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static void init_controller(struct m66592 *m66592)
+{
+	m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND),
+		    M66592_PINCFG);
+	m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);		/* High spd */
+	m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG);
+
+	m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
+	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+	m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+
+	m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+
+	msleep(3);
+
+	m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+
+	msleep(1);
+
+	m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+
+	m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
+	m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
+		     M66592_DMA0CFG);
+}
+
+static void disable_controller(struct m66592 *m66592)
+{
+	m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
+	udelay(1);
+	m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
+	udelay(1);
+	m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
+	udelay(1);
+	m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
+}
+
+static void m66592_start_xclock(struct m66592 *m66592)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_SYSCFG);
+	if (!(tmp & M66592_XCKE))
+		m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+}
+
+/*-------------------------------------------------------------------------*/
+static void transfer_complete(struct m66592_ep *ep,
+			      struct m66592_request *req,
+			      int status)
+{
+	int restart = 0;
+
+	if (unlikely(ep->pipenum == 0)) {
+		if (ep->internal_ccpl) {
+			ep->internal_ccpl = 0;
+			return;
+		}
+	}
+
+	list_del_init(&req->queue);
+	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		req->req.status = -ESHUTDOWN;
+	else
+		req->req.status = status;
+
+	if (!list_empty(&ep->queue))
+		restart = 1;
+
+	if (likely(req->req.complete))
+		req->req.complete(&ep->ep, &req->req);
+
+	if (restart) {
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		if (ep->desc)
+			start_packet(ep, req);
+	}
+}
+
+static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	int i;
+	volatile u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, pipenum);
+	m66592_bset(m66592, M66592_ISEL, ep->fifosel);
+
+	i = 0;
+	do {
+		tmp = m66592_read(m66592, ep->fifoctr);
+		if (i++ > 100000) {
+			printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus"
+				"conflict. please power off this controller.");
+			return;
+		}
+		ndelay(1);
+	} while ((tmp & M66592_FRDY) == 0);
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(m66592, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		if (size > 0)
+			m66592_write_fifo(m66592, ep->fifoaddr, buf, size);
+		if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
+			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length)) ||
+	    (size % ep->ep.maxpacket) || (size == 0)) {
+		disable_irq_ready(m66592, pipenum);
+		disable_irq_empty(m66592, pipenum);
+	} else {
+		disable_irq_ready(m66592, pipenum);
+		enable_irq_empty(m66592, pipenum);
+	}
+	pipe_start(m66592, pipenum);
+}
+
+static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, pipenum);
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0)) {
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum);
+		return;
+	}
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(m66592, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		m66592_write_fifo(m66592, ep->fifoaddr, buf, size);
+		if ((size == 0) || ((size % ep->ep.maxpacket) != 0) ||
+		    ((bufsize != ep->ep.maxpacket) && (bufsize > size)))
+			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length)) ||
+	    (size % ep->ep.maxpacket) || (size == 0)) {
+		disable_irq_ready(m66592, pipenum);
+		enable_irq_empty(m66592, pipenum);
+	} else {
+		disable_irq_empty(m66592, pipenum);
+		pipe_irq_enable(m66592, pipenum);
+	}
+}
+
+static void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 tmp;
+	int rcv_len, bufsize, req_len;
+	int size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+	int finish = 0;
+
+	pipe_change(m66592, pipenum);
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0)) {
+		req->req.status = -EPIPE;
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		printk(KERN_ERR "read fifo not ready");
+		return;
+	}
+
+	/* prepare parameters */
+	rcv_len = tmp & M66592_DTLN;
+	bufsize = get_buffer_size(m66592, pipenum);
+
+	buf = req->req.buf + req->req.actual;
+	req_len = req->req.length - req->req.actual;
+	if (rcv_len < bufsize)
+		size = min(rcv_len, req_len);
+	else
+		size = min(bufsize, req_len);
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length)) ||
+	    (size % ep->ep.maxpacket) || (size == 0)) {
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		finish = 1;
+	}
+
+	/* read fifo */
+	if (req->req.buf) {
+		if (size == 0)
+			m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+		else
+			m66592_read_fifo(m66592, ep->fifoaddr, buf, size);
+	}
+
+	if ((ep->pipenum != 0) && finish)
+		transfer_complete(ep, req, 0);
+}
+
+static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb)
+{
+	u16 check;
+	u16 pipenum;
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+
+	if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) {
+		m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS);
+		m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE,
+			    M66592_CFIFOSEL);
+
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		irq_packet_read(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				m66592_write(m66592, ~check, M66592_BRDYSTS);
+				ep = m66592->pipenum2ep[pipenum];
+				req = list_entry(ep->queue.next,
+						 struct m66592_request, queue);
+				if (ep->desc->bEndpointAddress & USB_DIR_IN)
+					irq_packet_write(ep, req);
+				else
+					irq_packet_read(ep, req);
+			}
+		}
+	}
+}
+
+static void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb)
+{
+	u16 tmp;
+	u16 check;
+	u16 pipenum;
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+
+	if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) {
+		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
+
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		irq_ep0_write(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				m66592_write(m66592, ~check, M66592_BEMPSTS);
+				tmp = control_reg_get(m66592, pipenum);
+				if ((tmp & M66592_INBUFM) == 0) {
+					disable_irq_empty(m66592, pipenum);
+					pipe_irq_disable(m66592, pipenum);
+					pipe_stop(m66592, pipenum);
+					ep = m66592->pipenum2ep[pipenum];
+					req = list_entry(ep->queue.next,
+							 struct m66592_request,
+							 queue);
+					if (!list_empty(&ep->queue))
+						transfer_complete(ep, req, 0);
+				}
+			}
+		}
+	}
+}
+
+static void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	struct m66592_ep *ep;
+	u16 pid;
+	u16 status = 0;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		status = 1;	/* selfpower */
+		break;
+	case USB_RECIP_INTERFACE:
+		status = 0;
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK];
+		pid = control_reg_get_pid(m66592, ep->pipenum);
+		if (pid == M66592_PID_STALL)
+			status = 1;
+		else
+			status = 0;
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		return;		/* exit */
+	}
+
+	*m66592->ep0_buf = status;
+	m66592->ep0_req->buf = m66592->ep0_buf;
+	m66592->ep0_req->length = 2;
+	m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL);
+}
+
+static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_INTERFACE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct m66592_ep *ep;
+		struct m66592_request *req;
+
+		ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK];
+		pipe_stop(m66592, ep->pipenum);
+		control_reg_sqclr(m66592, ep->pipenum);
+
+		control_end(m66592, 1);
+
+		req = list_entry(ep->queue.next,
+		struct m66592_request, queue);
+		if (ep->busy) {
+			ep->busy = 0;
+			if (list_empty(&ep->queue))
+				break;
+			start_packet(ep, req);
+		} else if (!list_empty(&ep->queue))
+			pipe_start(m66592, ep->pipenum);
+		}
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		break;
+	}
+}
+
+static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_INTERFACE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct m66592_ep *ep;
+
+		ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK];
+		pipe_stall(m66592, ep->pipenum);
+
+		control_end(m66592, 1);
+		}
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		break;
+	}
+}
+
+/* if return value is true, call class driver's setup() */
+static int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	u16 *p = (u16 *)ctrl;
+	unsigned long offset = M66592_USBREQ;
+	int i, ret = 0;
+
+	/* read fifo */
+	m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0);
+
+	for (i = 0; i < 4; i++)
+		p[i] = m66592_read(m66592, offset + i*2);
+
+	/* check request */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_STATUS:
+			get_status(m66592, ctrl);
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			clear_feature(m66592, ctrl);
+			break;
+		case USB_REQ_SET_FEATURE:
+			set_feature(m66592, ctrl);
+			break;
+		default:
+			ret = 1;
+			break;
+		}
+	} else
+		ret = 1;
+	return ret;
+}
+
+static void m66592_update_usb_speed(struct m66592 *m66592)
+{
+	u16 speed = get_usb_speed(m66592);
+
+	switch (speed) {
+	case M66592_HSMODE:
+		m66592->gadget.speed = USB_SPEED_HIGH;
+		break;
+	case M66592_FSMODE:
+		m66592->gadget.speed = USB_SPEED_FULL;
+		break;
+	default:
+		m66592->gadget.speed = USB_SPEED_UNKNOWN;
+		printk(KERN_ERR "USB speed unknown\n");
+	}
+}
+
+static void irq_device_state(struct m66592 *m66592)
+{
+	u16 dvsq;
+
+	dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ;
+	m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
+
+	if (dvsq == M66592_DS_DFLT) {	/* bus reset */
+		m66592->driver->disconnect(&m66592->gadget);
+		m66592_update_usb_speed(m66592);
+	}
+	if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
+		m66592_update_usb_speed(m66592);
+	if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS) &&
+	    m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		m66592_update_usb_speed(m66592);
+
+	m66592->old_dvsq = dvsq;
+}
+
+static void irq_control_stage(struct m66592 *m66592)
+{
+	struct usb_ctrlrequest ctrl;
+	u16 ctsq;
+
+	ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ;
+	m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0);
+
+	switch (ctsq) {
+	case M66592_CS_IDST: {
+		struct m66592_ep *ep;
+		struct m66592_request *req;
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		transfer_complete(ep, req, 0);
+		}
+		break;
+
+	case M66592_CS_RDDS:
+	case M66592_CS_WRDS:
+	case M66592_CS_WRND:
+		if (setup_packet(m66592, &ctrl)) {
+			if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0)
+				pipe_stall(m66592, 0);
+		}
+		break;
+	case M66592_CS_RDSS:
+	case M66592_CS_WRSS:
+		control_end(m66592, 0);
+		break;
+	default:
+		printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static irqreturn_t m66592_irq(int irq, void *_m66592)
+{
+	struct m66592 *m66592 = _m66592;
+	u16 intsts0;
+	u16 intenb0;
+	u16 brdysts, nrdysts, bempsts;
+	u16 brdyenb, nrdyenb, bempenb;
+	u16 savepipe;
+	u16 mask0;
+
+	intsts0 = m66592_read(m66592, M66592_INTSTS0);
+	intenb0 = m66592_read(m66592, M66592_INTENB0);
+
+	savepipe = m66592_read(m66592, M66592_CFIFOSEL);
+
+	mask0 = intsts0 & intenb0;
+	if (mask0) {
+		brdysts = m66592_read(m66592, M66592_BRDYSTS);
+		nrdysts = m66592_read(m66592, M66592_NRDYSTS);
+		bempsts = m66592_read(m66592, M66592_BEMPSTS);
+		brdyenb = m66592_read(m66592, M66592_BRDYENB);
+		nrdyenb = m66592_read(m66592, M66592_NRDYENB);
+		bempenb = m66592_read(m66592, M66592_BEMPENB);
+
+		if (mask0 & M66592_VBINT) {
+			m66592_write(m66592, (u16)~M66592_VBINT,
+				     M66592_INTSTS0);
+			m66592_start_xclock(m66592);
+
+			/* start vbus sampling */
+			m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0)
+					   & M66592_VBSTS;
+			m66592->scount = M66592_MAX_SAMPLING;
+
+			mod_timer(&m66592->timer,
+				  jiffies + msecs_to_jiffies(50));
+		}
+		if (intsts0 & M66592_DVSQ)
+			irq_device_state(m66592);
+
+		if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE) &&
+		    (brdysts & brdyenb)) {
+			irq_pipe_ready(m66592, brdysts, brdyenb);
+		}
+		if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE) &&
+		    (bempsts & bempenb)) {
+			irq_pipe_empty(m66592, bempsts, bempenb);
+		}
+
+		if (intsts0 & M66592_CTRT)
+			irq_control_stage(m66592);
+	}
+
+	m66592_write(m66592, savepipe, M66592_CFIFOSEL);
+
+	return IRQ_HANDLED;
+}
+
+static void m66592_timer(unsigned long _m66592)
+{
+	struct m66592 *m66592 = (struct m66592 *)_m66592;
+	unsigned long flags;
+	u16 tmp;
+
+	spin_lock_irqsave(&m66592->lock, flags);
+	tmp = m66592_read(m66592, M66592_SYSCFG);
+	if (!(tmp & M66592_RCKE)) {
+		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+		udelay(10);
+		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+	}
+	if (m66592->scount > 0) {
+		tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS;
+		if (tmp == m66592->old_vbus) {
+			m66592->scount--;
+			if (m66592->scount == 0) {
+				if (tmp == M66592_VBSTS)
+					m66592_usb_connect(m66592);
+				else
+					m66592_usb_disconnect(m66592);
+			} else {
+				mod_timer(&m66592->timer,
+					  jiffies + msecs_to_jiffies(50));
+			}
+		} else {
+			m66592->scount = M66592_MAX_SAMPLING;
+			m66592->old_vbus = tmp;
+			mod_timer(&m66592->timer,
+				  jiffies + msecs_to_jiffies(50));
+		}
+	}
+	spin_unlock_irqrestore(&m66592->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+static int m66592_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct m66592_ep *ep;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	return alloc_pipe_config(ep, desc);
+}
+
+static int m66592_disable(struct usb_ep *_ep)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	BUG_ON(!ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		spin_lock_irqsave(&ep->m66592->lock, flags);
+		transfer_complete(ep, req, -ECONNRESET);
+		spin_unlock_irqrestore(&ep->m66592->lock, flags);
+	}
+
+	pipe_irq_disable(ep->m66592, ep->pipenum);
+	return free_pipe_config(ep);
+}
+
+static struct usb_request *m66592_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct m66592_request *req;
+
+	req = kzalloc(sizeof(struct m66592_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct m66592_request *req;
+
+	req = container_of(_req, struct m66592_request, req);
+	kfree(req);
+}
+
+static void *m66592_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+				 dma_addr_t *dma, gfp_t gfp_flags)
+{
+	void *buf;
+
+	buf = kzalloc(bytes, gfp_flags);
+	if (dma)
+		*dma = virt_to_bus(buf);
+
+	return buf;
+}
+
+static void m66592_free_buffer(struct usb_ep *_ep, void *buf,
+			       dma_addr_t dma, unsigned bytes)
+{
+	kfree(buf);
+}
+
+static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+	int request = 0;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = container_of(_req, struct m66592_request, req);
+
+	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+
+	if (list_empty(&ep->queue))
+		request = 1;
+
+	list_add_tail(&req->queue, &ep->queue);
+	req->req.actual = 0;
+	req->req.status = -EINPROGRESS;
+
+	if (ep->desc == 0)	/* control */
+		start_ep0(ep, req);
+	else {
+		if (request && !ep->busy)
+			start_packet(ep, req);
+	}
+
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+
+	return 0;
+}
+
+static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = container_of(_req, struct m66592_request, req);
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (!list_empty(&ep->queue))
+		transfer_complete(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+
+	return 0;
+}
+
+static int m66592_set_halt(struct usb_ep *_ep, int value)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+	int ret = 0;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = list_entry(ep->queue.next, struct m66592_request, queue);
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (!list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	if (value) {
+		ep->busy = 1;
+		pipe_stall(ep->m66592, ep->pipenum);
+	} else {
+		ep->busy = 0;
+		pipe_stop(ep->m66592, ep->pipenum);
+	}
+
+out:
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+	return ret;
+}
+
+static int m66592_fifo_status(struct usb_ep *_ep)
+{
+	return -EOPNOTSUPP;
+}
+
+static void m66592_fifo_flush(struct usb_ep *_ep)
+{
+	struct m66592_ep *ep;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (list_empty(&ep->queue) && !ep->busy) {
+		pipe_stop(ep->m66592, ep->pipenum);
+		m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr);
+	}
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+}
+
+static struct usb_ep_ops m66592_ep_ops = {
+	.enable		= m66592_enable,
+	.disable	= m66592_disable,
+
+	.alloc_request	= m66592_alloc_request,
+	.free_request	= m66592_free_request,
+
+	.alloc_buffer	= m66592_alloc_buffer,
+	.free_buffer	= m66592_free_buffer,
+
+	.queue		= m66592_queue,
+	.dequeue	= m66592_dequeue,
+
+	.set_halt	= m66592_set_halt,
+	.fifo_status	= m66592_fifo_status,
+	.fifo_flush	= m66592_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct m66592 *the_controller;
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct m66592 *m66592 = the_controller;
+	int retval;
+
+	if (!driver ||
+	    driver->speed != USB_SPEED_HIGH ||
+	    !driver->bind ||
+	    !driver->unbind ||
+	    !driver->setup)
+		return -EINVAL;
+	if (!m66592)
+		return -ENODEV;
+	if (m66592->driver)
+		return -EBUSY;
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	m66592->driver = driver;
+	m66592->gadget.dev.driver = &driver->driver;
+
+	retval = device_add(&m66592->gadget.dev);
+	if (retval) {
+		printk(KERN_ERR "device_add error (%d)\n", retval);
+		goto error;
+	}
+
+	retval = driver->bind (&m66592->gadget);
+	if (retval) {
+		printk(KERN_ERR "bind to driver error (%d)\n", retval);
+		device_del(&m66592->gadget.dev);
+		goto error;
+	}
+
+	m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
+	if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
+		m66592_start_xclock(m66592);
+		/* start vbus sampling */
+		m66592->old_vbus = m66592_read(m66592,
+					 M66592_INTSTS0) & M66592_VBSTS;
+		m66592->scount = M66592_MAX_SAMPLING;
+		mod_timer(&m66592->timer,
+			  jiffies + msecs_to_jiffies(50));
+	}
+
+	return 0;
+
+error:
+	m66592->driver = NULL;
+	m66592->gadget.dev.driver = NULL;
+
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct m66592 *m66592 = the_controller;
+	unsigned long flags;
+
+	spin_lock_irqsave(&m66592->lock, flags);
+	if (m66592->gadget.speed != USB_SPEED_UNKNOWN)
+		m66592_usb_disconnect(m66592);
+	spin_unlock_irqrestore(&m66592->lock, flags);
+
+	m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
+
+	driver->unbind(&m66592->gadget);
+
+	init_controller(m66592);
+	disable_controller(m66592);
+
+	device_del(&m66592->gadget.dev);
+	m66592->driver = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------*/
+static int m66592_get_frame(struct usb_gadget *_gadget)
+{
+	struct m66592 *m66592 = gadget_to_m66592(_gadget);
+	return m66592_read(m66592, M66592_FRMNUM) & 0x03FF;
+}
+
+static struct usb_gadget_ops m66592_gadget_ops = {
+	.get_frame		= m66592_get_frame,
+};
+
+#if defined(CONFIG_PM)
+static int m66592_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	pdev->dev.power.power_state = state;
+	return 0;
+}
+
+static int m66592_resume(struct platform_device *pdev)
+{
+	pdev->dev.power.power_state = PMSG_ON;
+	return 0;
+}
+#else	/* if defined(CONFIG_PM) */
+#define m66592_suspend		NULL
+#define m66592_resume		NULL
+#endif
+
+static int __init_or_module m66592_remove(struct platform_device *pdev)
+{
+	struct m66592		*m66592 = dev_get_drvdata(&pdev->dev);
+
+	del_timer_sync(&m66592->timer);
+	iounmap(m66592->reg);
+	free_irq(platform_get_irq(pdev, 0), m66592);
+	kfree(m66592);
+	return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+static int __init m66592_probe(struct platform_device *pdev)
+{
+	struct resource *res = NULL;
+	int irq = -1;
+	void __iomem *reg = NULL;
+	struct m66592 *m66592 = NULL;
+	int ret = 0;
+	int i;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   (char *)udc_name);
+	if (!res) {
+		ret = -ENODEV;
+		printk(KERN_ERR "platform_get_resource_byname error.\n");
+		goto clean_up;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = -ENODEV;
+		printk(KERN_ERR "platform_get_irq error.\n");
+		goto clean_up;
+	}
+
+	reg = ioremap(res->start, resource_len(res));
+	if (reg == NULL) {
+		ret = -ENOMEM;
+		printk(KERN_ERR "ioremap error.\n");
+		goto clean_up;
+	}
+
+	/* initialize ucd */
+	m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
+	if (m66592 == NULL) {
+		printk(KERN_ERR "kzalloc error\n");
+		goto clean_up;
+	}
+
+	spin_lock_init(&m66592->lock);
+	dev_set_drvdata(&pdev->dev, m66592);
+
+	m66592->gadget.ops = &m66592_gadget_ops;
+	device_initialize(&m66592->gadget.dev);
+	strcpy(m66592->gadget.dev.bus_id, "gadget");
+	m66592->gadget.is_dualspeed = 1;
+	m66592->gadget.dev.parent = &pdev->dev;
+	m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	m66592->gadget.dev.release = pdev->dev.release;
+	m66592->gadget.name = udc_name;
+
+	init_timer(&m66592->timer);
+	m66592->timer.function = m66592_timer;
+	m66592->timer.data = (unsigned long)m66592;
+	m66592->reg = reg;
+
+	m66592->bi_bufnum = M66592_BASE_BUFNUM;
+
+	ret = request_irq(irq, m66592_irq, IRQF_DISABLED | IRQF_SHARED,
+			  udc_name, m66592);
+	if (ret < 0) {
+		printk(KERN_ERR "request_irq error (%d)\n", ret);
+		goto clean_up;
+	}
+
+	INIT_LIST_HEAD(&m66592->gadget.ep_list);
+	m66592->gadget.ep0 = &m66592->ep[0].ep;
+	INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
+	for (i = 0; i < M66592_MAX_NUM_PIPE; i++) {
+		struct m66592_ep *ep = &m66592->ep[i];
+
+		if (i != 0) {
+			INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list);
+			list_add_tail(&m66592->ep[i].ep.ep_list,
+				      &m66592->gadget.ep_list);
+		}
+		ep->m66592 = m66592;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->ep.name = m66592_ep_name[i];
+		ep->ep.ops = &m66592_ep_ops;
+		ep->ep.maxpacket = 512;
+	}
+	m66592->ep[0].ep.maxpacket = 64;
+	m66592->ep[0].pipenum = 0;
+	m66592->ep[0].fifoaddr = M66592_CFIFO;
+	m66592->ep[0].fifosel = M66592_CFIFOSEL;
+	m66592->ep[0].fifoctr = M66592_CFIFOCTR;
+	m66592->ep[0].fifotrn = 0;
+	m66592->ep[0].pipectr = get_pipectr_addr(0);
+	m66592->pipenum2ep[0] = &m66592->ep[0];
+	m66592->epaddr2ep[0] = &m66592->ep[0];
+
+	the_controller = m66592;
+
+	m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
+	if (m66592->ep0_req == NULL)
+		goto clean_up;
+	m66592->ep0_buf = m66592_alloc_buffer(&m66592->ep[0].ep, 2, NULL,
+					      GFP_KERNEL);
+	if (m66592->ep0_buf == NULL)
+		goto clean_up;
+
+	init_controller(m66592);
+
+	printk("driver %s, %s\n", udc_name, DRIVER_VERSION);
+	return 0;
+
+clean_up:
+	if (m66592) {
+		if (m66592->ep0_req)
+			m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
+		kfree(m66592);
+	}
+	if (reg)
+		iounmap(reg);
+
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver m66592_driver = {
+	.probe =	m66592_probe,
+	.remove =	m66592_remove,
+	.suspend =	m66592_suspend,
+	.resume =	m66592_resume,
+	.driver		= {
+		.name =	(char *) udc_name,
+	},
+};
+
+static int __init m66592_udc_init(void)
+{
+	return platform_driver_register(&m66592_driver);
+}
+module_init(m66592_udc_init);
+
+static void __exit m66592_udc_cleanup(void)
+{
+	platform_driver_unregister(&m66592_driver);
+}
+module_exit(m66592_udc_cleanup);
+
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
new file mode 100644
index 0000000..26b54f8
--- /dev/null
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -0,0 +1,577 @@
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __M66592_UDC_H__
+#define __M66592_UDC_H__
+
+#define M66592_SYSCFG		0x00
+#define	M66592_XTAL		0xC000	/* b15-14: Crystal selection */
+#define	  M66592_XTAL48		 0x8000		  /* 48MHz */
+#define   M66592_XTAL24		 0x4000		  /* 24MHz */
+#define	  M66592_XTAL12		 0x0000		  /* 12MHz */
+#define	M66592_XCKE		0x2000	/* b13: External clock enable */
+#define	M66592_RCKE		0x1000	/* b12: Register clock enable */
+#define	M66592_PLLC		0x0800	/* b11: PLL control */
+#define	M66592_SCKE		0x0400	/* b10: USB clock enable */
+#define	M66592_ATCKM		0x0100	/* b8: Automatic supply functional enable */
+#define	M66592_HSE		0x0080	/* b7: Hi-speed enable */
+#define	M66592_DCFM		0x0040	/* b6: Controller function select  */
+#define	M66592_DMRPD		0x0020	/* b5: D- pull down control */
+#define	M66592_DPRPU		0x0010	/* b4: D+ pull up control */
+#define	M66592_FSRPC		0x0004	/* b2: Full-speed receiver enable */
+#define	M66592_PCUT		0x0002	/* b1: Low power sleep enable */
+#define	M66592_USBE		0x0001	/* b0: USB module operation enable */
+
+#define M66592_SYSSTS		0x02
+#define	M66592_LNST		0x0003	/* b1-0: D+, D- line status */
+#define	  M66592_SE1		 0x0003		  /* SE1 */
+#define	  M66592_KSTS		 0x0002		  /* K State */
+#define	  M66592_JSTS		 0x0001		  /* J State */
+#define	  M66592_SE0		 0x0000		  /* SE0 */
+
+#define M66592_DVSTCTR		0x04
+#define	M66592_WKUP		0x0100	/* b8: Remote wakeup */
+#define	M66592_RWUPE		0x0080	/* b7: Remote wakeup sense */
+#define	M66592_USBRST		0x0040	/* b6: USB reset enable */
+#define	M66592_RESUME		0x0020	/* b5: Resume enable */
+#define	M66592_UACT		0x0010	/* b4: USB bus enable */
+#define	M66592_RHST		0x0003	/* b1-0: Reset handshake status */
+#define	  M66592_HSMODE		 0x0003		  /* Hi-Speed mode */
+#define	  M66592_FSMODE		 0x0002		  /* Full-Speed mode */
+#define	  M66592_HSPROC		 0x0001		  /* HS handshake is processing */
+
+#define M66592_TESTMODE		0x06
+#define	M66592_UTST		0x000F	/* b4-0: Test select */
+#define	  M66592_H_TST_PACKET	 0x000C		  /* HOST TEST Packet */
+#define	  M66592_H_TST_SE0_NAK	 0x000B		  /* HOST TEST SE0 NAK */
+#define	  M66592_H_TST_K	 0x000A		  /* HOST TEST K */
+#define	  M66592_H_TST_J	 0x0009		  /* HOST TEST J */
+#define	  M66592_H_TST_NORMAL	 0x0000		  /* HOST Normal Mode */
+#define	  M66592_P_TST_PACKET	 0x0004		  /* PERI TEST Packet */
+#define	  M66592_P_TST_SE0_NAK	 0x0003		  /* PERI TEST SE0 NAK */
+#define	  M66592_P_TST_K	 0x0002		  /* PERI TEST K */
+#define	  M66592_P_TST_J	 0x0001		  /* PERI TEST J */
+#define	  M66592_P_TST_NORMAL	 0x0000		  /* PERI Normal Mode */
+
+#define M66592_PINCFG		0x0A
+#define	M66592_LDRV		0x8000	/* b15: Drive Current Adjust */
+#define	M66592_BIGEND		0x0100	/* b8: Big endian mode */
+
+#define M66592_DMA0CFG		0x0C
+#define M66592_DMA1CFG		0x0E
+#define	M66592_DREQA		0x4000	/* b14: Dreq active select */
+#define	M66592_BURST		0x2000	/* b13: Burst mode */
+#define	M66592_DACKA		0x0400	/* b10: Dack active select */
+#define	M66592_DFORM		0x0380	/* b9-7: DMA mode select */
+#define	  M66592_CPU_ADR_RD_WR	 0x0000		  /* Address + RD/WR mode (CPU bus) */
+#define	  M66592_CPU_DACK_RD_WR	 0x0100		  /* DACK + RD/WR mode (CPU bus) */
+#define	  M66592_CPU_DACK_ONLY	 0x0180		  /* DACK only mode (CPU bus) */
+#define	  M66592_SPLIT_DACK_ONLY	 0x0200		  /* DACK only mode (SPLIT bus) */
+#define	  M66592_SPLIT_DACK_DSTB	 0x0300		  /* DACK + DSTB0 mode (SPLIT bus) */
+#define	M66592_DENDA		0x0040	/* b6: Dend active select */
+#define	M66592_PKTM		0x0020	/* b5: Packet mode */
+#define	M66592_DENDE		0x0010	/* b4: Dend enable */
+#define	M66592_OBUS		0x0004	/* b2: OUTbus mode */
+
+#define M66592_CFIFO		0x10
+#define M66592_D0FIFO		0x14
+#define M66592_D1FIFO		0x18
+
+#define M66592_CFIFOSEL		0x1E
+#define M66592_D0FIFOSEL	0x24
+#define M66592_D1FIFOSEL	0x2A
+#define	M66592_RCNT		0x8000	/* b15: Read count mode */
+#define	M66592_REW		0x4000	/* b14: Buffer rewind */
+#define	M66592_DCLRM		0x2000	/* b13: DMA buffer clear mode */
+#define	M66592_DREQE		0x1000	/* b12: DREQ output enable */
+#define	M66592_MBW		0x0400	/* b10: Maximum bit width for FIFO access */
+#define	  M66592_MBW_8		 0x0000	  /*  8bit */
+#define	  M66592_MBW_16		 0x0400		  /* 16bit */
+#define	M66592_TRENB		0x0200	/* b9: Transaction counter enable */
+#define	M66592_TRCLR		0x0100	/* b8: Transaction counter clear */
+#define	M66592_DEZPM		0x0080	/* b7: Zero-length packet additional mode */
+#define	M66592_ISEL		0x0020	/* b5: DCP FIFO port direction select */
+#define	M66592_CURPIPE		0x0007	/* b2-0: PIPE select */
+
+#define M66592_CFIFOCTR		0x20
+#define M66592_D0FIFOCTR	0x26
+#define M66592_D1FIFOCTR	0x2c
+#define	M66592_BVAL		0x8000	/* b15: Buffer valid flag */
+#define	M66592_BCLR		0x4000	/* b14: Buffer clear */
+#define	M66592_FRDY		0x2000	/* b13: FIFO ready */
+#define	M66592_DTLN		0x0FFF	/* b11-0: FIFO received data length */
+
+#define M66592_CFIFOSIE		0x22
+#define	M66592_TGL		0x8000	/* b15: Buffer toggle */
+#define	M66592_SCLR		0x4000	/* b14: Buffer clear */
+#define	M66592_SBUSY		0x2000	/* b13: SIE_FIFO busy */
+
+#define M66592_D0FIFOTRN	0x28
+#define M66592_D1FIFOTRN	0x2E
+#define	M66592_TRNCNT		0xFFFF	/* b15-0: Transaction counter */
+
+#define M66592_INTENB0	0x30
+#define	M66592_VBSE	0x8000	/* b15: VBUS interrupt */
+#define	M66592_RSME	0x4000	/* b14: Resume interrupt */
+#define	M66592_SOFE	0x2000	/* b13: Frame update interrupt */
+#define	M66592_DVSE	0x1000	/* b12: Device state transition interrupt */
+#define	M66592_CTRE	0x0800	/* b11: Control transfer stage transition interrupt */
+#define	M66592_BEMPE	0x0400	/* b10: Buffer empty interrupt */
+#define	M66592_NRDYE	0x0200	/* b9: Buffer not ready interrupt */
+#define	M66592_BRDYE	0x0100	/* b8: Buffer ready interrupt */
+#define	M66592_URST	0x0080	/* b7: USB reset detected interrupt */
+#define	M66592_SADR	0x0040	/* b6: Set address executed interrupt */
+#define	M66592_SCFG	0x0020	/* b5: Set configuration executed interrupt */
+#define	M66592_SUSP	0x0010	/* b4: Suspend detected interrupt */
+#define	M66592_WDST	0x0008	/* b3: Control write data stage completed interrupt */
+#define	M66592_RDST	0x0004	/* b2: Control read data stage completed interrupt */
+#define	M66592_CMPL	0x0002	/* b1: Control transfer complete interrupt */
+#define	M66592_SERR	0x0001	/* b0: Sequence error interrupt */
+
+#define M66592_INTENB1	0x32
+#define	M66592_BCHGE	0x4000	/* b14: USB us chenge interrupt */
+#define	M66592_DTCHE	0x1000	/* b12: Detach sense interrupt */
+#define	M66592_SIGNE	0x0020	/* b5: SETUP IGNORE interrupt */
+#define	M66592_SACKE	0x0010	/* b4: SETUP ACK interrupt */
+#define	M66592_BRDYM	0x0004	/* b2: BRDY clear timing */
+#define	M66592_INTL	0x0002	/* b1: Interrupt sense select */
+#define	M66592_PCSE	0x0001	/* b0: PCUT enable by CS assert */
+
+#define M66592_BRDYENB		0x36
+#define M66592_BRDYSTS		0x46
+#define	M66592_BRDY7		0x0080	/* b7: PIPE7 */
+#define	M66592_BRDY6		0x0040	/* b6: PIPE6 */
+#define	M66592_BRDY5		0x0020	/* b5: PIPE5 */
+#define	M66592_BRDY4		0x0010	/* b4: PIPE4 */
+#define	M66592_BRDY3		0x0008	/* b3: PIPE3 */
+#define	M66592_BRDY2		0x0004	/* b2: PIPE2 */
+#define	M66592_BRDY1		0x0002	/* b1: PIPE1 */
+#define	M66592_BRDY0		0x0001	/* b1: PIPE0 */
+
+#define M66592_NRDYENB		0x38
+#define M66592_NRDYSTS		0x48
+#define	M66592_NRDY7		0x0080	/* b7: PIPE7 */
+#define	M66592_NRDY6		0x0040	/* b6: PIPE6 */
+#define	M66592_NRDY5		0x0020	/* b5: PIPE5 */
+#define	M66592_NRDY4		0x0010	/* b4: PIPE4 */
+#define	M66592_NRDY3		0x0008	/* b3: PIPE3 */
+#define	M66592_NRDY2		0x0004	/* b2: PIPE2 */
+#define	M66592_NRDY1		0x0002	/* b1: PIPE1 */
+#define	M66592_NRDY0		0x0001	/* b1: PIPE0 */
+
+#define M66592_BEMPENB		0x3A
+#define M66592_BEMPSTS		0x4A
+#define	M66592_BEMP7		0x0080	/* b7: PIPE7 */
+#define	M66592_BEMP6		0x0040	/* b6: PIPE6 */
+#define	M66592_BEMP5		0x0020	/* b5: PIPE5 */
+#define	M66592_BEMP4		0x0010	/* b4: PIPE4 */
+#define	M66592_BEMP3		0x0008	/* b3: PIPE3 */
+#define	M66592_BEMP2		0x0004	/* b2: PIPE2 */
+#define	M66592_BEMP1		0x0002	/* b1: PIPE1 */
+#define	M66592_BEMP0		0x0001	/* b0: PIPE0 */
+
+#define M66592_SOFCFG		0x3C
+#define	M66592_SOFM		0x000C	/* b3-2: SOF palse mode */
+#define	  M66592_SOF_125US	 0x0008		  /* SOF OUT 125us uFrame Signal */
+#define	  M66592_SOF_1MS	 0x0004		  /* SOF OUT 1ms Frame Signal */
+#define	  M66592_SOF_DISABLE	 0x0000		  /* SOF OUT Disable */
+
+#define M66592_INTSTS0		0x40
+#define	M66592_VBINT		0x8000	/* b15: VBUS interrupt */
+#define	M66592_RESM		0x4000	/* b14: Resume interrupt */
+#define	M66592_SOFR		0x2000	/* b13: SOF frame update interrupt */
+#define	M66592_DVST		0x1000	/* b12: Device state transition interrupt */
+#define	M66592_CTRT		0x0800	/* b11: Control transfer stage transition interrupt */
+#define	M66592_BEMP		0x0400	/* b10: Buffer empty interrupt */
+#define	M66592_NRDY		0x0200	/* b9: Buffer not ready interrupt */
+#define	M66592_BRDY		0x0100	/* b8: Buffer ready interrupt */
+#define	M66592_VBSTS		0x0080	/* b7: VBUS input port */
+#define	M66592_DVSQ		0x0070	/* b6-4: Device state */
+#define	  M66592_DS_SPD_CNFG	 0x0070		  /* Suspend Configured */
+#define	  M66592_DS_SPD_ADDR	 0x0060		  /* Suspend Address */
+#define	  M66592_DS_SPD_DFLT	 0x0050		  /* Suspend Default */
+#define	  M66592_DS_SPD_POWR	 0x0040		  /* Suspend Powered */
+#define	  M66592_DS_SUSP	 0x0040		  /* Suspend */
+#define	  M66592_DS_CNFG	 0x0030		  /* Configured */
+#define	  M66592_DS_ADDS	 0x0020		  /* Address */
+#define	  M66592_DS_DFLT	 0x0010		  /* Default */
+#define	  M66592_DS_POWR	 0x0000		  /* Powered */
+#define	M66592_DVSQS		0x0030	/* b5-4: Device state */
+#define	M66592_VALID		0x0008	/* b3: Setup packet detected flag */
+#define	M66592_CTSQ		0x0007	/* b2-0: Control transfer stage */
+#define	  M66592_CS_SQER	 0x0006		  /* Sequence error */
+#define	  M66592_CS_WRND	 0x0005		  /* Control write nodata status stage */
+#define	  M66592_CS_WRSS	 0x0004		  /* Control write status stage */
+#define	  M66592_CS_WRDS	 0x0003		  /* Control write data stage */
+#define	  M66592_CS_RDSS	 0x0002		  /* Control read status stage */
+#define	  M66592_CS_RDDS	 0x0001		  /* Control read data stage */
+#define	  M66592_CS_IDST	 0x0000		  /* Idle or setup stage */
+
+#define M66592_INTSTS1		0x42
+#define	M66592_BCHG		0x4000	/* b14: USB bus chenge interrupt */
+#define	M66592_DTCH		0x1000	/* b12: Detach sense interrupt */
+#define	M66592_SIGN		0x0020	/* b5: SETUP IGNORE interrupt */
+#define	M66592_SACK		0x0010	/* b4: SETUP ACK interrupt */
+
+#define M66592_FRMNUM		0x4C
+#define	M66592_OVRN		0x8000	/* b15: Overrun error */
+#define	M66592_CRCE		0x4000	/* b14: Received data error */
+#define	M66592_SOFRM		0x0800	/* b11: SOF output mode */
+#define	M66592_FRNM		0x07FF	/* b10-0: Frame number */
+
+#define M66592_UFRMNUM		0x4E
+#define	M66592_UFRNM		0x0007	/* b2-0: Micro frame number */
+
+#define M66592_RECOVER		0x50
+#define	M66592_STSRECOV		0x0700	/* Status recovery */
+#define	  M66592_STSR_HI	 0x0400		  /* FULL(0) or HI(1) Speed */
+#define	  M66592_STSR_DEFAULT	 0x0100		  /* Default state */
+#define	  M66592_STSR_ADDRESS	 0x0200		  /* Address state */
+#define	  M66592_STSR_CONFIG	 0x0300		  /* Configured state */
+#define	M66592_USBADDR		0x007F	/* b6-0: USB address */
+
+#define M66592_USBREQ			0x54
+#define	M66592_bRequest			0xFF00	/* b15-8: bRequest */
+#define	  M66592_GET_STATUS		 0x0000
+#define	  M66592_CLEAR_FEATURE		 0x0100
+#define	  M66592_ReqRESERVED		 0x0200
+#define	  M66592_SET_FEATURE		 0x0300
+#define	  M66592_ReqRESERVED1		 0x0400
+#define	  M66592_SET_ADDRESS		 0x0500
+#define	  M66592_GET_DESCRIPTOR		 0x0600
+#define	  M66592_SET_DESCRIPTOR		 0x0700
+#define	  M66592_GET_CONFIGURATION	 0x0800
+#define	  M66592_SET_CONFIGURATION	 0x0900
+#define	  M66592_GET_INTERFACE		 0x0A00
+#define	  M66592_SET_INTERFACE		 0x0B00
+#define	  M66592_SYNCH_FRAME		 0x0C00
+#define	M66592_bmRequestType		0x00FF	/* b7-0: bmRequestType */
+#define	M66592_bmRequestTypeDir		0x0080	/* b7  : Data transfer direction */
+#define	  M66592_HOST_TO_DEVICE		 0x0000
+#define	  M66592_DEVICE_TO_HOST		 0x0080
+#define	M66592_bmRequestTypeType	0x0060	/* b6-5: Type */
+#define	  M66592_STANDARD		 0x0000
+#define	  M66592_CLASS			 0x0020
+#define	  M66592_VENDOR			 0x0040
+#define	M66592_bmRequestTypeRecip	0x001F	/* b4-0: Recipient */
+#define	  M66592_DEVICE			 0x0000
+#define	  M66592_INTERFACE		 0x0001
+#define	  M66592_ENDPOINT		 0x0002
+
+#define M66592_USBVAL				0x56
+#define	M66592_wValue				0xFFFF	/* b15-0: wValue */
+/* Standard Feature Selector */
+#define	  M66592_ENDPOINT_HALT			0x0000
+#define	  M66592_DEVICE_REMOTE_WAKEUP		0x0001
+#define	  M66592_TEST_MODE			0x0002
+/* Descriptor Types */
+#define	M66592_DT_TYPE				0xFF00
+#define	M66592_GET_DT_TYPE(v)			(((v) & DT_TYPE) >> 8)
+#define	  M66592_DT_DEVICE			0x01
+#define	  M66592_DT_CONFIGURATION		0x02
+#define	  M66592_DT_STRING			0x03
+#define	  M66592_DT_INTERFACE			0x04
+#define	  M66592_DT_ENDPOINT			0x05
+#define	  M66592_DT_DEVICE_QUALIFIER		0x06
+#define	  M66592_DT_OTHER_SPEED_CONFIGURATION	0x07
+#define	  M66592_DT_INTERFACE_POWER		0x08
+#define	M66592_DT_INDEX				0x00FF
+#define	M66592_CONF_NUM				0x00FF
+#define	M66592_ALT_SET				0x00FF
+
+#define M66592_USBINDEX			0x58
+#define	M66592_wIndex			0xFFFF	/* b15-0: wIndex */
+#define	M66592_TEST_SELECT		0xFF00	/* b15-b8: Test Mode Selectors */
+#define	  M66592_TEST_J			 0x0100		  /* Test_J */
+#define	  M66592_TEST_K			 0x0200		  /* Test_K */
+#define	  M66592_TEST_SE0_NAK		 0x0300		  /* Test_SE0_NAK */
+#define	  M66592_TEST_PACKET		 0x0400		  /* Test_Packet */
+#define	  M66592_TEST_FORCE_ENABLE	 0x0500		  /* Test_Force_Enable */
+#define	  M66592_TEST_STSelectors	 0x0600		  /* Standard test selectors */
+#define	  M66592_TEST_Reserved		 0x4000		  /* Reserved */
+#define	  M66592_TEST_VSTModes		 0xC000		  /* Vendor-specific test modes */
+#define	M66592_EP_DIR			0x0080	/* b7: Endpoint Direction */
+#define	  M66592_EP_DIR_IN		 0x0080
+#define	  M66592_EP_DIR_OUT		 0x0000
+
+#define M66592_USBLENG		0x5A
+#define	M66592_wLength		0xFFFF	/* b15-0: wLength */
+
+#define M66592_DCPCFG		0x5C
+#define	M66592_CNTMD		0x0100	/* b8: Continuous transfer mode select */
+#define	M66592_DIR		0x0010	/* b4: Control transfer DIR select */
+
+#define M66592_DCPMAXP		0x5E
+#define	M66592_DEVSEL		0xC000	/* b15-14: Device address select */
+#define	  M66592_DEVICE_0	 0x0000		  /* Device address 0 */
+#define	  M66592_DEVICE_1	 0x4000		  /* Device address 1 */
+#define	  M66592_DEVICE_2	 0x8000		  /* Device address 2 */
+#define	  M66592_DEVICE_3	 0xC000		  /* Device address 3 */
+#define	M66592_MAXP		0x007F	/* b6-0: Maxpacket size of default control pipe */
+
+#define M66592_DCPCTR		0x60
+#define	M66592_BSTS		0x8000	/* b15: Buffer status */
+#define	M66592_SUREQ		0x4000	/* b14: Send USB request  */
+#define	M66592_SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	M66592_SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	M66592_SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	M66592_CCPL		0x0004	/* b2: Enable control transfer complete */
+#define	M66592_PID		0x0003	/* b1-0: Response PID */
+#define	  M66592_PID_STALL	 0x0002		  /* STALL */
+#define	  M66592_PID_BUF	 0x0001		  /* BUF */
+#define	  M66592_PID_NAK	 0x0000		  /* NAK */
+
+#define M66592_PIPESEL		0x64
+#define	M66592_PIPENM		0x0007	/* b2-0: Pipe select */
+#define	  M66592_PIPE0		 0x0000		  /* PIPE 0 */
+#define	  M66592_PIPE1		 0x0001		  /* PIPE 1 */
+#define	  M66592_PIPE2		 0x0002		  /* PIPE 2 */
+#define	  M66592_PIPE3		 0x0003		  /* PIPE 3 */
+#define	  M66592_PIPE4		 0x0004		  /* PIPE 4 */
+#define	  M66592_PIPE5		 0x0005		  /* PIPE 5 */
+#define	  M66592_PIPE6		 0x0006		  /* PIPE 6 */
+#define	  M66592_PIPE7		 0x0007		  /* PIPE 7 */
+
+#define M66592_PIPECFG		0x66
+#define	M66592_TYP		0xC000	/* b15-14: Transfer type */
+#define	  M66592_ISO		 0xC000		  /* Isochronous */
+#define	  M66592_INT		 0x8000		  /* Interrupt */
+#define	  M66592_BULK		 0x4000		  /* Bulk */
+#define	M66592_BFRE		0x0400	/* b10: Buffer ready interrupt mode select */
+#define	M66592_DBLB		0x0200	/* b9: Double buffer mode select */
+#define	M66592_CNTMD		0x0100	/* b8: Continuous transfer mode select */
+#define	M66592_SHTNAK		0x0080	/* b7: Transfer end NAK */
+#define	M66592_DIR		0x0010	/* b4: Transfer direction select */
+#define	  M66592_DIR_H_OUT	 0x0010		  /* HOST OUT */
+#define	  M66592_DIR_P_IN	 0x0010		  /* PERI IN */
+#define	  M66592_DIR_H_IN	 0x0000		  /* HOST IN */
+#define	  M66592_DIR_P_OUT	 0x0000		  /* PERI OUT */
+#define	M66592_EPNUM		0x000F	/* b3-0: Eendpoint number select */
+#define	  M66592_EP1		 0x0001
+#define	  M66592_EP2		 0x0002
+#define	  M66592_EP3		 0x0003
+#define	  M66592_EP4		 0x0004
+#define	  M66592_EP5		 0x0005
+#define	  M66592_EP6		 0x0006
+#define	  M66592_EP7		 0x0007
+#define	  M66592_EP8		 0x0008
+#define	  M66592_EP9		 0x0009
+#define	  M66592_EP10		 0x000A
+#define	  M66592_EP11		 0x000B
+#define	  M66592_EP12		 0x000C
+#define	  M66592_EP13		 0x000D
+#define	  M66592_EP14		 0x000E
+#define	  M66592_EP15		 0x000F
+
+#define M66592_PIPEBUF		0x68
+#define	M66592_BUFSIZE		0x7C00	/* b14-10: Pipe buffer size */
+#define	M66592_BUF_SIZE(x)	((((x) / 64) - 1) << 10)
+#define	M66592_BUFNMB		0x00FF	/* b7-0: Pipe buffer number */
+
+#define M66592_PIPEMAXP		0x6A
+#define	M66592_MXPS		0x07FF	/* b10-0: Maxpacket size */
+
+#define M66592_PIPEPERI		0x6C
+#define	M66592_IFIS		0x1000	/* b12: Isochronous in-buffer flush mode select */
+#define	M66592_IITV		0x0007	/* b2-0: Isochronous interval */
+
+#define M66592_PIPE1CTR		0x70
+#define M66592_PIPE2CTR		0x72
+#define M66592_PIPE3CTR		0x74
+#define M66592_PIPE4CTR		0x76
+#define M66592_PIPE5CTR		0x78
+#define M66592_PIPE6CTR		0x7A
+#define M66592_PIPE7CTR		0x7C
+#define	M66592_BSTS		0x8000	/* b15: Buffer status */
+#define	M66592_INBUFM		0x4000	/* b14: IN buffer monitor (Only for PIPE1 to 5) */
+#define	M66592_ACLRM		0x0200	/* b9: Out buffer auto clear mode */
+#define	M66592_SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	M66592_SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	M66592_SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	M66592_PID		0x0003	/* b1-0: Response PID */
+
+#define M66592_INVALID_REG	0x7E
+
+
+#define __iomem
+
+#define get_pipectr_addr(pipenum)	(M66592_PIPE1CTR + (pipenum - 1) * 2)
+
+#define M66592_MAX_SAMPLING	10
+
+#define M66592_MAX_NUM_PIPE	8
+#define M66592_MAX_NUM_BULK	3
+#define M66592_MAX_NUM_ISOC	2
+#define M66592_MAX_NUM_INT	2
+
+#define M66592_BASE_PIPENUM_BULK	3
+#define M66592_BASE_PIPENUM_ISOC	1
+#define M66592_BASE_PIPENUM_INT		6
+
+#define M66592_BASE_BUFNUM	6
+#define M66592_MAX_BUFNUM	0x4F
+
+struct m66592_pipe_info {
+	u16	pipe;
+	u16	epnum;
+	u16	maxpacket;
+	u16	type;
+	u16	interval;
+	u16	dir_in;
+};
+
+struct m66592_request {
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+struct m66592_ep {
+	struct usb_ep		ep;
+	struct m66592		*m66592;
+
+	struct list_head	queue;
+	unsigned 		busy:1;
+	unsigned		internal_ccpl:1;	/* use only control */
+
+	/* this member can able to after m66592_enable */
+	unsigned		use_dma:1;
+	u16			pipenum;
+	u16			type;
+	const struct usb_endpoint_descriptor	*desc;
+	/* register address */
+	unsigned long		fifoaddr;
+	unsigned long		fifosel;
+	unsigned long		fifoctr;
+	unsigned long		fifotrn;
+	unsigned long		pipectr;
+};
+
+struct m66592 {
+	spinlock_t		lock;
+	void __iomem		*reg;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	struct m66592_ep	ep[M66592_MAX_NUM_PIPE];
+	struct m66592_ep	*pipenum2ep[M66592_MAX_NUM_PIPE];
+	struct m66592_ep	*epaddr2ep[16];
+
+	struct usb_request	*ep0_req;	/* for internal request */
+	u16			*ep0_buf;	/* for internal request */
+
+	struct timer_list	timer;
+
+	u16			old_vbus;
+	int			scount;
+
+	int			old_dvsq;
+
+	/* pipe config */
+	int bulk;
+	int interrupt;
+	int isochronous;
+	int num_dma;
+	int bi_bufnum;	/* bulk and isochronous's bufnum */
+};
+
+#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
+#define m66592_to_gadget(m66592) (&m66592->gadget)
+
+#define is_bulk_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_BULK) && \
+	 (pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK)))
+#define is_interrupt_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_INT) && \
+	 (pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT)))
+#define is_isoc_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_ISOC) && \
+	 (pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC)))
+
+#define enable_irq_ready(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
+#define disable_irq_ready(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
+#define enable_irq_empty(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
+#define disable_irq_empty(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
+#define enable_irq_nrdy(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
+#define disable_irq_nrdy(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
+
+/*-------------------------------------------------------------------------*/
+static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset)
+{
+	return inw((unsigned long)m66592->reg + offset);
+}
+
+static inline void m66592_read_fifo(struct m66592 *m66592,
+				    unsigned long offset,
+				    void *buf, unsigned long len)
+{
+	unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
+
+	len = (len + 1) / 2;
+	insw(fifoaddr, buf, len);
+}
+
+static inline void m66592_write(struct m66592 *m66592, u16 val,
+				unsigned long offset)
+{
+	outw(val, (unsigned long)m66592->reg + offset);
+}
+
+static inline void m66592_write_fifo(struct m66592 *m66592,
+				     unsigned long offset,
+				     void *buf, unsigned long len)
+{
+	unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
+	unsigned long odd = len & 0x0001;
+
+	len = len / 2;
+	outsw(fifoaddr, buf, len);
+	if (odd) {
+		unsigned char *p = buf + len*2;
+		outb(*p, fifoaddr);
+	}
+}
+
+static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
+			       unsigned long offset)
+{
+	u16 tmp;
+	tmp = m66592_read(m66592, offset);
+	tmp = tmp & (~pat);
+	tmp = tmp | val;
+	m66592_write(m66592, tmp, offset);
+}
+
+#define m66592_bclr(m66592, val, offset)	\
+			m66592_mdfy(m66592, 0, val, offset)
+#define m66592_bset(m66592, val, offset)	\
+			m66592_mdfy(m66592, val, 0, offset)
+
+#endif	/* ifndef __M66592_UDC_H__ */
+
+
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 00fda33..c3d364e 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -450,100 +450,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * dma-coherent memory allocation (for dma-capable endpoints)
- *
- * 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).
- */
-
-static void *
-net2280_alloc_buffer (
-	struct usb_ep		*_ep,
-	unsigned		bytes,
-	dma_addr_t		*dma,
-	gfp_t			gfp_flags
-)
-{
-	void			*retval;
-	struct net2280_ep	*ep;
-
-	ep = container_of (_ep, struct net2280_ep, ep);
-	if (!_ep)
-		return NULL;
-	*dma = DMA_ADDR_INVALID;
-
-	if (ep->dma)
-		retval = dma_alloc_coherent(&ep->dev->pdev->dev,
-				bytes, dma, gfp_flags);
-	else
-		retval = kmalloc(bytes, gfp_flags);
-	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 *address,
-	dma_addr_t dma,
-	unsigned bytes
-) {
-	/* free memory into the right allocator */
-	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;
-
-		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
-		kfree (address);
-}
-
-/*-------------------------------------------------------------------------*/
-
 /* load a packet into the fifo we use for usb IN transfers.
  * works for all endpoints.
  *
@@ -1392,9 +1298,6 @@
 	.alloc_request	= net2280_alloc_request,
 	.free_request	= net2280_free_request,
 
-	.alloc_buffer	= net2280_alloc_buffer,
-	.free_buffer	= net2280_free_buffer,
-
 	.queue		= net2280_queue,
 	.dequeue	= net2280_dequeue,
 
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index c4975a6..9b0f092 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -296,111 +296,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * dma-coherent memory allocation (for dma-capable endpoints)
- *
- * 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).
- */
-
-static void *
-omap_alloc_buffer(
-	struct usb_ep	*_ep,
-	unsigned	bytes,
-	dma_addr_t	*dma,
-	gfp_t		gfp_flags
-)
-{
-	void		*retval;
-	struct omap_ep	*ep;
-
-	if (!_ep)
-		return NULL;
-
-	ep = container_of(_ep, struct omap_ep, ep);
-	if (use_dma && ep->has_dma) {
-		static int	warned;
-		if (!warned && bytes < PAGE_SIZE) {
-			dev_warn(ep->udc->gadget.dev.parent,
-				"using dma_alloc_coherent for "
-				"small allocations wastes memory\n");
-			warned++;
-		}
-		return dma_alloc_coherent(ep->udc->gadget.dev.parent,
-				bytes, dma, gfp_flags);
-	}
-
-	retval = kmalloc(bytes, gfp_flags);
-	if (retval)
-		*dma = virt_to_phys(retval);
-	return retval;
-}
-
-static DEFINE_SPINLOCK(buflock);
-static LIST_HEAD(buffers);
-
-struct free_record {
-	struct list_head	list;
-	struct device		*dev;
-	unsigned		bytes;
-	dma_addr_t		dma;
-};
-
-static void do_free(unsigned long ignored)
-{
-	spin_lock_irq(&buflock);
-	while (!list_empty(&buffers)) {
-		struct free_record	*buf;
-
-		buf = list_entry(buffers.next, struct free_record, list);
-		list_del(&buf->list);
-		spin_unlock_irq(&buflock);
-
-		dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
-
-		spin_lock_irq(&buflock);
-	}
-	spin_unlock_irq(&buflock);
-}
-
-static DECLARE_TASKLET(deferred_free, do_free, 0);
-
-static void omap_free_buffer(
-	struct usb_ep	*_ep,
-	void		*buf,
-	dma_addr_t	dma,
-	unsigned	bytes
-)
-{
-	if (!_ep) {
-		WARN_ON(1);
-		return;
-	}
-
-	/* free memory into the right allocator */
-	if (dma != DMA_ADDR_INVALID) {
-		struct omap_ep		*ep;
-		struct free_record	*rec = buf;
-		unsigned long		flags;
-
-		ep = container_of(_ep, struct omap_ep, ep);
-
-		rec->dev = ep->udc->gadget.dev.parent;
-		rec->bytes = bytes;
-		rec->dma = dma;
-
-		spin_lock_irqsave(&buflock, flags);
-		list_add_tail(&rec->list, &buffers);
-		tasklet_schedule(&deferred_free);
-		spin_unlock_irqrestore(&buflock, flags);
-	} else
-		kfree(buf);
-}
-
-/*-------------------------------------------------------------------------*/
-
 static void
 done(struct omap_ep *ep, struct omap_req *req, int status)
 {
@@ -1271,9 +1166,6 @@
 	.alloc_request	= omap_alloc_request,
 	.free_request	= omap_free_request,
 
-	.alloc_buffer	= omap_alloc_buffer,
-	.free_buffer	= omap_free_buffer,
-
 	.queue		= omap_ep_queue,
 	.dequeue	= omap_ep_dequeue,
 
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index 84392e8..63b9521 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -24,9 +24,9 @@
  *
  */
 
-#undef	DEBUG
 // #define	VERBOSE	DBG_VERBOSE
 
+#include <linux/device.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
@@ -46,19 +46,17 @@
 
 #include <asm/byteorder.h>
 #include <asm/dma.h>
+#include <asm/gpio.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/mach-types.h>
 #include <asm/unaligned.h>
 #include <asm/hardware.h>
-#ifdef CONFIG_ARCH_PXA
-#include <asm/arch/pxa-regs.h>
-#endif
 
 #include <linux/usb/ch9.h>
 #include <linux/usb_gadget.h>
 
-#include <asm/arch/udc.h>
+#include <asm/mach/udc_pxa2xx.h>
 
 
 /*
@@ -76,9 +74,17 @@
  * it constrains the sorts of USB configuration change events that work.
  * The errata for these chips are misleading; some "fixed" bugs from
  * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
+ *
+ * Note that the UDC hardware supports DMA (except on IXP) but that's
+ * not used here.  IN-DMA (to host) is simple enough, when the data is
+ * suitably aligned (16 bytes) ... the network stack doesn't do that,
+ * other software can.  OUT-DMA is buggy in most chip versions, as well
+ * as poorly designed (data toggle not automatic).  So this driver won't
+ * bother using DMA.  (Mostly-working IN-DMA support was available in
+ * kernels before 2.6.23, but was never enabled or well tested.)
  */
 
-#define	DRIVER_VERSION	"4-May-2005"
+#define	DRIVER_VERSION	"30-June-2007"
 #define	DRIVER_DESC	"PXA 25x USB Device Controller driver"
 
 
@@ -87,12 +93,9 @@
 static const char ep0name [] = "ep0";
 
 
-// #define	USE_DMA
-// #define	USE_OUT_DMA
 // #define	DISABLE_TEST_MODE
 
 #ifdef CONFIG_ARCH_IXP4XX
-#undef USE_DMA
 
 /* cpu-specific register addresses are compiled in to this code */
 #ifdef CONFIG_ARCH_PXA
@@ -104,25 +107,6 @@
 #include "pxa2xx_udc.h"
 
 
-#ifdef	USE_DMA
-static int use_dma = 1;
-module_param(use_dma, bool, 0);
-MODULE_PARM_DESC (use_dma, "true to use dma");
-
-static void dma_nodesc_handler (int dmach, void *_ep);
-static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req);
-
-#ifdef USE_OUT_DMA
-#define	DMASTR " (dma support)"
-#else
-#define	DMASTR " (dma in)"
-#endif
-
-#else	/* !USE_DMA */
-#define	DMASTR " (pio only)"
-#undef	USE_OUT_DMA
-#endif
-
 #ifdef	CONFIG_USB_PXA2XX_SMALL
 #define SIZE_STR	" (small)"
 #else
@@ -155,7 +139,7 @@
 	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
 
 	if (mach->gpio_vbus)
-		return udc_gpio_get(mach->gpio_vbus);
+		return gpio_get_value(mach->gpio_vbus);
 	if (mach->udc_is_connected)
 		return mach->udc_is_connected();
 	return 1;
@@ -167,7 +151,7 @@
 	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
 
 	if (mach->gpio_pullup)
-		udc_gpio_set(mach->gpio_pullup, 0);
+		gpio_set_value(mach->gpio_pullup, 0);
 	else if (mach->udc_command)
 		mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
 }
@@ -177,7 +161,7 @@
 	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
 
 	if (mach->gpio_pullup)
-		udc_gpio_set(mach->gpio_pullup, 1);
+		gpio_set_value(mach->gpio_pullup, 1);
 	else if (mach->udc_command)
 		mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
 }
@@ -281,9 +265,8 @@
 	}
 
 	ep->desc = desc;
-	ep->dma = -1;
 	ep->stopped = 0;
-	ep->pio_irqs = ep->dma_irqs = 0;
+	ep->pio_irqs = 0;
 	ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);
 
 	/* flush fifo (mostly for OUT buffers) */
@@ -291,30 +274,6 @@
 
 	/* ... reset halt state too, if we could ... */
 
-#ifdef	USE_DMA
-	/* for (some) bulk and ISO endpoints, try to get a DMA channel and
-	 * bind it to the endpoint.  otherwise use PIO.
-	 */
-	switch (ep->bmAttributes) {
-	case USB_ENDPOINT_XFER_ISOC:
-		if (le16_to_cpu(desc->wMaxPacketSize) % 32)
-			break;
-		// fall through
-	case USB_ENDPOINT_XFER_BULK:
-		if (!use_dma || !ep->reg_drcmr)
-			break;
-		ep->dma = pxa_request_dma ((char *)_ep->name,
-				(le16_to_cpu (desc->wMaxPacketSize) > 64)
-					? DMA_PRIO_MEDIUM /* some iso */
-					: DMA_PRIO_LOW,
-				dma_nodesc_handler, ep);
-		if (ep->dma >= 0) {
-			*ep->reg_drcmr = DRCMR_MAPVLD | ep->dma;
-			DMSG("%s using dma%d\n", _ep->name, ep->dma);
-		}
-	}
-#endif
-
 	DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
 	return 0;
 }
@@ -334,14 +293,6 @@
 
 	nuke (ep, -ESHUTDOWN);
 
-#ifdef	USE_DMA
-	if (ep->dma >= 0) {
-		*ep->reg_drcmr = 0;
-		pxa_free_dma (ep->dma);
-		ep->dma = -1;
-	}
-#endif
-
 	/* flush fifo (mostly for IN buffers) */
 	pxa2xx_ep_fifo_flush (_ep);
 
@@ -390,35 +341,6 @@
 	kfree(req);
 }
 
-
-/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's
- * no device-affinity and the heap works perfectly well for i/o buffers.
- * It wastes much less memory than dma_alloc_coherent() would, and even
- * prevents cacheline (32 bytes wide) sharing problems.
- */
-static void *
-pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
-	dma_addr_t *dma, gfp_t gfp_flags)
-{
-	char			*retval;
-
-	retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM));
-	if (retval)
-#ifdef	USE_DMA
-		*dma = virt_to_bus (retval);
-#else
-		*dma = (dma_addr_t)~0;
-#endif
-	return retval;
-}
-
-static void
-pxa2xx_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma,
-		unsigned bytes)
-{
-	kfree (buf);
-}
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -518,18 +440,8 @@
 		/* requests complete when all IN data is in the FIFO */
 		if (is_last) {
 			done (ep, req, 0);
-			if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) {
+			if (list_empty(&ep->queue))
 				pio_irq_disable (ep->bEndpointAddress);
-#ifdef USE_DMA
-				/* unaligned data and zlps couldn't use dma */
-				if (unlikely(!list_empty(&ep->queue))) {
-					req = list_entry(ep->queue.next,
-						struct pxa2xx_request, queue);
-					kick_dma(ep,req);
-					return 0;
-				}
-#endif
-			}
 			return 1;
 		}
 
@@ -728,182 +640,6 @@
 	return 0;
 }
 
-#ifdef	USE_DMA
-
-#define	MAX_IN_DMA	((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)
-
-static void
-start_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in)
-{
-	u32	dcmd = req->req.length;
-	u32	buf = req->req.dma;
-	u32	fifo = io_v2p ((u32)ep->reg_uddr);
-
-	/* caller guarantees there's a packet or more remaining
-	 *  - IN may end with a short packet (TSP set separately),
-	 *  - OUT is always full length
-	 */
-	buf += req->req.actual;
-	dcmd -= req->req.actual;
-	ep->dma_fixup = 0;
-
-	/* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */
-	DCSR(ep->dma) = DCSR_NODESC;
-	if (is_in) {
-		DSADR(ep->dma) = buf;
-		DTADR(ep->dma) = fifo;
-		if (dcmd > MAX_IN_DMA)
-			dcmd = MAX_IN_DMA;
-		else
-			ep->dma_fixup = (dcmd % ep->ep.maxpacket) != 0;
-		dcmd |= DCMD_BURST32 | DCMD_WIDTH1
-			| DCMD_FLOWTRG | DCMD_INCSRCADDR;
-	} else {
-#ifdef USE_OUT_DMA
-		DSADR(ep->dma) = fifo;
-		DTADR(ep->dma) = buf;
-		if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)
-			dcmd = ep->ep.maxpacket;
-		dcmd |= DCMD_BURST32 | DCMD_WIDTH1
-			| DCMD_FLOWSRC | DCMD_INCTRGADDR;
-#endif
-	}
-	DCMD(ep->dma) = dcmd;
-	DCSR(ep->dma) = DCSR_RUN | DCSR_NODESC
-		| (unlikely(is_in)
-			? DCSR_STOPIRQEN	/* use dma_nodesc_handler() */
-			: 0);			/* use handle_ep() */
-}
-
-static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req)
-{
-	int	is_in = ep->bEndpointAddress & USB_DIR_IN;
-
-	if (is_in) {
-		/* unaligned tx buffers and zlps only work with PIO */
-		if ((req->req.dma & 0x0f) != 0
-				|| unlikely((req->req.length - req->req.actual)
-						== 0)) {
-			pio_irq_enable(ep->bEndpointAddress);
-			if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0)
-				(void) write_fifo(ep, req);
-		} else {
-			start_dma_nodesc(ep, req, USB_DIR_IN);
-		}
-	} else {
-		if ((req->req.length - req->req.actual) < ep->ep.maxpacket) {
-			DMSG("%s short dma read...\n", ep->ep.name);
-			/* we're always set up for pio out */
-			read_fifo (ep, req);
-		} else {
-			*ep->reg_udccs = UDCCS_BO_DME
-				| (*ep->reg_udccs & UDCCS_BO_FST);
-			start_dma_nodesc(ep, req, USB_DIR_OUT);
-		}
-	}
-}
-
-static void cancel_dma(struct pxa2xx_ep *ep)
-{
-	struct pxa2xx_request	*req;
-	u32			tmp;
-
-	if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))
-		return;
-
-	DCSR(ep->dma) = 0;
-	while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)
-		cpu_relax();
-
-	req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
-	tmp = DCMD(ep->dma) & DCMD_LENGTH;
-	req->req.actual = req->req.length - (tmp & DCMD_LENGTH);
-
-	/* the last tx packet may be incomplete, so flush the fifo.
-	 * FIXME correct req.actual if we can
-	 */
-	if (ep->bEndpointAddress & USB_DIR_IN)
-		*ep->reg_udccs = UDCCS_BI_FTF;
-}
-
-/* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */
-static void dma_nodesc_handler(int dmach, void *_ep)
-{
-	struct pxa2xx_ep	*ep = _ep;
-	struct pxa2xx_request	*req;
-	u32			tmp, completed;
-
-	local_irq_disable();
-
-	req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
-
-	ep->dma_irqs++;
-	ep->dev->stats.irqs++;
-	HEX_DISPLAY(ep->dev->stats.irqs);
-
-	/* ack/clear */
-	tmp = DCSR(ep->dma);
-	DCSR(ep->dma) = tmp;
-	if ((tmp & DCSR_STOPSTATE) == 0
-			|| (DDADR(ep->dma) & DDADR_STOP) != 0) {
-		DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n",
-			ep->ep.name, DCSR(ep->dma), DDADR(ep->dma));
-		goto done;
-	}
-	DCSR(ep->dma) = 0;	/* clear DCSR_STOPSTATE */
-
-	/* update transfer status */
-	completed = tmp & DCSR_BUSERR;
-	if (ep->bEndpointAddress & USB_DIR_IN)
-		tmp = DSADR(ep->dma);
-	else
-		tmp = DTADR(ep->dma);
-	req->req.actual = tmp - req->req.dma;
-
-	/* FIXME seems we sometimes see partial transfers... */
-
-	if (unlikely(completed != 0))
-		req->req.status = -EIO;
-	else if (req->req.actual) {
-		/* these registers have zeroes in low bits; they miscount
-		 * some (end-of-transfer) short packets:  tx 14 as tx 12
-		 */
-		if (ep->dma_fixup)
-			req->req.actual = min(req->req.actual + 3,
-						req->req.length);
-
-		tmp = (req->req.length - req->req.actual);
-		completed = (tmp == 0);
-		if (completed && (ep->bEndpointAddress & USB_DIR_IN)) {
-
-			/* maybe validate final short packet ... */
-			if ((req->req.actual % ep->ep.maxpacket) != 0)
-				*ep->reg_udccs = UDCCS_BI_TSP/*|UDCCS_BI_TPC*/;
-
-			/* ... or zlp, using pio fallback */
-			else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK
-					&& req->req.zero) {
-				DMSG("%s zlp terminate ...\n", ep->ep.name);
-				completed = 0;
-			}
-		}
-	}
-
-	if (likely(completed)) {
-		done(ep, req, 0);
-
-		/* maybe re-activate after completion */
-		if (ep->stopped || list_empty(&ep->queue))
-			goto done;
-		req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
-	}
-	kick_dma(ep, req);
-done:
-	local_irq_enable();
-}
-
-#endif
-
 /*-------------------------------------------------------------------------*/
 
 static int
@@ -942,19 +678,8 @@
 						(ep->desc->wMaxPacketSize)))
 		return -EMSGSIZE;
 
-#ifdef	USE_DMA
-	// FIXME caller may already have done the dma mapping
-	if (ep->dma >= 0) {
-		_req->dma = dma_map_single(dev->dev,
-			_req->buf, _req->length,
-			((ep->bEndpointAddress & USB_DIR_IN) != 0)
-				? DMA_TO_DEVICE
-				: DMA_FROM_DEVICE);
-	}
-#endif
-
 	DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
-	     _ep->name, _req, _req->length, _req->buf);
+		_ep->name, _req, _req->length, _req->buf);
 
 	local_irq_save(flags);
 
@@ -1002,11 +727,6 @@
 				local_irq_restore (flags);
 				return -EL2HLT;
 			}
-#ifdef	USE_DMA
-		/* either start dma or prime pio pump */
-		} else if (ep->dma >= 0) {
-			kick_dma(ep, req);
-#endif
 		/* can the FIFO can satisfy the request immediately? */
 		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
 			if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
@@ -1017,7 +737,7 @@
 			req = NULL;
 		}
 
-		if (likely (req && ep->desc) && ep->dma < 0)
+		if (likely (req && ep->desc))
 			pio_irq_enable(ep->bEndpointAddress);
 	}
 
@@ -1038,10 +758,6 @@
 	struct pxa2xx_request *req;
 
 	/* called with irqs blocked */
-#ifdef	USE_DMA
-	if (ep->dma >= 0 && !ep->stopped)
-		cancel_dma(ep);
-#endif
 	while (!list_empty(&ep->queue)) {
 		req = list_entry(ep->queue.next,
 				struct pxa2xx_request,
@@ -1076,19 +792,7 @@
 		return -EINVAL;
 	}
 
-#ifdef	USE_DMA
-	if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
-		cancel_dma(ep);
-		done(ep, req, -ECONNRESET);
-		/* restart i/o */
-		if (!list_empty(&ep->queue)) {
-			req = list_entry(ep->queue.next,
-					struct pxa2xx_request, queue);
-			kick_dma(ep, req);
-		}
-	} else
-#endif
-		done(ep, req, -ECONNRESET);
+	done(ep, req, -ECONNRESET);
 
 	local_irq_restore(flags);
 	return 0;
@@ -1203,9 +907,6 @@
 	.alloc_request	= pxa2xx_ep_alloc_request,
 	.free_request	= pxa2xx_ep_free_request,
 
-	.alloc_buffer	= pxa2xx_ep_alloc_buffer,
-	.free_buffer	= pxa2xx_ep_free_buffer,
-
 	.queue		= pxa2xx_ep_queue,
 	.dequeue	= pxa2xx_ep_dequeue,
 
@@ -1325,7 +1026,7 @@
 	/* basic device status */
 	t = scnprintf(next, size, DRIVER_DESC "\n"
 		"%s version: %s\nGadget driver: %s\nHost %s\n\n",
-		driver_name, DRIVER_VERSION SIZE_STR DMASTR,
+		driver_name, DRIVER_VERSION SIZE_STR "(pio)",
 		dev->driver ? dev->driver->driver.name : "(none)",
 		is_vbus_present() ? "full speed" : "disconnected");
 	size -= t;
@@ -1390,7 +1091,6 @@
 	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
 		struct pxa2xx_ep	*ep = &dev->ep [i];
 		struct pxa2xx_request	*req;
-		int			t;
 
 		if (i != 0) {
 			const struct usb_endpoint_descriptor	*d;
@@ -1400,10 +1100,9 @@
 				continue;
 			tmp = *dev->ep [i].reg_udccs;
 			t = scnprintf(next, size,
-				"%s max %d %s udccs %02x irqs %lu/%lu\n",
+				"%s max %d %s udccs %02x irqs %lu\n",
 				ep->ep.name, le16_to_cpu (d->wMaxPacketSize),
-				(ep->dma >= 0) ? "dma" : "pio", tmp,
-				ep->pio_irqs, ep->dma_irqs);
+				"pio", tmp, ep->pio_irqs);
 			/* TODO translate all five groups of udccs bits! */
 
 		} else /* ep0 should only have one transfer queued */
@@ -1423,19 +1122,7 @@
 			continue;
 		}
 		list_for_each_entry(req, &ep->queue, queue) {
-#ifdef	USE_DMA
-			if (ep->dma >= 0 && req->queue.prev == &ep->queue)
-				t = scnprintf(next, size,
-					"\treq %p len %d/%d "
-					"buf %p (dma%d dcmd %08x)\n",
-					&req->req, req->req.actual,
-					req->req.length, req->req.buf,
-					ep->dma, DCMD(ep->dma)
-					// low 13 bits == bytes-to-go
-					);
-			else
-#endif
-				t = scnprintf(next, size,
+			t = scnprintf(next, size,
 					"\treq %p len %d/%d buf %p\n",
 					&req->req, req->req.actual,
 					req->req.length, req->req.buf);
@@ -1488,7 +1175,6 @@
 
 	ep0_idle (dev);
 	dev->gadget.speed = USB_SPEED_UNKNOWN;
-	LED_CONNECTED_OFF;
 }
 
 
@@ -1514,7 +1200,7 @@
 		ep->desc = NULL;
 		ep->stopped = 0;
 		INIT_LIST_HEAD (&ep->queue);
-		ep->pio_irqs = ep->dma_irqs = 0;
+		ep->pio_irqs = 0;
 	}
 
 	/* the rest was statically initialized, and is read-only */
@@ -1666,7 +1352,6 @@
 	del_timer_sync(&dev->timer);
 
 	/* report disconnect; the driver is already quiesced */
-	LED_CONNECTED_OFF;
 	if (driver)
 		driver->disconnect(&dev->gadget);
 
@@ -1715,16 +1400,13 @@
 	int			vbus;
 
 	dev->stats.irqs++;
-	HEX_DISPLAY(dev->stats.irqs);
 	switch (irq) {
 	case LUBBOCK_USB_IRQ:
-		LED_CONNECTED_ON;
 		vbus = 1;
 		disable_irq(LUBBOCK_USB_IRQ);
 		enable_irq(LUBBOCK_USB_DISC_IRQ);
 		break;
 	case LUBBOCK_USB_DISC_IRQ:
-		LED_CONNECTED_OFF;
 		vbus = 0;
 		disable_irq(LUBBOCK_USB_DISC_IRQ);
 		enable_irq(LUBBOCK_USB_IRQ);
@@ -1742,7 +1424,7 @@
 static irqreturn_t udc_vbus_irq(int irq, void *_dev)
 {
 	struct pxa2xx_udc	*dev = _dev;
-	int			vbus = udc_gpio_get(dev->mach->gpio_vbus);
+	int			vbus = gpio_get_value(dev->mach->gpio_vbus);
 
 	pxa2xx_udc_vbus_session(&dev->gadget, vbus);
 	return IRQ_HANDLED;
@@ -2040,18 +1722,6 @@
 
 			/* fifos can hold packets, ready for reading... */
 			if (likely(req)) {
-#ifdef USE_OUT_DMA
-// TODO didn't yet debug out-dma.  this approach assumes
-// the worst about short packets and RPC; it might be better.
-
-				if (likely(ep->dma >= 0)) {
-					if (!(udccs & UDCCS_BO_RSP)) {
-						*ep->reg_udccs = UDCCS_BO_RPC;
-						ep->dma_irqs++;
-						return;
-					}
-				}
-#endif
 				completed = read_fifo(ep, req);
 			} else
 				pio_irq_disable (ep->bEndpointAddress);
@@ -2074,7 +1744,6 @@
 	int			handled;
 
 	dev->stats.irqs++;
-	HEX_DISPLAY(dev->stats.irqs);
 	do {
 		u32		udccr = UDCCR;
 
@@ -2125,7 +1794,6 @@
 			} else {
 				DBG(DBG_VERBOSE, "USB reset end\n");
 				dev->gadget.speed = USB_SPEED_FULL;
-				LED_CONNECTED_ON;
 				memset(&dev->stats, 0, sizeof dev->stats);
 				/* driver and endpoints are still reset */
 			}
@@ -2217,7 +1885,6 @@
 		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
 		.reg_udccs	= &UDCCS1,
 		.reg_uddr	= &UDDR1,
-		drcmr (25)
 	},
 	.ep[2] = {
 		.ep = {
@@ -2232,7 +1899,6 @@
 		.reg_udccs	= &UDCCS2,
 		.reg_ubcr	= &UBCR2,
 		.reg_uddr	= &UDDR2,
-		drcmr (26)
 	},
 #ifndef CONFIG_USB_PXA2XX_SMALL
 	.ep[3] = {
@@ -2247,7 +1913,6 @@
 		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
 		.reg_udccs	= &UDCCS3,
 		.reg_uddr	= &UDDR3,
-		drcmr (27)
 	},
 	.ep[4] = {
 		.ep = {
@@ -2262,7 +1927,6 @@
 		.reg_udccs	= &UDCCS4,
 		.reg_ubcr	= &UBCR4,
 		.reg_uddr	= &UDDR4,
-		drcmr (28)
 	},
 	.ep[5] = {
 		.ep = {
@@ -2291,7 +1955,6 @@
 		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
 		.reg_udccs	= &UDCCS6,
 		.reg_uddr	= &UDDR6,
-		drcmr (30)
 	},
 	.ep[7] = {
 		.ep = {
@@ -2306,7 +1969,6 @@
 		.reg_udccs	= &UDCCS7,
 		.reg_ubcr	= &UBCR7,
 		.reg_uddr	= &UDDR7,
-		drcmr (31)
 	},
 	.ep[8] = {
 		.ep = {
@@ -2320,7 +1982,6 @@
 		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
 		.reg_udccs	= &UDCCS8,
 		.reg_uddr	= &UDDR8,
-		drcmr (32)
 	},
 	.ep[9] = {
 		.ep = {
@@ -2335,7 +1996,6 @@
 		.reg_udccs	= &UDCCS9,
 		.reg_ubcr	= &UBCR9,
 		.reg_uddr	= &UDDR9,
-		drcmr (33)
 	},
 	.ep[10] = {
 		.ep = {
@@ -2364,7 +2024,6 @@
 		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
 		.reg_udccs	= &UDCCS11,
 		.reg_uddr	= &UDDR11,
-		drcmr (35)
 	},
 	.ep[12] = {
 		.ep = {
@@ -2379,7 +2038,6 @@
 		.reg_udccs	= &UDCCS12,
 		.reg_ubcr	= &UBCR12,
 		.reg_uddr	= &UDDR12,
-		drcmr (36)
 	},
 	.ep[13] = {
 		.ep = {
@@ -2393,7 +2051,6 @@
 		.bmAttributes	= USB_ENDPOINT_XFER_ISOC,
 		.reg_udccs	= &UDCCS13,
 		.reg_uddr	= &UDDR13,
-		drcmr (37)
 	},
 	.ep[14] = {
 		.ep = {
@@ -2408,7 +2065,6 @@
 		.reg_udccs	= &UDCCS14,
 		.reg_ubcr	= &UBCR14,
 		.reg_uddr	= &UDDR14,
-		drcmr (38)
 	},
 	.ep[15] = {
 		.ep = {
@@ -2466,7 +2122,7 @@
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 {
 	struct pxa2xx_udc *dev = &memory;
-	int retval, out_dma = 1, vbus_irq, irq;
+	int retval, vbus_irq, irq;
 	u32 chiprev;
 
 	/* insist on Intel/ARM/XScale */
@@ -2489,7 +2145,7 @@
 	case PXA250_B2: case PXA210_B2:
 	case PXA250_B1: case PXA210_B1:
 	case PXA250_B0: case PXA210_B0:
-		out_dma = 0;
+		/* OUT-DMA is broken ... */
 		/* fall through */
 	case PXA250_C0: case PXA210_C0:
 		break;
@@ -2498,11 +2154,9 @@
 	case IXP425_B0:
 	case IXP465_AD:
 		dev->has_cfr = 1;
-		out_dma = 0;
 		break;
 #endif
 	default:
-		out_dma = 0;
 		printk(KERN_ERR "%s: unrecognized processor: %08x\n",
 			driver_name, chiprev);
 		/* iop3xx, ixp4xx, ... */
@@ -2513,36 +2167,41 @@
 	if (irq < 0)
 		return -ENODEV;
 
-	pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq,
+	pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
 		dev->has_cfr ? "" : " (!cfr)",
-		out_dma ? "" : " (broken dma-out)",
-		SIZE_STR DMASTR
+		SIZE_STR "(pio)"
 		);
 
-#ifdef	USE_DMA
-#ifndef	USE_OUT_DMA
-	out_dma = 0;
-#endif
-	/* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */
-	if (!out_dma) {
-		DMSG("disabled OUT dma\n");
-		dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0;
-		dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0;
-		dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0;
-	}
-#endif
-
 	/* other non-static parts of init */
 	dev->dev = &pdev->dev;
 	dev->mach = pdev->dev.platform_data;
+
 	if (dev->mach->gpio_vbus) {
-		udc_gpio_init_vbus(dev->mach->gpio_vbus);
-		vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus);
+		if ((retval = gpio_request(dev->mach->gpio_vbus,
+				"pxa2xx_udc GPIO VBUS"))) {
+			dev_dbg(&pdev->dev,
+				"can't get vbus gpio %d, err: %d\n",
+				dev->mach->gpio_vbus, retval);
+			return -EBUSY;
+		}
+		gpio_direction_input(dev->mach->gpio_vbus);
+		vbus_irq = gpio_to_irq(dev->mach->gpio_vbus);
 		set_irq_type(vbus_irq, IRQT_BOTHEDGE);
 	} else
 		vbus_irq = 0;
-	if (dev->mach->gpio_pullup)
-		udc_gpio_init_pullup(dev->mach->gpio_pullup);
+
+	if (dev->mach->gpio_pullup) {
+		if ((retval = gpio_request(dev->mach->gpio_pullup,
+				"pca2xx_udc GPIO PULLUP"))) {
+			dev_dbg(&pdev->dev,
+				"can't get pullup gpio %d, err: %d\n",
+				dev->mach->gpio_pullup, retval);
+			if (dev->mach->gpio_vbus)
+				gpio_free(dev->mach->gpio_vbus);
+			return -EBUSY;
+		}
+		gpio_direction_output(dev->mach->gpio_pullup, 0);
+	}
 
 	init_timer(&dev->timer);
 	dev->timer.function = udc_watchdog;
@@ -2566,6 +2225,10 @@
 	if (retval != 0) {
 		printk(KERN_ERR "%s: can't get irq %d, err %d\n",
 			driver_name, irq, retval);
+		if (dev->mach->gpio_pullup)
+			gpio_free(dev->mach->gpio_pullup);
+		if (dev->mach->gpio_vbus)
+			gpio_free(dev->mach->gpio_vbus);
 		return -EBUSY;
 	}
 	dev->got_irq = 1;
@@ -2581,6 +2244,10 @@
 				driver_name, LUBBOCK_USB_DISC_IRQ, retval);
 lubbock_fail0:
 			free_irq(irq, dev);
+			if (dev->mach->gpio_pullup)
+				gpio_free(dev->mach->gpio_pullup);
+			if (dev->mach->gpio_vbus)
+				gpio_free(dev->mach->gpio_vbus);
 			return -EBUSY;
 		}
 		retval = request_irq(LUBBOCK_USB_IRQ,
@@ -2593,11 +2260,6 @@
 			free_irq(LUBBOCK_USB_DISC_IRQ, dev);
 			goto lubbock_fail0;
 		}
-#ifdef DEBUG
-		/* with U-Boot (but not BLOB), hex is off by default */
-		HEX_DISPLAY(dev->stats.irqs);
-		LUB_DISC_BLNK_LED &= 0xff;
-#endif
 	} else
 #endif
 	if (vbus_irq) {
@@ -2608,6 +2270,10 @@
 			printk(KERN_ERR "%s: can't get irq %i, err %d\n",
 				driver_name, vbus_irq, retval);
 			free_irq(irq, dev);
+			if (dev->mach->gpio_pullup)
+				gpio_free(dev->mach->gpio_pullup);
+			if (dev->mach->gpio_vbus)
+				gpio_free(dev->mach->gpio_vbus);
 			return -EBUSY;
 		}
 	}
@@ -2641,8 +2307,13 @@
 		free_irq(LUBBOCK_USB_IRQ, dev);
 	}
 #endif
-	if (dev->mach->gpio_vbus)
-		free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
+	if (dev->mach->gpio_vbus) {
+		free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev);
+		gpio_free(dev->mach->gpio_vbus);
+	}
+	if (dev->mach->gpio_pullup)
+		gpio_free(dev->mach->gpio_pullup);
+
 	platform_set_drvdata(pdev, NULL);
 	the_controller = NULL;
 	return 0;
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
index 773e549..0e5d0e6 100644
--- a/drivers/usb/gadget/pxa2xx_udc.h
+++ b/drivers/usb/gadget/pxa2xx_udc.h
@@ -54,8 +54,6 @@
 	const struct usb_endpoint_descriptor	*desc;
 	struct list_head			queue;
 	unsigned long				pio_irqs;
-	unsigned long				dma_irqs;
-	short					dma; 
 
 	unsigned short				fifo_size;
 	u8					bEndpointAddress;
@@ -63,7 +61,7 @@
 
 	unsigned				stopped : 1;
 	unsigned				dma_fixup : 1;
-							 
+
 	/* UDCCS = UDC Control/Status for this EP
 	 * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
 	 * UDDR = UDC Endpoint Data Register (the fifo)
@@ -72,12 +70,6 @@
 	volatile u32				*reg_udccs;
 	volatile u32				*reg_ubcr;
 	volatile u32				*reg_uddr;
-#ifdef USE_DMA
-	volatile u32			*reg_drcmr;
-#define	drcmr(n)  .reg_drcmr = & DRCMR ## n ,
-#else
-#define	drcmr(n)  
-#endif
 };
 
 struct pxa2xx_request {
@@ -85,7 +77,7 @@
 	struct list_head			queue;
 };
 
-enum ep0_state { 
+enum ep0_state {
 	EP0_IDLE,
 	EP0_IN_DATA_PHASE,
 	EP0_OUT_DATA_PHASE,
@@ -108,7 +100,6 @@
 
 #ifdef CONFIG_USB_PXA2XX_SMALL
 /* when memory's tight, SMALL config saves code+data.  */
-#undef	USE_DMA
 #define	PXA_UDC_NUM_ENDPOINTS	3
 #endif
 
@@ -144,37 +135,8 @@
 #ifdef CONFIG_ARCH_LUBBOCK
 #include <asm/arch/lubbock.h>
 /* lubbock can also report usb connect/disconnect irqs */
-
-#ifdef DEBUG
-#define HEX_DISPLAY(n)	if (machine_is_lubbock()) { LUB_HEXLED = (n); }
 #endif
 
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/* LEDs are only for debug */
-#ifndef HEX_DISPLAY
-#define HEX_DISPLAY(n)		do {} while(0)
-#endif
-
-#ifdef DEBUG
-#include <asm/leds.h>
-
-#define LED_CONNECTED_ON	leds_event(led_green_on)
-#define LED_CONNECTED_OFF	do { \
-					leds_event(led_green_off); \
-					HEX_DISPLAY(0); \
-				} while(0)
-#endif
-
-#ifndef LED_CONNECTED_ON
-#define LED_CONNECTED_ON	do {} while(0)
-#define LED_CONNECTED_OFF	do {} while(0)
-#endif
-
-/*-------------------------------------------------------------------------*/
-
 static struct pxa2xx_udc *the_controller;
 
 /*-------------------------------------------------------------------------*/
@@ -204,7 +166,7 @@
 #    define UDC_DEBUG DBG_NORMAL
 #endif
 
-static void __attribute__ ((__unused__))
+static void __maybe_unused
 dump_udccr(const char *label)
 {
 	u32	udccr = UDCCR;
@@ -220,7 +182,7 @@
 		(udccr & UDCCR_UDE) ? " ude" : "");
 }
 
-static void __attribute__ ((__unused__))
+static void __maybe_unused
 dump_udccs0(const char *label)
 {
 	u32		udccs0 = UDCCS0;
@@ -237,7 +199,7 @@
 		(udccs0 & UDCCS0_OPR) ? " opr" : "");
 }
 
-static void __attribute__ ((__unused__))
+static void __maybe_unused
 dump_state(struct pxa2xx_udc *dev)
 {
 	u32		tmp;
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index 708657c..db1b2bf 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -53,7 +53,7 @@
  */
 
 #if 0
-#define DEBUG(str,args...) do { \
+#define DBG(str,args...) do { \
 	if (rndis_debug) \
 		printk(KERN_DEBUG str , ## args ); \
 	} while (0)
@@ -65,7 +65,7 @@
 #else
 
 #define rndis_debug		0
-#define DEBUG(str,args...)	do{}while(0)
+#define DBG(str,args...)	do{}while(0)
 #endif
 
 #define RNDIS_MAX_CONFIGS	1
@@ -183,9 +183,9 @@
 	if (!resp) return -ENOMEM;
 
 	if (buf_len && rndis_debug > 1) {
-		DEBUG("query OID %08x value, len %d:\n", OID, buf_len);
+		DBG("query OID %08x value, len %d:\n", OID, buf_len);
 		for (i = 0; i < buf_len; i += 16) {
-			DEBUG ("%03d: %08x %08x %08x %08x\n", i,
+			DBG("%03d: %08x %08x %08x %08x\n", i,
 				le32_to_cpu(get_unaligned((__le32 *)
 					&buf[i])),
 				le32_to_cpu(get_unaligned((__le32 *)
@@ -207,7 +207,7 @@
 
 	/* mandatory */
 	case OID_GEN_SUPPORTED_LIST:
-		DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
+		DBG("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__);
 		length = sizeof (oid_supported_list);
 		count  = length / sizeof (u32);
 		for (i = 0; i < count; i++)
@@ -217,7 +217,7 @@
 
 	/* mandatory */
 	case OID_GEN_HARDWARE_STATUS:
-		DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
+		DBG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__);
 		/* Bogus question!
 		 * Hardware must be ready to receive high level protocols.
 		 * BTW:
@@ -230,14 +230,14 @@
 
 	/* mandatory */
 	case OID_GEN_MEDIA_SUPPORTED:
-		DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__);
 		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
 		retval = 0;
 		break;
 
 	/* mandatory */
 	case OID_GEN_MEDIA_IN_USE:
-		DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__);
 		/* one medium, one transport... (maybe you do it better) */
 		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
 		retval = 0;
@@ -245,7 +245,7 @@
 
 	/* mandatory */
 	case OID_GEN_MAXIMUM_FRAME_SIZE:
-		DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
 			*outbuf = cpu_to_le32 (
 				rndis_per_dev_params [configNr].dev->mtu);
@@ -256,7 +256,7 @@
 	/* mandatory */
 	case OID_GEN_LINK_SPEED:
 		if (rndis_debug > 1)
-			DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
+			DBG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].media_state
 				== NDIS_MEDIA_STATE_DISCONNECTED)
 			*outbuf = __constant_cpu_to_le32 (0);
@@ -268,7 +268,7 @@
 
 	/* mandatory */
 	case OID_GEN_TRANSMIT_BLOCK_SIZE:
-		DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
+		DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
 			*outbuf = cpu_to_le32 (
 				rndis_per_dev_params [configNr].dev->mtu);
@@ -278,7 +278,7 @@
 
 	/* mandatory */
 	case OID_GEN_RECEIVE_BLOCK_SIZE:
-		DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
+		DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
 			*outbuf = cpu_to_le32 (
 				rndis_per_dev_params [configNr].dev->mtu);
@@ -288,7 +288,7 @@
 
 	/* mandatory */
 	case OID_GEN_VENDOR_ID:
-		DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
+		DBG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__);
 		*outbuf = cpu_to_le32 (
 			rndis_per_dev_params [configNr].vendorID);
 		retval = 0;
@@ -296,7 +296,7 @@
 
 	/* mandatory */
 	case OID_GEN_VENDOR_DESCRIPTION:
-		DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
+		DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__);
 		length = strlen (rndis_per_dev_params [configNr].vendorDescr);
 		memcpy (outbuf,
 			rndis_per_dev_params [configNr].vendorDescr, length);
@@ -304,7 +304,7 @@
 		break;
 
 	case OID_GEN_VENDOR_DRIVER_VERSION:
-		DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
+		DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__);
 		/* Created as LE */
 		*outbuf = rndis_driver_version;
 		retval = 0;
@@ -312,14 +312,14 @@
 
 	/* mandatory */
 	case OID_GEN_CURRENT_PACKET_FILTER:
-		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
+		DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
 		*outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
 		retval = 0;
 		break;
 
 	/* mandatory */
 	case OID_GEN_MAXIMUM_TOTAL_SIZE:
-		DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
 		retval = 0;
 		break;
@@ -327,14 +327,14 @@
 	/* mandatory */
 	case OID_GEN_MEDIA_CONNECT_STATUS:
 		if (rndis_debug > 1)
-			DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
+			DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__);
 		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 						.media_state);
 		retval = 0;
 		break;
 
 	case OID_GEN_PHYSICAL_MEDIUM:
-		DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
+		DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32 (0);
 		retval = 0;
 		break;
@@ -344,7 +344,7 @@
 	 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
 	 */
 	case OID_GEN_MAC_OPTIONS:		/* from WinME */
-		DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32(
 			  NDIS_MAC_OPTION_RECEIVE_SERIALIZED
 			| NDIS_MAC_OPTION_FULL_DUPLEX);
@@ -356,7 +356,7 @@
 	/* mandatory */
 	case OID_GEN_XMIT_OK:
 		if (rndis_debug > 1)
-			DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
+			DBG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (
 			    rndis_per_dev_params [configNr].stats->tx_packets -
@@ -369,7 +369,7 @@
 	/* mandatory */
 	case OID_GEN_RCV_OK:
 		if (rndis_debug > 1)
-			DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
+			DBG("%s: OID_GEN_RCV_OK\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (
 			    rndis_per_dev_params [configNr].stats->rx_packets -
@@ -382,7 +382,7 @@
 	/* mandatory */
 	case OID_GEN_XMIT_ERROR:
 		if (rndis_debug > 1)
-			DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
+			DBG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->tx_errors);
@@ -393,7 +393,7 @@
 	/* mandatory */
 	case OID_GEN_RCV_ERROR:
 		if (rndis_debug > 1)
-			DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
+			DBG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->rx_errors);
@@ -403,7 +403,7 @@
 
 	/* mandatory */
 	case OID_GEN_RCV_NO_BUFFER:
-		DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
+		DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->rx_dropped);
@@ -413,7 +413,7 @@
 
 #ifdef	RNDIS_OPTIONAL_STATS
 	case OID_GEN_DIRECTED_BYTES_XMIT:
-		DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
+		DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__);
 		/*
 		 * Aunt Tilly's size of shoes
 		 * minus antarctica count of penguins
@@ -433,7 +433,7 @@
 		break;
 
 	case OID_GEN_DIRECTED_FRAMES_XMIT:
-		DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
+		DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__);
 		/* dito */
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (
@@ -449,7 +449,7 @@
 		break;
 
 	case OID_GEN_MULTICAST_BYTES_XMIT:
-		DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->multicast*1234);
@@ -458,7 +458,7 @@
 		break;
 
 	case OID_GEN_MULTICAST_FRAMES_XMIT:
-		DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->multicast);
@@ -467,7 +467,7 @@
 		break;
 
 	case OID_GEN_BROADCAST_BYTES_XMIT:
-		DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
+		DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->tx_packets/42*255);
@@ -476,7 +476,7 @@
 		break;
 
 	case OID_GEN_BROADCAST_FRAMES_XMIT:
-		DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
+		DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->tx_packets/42);
@@ -485,19 +485,19 @@
 		break;
 
 	case OID_GEN_DIRECTED_BYTES_RCV:
-		DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
+		DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32 (0);
 		retval = 0;
 		break;
 
 	case OID_GEN_DIRECTED_FRAMES_RCV:
-		DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
+		DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32 (0);
 		retval = 0;
 		break;
 
 	case OID_GEN_MULTICAST_BYTES_RCV:
-		DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->multicast * 1111);
@@ -506,7 +506,7 @@
 		break;
 
 	case OID_GEN_MULTICAST_FRAMES_RCV:
-		DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
+		DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->multicast);
@@ -515,7 +515,7 @@
 		break;
 
 	case OID_GEN_BROADCAST_BYTES_RCV:
-		DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
+		DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->rx_packets/42*255);
@@ -524,7 +524,7 @@
 		break;
 
 	case OID_GEN_BROADCAST_FRAMES_RCV:
-		DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
+		DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->rx_packets/42);
@@ -533,7 +533,7 @@
 		break;
 
 	case OID_GEN_RCV_CRC_ERROR:
-		DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
+		DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->rx_crc_errors);
@@ -542,7 +542,7 @@
 		break;
 
 	case OID_GEN_TRANSMIT_QUEUE_LENGTH:
-		DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
+		DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32 (0);
 		retval = 0;
 		break;
@@ -552,7 +552,7 @@
 
 	/* mandatory */
 	case OID_802_3_PERMANENT_ADDRESS:
-		DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
+		DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
 			length = ETH_ALEN;
 			memcpy (outbuf,
@@ -564,7 +564,7 @@
 
 	/* mandatory */
 	case OID_802_3_CURRENT_ADDRESS:
-		DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
+		DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].dev) {
 			length = ETH_ALEN;
 			memcpy (outbuf,
@@ -576,7 +576,7 @@
 
 	/* mandatory */
 	case OID_802_3_MULTICAST_LIST:
-		DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
+		DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
 		/* Multicast base address only */
 		*outbuf = __constant_cpu_to_le32 (0xE0000000);
 		retval = 0;
@@ -584,21 +584,21 @@
 
 	/* mandatory */
 	case OID_802_3_MAXIMUM_LIST_SIZE:
-		DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
+		DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__);
 		/* Multicast base address only */
 		*outbuf = __constant_cpu_to_le32 (1);
 		retval = 0;
 		break;
 
 	case OID_802_3_MAC_OPTIONS:
-		DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
+		DBG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__);
 		break;
 
 	/* ieee802.3 statistics OIDs (table 4-4) */
 
 	/* mandatory */
 	case OID_802_3_RCV_ERROR_ALIGNMENT:
-		DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
+		DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__);
 		if (rndis_per_dev_params [configNr].stats) {
 			*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
 					.stats->rx_frame_errors);
@@ -608,51 +608,51 @@
 
 	/* mandatory */
 	case OID_802_3_XMIT_ONE_COLLISION:
-		DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32 (0);
 		retval = 0;
 		break;
 
 	/* mandatory */
 	case OID_802_3_XMIT_MORE_COLLISIONS:
-		DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__);
 		*outbuf = __constant_cpu_to_le32 (0);
 		retval = 0;
 		break;
 
 #ifdef	RNDIS_OPTIONAL_STATS
 	case OID_802_3_XMIT_DEFERRED:
-		DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__);
 		/* TODO */
 		break;
 
 	case OID_802_3_XMIT_MAX_COLLISIONS:
-		DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__);
 		/* TODO */
 		break;
 
 	case OID_802_3_RCV_OVERRUN:
-		DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
+		DBG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__);
 		/* TODO */
 		break;
 
 	case OID_802_3_XMIT_UNDERRUN:
-		DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__);
 		/* TODO */
 		break;
 
 	case OID_802_3_XMIT_HEARTBEAT_FAILURE:
-		DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__);
 		/* TODO */
 		break;
 
 	case OID_802_3_XMIT_TIMES_CRS_LOST:
-		DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__);
 		/* TODO */
 		break;
 
 	case OID_802_3_XMIT_LATE_COLLISIONS:
-		DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
+		DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__);
 		/* TODO */
 		break;
 #endif	/* RNDIS_OPTIONAL_STATS */
@@ -660,7 +660,7 @@
 #ifdef	RNDIS_PM
 	/* power management OIDs (table 4-5) */
 	case OID_PNP_CAPABILITIES:
-		DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
+		DBG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__);
 
 		/* for now, no wakeup capabilities */
 		length = sizeof (struct NDIS_PNP_CAPABILITIES);
@@ -668,7 +668,7 @@
 		retval = 0;
 		break;
 	case OID_PNP_QUERY_POWER:
-		DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
+		DBG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__,
 				le32_to_cpu(get_unaligned((__le32 *)buf)) - 1);
 		/* only suspend is a real power state, and
 		 * it can't be entered by OID_PNP_SET_POWER...
@@ -705,9 +705,9 @@
 		return -ENOMEM;
 
 	if (buf_len && rndis_debug > 1) {
-		DEBUG("set OID %08x value, len %d:\n", OID, buf_len);
+		DBG("set OID %08x value, len %d:\n", OID, buf_len);
 		for (i = 0; i < buf_len; i += 16) {
-			DEBUG ("%03d: %08x %08x %08x %08x\n", i,
+			DBG("%03d: %08x %08x %08x %08x\n", i,
 				le32_to_cpu(get_unaligned((__le32 *)
 					&buf[i])),
 				le32_to_cpu(get_unaligned((__le32 *)
@@ -731,7 +731,7 @@
 		 */
 		*params->filter = (u16) le32_to_cpu(get_unaligned(
 				(__le32 *)buf));
-		DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+		DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
 			__FUNCTION__, *params->filter);
 
 		/* this call has a significant side effect:  it's
@@ -756,7 +756,7 @@
 
 	case OID_802_3_MULTICAST_LIST:
 		/* I think we can ignore this */
-		DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
+		DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__);
 		retval = 0;
 		break;
 #if 0
@@ -764,7 +764,7 @@
 		{
 		struct rndis_config_parameter	*param;
 		param = (struct rndis_config_parameter *) buf;
-		DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
+		DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
 			__FUNCTION__,
 			min(cpu_to_le32(param->ParameterNameLength),80),
 			buf + param->ParameterNameOffset);
@@ -781,7 +781,7 @@
 		 * FIXME ... then things go batty; Windows wedges itself.
 		 */
 		i = le32_to_cpu(get_unaligned((__le32 *)buf));
-		DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
+		DBG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1);
 		switch (i) {
 		case NdisDeviceStateD0:
 			*params->filter = params->saved_filter;
@@ -858,7 +858,7 @@
 	rndis_query_cmplt_type *resp;
 	rndis_resp_t            *r;
 
-	// DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
+	// DBG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID));
 	if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
 
 	/*
@@ -911,15 +911,15 @@
 	BufOffset = le32_to_cpu (buf->InformationBufferOffset);
 
 #ifdef	VERBOSE
-	DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength);
-	DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
-	DEBUG("%s: InfoBuffer: ", __FUNCTION__);
+	DBG("%s: Length: %d\n", __FUNCTION__, BufLength);
+	DBG("%s: Offset: %d\n", __FUNCTION__, BufOffset);
+	DBG("%s: InfoBuffer: ", __FUNCTION__);
 
 	for (i = 0; i < BufLength; i++) {
-		DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+		DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
 	}
 
-	DEBUG ("\n");
+	DBG("\n");
 #endif
 
 	resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
@@ -1082,14 +1082,14 @@
 	/* For USB: responses may take up to 10 seconds */
 	switch (MsgType) {
 	case REMOTE_NDIS_INITIALIZE_MSG:
-		DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
+		DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
 			__FUNCTION__ );
 		params->state = RNDIS_INITIALIZED;
 		return  rndis_init_response (configNr,
 					(rndis_init_msg_type *) buf);
 
 	case REMOTE_NDIS_HALT_MSG:
-		DEBUG("%s: REMOTE_NDIS_HALT_MSG\n",
+		DBG("%s: REMOTE_NDIS_HALT_MSG\n",
 			__FUNCTION__ );
 		params->state = RNDIS_UNINITIALIZED;
 		if (params->dev) {
@@ -1107,7 +1107,7 @@
 					(rndis_set_msg_type *) buf);
 
 	case REMOTE_NDIS_RESET_MSG:
-		DEBUG("%s: REMOTE_NDIS_RESET_MSG\n",
+		DBG("%s: REMOTE_NDIS_RESET_MSG\n",
 			__FUNCTION__ );
 		return rndis_reset_response (configNr,
 					(rndis_reset_msg_type *) buf);
@@ -1115,7 +1115,7 @@
 	case REMOTE_NDIS_KEEPALIVE_MSG:
 		/* For USB: host does this every 5 seconds */
 		if (rndis_debug > 1)
-			DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
+			DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
 				__FUNCTION__ );
 		return rndis_keepalive_response (configNr,
 						 (rndis_keepalive_msg_type *)
@@ -1132,7 +1132,7 @@
 		{
 			unsigned i;
 			for (i = 0; i < MsgLength; i += 16) {
-				DEBUG ("%03d: "
+				DBG("%03d: "
 					" %02x %02x %02x %02x"
 					" %02x %02x %02x %02x"
 					" %02x %02x %02x %02x"
@@ -1163,18 +1163,18 @@
 		if (!rndis_per_dev_params [i].used) {
 			rndis_per_dev_params [i].used = 1;
 			rndis_per_dev_params [i].ack = rndis_control_ack;
-			DEBUG("%s: configNr = %d\n", __FUNCTION__, i);
+			DBG("%s: configNr = %d\n", __FUNCTION__, i);
 			return i;
 		}
 	}
-	DEBUG("failed\n");
+	DBG("failed\n");
 
 	return -1;
 }
 
 void rndis_deregister (int configNr)
 {
-	DEBUG("%s: \n", __FUNCTION__ );
+	DBG("%s: \n", __FUNCTION__ );
 
 	if (configNr >= RNDIS_MAX_CONFIGS) return;
 	rndis_per_dev_params [configNr].used = 0;
@@ -1186,7 +1186,7 @@
 			 struct net_device_stats *stats,
 			 u16 *cdc_filter)
 {
-	DEBUG("%s:\n", __FUNCTION__ );
+	DBG("%s:\n", __FUNCTION__ );
 	if (!dev || !stats) return -1;
 	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
 
@@ -1199,7 +1199,7 @@
 
 int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
 {
-	DEBUG("%s:\n", __FUNCTION__ );
+	DBG("%s:\n", __FUNCTION__ );
 	if (!vendorDescr) return -1;
 	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
 
@@ -1211,7 +1211,7 @@
 
 int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
 {
-	DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed);
+	DBG("%s: %u %u\n", __FUNCTION__, medium, speed);
 	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
 
 	rndis_per_dev_params [configNr].medium = medium;
@@ -1390,7 +1390,7 @@
 			break;
 		default:
 			if (fl_speed) p->speed = speed;
-			else DEBUG ("%c is not valid\n", c);
+			else DBG("%c is not valid\n", c);
 			break;
 		}
 
@@ -1419,12 +1419,12 @@
 		if (!(rndis_connect_state [i]
 				= create_proc_entry (name, 0660, NULL)))
 		{
-			DEBUG ("%s :remove entries", __FUNCTION__);
+			DBG("%s :remove entries", __FUNCTION__);
 			while (i) {
 				sprintf (name, NAME_TEMPLATE, --i);
 				remove_proc_entry (name, NULL);
 			}
-			DEBUG ("\n");
+			DBG("\n");
 			return -EIO;
 		}
 
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
new file mode 100644
index 0000000..0be80c6
--- /dev/null
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -0,0 +1,2045 @@
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.c
+ *
+ * Samsung S3C24xx series on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
+ *	Additional cleanups by Ben Dooks <ben-linux@fluff.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+#include <linux/clk.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <linux/usb.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/arch/irqs.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-udc.h>
+#include <asm/arch/udc.h>
+
+#include <asm/mach-types.h>
+
+#include "s3c2410_udc.h"
+
+#define DRIVER_DESC	"S3C2410 USB Device Controller Gadget"
+#define DRIVER_VERSION	"29 Apr 2007"
+#define DRIVER_AUTHOR	"Herbert Pötzl <herbert@13thfloor.at>, " \
+			"Arnaud Patard <arnaud.patard@rtp-net.org>"
+
+static const char		gadget_name[] = "s3c2410_udc";
+static const char		driver_desc[] = DRIVER_DESC;
+
+static struct s3c2410_udc	*the_controller;
+static struct clk		*udc_clock;
+static struct clk		*usb_bus_clock;
+static void __iomem		*base_addr;
+static u64			rsrc_start;
+static u64			rsrc_len;
+static struct dentry		*s3c2410_udc_debugfs_root;
+
+static inline u32 udc_read(u32 reg)
+{
+	return readb(base_addr + reg);
+}
+
+static inline void udc_write(u32 value, u32 reg)
+{
+	writeb(value, base_addr + reg);
+}
+
+static inline void udc_writeb(void __iomem *base, u32 value, u32 reg)
+{
+	writeb(value, base + reg);
+}
+
+static struct s3c2410_udc_mach_info *udc_info;
+
+/*************************** DEBUG FUNCTION ***************************/
+#define DEBUG_NORMAL	1
+#define DEBUG_VERBOSE	2
+
+#ifdef CONFIG_USB_S3C2410_DEBUG
+#define USB_S3C2410_DEBUG_LEVEL 0
+
+static uint32_t s3c2410_ticks = 0;
+
+static int dprintk(int level, const char *fmt, ...)
+{
+	static char printk_buf[1024];
+	static long prevticks;
+	static int invocation;
+	va_list args;
+	int len;
+
+	if (level > USB_S3C2410_DEBUG_LEVEL)
+		return 0;
+
+	if (s3c2410_ticks != prevticks) {
+		prevticks = s3c2410_ticks;
+		invocation = 0;
+	}
+
+	len = scnprintf(printk_buf,
+			sizeof(printk_buf), "%1lu.%02d USB: ",
+			prevticks, invocation++);
+
+	va_start(args, fmt);
+	len = vscnprintf(printk_buf+len,
+			sizeof(printk_buf)-len, fmt, args);
+	va_end(args);
+
+	return printk(KERN_DEBUG "%s", printk_buf);
+}
+#else
+static int dprintk(int level, const char *fmt, ...)
+{
+	return 0;
+}
+#endif
+static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
+{
+	u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
+	u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
+	u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
+	u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
+
+	addr_reg       = udc_read(S3C2410_UDC_FUNC_ADDR_REG);
+	pwr_reg        = udc_read(S3C2410_UDC_PWR_REG);
+	ep_int_reg     = udc_read(S3C2410_UDC_EP_INT_REG);
+	usb_int_reg    = udc_read(S3C2410_UDC_USB_INT_REG);
+	ep_int_en_reg  = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG);
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+	ep0_csr        = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	udc_write(1, S3C2410_UDC_INDEX_REG);
+	ep1_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep1_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	ep1_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep1_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	udc_write(2, S3C2410_UDC_INDEX_REG);
+	ep2_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep2_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+	ep2_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);
+	ep2_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);
+
+	seq_printf(m, "FUNC_ADDR_REG  : 0x%04X\n"
+		 "PWR_REG        : 0x%04X\n"
+		 "EP_INT_REG     : 0x%04X\n"
+		 "USB_INT_REG    : 0x%04X\n"
+		 "EP_INT_EN_REG  : 0x%04X\n"
+		 "USB_INT_EN_REG : 0x%04X\n"
+		 "EP0_CSR        : 0x%04X\n"
+		 "EP1_I_CSR1     : 0x%04X\n"
+		 "EP1_I_CSR2     : 0x%04X\n"
+		 "EP1_O_CSR1     : 0x%04X\n"
+		 "EP1_O_CSR2     : 0x%04X\n"
+		 "EP2_I_CSR1     : 0x%04X\n"
+		 "EP2_I_CSR2     : 0x%04X\n"
+		 "EP2_O_CSR1     : 0x%04X\n"
+		 "EP2_O_CSR2     : 0x%04X\n",
+			addr_reg,pwr_reg,ep_int_reg,usb_int_reg,
+			ep_int_en_reg, usb_int_en_reg, ep0_csr,
+			ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2,
+			ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2
+		);
+
+	return 0;
+}
+
+static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
+					 struct file *file)
+{
+	return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
+}
+
+static const struct file_operations s3c2410_udc_debugfs_fops = {
+	.open		= s3c2410_udc_debugfs_fops_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.owner		= THIS_MODULE,
+};
+
+/* io macros */
+
+static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY,
+			S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_clear_ep0_se(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);
+}
+
+inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
+{
+	udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+
+	udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY
+				| S3C2410_UDC_EP0_CSR_DE),
+			S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
+				| S3C2410_UDC_EP0_CSR_SSE),
+			S3C2410_UDC_EP0_CSR_REG);
+}
+
+static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
+{
+	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY
+			| S3C2410_UDC_EP0_CSR_DE),
+		S3C2410_UDC_EP0_CSR_REG);
+}
+
+/*------------------------- I/O ----------------------------------*/
+
+/*
+ *	s3c2410_udc_done
+ */
+static void s3c2410_udc_done(struct s3c2410_ep *ep,
+		struct s3c2410_request *req, int status)
+{
+	unsigned halted = ep->halted;
+
+	list_del_init(&req->queue);
+
+	if (likely (req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	ep->halted = 1;
+	req->req.complete(&ep->ep, &req->req);
+	ep->halted = halted;
+}
+
+static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
+		struct s3c2410_ep *ep, int status)
+{
+	/* Sanity check */
+	if (&ep->queue == NULL)
+		return;
+
+	while (!list_empty (&ep->queue)) {
+		struct s3c2410_request *req;
+		req = list_entry (ep->queue.next, struct s3c2410_request,
+				queue);
+		s3c2410_udc_done(ep, req, status);
+	}
+}
+
+static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev)
+{
+	unsigned i;
+
+	/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
+	 * fifos, and pending transactions mustn't be continued in any case.
+	 */
+
+	for (i = 1; i < S3C2410_ENDPOINTS; i++)
+		s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
+}
+
+static inline int s3c2410_udc_fifo_count_out(void)
+{
+	int tmp;
+
+	tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
+	tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG);
+	return tmp;
+}
+
+/*
+ *	s3c2410_udc_write_packet
+ */
+static inline int s3c2410_udc_write_packet(int fifo,
+		struct s3c2410_request *req,
+		unsigned max)
+{
+	unsigned len = min(req->req.length - req->req.actual, max);
+	u8 *buf = req->req.buf + req->req.actual;
+
+	prefetch(buf);
+
+	dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__,
+		req->req.actual, req->req.length, len, req->req.actual + len);
+
+	req->req.actual += len;
+
+	udelay(5);
+	writesb(base_addr + fifo, buf, len);
+	return len;
+}
+
+/*
+ *	s3c2410_udc_write_fifo
+ *
+ * return:  0 = still running, 1 = completed, negative = errno
+ */
+static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
+		struct s3c2410_request *req)
+{
+	unsigned	count;
+	int		is_last;
+	u32		idx;
+	int		fifo_reg;
+	u32		ep_csr;
+
+	idx = ep->bEndpointAddress & 0x7F;
+	switch (idx) {
+	default:
+		idx = 0;
+	case 0:
+		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+		break;
+	case 1:
+		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+		break;
+	case 2:
+		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+		break;
+	case 3:
+		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+		break;
+	case 4:
+		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+		break;
+	}
+
+	count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket);
+
+	/* last packet is often short (sometimes a zlp) */
+	if (count != ep->ep.maxpacket)
+		is_last = 1;
+	else if (req->req.length != req->req.actual || req->req.zero)
+		is_last = 0;
+	else
+		is_last = 2;
+
+	/* Only ep0 debug messages are interesting */
+	if (idx == 0)
+		dprintk(DEBUG_NORMAL,
+			"Written ep%d %d.%d of %d b [last %d,z %d]\n",
+			idx, count, req->req.actual, req->req.length,
+			is_last, req->req.zero);
+
+	if (is_last) {
+		/* The order is important. It prevents sending 2 packets
+		 * at the same time */
+
+		if (idx == 0) {
+			/* Reset signal => no need to say 'data sent' */
+			if (! (udc_read(S3C2410_UDC_USB_INT_REG)
+					& S3C2410_UDC_USBINT_RESET))
+				s3c2410_udc_set_ep0_de_in(base_addr);
+			ep->dev->ep0state=EP0_IDLE;
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
+					S3C2410_UDC_IN_CSR1_REG);
+		}
+
+		s3c2410_udc_done(ep, req, 0);
+		is_last = 1;
+	} else {
+		if (idx == 0) {
+			/* Reset signal => no need to say 'data sent' */
+			if (! (udc_read(S3C2410_UDC_USB_INT_REG)
+					& S3C2410_UDC_USBINT_RESET))
+				s3c2410_udc_set_ep0_ipr(base_addr);
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
+					S3C2410_UDC_IN_CSR1_REG);
+		}
+	}
+
+	return is_last;
+}
+
+static inline int s3c2410_udc_read_packet(int fifo, u8 *buf,
+		struct s3c2410_request *req, unsigned avail)
+{
+	unsigned len;
+
+	len = min(req->req.length - req->req.actual, avail);
+	req->req.actual += len;
+
+	readsb(fifo + base_addr, buf, len);
+	return len;
+}
+
+/*
+ * return:  0 = still running, 1 = queue empty, negative = errno
+ */
+static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
+				 struct s3c2410_request *req)
+{
+	u8		*buf;
+	u32		ep_csr;
+	unsigned	bufferspace;
+	int		is_last=1;
+	unsigned	avail;
+	int		fifo_count = 0;
+	u32		idx;
+	int		fifo_reg;
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	switch (idx) {
+	default:
+		idx = 0;
+	case 0:
+		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
+		break;
+	case 1:
+		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
+		break;
+	case 2:
+		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
+		break;
+	case 3:
+		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
+		break;
+	case 4:
+		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
+		break;
+	}
+
+	if (!req->req.length)
+		return 1;
+
+	buf = req->req.buf + req->req.actual;
+	bufferspace = req->req.length - req->req.actual;
+	if (!bufferspace) {
+		dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__);
+		return -1;
+	}
+
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+
+	fifo_count = s3c2410_udc_fifo_count_out();
+	dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count);
+
+	if (fifo_count > ep->ep.maxpacket)
+		avail = ep->ep.maxpacket;
+	else
+		avail = fifo_count;
+
+	fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail);
+
+	/* checking this with ep0 is not accurate as we already
+	 * read a control request
+	 **/
+	if (idx != 0 && fifo_count < ep->ep.maxpacket) {
+		is_last = 1;
+		/* overflowed this request?  flush extra data */
+		if (fifo_count != avail)
+			req->req.status = -EOVERFLOW;
+	} else {
+		is_last = (req->req.length <= req->req.actual) ? 1 : 0;
+	}
+
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+	fifo_count = s3c2410_udc_fifo_count_out();
+
+	/* Only ep0 debug messages are interesting */
+	if (idx == 0)
+		dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n",
+			__func__, fifo_count,is_last);
+
+	if (is_last) {
+		if (idx == 0) {
+			s3c2410_udc_set_ep0_de_out(base_addr);
+			ep->dev->ep0state = EP0_IDLE;
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
+					S3C2410_UDC_OUT_CSR1_REG);
+		}
+
+		s3c2410_udc_done(ep, req, 0);
+	} else {
+		if (idx == 0) {
+			s3c2410_udc_clear_ep0_opr(base_addr);
+		} else {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
+					S3C2410_UDC_OUT_CSR1_REG);
+		}
+	}
+
+	return is_last;
+}
+
+static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq)
+{
+	unsigned char *outbuf = (unsigned char*)crq;
+	int bytes_read = 0;
+
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+
+	bytes_read = s3c2410_udc_fifo_count_out();
+
+	dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read);
+
+	if (bytes_read > sizeof(struct usb_ctrlrequest))
+		bytes_read = sizeof(struct usb_ctrlrequest);
+
+	readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read);
+
+	dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__,
+		bytes_read, crq->bRequest, crq->bRequestType,
+		crq->wValue, crq->wIndex, crq->wLength);
+
+	return bytes_read;
+}
+
+static int s3c2410_udc_get_status(struct s3c2410_udc *dev,
+		struct usb_ctrlrequest *crq)
+{
+	u16 status = 0;
+	u8 ep_num = crq->wIndex & 0x7F;
+	u8 is_in = crq->wIndex & USB_DIR_IN;
+
+	switch (crq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		break;
+
+	case USB_RECIP_DEVICE:
+		status = dev->devstatus;
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		if (ep_num > 4 || crq->wLength > 2)
+			return 1;
+
+		if (ep_num == 0) {
+			udc_write(0, S3C2410_UDC_INDEX_REG);
+			status = udc_read(S3C2410_UDC_IN_CSR1_REG);
+			status = status & S3C2410_UDC_EP0_CSR_SENDSTL;
+		} else {
+			udc_write(ep_num, S3C2410_UDC_INDEX_REG);
+			if (is_in) {
+				status = udc_read(S3C2410_UDC_IN_CSR1_REG);
+				status = status & S3C2410_UDC_ICSR1_SENDSTL;
+			} else {
+				status = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+				status = status & S3C2410_UDC_OCSR1_SENDSTL;
+			}
+		}
+
+		status = status ? 1 : 0;
+		break;
+
+	default:
+		return 1;
+	}
+
+	/* Seems to be needed to get it working. ouch :( */
+	udelay(5);
+	udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG);
+	udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG);
+	s3c2410_udc_set_ep0_de_in(base_addr);
+
+	return 0;
+}
+/*------------------------- usb state machine -------------------------------*/
+static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value);
+
+static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
+					struct s3c2410_ep *ep,
+					struct usb_ctrlrequest *crq,
+					u32 ep0csr)
+{
+	int len, ret, tmp;
+
+	/* start control request? */
+	if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY))
+		return;
+
+	s3c2410_udc_nuke(dev, ep, -EPROTO);
+
+	len = s3c2410_udc_read_fifo_crq(crq);
+	if (len != sizeof(*crq)) {
+		dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
+			" wanted %d bytes got %d. Stalling out...\n",
+			sizeof(*crq), len);
+		s3c2410_udc_set_ep0_ss(base_addr);
+		return;
+	}
+
+	dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n",
+		crq->bRequest, crq->bRequestType, crq->wLength);
+
+	/* cope with automagic for some standard requests. */
+	dev->req_std = (crq->bRequestType & USB_TYPE_MASK)
+		== USB_TYPE_STANDARD;
+	dev->req_config = 0;
+	dev->req_pending = 1;
+
+	switch (crq->bRequest) {
+	case USB_REQ_SET_CONFIGURATION:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
+
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			dev->req_config = 1;
+			s3c2410_udc_set_ep0_de_out(base_addr);
+		}
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
+
+		if (crq->bRequestType == USB_RECIP_INTERFACE) {
+			dev->req_config = 1;
+			s3c2410_udc_set_ep0_de_out(base_addr);
+		}
+		break;
+
+	case USB_REQ_SET_ADDRESS:
+		dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
+
+		if (crq->bRequestType == USB_RECIP_DEVICE) {
+			tmp = crq->wValue & 0x7F;
+			dev->address = tmp;
+			udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE),
+					S3C2410_UDC_FUNC_ADDR_REG);
+			s3c2410_udc_set_ep0_de_out(base_addr);
+			return;
+		}
+		break;
+
+	case USB_REQ_GET_STATUS:
+		dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (dev->req_std) {
+			if (!s3c2410_udc_get_status(dev, crq)) {
+				return;
+			}
+		}
+		break;
+
+	case USB_REQ_CLEAR_FEATURE:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (crq->bRequestType != USB_RECIP_ENDPOINT)
+			break;
+
+		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
+			break;
+
+		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+
+		if (crq->bRequestType != USB_RECIP_ENDPOINT)
+			break;
+
+		if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
+			break;
+
+		s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		return;
+
+	default:
+		s3c2410_udc_clear_ep0_opr(base_addr);
+		break;
+	}
+
+	if (crq->bRequestType & USB_DIR_IN)
+		dev->ep0state = EP0_IN_DATA_PHASE;
+	else
+		dev->ep0state = EP0_OUT_DATA_PHASE;
+
+	ret = dev->driver->setup(&dev->gadget, crq);
+	if (ret < 0) {
+		if (dev->req_config) {
+			dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
+				crq->bRequest, ret);
+			return;
+		}
+
+		if (ret == -EOPNOTSUPP)
+			dprintk(DEBUG_NORMAL, "Operation not supported\n");
+		else
+			dprintk(DEBUG_NORMAL,
+				"dev->driver->setup failed. (%d)\n", ret);
+
+		udelay(5);
+		s3c2410_udc_set_ep0_ss(base_addr);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+		dev->ep0state = EP0_IDLE;
+		/* deferred i/o == no response yet */
+	} else if (dev->req_pending) {
+		dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
+		dev->req_pending=0;
+	}
+
+	dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);
+}
+
+static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
+{
+	u32			ep0csr;
+	struct s3c2410_ep	*ep = &dev->ep[0];
+	struct s3c2410_request	*req;
+	struct usb_ctrlrequest	crq;
+
+	if (list_empty(&ep->queue))
+		req = NULL;
+	else
+		req = list_entry(ep->queue.next, struct s3c2410_request, queue);
+
+	/* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
+	 * S3C2410_UDC_EP0_CSR_REG when index is zero */
+
+	udc_write(0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+
+	dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
+		ep0csr, ep0states[dev->ep0state]);
+
+	/* clear stall status */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
+		s3c2410_udc_nuke(dev, ep, -EPIPE);
+		dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
+		s3c2410_udc_clear_ep0_sst(base_addr);
+		dev->ep0state = EP0_IDLE;
+		return;
+	}
+
+	/* clear setup end */
+	if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
+		dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
+		s3c2410_udc_nuke(dev, ep, 0);
+		s3c2410_udc_clear_ep0_se(base_addr);
+		dev->ep0state = EP0_IDLE;
+	}
+
+	switch (dev->ep0state) {
+	case EP0_IDLE:
+		s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
+		break;
+
+	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */
+		dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
+		if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
+			s3c2410_udc_write_fifo(ep, req);
+		}
+		break;
+
+	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */
+		dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
+		if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
+			s3c2410_udc_read_fifo(ep,req);
+		}
+		break;
+
+	case EP0_END_XFER:
+		dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
+		dev->ep0state = EP0_IDLE;
+		break;
+
+	case EP0_STALL:
+		dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
+		dev->ep0state = EP0_IDLE;
+		break;
+	}
+}
+
+/*
+ *	handle_ep - Manage I/O endpoints
+ */
+
+static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
+{
+	struct s3c2410_request	*req;
+	int			is_in = ep->bEndpointAddress & USB_DIR_IN;
+	u32			ep_csr1;
+	u32			idx;
+
+	if (likely (!list_empty(&ep->queue)))
+		req = list_entry(ep->queue.next,
+				struct s3c2410_request, queue);
+	else
+		req = NULL;
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	if (is_in) {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
+		dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",
+			idx, ep_csr1, req ? 1 : 0);
+
+		if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
+			dprintk(DEBUG_VERBOSE, "st\n");
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
+					S3C2410_UDC_IN_CSR1_REG);
+			return;
+		}
+
+		if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
+			s3c2410_udc_write_fifo(ep,req);
+		}
+	} else {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
+		dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
+
+		if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
+			udc_write(idx, S3C2410_UDC_INDEX_REG);
+			udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
+					S3C2410_UDC_OUT_CSR1_REG);
+			return;
+		}
+
+		if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
+			s3c2410_udc_read_fifo(ep,req);
+		}
+	}
+}
+
+#include <asm/arch/regs-irq.h>
+
+/*
+ *	s3c2410_udc_irq - interrupt handler
+ */
+static irqreturn_t s3c2410_udc_irq(int irq, void *_dev)
+{
+	struct s3c2410_udc *dev = _dev;
+	int usb_status;
+	int usbd_status;
+	int pwr_reg;
+	int ep0csr;
+	int i;
+	u32 idx;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* Driver connected ? */
+	if (!dev->driver) {
+		/* Clear interrupts */
+		udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
+				S3C2410_UDC_USB_INT_REG);
+		udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
+				S3C2410_UDC_EP_INT_REG);
+	}
+
+	/* Save index */
+	idx = udc_read(S3C2410_UDC_INDEX_REG);
+
+	/* Read status registers */
+	usb_status = udc_read(S3C2410_UDC_USB_INT_REG);
+	usbd_status = udc_read(S3C2410_UDC_EP_INT_REG);
+	pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
+
+	udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
+	ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+
+	dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
+		usb_status, usbd_status, pwr_reg, ep0csr);
+
+	/*
+	 * Now, handle interrupts. There's two types :
+	 * - Reset, Resume, Suspend coming -> usb_int_reg
+	 * - EP -> ep_int_reg
+	 */
+
+	/* RESET */
+	if (usb_status & S3C2410_UDC_USBINT_RESET) {
+		/* two kind of reset :
+		 * - reset start -> pwr reg = 8
+		 * - reset end   -> pwr reg = 0
+		 **/
+		dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",
+			ep0csr, pwr_reg);
+
+		dev->gadget.speed = USB_SPEED_UNKNOWN;
+		udc_write(0x00, S3C2410_UDC_INDEX_REG);
+		udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
+				S3C2410_UDC_MAXP_REG);
+		dev->address = 0;
+
+		dev->ep0state = EP0_IDLE;
+		dev->gadget.speed = USB_SPEED_FULL;
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_RESET,
+				S3C2410_UDC_USB_INT_REG);
+
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return IRQ_HANDLED;
+	}
+
+	/* RESUME */
+	if (usb_status & S3C2410_UDC_USBINT_RESUME) {
+		dprintk(DEBUG_NORMAL, "USB resume\n");
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_RESUME,
+				S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->resume)
+			dev->driver->resume(&dev->gadget);
+	}
+
+	/* SUSPEND */
+	if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
+		dprintk(DEBUG_NORMAL, "USB suspend\n");
+
+		/* clear interrupt */
+		udc_write(S3C2410_UDC_USBINT_SUSPEND,
+				S3C2410_UDC_USB_INT_REG);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+				&& dev->driver
+				&& dev->driver->suspend)
+			dev->driver->suspend(&dev->gadget);
+
+		dev->ep0state = EP0_IDLE;
+	}
+
+	/* EP */
+	/* control traffic */
+	/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
+	 * generate an interrupt
+	 */
+	if (usbd_status & S3C2410_UDC_INT_EP0) {
+		dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
+		/* Clear the interrupt bit by setting it to 1 */
+		udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
+		s3c2410_udc_handle_ep0(dev);
+	}
+
+	/* endpoint data transfers */
+	for (i = 1; i < S3C2410_ENDPOINTS; i++) {
+		u32 tmp = 1 << i;
+		if (usbd_status & tmp) {
+			dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
+
+			/* Clear the interrupt bit by setting it to 1 */
+			udc_write(tmp, S3C2410_UDC_EP_INT_REG);
+			s3c2410_udc_handle_ep(&dev->ep[i]);
+		}
+	}
+
+	dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq);
+
+	/* Restore old index */
+	udc_write(idx, S3C2410_UDC_INDEX_REG);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return IRQ_HANDLED;
+}
+/*------------------------- s3c2410_ep_ops ----------------------------------*/
+
+static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c2410_ep, ep);
+}
+
+static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c2410_udc, gadget);
+}
+
+static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c2410_request, req);
+}
+
+/*
+ *	s3c2410_udc_ep_enable
+ */
+static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
+				 const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c2410_udc	*dev;
+	struct s3c2410_ep	*ep;
+	u32			max, tmp;
+	unsigned long		flags;
+	u32			csr1,csr2;
+	u32			int_en_reg;
+
+	ep = to_s3c2410_ep(_ep);
+
+	if (!_ep || !desc || ep->desc
+			|| _ep->name == ep0name
+			|| desc->bDescriptorType != USB_DT_ENDPOINT)
+		return -EINVAL;
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
+
+	local_irq_save (flags);
+	_ep->maxpacket = max & 0x7ff;
+	ep->desc = desc;
+	ep->halted = 0;
+	ep->bEndpointAddress = desc->bEndpointAddress;
+
+	/* set max packet */
+	udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+	udc_write(max >> 3, S3C2410_UDC_MAXP_REG);
+
+	/* set type, direction, address; reset fifo counters */
+	if (desc->bEndpointAddress & USB_DIR_IN) {
+		csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
+	} else {
+		/* don't flush in fifo or it will cause endpoint interrupt */
+		csr1 = S3C2410_UDC_ICSR1_CLRDT;
+		csr2 = S3C2410_UDC_ICSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
+
+		csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
+		csr2 = S3C2410_UDC_OCSR2_DMAIEN;
+
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
+		udc_write(ep->num, S3C2410_UDC_INDEX_REG);
+		udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
+	}
+
+	/* enable irqs */
+	int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG);
+
+	/* print some debug message */
+	tmp = desc->bEndpointAddress;
+	dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
+		 _ep->name,ep->num, tmp,
+		 desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
+
+	local_irq_restore (flags);
+	s3c2410_udc_set_halt(_ep, 0);
+
+	return 0;
+}
+
+/*
+ * s3c2410_udc_ep_disable
+ */
+static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
+	unsigned long flags;
+	u32 int_en_reg;
+
+	if (!_ep || !ep->desc) {
+		dprintk(DEBUG_NORMAL, "%s not enabled\n",
+			_ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
+
+	ep->desc = NULL;
+	ep->halted = 1;
+
+	s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);
+
+	/* disable irqs */
+	int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
+	udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);
+
+	local_irq_restore(flags);
+
+	dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
+
+	return 0;
+}
+
+/*
+ * s3c2410_udc_alloc_request
+ */
+static struct usb_request *
+s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
+{
+	struct s3c2410_request *req;
+
+	dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);
+
+	if (!_ep)
+		return NULL;
+
+	req = kzalloc (sizeof(struct s3c2410_request), mem_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD (&req->queue);
+	return &req->req;
+}
+
+/*
+ * s3c2410_udc_free_request
+ */
+static void
+s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	struct s3c2410_request	*req = to_s3c2410_req(_req);
+
+	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
+
+	if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
+		return;
+
+	WARN_ON (!list_empty (&req->queue));
+	kfree(req);
+}
+
+/*
+ *	s3c2410_udc_queue
+ */
+static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
+		gfp_t gfp_flags)
+{
+	struct s3c2410_request	*req = to_s3c2410_req(_req);
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	struct s3c2410_udc	*dev;
+	u32			ep_csr = 0;
+	int			fifo_count = 0;
+	unsigned long		flags;
+
+	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+		dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
+		return -EINVAL;
+	}
+
+	dev = ep->dev;
+	if (unlikely (!dev->driver
+			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+		return -ESHUTDOWN;
+	}
+
+	local_irq_save (flags);
+
+	if (unlikely(!_req || !_req->complete
+			|| !_req->buf || !list_empty(&req->queue))) {
+		if (!_req)
+			dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
+		else {
+			dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
+				__func__, !_req->complete,!_req->buf,
+				!list_empty(&req->queue));
+		}
+
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
+		 __func__, ep->bEndpointAddress, _req->length);
+
+	if (ep->bEndpointAddress) {
+		udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
+
+		ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
+				? S3C2410_UDC_IN_CSR1_REG
+				: S3C2410_UDC_OUT_CSR1_REG);
+		fifo_count = s3c2410_udc_fifo_count_out();
+	} else {
+		udc_write(0, S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
+		fifo_count = s3c2410_udc_fifo_count_out();
+	}
+
+	/* kickstart this i/o queue? */
+	if (list_empty(&ep->queue) && !ep->halted) {
+		if (ep->bEndpointAddress == 0 /* ep0 */) {
+			switch (dev->ep0state) {
+			case EP0_IN_DATA_PHASE:
+				if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
+						&& s3c2410_udc_write_fifo(ep,
+							req)) {
+					dev->ep0state = EP0_IDLE;
+					req = NULL;
+				}
+				break;
+
+			case EP0_OUT_DATA_PHASE:
+				if ((!_req->length)
+					|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
+						&& s3c2410_udc_read_fifo(ep,
+							req))) {
+					dev->ep0state = EP0_IDLE;
+					req = NULL;
+				}
+				break;
+
+			default:
+				local_irq_restore(flags);
+				return -EL2HLT;
+			}
+		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+				&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
+				&& s3c2410_udc_write_fifo(ep, req)) {
+			req = NULL;
+		} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
+				&& fifo_count
+				&& s3c2410_udc_read_fifo(ep, req)) {
+			req = NULL;
+		}
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely (req != 0))
+		list_add_tail(&req->queue, &ep->queue);
+
+	local_irq_restore(flags);
+
+	dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
+	return 0;
+}
+
+/*
+ *	s3c2410_udc_dequeue
+ */
+static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	struct s3c2410_udc	*udc;
+	int			retval = -EINVAL;
+	unsigned long		flags;
+	struct s3c2410_request	*req = NULL;
+
+	dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
+
+	if (!the_controller->driver)
+		return -ESHUTDOWN;
+
+	if (!_ep || !_req)
+		return retval;
+
+	udc = to_s3c2410_udc(ep->gadget);
+
+	local_irq_save (flags);
+
+	list_for_each_entry (req, &ep->queue, queue) {
+		if (&req->req == _req) {
+			list_del_init (&req->queue);
+			_req->status = -ECONNRESET;
+			retval = 0;
+			break;
+		}
+	}
+
+	if (retval == 0) {
+		dprintk(DEBUG_VERBOSE,
+			"dequeued req %p from %s, len %d buf %p\n",
+			req, _ep->name, _req->length, _req->buf);
+
+		s3c2410_udc_done(ep, req, -ECONNRESET);
+	}
+
+	local_irq_restore (flags);
+	return retval;
+}
+
+/*
+ * s3c2410_udc_set_halt
+ */
+static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c2410_ep	*ep = to_s3c2410_ep(_ep);
+	u32			ep_csr = 0;
+	unsigned long		flags;
+	u32			idx;
+
+	if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+		dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__);
+		return -EINVAL;
+	}
+
+	local_irq_save (flags);
+
+	idx = ep->bEndpointAddress & 0x7F;
+
+	if (idx == 0) {
+		s3c2410_udc_set_ep0_ss(base_addr);
+		s3c2410_udc_set_ep0_de_out(base_addr);
+	} else {
+		udc_write(idx, S3C2410_UDC_INDEX_REG);
+		ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN)
+				? S3C2410_UDC_IN_CSR1_REG
+				: S3C2410_UDC_OUT_CSR1_REG);
+
+		if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+			if (value)
+				udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL,
+					S3C2410_UDC_IN_CSR1_REG);
+			else {
+				ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL;
+				udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
+				ep_csr |= S3C2410_UDC_ICSR1_CLRDT;
+				udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
+			}
+		} else {
+			if (value)
+				udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL,
+					S3C2410_UDC_OUT_CSR1_REG);
+			else {
+				ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL;
+				udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
+				ep_csr |= S3C2410_UDC_OCSR1_CLRDT;
+				udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
+			}
+		}
+	}
+
+	ep->halted = value ? 1 : 0;
+	local_irq_restore (flags);
+
+	return 0;
+}
+
+static const struct usb_ep_ops s3c2410_ep_ops = {
+	.enable		= s3c2410_udc_ep_enable,
+	.disable	= s3c2410_udc_ep_disable,
+
+	.alloc_request	= s3c2410_udc_alloc_request,
+	.free_request	= s3c2410_udc_free_request,
+
+	.queue		= s3c2410_udc_queue,
+	.dequeue	= s3c2410_udc_dequeue,
+
+	.set_halt	= s3c2410_udc_set_halt,
+};
+
+/*------------------------- usb_gadget_ops ----------------------------------*/
+
+/*
+ *	s3c2410_udc_get_frame
+ */
+static int s3c2410_udc_get_frame(struct usb_gadget *_gadget)
+{
+	int tmp;
+
+	dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
+
+	tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8;
+	tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG);
+	return tmp;
+}
+
+/*
+ *	s3c2410_udc_wakeup
+ */
+static int s3c2410_udc_wakeup(struct usb_gadget *_gadget)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+	return 0;
+}
+
+/*
+ *	s3c2410_udc_set_selfpowered
+ */
+static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
+{
+	struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (value)
+		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	return 0;
+}
+
+static void s3c2410_udc_disable(struct s3c2410_udc *dev);
+static void s3c2410_udc_enable(struct s3c2410_udc *dev);
+
+static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (udc_info && udc_info->udc_command) {
+		if (is_on)
+			s3c2410_udc_enable(udc);
+		else {
+			if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+				if (udc->driver && udc->driver->disconnect)
+					udc->driver->disconnect(&udc->gadget);
+
+			}
+			s3c2410_udc_disable(udc);
+		}
+	}
+	else
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	udc->vbus = (is_active != 0);
+	s3c2410_udc_set_pullup(udc, is_active);
+	return 0;
+}
+
+static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	s3c2410_udc_set_pullup(udc, is_on ? 0 : 1);
+	return 0;
+}
+
+static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
+{
+	struct s3c2410_udc	*dev = _dev;
+	unsigned int		value;
+
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+	value = s3c2410_gpio_getpin(udc_info->vbus_pin);
+
+	if (udc_info->vbus_pin_inverted)
+		value = !value;
+
+	if (value != dev->vbus)
+		s3c2410_udc_vbus_session(&dev->gadget, value);
+
+	return IRQ_HANDLED;
+}
+
+static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	if (udc_info && udc_info->vbus_draw) {
+		udc_info->vbus_draw(ma);
+		return 0;
+	}
+
+	return -ENOTSUPP;
+}
+
+static const struct usb_gadget_ops s3c2410_ops = {
+	.get_frame		= s3c2410_udc_get_frame,
+	.wakeup			= s3c2410_udc_wakeup,
+	.set_selfpowered	= s3c2410_udc_set_selfpowered,
+	.pullup			= s3c2410_udc_pullup,
+	.vbus_session		= s3c2410_udc_vbus_session,
+	.vbus_draw		= s3c2410_vbus_draw,
+};
+
+/*------------------------- gadget driver handling---------------------------*/
+/*
+ * s3c2410_udc_disable
+ */
+static void s3c2410_udc_disable(struct s3c2410_udc *dev)
+{
+	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+
+	/* Disable all interrupts */
+	udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG);
+	udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG);
+
+	/* Clear the interrupt registers */
+	udc_write(S3C2410_UDC_USBINT_RESET
+				| S3C2410_UDC_USBINT_RESUME
+				| S3C2410_UDC_USBINT_SUSPEND,
+			S3C2410_UDC_USB_INT_REG);
+
+	udc_write(0x1F, S3C2410_UDC_EP_INT_REG);
+
+	/* Good bye, cruel world */
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+
+	/* Set speed to unknown */
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+/*
+ * s3c2410_udc_reinit
+ */
+static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
+{
+	u32 i;
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD (&dev->gadget.ep_list);
+	INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+	dev->ep0state = EP0_IDLE;
+
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+		struct s3c2410_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->dev = dev;
+		ep->desc = NULL;
+		ep->halted = 0;
+		INIT_LIST_HEAD (&ep->queue);
+	}
+}
+
+/*
+ * s3c2410_udc_enable
+ */
+static void s3c2410_udc_enable(struct s3c2410_udc *dev)
+{
+	int i;
+
+	dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n");
+
+	/* dev->gadget.speed = USB_SPEED_UNKNOWN; */
+	dev->gadget.speed = USB_SPEED_FULL;
+
+	/* Set MAXP for all endpoints */
+	for (i = 0; i < S3C2410_ENDPOINTS; i++) {
+		udc_write(i, S3C2410_UDC_INDEX_REG);
+		udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3,
+				S3C2410_UDC_MAXP_REG);
+	}
+
+	/* Set default power state */
+	udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
+
+	/* Enable reset and suspend interrupt interrupts */
+	udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND,
+			S3C2410_UDC_USB_INT_EN_REG);
+
+	/* Enable ep0 interrupt */
+	udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
+
+	/* time to say "hello, world" */
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+}
+
+/*
+ *	usb_gadget_register_driver
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+	int		retval;
+
+	dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
+		driver->driver.name);
+
+	/* Sanity checks */
+	if (!udc)
+		return -ENODEV;
+
+	if (udc->driver)
+		return -EBUSY;
+
+	if (!driver->bind || !driver->setup
+			|| driver->speed != USB_SPEED_FULL) {
+		printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
+			driver->bind, driver->setup, driver->speed);
+		return -EINVAL;
+	}
+#if defined(MODULE)
+	if (!driver->unbind) {
+		printk(KERN_ERR "Invalid driver: no unbind method\n");
+		return -EINVAL;
+	}
+#endif
+
+	/* Hook the driver */
+	udc->driver = driver;
+	udc->gadget.dev.driver = &driver->driver;
+
+	/* Bind the driver */
+	if ((retval = device_add(&udc->gadget.dev)) != 0) {
+		printk(KERN_ERR "Error in device_add() : %d\n",retval);
+		goto register_error;
+	}
+
+	dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n",
+		driver->driver.name);
+
+	if ((retval = driver->bind (&udc->gadget)) != 0) {
+		device_del(&udc->gadget.dev);
+		goto register_error;
+	}
+
+	/* Enable udc */
+	s3c2410_udc_enable(udc);
+
+	return 0;
+
+register_error:
+	udc->driver = NULL;
+	udc->gadget.dev.driver = NULL;
+	return retval;
+}
+
+/*
+ *	usb_gadget_unregister_driver
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c2410_udc *udc = the_controller;
+
+	if (!udc)
+		return -ENODEV;
+
+	if (!driver || driver != udc->driver || !driver->unbind)
+		return -EINVAL;
+
+	dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
+		driver->driver.name);
+
+	if (driver->disconnect)
+		driver->disconnect(&udc->gadget);
+
+	device_del(&udc->gadget.dev);
+	udc->driver = NULL;
+
+	/* Disable udc */
+	s3c2410_udc_disable(udc);
+
+	return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+static struct s3c2410_udc memory = {
+	.gadget = {
+		.ops		= &s3c2410_ops,
+		.ep0		= &memory.ep[0].ep,
+		.name		= gadget_name,
+		.dev = {
+			.bus_id		= "gadget",
+		},
+	},
+
+	/* control endpoint */
+	.ep[0] = {
+		.num		= 0,
+		.ep = {
+			.name		= ep0name,
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP0_FIFO_SIZE,
+		},
+		.dev		= &memory,
+	},
+
+	/* first group of endpoints */
+	.ep[1] = {
+		.num		= 1,
+		.ep = {
+			.name		= "ep1-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 1,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[2] = {
+		.num		= 2,
+		.ep = {
+			.name		= "ep2-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 2,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[3] = {
+		.num		= 3,
+		.ep = {
+			.name		= "ep3-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 3,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	},
+	.ep[4] = {
+		.num		= 4,
+		.ep = {
+			.name		= "ep4-bulk",
+			.ops		= &s3c2410_ep_ops,
+			.maxpacket	= EP_FIFO_SIZE,
+		},
+		.dev		= &memory,
+		.fifo_size	= EP_FIFO_SIZE,
+		.bEndpointAddress = 4,
+		.bmAttributes	= USB_ENDPOINT_XFER_BULK,
+	}
+
+};
+
+/*
+ *	probe - binds to the platform device
+ */
+static int s3c2410_udc_probe(struct platform_device *pdev)
+{
+	struct s3c2410_udc *udc = &memory;
+	struct device *dev = &pdev->dev;
+	int retval;
+	unsigned int irq;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
+	if (IS_ERR(usb_bus_clock)) {
+		dev_err(dev, "failed to get usb bus clock source\n");
+		return PTR_ERR(usb_bus_clock);
+	}
+
+	clk_enable(usb_bus_clock);
+
+	udc_clock = clk_get(NULL, "usb-device");
+	if (IS_ERR(udc_clock)) {
+		dev_err(dev, "failed to get udc clock source\n");
+		return PTR_ERR(udc_clock);
+	}
+
+	clk_enable(udc_clock);
+
+	mdelay(10);
+
+	dev_dbg(dev, "got and enabled clocks\n");
+
+	if (strncmp(pdev->name, "s3c2440", 7) == 0) {
+		dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
+		memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
+		memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
+	}
+
+	spin_lock_init (&udc->lock);
+	udc_info = pdev->dev.platform_data;
+
+	rsrc_start = S3C2410_PA_USBDEV;
+	rsrc_len   = S3C24XX_SZ_USBDEV;
+
+	if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
+		return -EBUSY;
+
+	base_addr = ioremap(rsrc_start, rsrc_len);
+	if (!base_addr) {
+		retval = -ENOMEM;
+		goto err_mem;
+	}
+
+	device_initialize(&udc->gadget.dev);
+	udc->gadget.dev.parent = &pdev->dev;
+	udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+	the_controller = udc;
+	platform_set_drvdata(pdev, udc);
+
+	s3c2410_udc_disable(udc);
+	s3c2410_udc_reinit(udc);
+
+	/* irq setup after old hardware state is cleaned up */
+	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
+			IRQF_DISABLED, gadget_name, udc);
+
+	if (retval != 0) {
+		dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
+		retval = -EBUSY;
+		goto err_map;
+	}
+
+	dev_dbg(dev, "got irq %i\n", IRQ_USBD);
+
+	if (udc_info && udc_info->vbus_pin > 0) {
+		irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
+		retval = request_irq(irq, s3c2410_udc_vbus_irq,
+				IRQF_DISABLED | IRQF_TRIGGER_RISING
+				| IRQF_TRIGGER_FALLING,
+				gadget_name, udc);
+
+		if (retval != 0) {
+			dev_err(dev, "can't get vbus irq %i, err %d\n",
+				irq, retval);
+			retval = -EBUSY;
+			goto err_int;
+		}
+
+		dev_dbg(dev, "got irq %i\n", irq);
+	} else {
+		udc->vbus = 1;
+	}
+
+	if (s3c2410_udc_debugfs_root) {
+		udc->regs_info = debugfs_create_file("registers", S_IRUGO,
+				s3c2410_udc_debugfs_root,
+				udc, &s3c2410_udc_debugfs_fops);
+		if (IS_ERR(udc->regs_info)) {
+			dev_warn(dev, "debugfs file creation failed %ld\n",
+				 PTR_ERR(udc->regs_info));
+			udc->regs_info = NULL;
+		}
+	}
+
+	dev_dbg(dev, "probe ok\n");
+
+	return 0;
+
+err_int:
+	free_irq(IRQ_USBD, udc);
+err_map:
+	iounmap(base_addr);
+err_mem:
+	release_mem_region(rsrc_start, rsrc_len);
+
+	return retval;
+}
+
+/*
+ *	s3c2410_udc_remove
+ */
+static int s3c2410_udc_remove(struct platform_device *pdev)
+{
+	struct s3c2410_udc *udc = platform_get_drvdata(pdev);
+	unsigned int irq;
+
+	dev_dbg(&pdev->dev, "%s()\n", __func__);
+	if (udc->driver)
+		return -EBUSY;
+
+	debugfs_remove(udc->regs_info);
+
+	if (udc_info && udc_info->vbus_pin > 0) {
+		irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
+		free_irq(irq, udc);
+	}
+
+	free_irq(IRQ_USBD, udc);
+
+	iounmap(base_addr);
+	release_mem_region(rsrc_start, rsrc_len);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (!IS_ERR(udc_clock) && udc_clock != NULL) {
+		clk_disable(udc_clock);
+		clk_put(udc_clock);
+		udc_clock = NULL;
+	}
+
+	if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
+		clk_disable(usb_bus_clock);
+		clk_put(usb_bus_clock);
+		usb_bus_clock = NULL;
+	}
+
+	dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
+{
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_DISABLE);
+
+	return 0;
+}
+
+static int s3c2410_udc_resume(struct platform_device *pdev)
+{
+	if (udc_info && udc_info->udc_command)
+		udc_info->udc_command(S3C2410_UDC_P_ENABLE);
+
+	return 0;
+}
+#else
+#define s3c2410_udc_suspend	NULL
+#define s3c2410_udc_resume	NULL
+#endif
+
+static struct platform_driver udc_driver_2410 = {
+	.driver		= {
+		.name	= "s3c2410-usbgadget",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= s3c2410_udc_probe,
+	.remove		= s3c2410_udc_remove,
+	.suspend	= s3c2410_udc_suspend,
+	.resume		= s3c2410_udc_resume,
+};
+
+static struct platform_driver udc_driver_2440 = {
+	.driver		= {
+		.name	= "s3c2440-usbgadget",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= s3c2410_udc_probe,
+	.remove		= s3c2410_udc_remove,
+	.suspend	= s3c2410_udc_suspend,
+	.resume		= s3c2410_udc_resume,
+};
+
+static int __init udc_init(void)
+{
+	int retval;
+
+	dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
+
+	s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
+	if (IS_ERR(s3c2410_udc_debugfs_root)) {
+		printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",
+			gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
+		s3c2410_udc_debugfs_root = NULL;
+	}
+
+	retval = platform_driver_register(&udc_driver_2410);
+	if (retval)
+		goto err;
+
+	retval = platform_driver_register(&udc_driver_2440);
+	if (retval)
+		goto err;
+
+	return 0;
+
+err:
+	debugfs_remove(s3c2410_udc_debugfs_root);
+	return retval;
+}
+
+static void __exit udc_exit(void)
+{
+	platform_driver_unregister(&udc_driver_2410);
+	platform_driver_unregister(&udc_driver_2440);
+	debugfs_remove(s3c2410_udc_debugfs_root);
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h
new file mode 100644
index 0000000..9e0bece
--- /dev/null
+++ b/drivers/usb/gadget/s3c2410_udc.h
@@ -0,0 +1,110 @@
+/*
+ * linux/drivers/usb/gadget/s3c2410_udc.h
+ * Samsung on-chip full speed USB device controllers
+ *
+ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
+ *	Additional cleanups by Ben Dooks <ben-linux@fluff.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _S3C2410_UDC_H
+#define _S3C2410_UDC_H
+
+struct s3c2410_ep {
+	struct list_head		queue;
+	unsigned long			last_io;	/* jiffies timestamp */
+	struct usb_gadget		*gadget;
+	struct s3c2410_udc		*dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_ep			ep;
+	u8				num;
+
+	unsigned short			fifo_size;
+	u8				bEndpointAddress;
+	u8				bmAttributes;
+
+	unsigned			halted : 1;
+	unsigned			already_seen : 1;
+	unsigned			setup_stage : 1;
+};
+
+
+/* Warning : ep0 has a fifo of 16 bytes */
+/* Don't try to set 32 or 64            */
+/* also testusb 14 fails  wit 16 but is */
+/* fine with 8                          */
+#define EP0_FIFO_SIZE		 8
+#define EP_FIFO_SIZE		64
+#define DEFAULT_POWER_STATE	0x00
+
+#define S3C2440_EP_FIFO_SIZE	128
+
+static const char ep0name [] = "ep0";
+
+static const char *const ep_name[] = {
+	ep0name,                                /* everyone has ep0 */
+	/* s3c2410 four bidirectional bulk endpoints */
+	"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
+};
+
+#define S3C2410_ENDPOINTS       ARRAY_SIZE(ep_name)
+
+struct s3c2410_request {
+	struct list_head		queue;		/* ep's requests */
+	struct usb_request		req;
+};
+
+enum ep0_state {
+        EP0_IDLE,
+        EP0_IN_DATA_PHASE,
+        EP0_OUT_DATA_PHASE,
+        EP0_END_XFER,
+        EP0_STALL,
+};
+
+static const char *ep0states[]= {
+        "EP0_IDLE",
+        "EP0_IN_DATA_PHASE",
+        "EP0_OUT_DATA_PHASE",
+        "EP0_END_XFER",
+        "EP0_STALL",
+};
+
+struct s3c2410_udc {
+	spinlock_t			lock;
+
+	struct s3c2410_ep		ep[S3C2410_ENDPOINTS];
+	int				address;
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	struct s3c2410_request		fifo_req;
+	u8				fifo_buf[EP_FIFO_SIZE];
+	u16				devstatus;
+
+	u32				port_status;
+	int				ep0state;
+
+	unsigned			got_irq : 1;
+
+	unsigned			req_std : 1;
+	unsigned			req_config : 1;
+	unsigned			req_pending : 1;
+	u8				vbus;
+	struct dentry			*regs_info;
+};
+
+#endif
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index f847c34..dd33ff0 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -2215,7 +2215,7 @@
  *
  * Free the buffer and all associated memory.
  */
-void gs_buf_free(struct gs_buf *gb)
+static void gs_buf_free(struct gs_buf *gb)
 {
 	if (gb) {
 		kfree(gb->buf_buf);
@@ -2228,7 +2228,7 @@
  *
  * Clear out all data in the circular buffer.
  */
-void gs_buf_clear(struct gs_buf *gb)
+static void gs_buf_clear(struct gs_buf *gb)
 {
 	if (gb != NULL)
 		gb->buf_get = gb->buf_put;
@@ -2241,7 +2241,7 @@
  * Return the number of bytes of data available in the circular
  * buffer.
  */
-unsigned int gs_buf_data_avail(struct gs_buf *gb)
+static unsigned int gs_buf_data_avail(struct gs_buf *gb)
 {
 	if (gb != NULL)
 		return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
@@ -2255,7 +2255,7 @@
  * Return the number of bytes of space available in the circular
  * buffer.
  */
-unsigned int gs_buf_space_avail(struct gs_buf *gb)
+static unsigned int gs_buf_space_avail(struct gs_buf *gb)
 {
 	if (gb != NULL)
 		return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
@@ -2271,7 +2271,8 @@
  *
  * Return the number of bytes copied.
  */
-unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
+static unsigned int
+gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
 {
 	unsigned int len;
 
@@ -2309,7 +2310,8 @@
  *
  * Return the number of bytes copied.
  */
-unsigned int gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
+static unsigned int
+gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
 {
 	unsigned int len;
 
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 7078374..a2e6e3fc 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -481,8 +481,7 @@
 	req = usb_ep_alloc_request (ep, GFP_ATOMIC);
 	if (req) {
 		req->length = length;
-		req->buf = usb_ep_alloc_buffer (ep, length,
-				&req->dma, GFP_ATOMIC);
+		req->buf = kmalloc(length, GFP_ATOMIC);
 		if (!req->buf) {
 			usb_ep_free_request (ep, req);
 			req = NULL;
@@ -493,8 +492,7 @@
 
 static void free_ep_req (struct usb_ep *ep, struct usb_request *req)
 {
-	if (req->buf)
-		usb_ep_free_buffer (ep, req->buf, req->dma, req->length);
+	kfree(req->buf);
 	usb_ep_free_request (ep, req);
 }
 
@@ -1199,8 +1197,7 @@
 	dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
 	if (!dev->req)
 		goto enomem;
-	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
-				&dev->req->dma, GFP_KERNEL);
+	dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
 	if (!dev->req->buf)
 		goto enomem;
 
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 6271187..2f52982 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -69,8 +69,20 @@
 
 config USB_EHCI_BIG_ENDIAN_MMIO
 	bool
-	depends on USB_EHCI_HCD
-	default n
+	depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX)
+	default y
+
+config USB_EHCI_BIG_ENDIAN_DESC
+	bool
+	depends on USB_EHCI_HCD && 440EPX
+	default y
+
+config USB_EHCI_FSL
+	bool
+	select USB_EHCI_ROOT_HUB_TT
+	default y if MPC834x || PPC_MPC831x
+	---help---
+	  Variation of ARC USB block used in some Freescale chips.
 
 config USB_ISP116X_HCD
 	tristate "ISP116X HCD support"
@@ -224,3 +236,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called "sl811_cs".
 
+config USB_R8A66597_HCD
+	tristate "R8A66597 HCD suppoort"
+	depends on USB
+	help
+	  The R8A66597 is a USB 2.0 host and peripheral controller.
+
+	  Enable this option if your board has this chip, and you want
+	  to use it as a host controller.  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called r8a66597-hcd.
+
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 2ff396bd..bb8e9d4 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -15,3 +15,5 @@
 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_USB_R8A66597_HCD)	+= r8a66597-hcd.o
+
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 43eddae..c9cc441 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -52,7 +52,7 @@
 		HCS_INDICATOR (params) ? " ind" : "",
 		HCS_N_CC (params),
 		HCS_N_PCC (params),
-	        HCS_PORTROUTED (params) ? "" : " ordered",
+		HCS_PORTROUTED (params) ? "" : " ordered",
 		HCS_PPC (params) ? "" : " !ppc",
 		HCS_N_PORTS (params)
 		);
@@ -91,20 +91,20 @@
 
 	if (HCC_ISOC_CACHE (params)) {
 		ehci_dbg (ehci,
-		     "%s hcc_params %04x caching frame %s%s%s\n",
-		     label, params,
-		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
-		     HCC_CANPARK (params) ? " park" : "",
-		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+			"%s hcc_params %04x caching frame %s%s%s\n",
+			label, params,
+			HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
+			HCC_CANPARK(params) ? " park" : "",
+			HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
 	} else {
 		ehci_dbg (ehci,
-		     "%s hcc_params %04x thresh %d uframes %s%s%s\n",
-		     label,
-		     params,
-		     HCC_ISOC_THRES (params),
-		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
-		     HCC_CANPARK (params) ? " park" : "",
-		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
+			"%s hcc_params %04x thresh %d uframes %s%s%s\n",
+			label,
+			params,
+			HCC_ISOC_THRES(params),
+			HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
+			HCC_CANPARK(params) ? " park" : "",
+			HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
 	}
 }
 #else
@@ -115,23 +115,23 @@
 
 #ifdef	DEBUG
 
-static void __attribute__((__unused__))
+static void __maybe_unused
 dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
 {
-	ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
-		le32_to_cpup (&qtd->hw_next),
-		le32_to_cpup (&qtd->hw_alt_next),
-		le32_to_cpup (&qtd->hw_token),
-		le32_to_cpup (&qtd->hw_buf [0]));
+	ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+		hc32_to_cpup(ehci, &qtd->hw_next),
+		hc32_to_cpup(ehci, &qtd->hw_alt_next),
+		hc32_to_cpup(ehci, &qtd->hw_token),
+		hc32_to_cpup(ehci, &qtd->hw_buf [0]));
 	if (qtd->hw_buf [1])
-		ehci_dbg (ehci, "  p1=%08x p2=%08x p3=%08x p4=%08x\n",
-			le32_to_cpup (&qtd->hw_buf [1]),
-			le32_to_cpup (&qtd->hw_buf [2]),
-			le32_to_cpup (&qtd->hw_buf [3]),
-			le32_to_cpup (&qtd->hw_buf [4]));
+		ehci_dbg(ehci, "  p1=%08x p2=%08x p3=%08x p4=%08x\n",
+			hc32_to_cpup(ehci, &qtd->hw_buf[1]),
+			hc32_to_cpup(ehci, &qtd->hw_buf[2]),
+			hc32_to_cpup(ehci, &qtd->hw_buf[3]),
+			hc32_to_cpup(ehci, &qtd->hw_buf[4]));
 }
 
-static void __attribute__((__unused__))
+static void __maybe_unused
 dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
@@ -140,51 +140,53 @@
 	dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
 }
 
-static void __attribute__((__unused__))
+static void __maybe_unused
 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);
+		label, itd->frame, itd, hc32_to_cpu(ehci, itd->hw_next),
+		itd->urb);
 	ehci_dbg (ehci,
 		"  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]),
-		le32_to_cpu(itd->hw_transaction[3]),
-		le32_to_cpu(itd->hw_transaction[4]),
-		le32_to_cpu(itd->hw_transaction[5]),
-		le32_to_cpu(itd->hw_transaction[6]),
-		le32_to_cpu(itd->hw_transaction[7]));
+		hc32_to_cpu(ehci, itd->hw_transaction[0]),
+		hc32_to_cpu(ehci, itd->hw_transaction[1]),
+		hc32_to_cpu(ehci, itd->hw_transaction[2]),
+		hc32_to_cpu(ehci, itd->hw_transaction[3]),
+		hc32_to_cpu(ehci, itd->hw_transaction[4]),
+		hc32_to_cpu(ehci, itd->hw_transaction[5]),
+		hc32_to_cpu(ehci, itd->hw_transaction[6]),
+		hc32_to_cpu(ehci, itd->hw_transaction[7]));
 	ehci_dbg (ehci,
 		"  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]),
-		le32_to_cpu(itd->hw_bufp[3]),
-		le32_to_cpu(itd->hw_bufp[4]),
-		le32_to_cpu(itd->hw_bufp[5]),
-		le32_to_cpu(itd->hw_bufp[6]));
+		hc32_to_cpu(ehci, itd->hw_bufp[0]),
+		hc32_to_cpu(ehci, itd->hw_bufp[1]),
+		hc32_to_cpu(ehci, itd->hw_bufp[2]),
+		hc32_to_cpu(ehci, itd->hw_bufp[3]),
+		hc32_to_cpu(ehci, itd->hw_bufp[4]),
+		hc32_to_cpu(ehci, itd->hw_bufp[5]),
+		hc32_to_cpu(ehci, itd->hw_bufp[6]));
 	ehci_dbg (ehci, "  index: %d %d %d %d %d %d %d %d\n",
 		itd->index[0], itd->index[1], itd->index[2],
 		itd->index[3], itd->index[4], itd->index[5],
 		itd->index[6], itd->index[7]);
 }
 
-static void __attribute__((__unused__))
+static void __maybe_unused
 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);
+		label, sitd->frame, sitd, hc32_to_cpu(ehci, sitd->hw_next),
+		sitd->urb);
 	ehci_dbg (ehci,
 		"  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),
-		le32_to_cpu(sitd->hw_buf [0]),
-		le32_to_cpu(sitd->hw_buf [1]));
+		hc32_to_cpu(ehci, sitd->hw_fullspeed_ep),
+		hc32_to_cpu(ehci, sitd->hw_uframe),
+		hc32_to_cpu(ehci, sitd->hw_results),
+		hc32_to_cpu(ehci, sitd->hw_buf[0]),
+		hc32_to_cpu(ehci, sitd->hw_buf[1]));
 }
 
-static int __attribute__((__unused__))
+static int __maybe_unused
 dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
 {
 	return scnprintf (buf, len,
@@ -203,7 +205,7 @@
 		);
 }
 
-static int __attribute__((__unused__))
+static int __maybe_unused
 dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
 {
 	return scnprintf (buf, len,
@@ -267,28 +269,27 @@
 		(status & PORT_PEC) ? " PEC" : "",
 		(status & PORT_PE) ? " PE" : "",
 		(status & PORT_CSC) ? " CSC" : "",
-		(status & PORT_CONNECT) ? " CONNECT" : ""
-	    );
+		(status & PORT_CONNECT) ? " CONNECT" : "");
 }
 
 #else
-static inline void __attribute__((__unused__))
+static inline void __maybe_unused
 dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
 {}
 
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
 dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
 { return 0; }
 
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
 dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
 { return 0; }
 
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
 dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
 { return 0; }
 
-static inline int __attribute__((__unused__))
+static inline int __maybe_unused
 dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status)
 { return 0; }
 
@@ -332,9 +333,10 @@
 		default: tmp = '?'; break; \
 		}; tmp; })
 
-static inline char token_mark (__le32 token)
+static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
 {
-	__u32 v = le32_to_cpu (token);
+	__u32 v = hc32_to_cpu(ehci, token);
+
 	if (v & QTD_STS_ACTIVE)
 		return '*';
 	if (v & QTD_STS_HALT)
@@ -360,46 +362,48 @@
 	unsigned		size = *sizep;
 	char			*next = *nextp;
 	char			mark;
+	u32			list_end = EHCI_LIST_END(ehci);
 
-	if (qh->hw_qtd_next == EHCI_LIST_END)	/* NEC does this */
+	if (qh->hw_qtd_next == list_end)	/* NEC does this */
 		mark = '@';
 	else
-		mark = token_mark (qh->hw_token);
+		mark = token_mark(ehci, qh->hw_token);
 	if (mark == '/') {	/* qh_alt_next controls qh advance? */
-		if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
+		if ((qh->hw_alt_next & QTD_MASK(ehci))
+				== ehci->async->hw_alt_next)
 			mark = '#';	/* blocked */
-		else if (qh->hw_alt_next == EHCI_LIST_END)
+		else if (qh->hw_alt_next == list_end)
 			mark = '.';	/* use hw_qtd_next */
 		/* else alt_next points to some other qtd */
 	}
-	scratch = le32_to_cpup (&qh->hw_info1);
-	hw_curr = (mark == '*') ? le32_to_cpup (&qh->hw_current) : 0;
+	scratch = hc32_to_cpup(ehci, &qh->hw_info1);
+	hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0;
 	temp = scnprintf (next, size,
 			"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
 			qh, scratch & 0x007f,
 			speed_char (scratch),
 			(scratch >> 8) & 0x000f,
-			scratch, le32_to_cpup (&qh->hw_info2),
-			le32_to_cpup (&qh->hw_token), mark,
-			(__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
+			scratch, hc32_to_cpup(ehci, &qh->hw_info2),
+			hc32_to_cpup(ehci, &qh->hw_token), mark,
+			(cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token)
 				? "data1" : "data0",
-			(le32_to_cpup (&qh->hw_alt_next) >> 1) & 0x0f);
+			(hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f);
 	size -= temp;
 	next += temp;
 
 	/* hc may be modifying the list as we read it ... */
 	list_for_each (entry, &qh->qtd_list) {
 		td = list_entry (entry, struct ehci_qtd, qtd_list);
-		scratch = le32_to_cpup (&td->hw_token);
+		scratch = hc32_to_cpup(ehci, &td->hw_token);
 		mark = ' ';
 		if (hw_curr == td->qtd_dma)
 			mark = '*';
-		else if (qh->hw_qtd_next == cpu_to_le32(td->qtd_dma))
+		else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
 			mark = '+';
 		else if (QTD_LENGTH (scratch)) {
 			if (td->hw_alt_next == ehci->async->hw_alt_next)
 				mark = '#';
-			else if (td->hw_alt_next != EHCI_LIST_END)
+			else if (td->hw_alt_next != list_end)
 				mark = '/';
 		}
 		temp = snprintf (next, size,
@@ -490,7 +494,7 @@
 	unsigned		temp, size, seen_count;
 	char			*next;
 	unsigned		i;
-	__le32			tag;
+	__hc32			tag;
 
 	if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
 		return 0;
@@ -514,18 +518,19 @@
 		p = ehci->pshadow [i];
 		if (likely (!p.ptr))
 			continue;
-		tag = Q_NEXT_TYPE (ehci->periodic [i]);
+		tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]);
 
 		temp = scnprintf (next, size, "%4d: ", i);
 		size -= temp;
 		next += temp;
 
 		do {
-			switch (tag) {
+			switch (hc32_to_cpu(ehci, tag)) {
 			case Q_TYPE_QH:
 				temp = scnprintf (next, size, " qh%d-%04x/%p",
 						p.qh->period,
-						le32_to_cpup (&p.qh->hw_info2)
+						hc32_to_cpup(ehci,
+								&p.qh->hw_info2)
 							/* uframe masks */
 							& (QH_CMASK | QH_SMASK),
 						p.qh);
@@ -543,7 +548,7 @@
 				}
 				/* show more info the first time around */
 				if (temp == seen_count && p.ptr) {
-					u32	scratch = le32_to_cpup (
+					u32	scratch = hc32_to_cpup(ehci,
 							&p.qh->hw_info1);
 					struct ehci_qtd	*qtd;
 					char		*type = "";
@@ -554,7 +559,8 @@
 							&p.qh->qtd_list,
 							qtd_list) {
 						temp++;
-						switch (0x03 & (le32_to_cpu (
+						switch (0x03 & (hc32_to_cpu(
+							ehci,
 							qtd->hw_token) >> 8)) {
 						case 0: type = "out"; continue;
 						case 1: type = "in"; continue;
@@ -576,7 +582,7 @@
 				} else
 					temp = 0;
 				if (p.qh) {
-					tag = Q_NEXT_TYPE (p.qh->hw_next);
+					tag = Q_NEXT_TYPE(ehci, p.qh->hw_next);
 					p = p.qh->qh_next;
 				}
 				break;
@@ -584,23 +590,23 @@
 				temp = scnprintf (next, size,
 					" fstn-%8x/%p", p.fstn->hw_prev,
 					p.fstn);
-				tag = Q_NEXT_TYPE (p.fstn->hw_next);
+				tag = Q_NEXT_TYPE(ehci, p.fstn->hw_next);
 				p = p.fstn->fstn_next;
 				break;
 			case Q_TYPE_ITD:
 				temp = scnprintf (next, size,
 					" itd/%p", p.itd);
-				tag = Q_NEXT_TYPE (p.itd->hw_next);
+				tag = Q_NEXT_TYPE(ehci, p.itd->hw_next);
 				p = p.itd->itd_next;
 				break;
 			case Q_TYPE_SITD:
 				temp = scnprintf (next, size,
 					" sitd%d-%04x/%p",
 					p.sitd->stream->interval,
-					le32_to_cpup (&p.sitd->hw_uframe)
+					hc32_to_cpup(ehci, &p.sitd->hw_uframe)
 						& 0x0000ffff,
 					p.sitd);
-				tag = Q_NEXT_TYPE (p.sitd->hw_next);
+				tag = Q_NEXT_TYPE(ehci, p.sitd->hw_next);
 				p = p.sitd->sitd_next;
 				break;
 			}
@@ -673,7 +679,8 @@
 		unsigned	count = 256/4;
 
 		pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
-		offset = HCC_EXT_CAPS (ehci_readl(ehci, &ehci->caps->hcc_params));
+		offset = HCC_EXT_CAPS(ehci_readl(ehci,
+				&ehci->caps->hcc_params));
 		while (offset && count--) {
 			pci_read_config_dword (pdev, offset, &cap);
 			switch (cap & 0xff) {
@@ -740,14 +747,16 @@
 
 	for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) {
 		temp = dbg_port_buf (scratch, sizeof scratch, label, i,
-				ehci_readl(ehci, &ehci->regs->port_status [i - 1]));
+				ehci_readl(ehci,
+					&ehci->regs->port_status[i - 1]));
 		temp = scnprintf (next, size, fmt, temp, scratch);
 		size -= temp;
 		next += temp;
 		if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) {
 			temp = scnprintf (next, size,
 					"    debug control %08x\n",
-					ehci_readl(ehci, &ehci->debug->control));
+					ehci_readl(ehci,
+						&ehci->debug->control));
 			size -= temp;
 			next += temp;
 		}
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index c7a7c59..b7b7bfb 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -67,7 +67,8 @@
 	 * in host mode.
 	 */
 	if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
-	      (pdata->operating_mode == FSL_USB2_MPH_HOST))) {
+	      (pdata->operating_mode == FSL_USB2_MPH_HOST) ||
+	      (pdata->operating_mode == FSL_USB2_DR_OTG))) {
 		dev_err(&pdev->dev,
 			"Non Host Mode configured for %s. Wrong driver linked.\n",
 			pdev->dev.bus_id);
@@ -185,12 +186,14 @@
 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 	struct fsl_usb2_platform_data *pdata;
 	void __iomem *non_ehci = hcd->regs;
+	u32 temp;
 
 	pdata =
 	    (struct fsl_usb2_platform_data *)hcd->self.controller->
 	    platform_data;
 	/* Enable PHY interface in the control reg. */
-	out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004);
+	temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+	out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
 	out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
 
 #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
@@ -206,7 +209,8 @@
 	out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB);
 #endif
 
-	if (pdata->operating_mode == FSL_USB2_DR_HOST)
+	if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+			(pdata->operating_mode == FSL_USB2_DR_OTG))
 		mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
 
 	if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 099aff6..c4e15ed 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -41,10 +41,6 @@
 #include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/unaligned.h>
-#ifdef CONFIG_PPC_PS3
-#include <asm/firmware.h>
-#endif
-
 
 /*-------------------------------------------------------------------------*/
 
@@ -201,9 +197,15 @@
 	u32 __iomem	*reg_ptr;
 	u32		tmp;
 
-	reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68);
+	reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE);
 	tmp = ehci_readl(ehci, reg_ptr);
-	tmp |= 0x3;
+	tmp |= USBMODE_CM_HC;
+	/* The default byte access to MMR space is LE after
+	 * controller reset. Set the required endian mode
+	 * for transfer buffers to match the host microprocessor
+	 */
+	if (ehci_big_endian_mmio(ehci))
+		tmp |= USBMODE_BE;
 	ehci_writel(ehci, tmp, reg_ptr);
 }
 
@@ -273,6 +275,58 @@
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_CPU_FREQ
+
+#include <linux/cpufreq.h>
+
+static void ehci_cpufreq_pause (struct ehci_hcd *ehci)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&ehci->lock, flags);
+	if (!ehci->cpufreq_changing++)
+		qh_inactivate_split_intr_qhs(ehci);
+	spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+static void ehci_cpufreq_unpause (struct ehci_hcd *ehci)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&ehci->lock, flags);
+	if (!--ehci->cpufreq_changing)
+		qh_reactivate_split_intr_qhs(ehci);
+	spin_unlock_irqrestore(&ehci->lock, flags);
+}
+
+/*
+ * ehci_cpufreq_notifier is needed to avoid MMF errors that occur when
+ * EHCI controllers that don't cache many uframes get delayed trying to
+ * read main memory during CPU frequency transitions.  This can cause
+ * split interrupt transactions to not be completed in the required uframe.
+ * This has been observed on the Broadcom/ServerWorks HT1000 controller.
+ */
+static int ehci_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+				 void *data)
+{
+	struct ehci_hcd *ehci = container_of(nb, struct ehci_hcd,
+					     cpufreq_transition);
+
+	switch (val) {
+	case CPUFREQ_PRECHANGE:
+		ehci_cpufreq_pause(ehci);
+		break;
+	case CPUFREQ_POSTCHANGE:
+		ehci_cpufreq_unpause(ehci);
+		break;
+	}
+	return 0;
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 static void ehci_watchdog (unsigned long param)
 {
 	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
@@ -347,6 +401,8 @@
 				is_on ? SetPortFeature : ClearPortFeature,
 				USB_PORT_FEAT_POWER,
 				port--, NULL, 0);
+	/* Flush those writes */
+	ehci_readl(ehci, &ehci->regs->command);
 	msleep(20);
 }
 
@@ -404,6 +460,10 @@
 	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
 	spin_unlock_irq(&ehci->lock);
 
+#ifdef CONFIG_CPU_FREQ
+	cpufreq_unregister_notifier(&ehci->cpufreq_transition,
+				    CPUFREQ_TRANSITION_NOTIFIER);
+#endif
 	/* let companion controllers work when we aren't */
 	ehci_writel(ehci, 0, &ehci->regs->configured_flag);
 
@@ -470,12 +530,12 @@
 	 * from automatically advancing to the next td after short reads.
 	 */
 	ehci->async->qh_next.qh = NULL;
-	ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma);
-	ehci->async->hw_info1 = cpu_to_le32(QH_HEAD);
-	ehci->async->hw_token = cpu_to_le32(QTD_STS_HALT);
-	ehci->async->hw_qtd_next = EHCI_LIST_END;
+	ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
+	ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
+	ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
+	ehci->async->hw_qtd_next = EHCI_LIST_END(ehci);
 	ehci->async->qh_state = QH_STATE_LINKED;
-	ehci->async->hw_alt_next = QTD_NEXT(ehci->async->dummy->qtd_dma);
+	ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
 
 	/* clear interrupt enables, set irq latency */
 	if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -509,6 +569,17 @@
 	}
 	ehci->command = temp;
 
+#ifdef CONFIG_CPU_FREQ
+	INIT_LIST_HEAD(&ehci->split_intr_qhs);
+	/*
+	 * If the EHCI controller caches enough uframes, this probably
+	 * isn't needed unless there are so many low/full speed devices
+	 * that the controller's can't cache it all.
+	 */
+	ehci->cpufreq_transition.notifier_call = ehci_cpufreq_notifier;
+	cpufreq_register_notifier(&ehci->cpufreq_transition,
+				  CPUFREQ_TRANSITION_NOTIFIER);
+#endif
 	return 0;
 }
 
@@ -925,7 +996,7 @@
 #define	PCI_DRIVER		ehci_pci_driver
 #endif
 
-#ifdef CONFIG_MPC834x
+#ifdef CONFIG_USB_EHCI_FSL
 #include "ehci-fsl.c"
 #define	PLATFORM_DRIVER		ehci_fsl_driver
 #endif
@@ -937,7 +1008,12 @@
 
 #ifdef CONFIG_PPC_PS3
 #include "ehci-ps3.c"
-#define	PS3_SYSTEM_BUS_DRIVER	ps3_ehci_sb_driver
+#define	PS3_SYSTEM_BUS_DRIVER	ps3_ehci_driver
+#endif
+
+#ifdef CONFIG_440EPX
+#include "ehci-ppc-soc.c"
+#define	PLATFORM_DRIVER		ehci_ppc_soc_driver
 #endif
 
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
@@ -971,18 +1047,15 @@
 #endif
 
 #ifdef PS3_SYSTEM_BUS_DRIVER
-	if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
-		retval = ps3_system_bus_driver_register(
-				&PS3_SYSTEM_BUS_DRIVER);
-		if (retval < 0) {
+	retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
+	if (retval < 0) {
 #ifdef PLATFORM_DRIVER
-			platform_driver_unregister(&PLATFORM_DRIVER);
+		platform_driver_unregister(&PLATFORM_DRIVER);
 #endif
 #ifdef PCI_DRIVER
-			pci_unregister_driver(&PCI_DRIVER);
+		pci_unregister_driver(&PCI_DRIVER);
 #endif
-			return retval;
-		}
+		return retval;
 	}
 #endif
 
@@ -999,8 +1072,7 @@
 	pci_unregister_driver(&PCI_DRIVER);
 #endif
 #ifdef PS3_SYSTEM_BUS_DRIVER
-	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
-		ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+	ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
 #endif
 }
 module_exit(ehci_hcd_cleanup);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index f4d301b..0dcb416 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -28,6 +28,89 @@
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef	CONFIG_USB_PERSIST
+
+static int ehci_hub_control(
+	struct usb_hcd	*hcd,
+	u16		typeReq,
+	u16		wValue,
+	u16		wIndex,
+	char		*buf,
+	u16		wLength
+);
+
+/* After a power loss, ports that were owned by the companion must be
+ * reset so that the companion can still own them.
+ */
+static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
+{
+	u32 __iomem	*reg;
+	u32		status;
+	int		port;
+	__le32		buf;
+	struct usb_hcd	*hcd = ehci_to_hcd(ehci);
+
+	if (!ehci->owned_ports)
+		return;
+
+	/* Give the connections some time to appear */
+	msleep(20);
+
+	port = HCS_N_PORTS(ehci->hcs_params);
+	while (port--) {
+		if (test_bit(port, &ehci->owned_ports)) {
+			reg = &ehci->regs->port_status[port];
+			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+
+			/* Port already owned by companion? */
+			if (status & PORT_OWNER)
+				clear_bit(port, &ehci->owned_ports);
+			else if (test_bit(port, &ehci->companion_ports))
+				ehci_writel(ehci, status & ~PORT_PE, reg);
+			else
+				ehci_hub_control(hcd, SetPortFeature,
+						USB_PORT_FEAT_RESET, port + 1,
+						NULL, 0);
+		}
+	}
+
+	if (!ehci->owned_ports)
+		return;
+	msleep(90);		/* Wait for resets to complete */
+
+	port = HCS_N_PORTS(ehci->hcs_params);
+	while (port--) {
+		if (test_bit(port, &ehci->owned_ports)) {
+			ehci_hub_control(hcd, GetPortStatus,
+					0, port + 1,
+					(char *) &buf, sizeof(buf));
+
+			/* The companion should now own the port,
+			 * but if something went wrong the port must not
+			 * remain enabled.
+			 */
+			reg = &ehci->regs->port_status[port];
+			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
+			if (status & PORT_OWNER)
+				ehci_writel(ehci, status | PORT_CSC, reg);
+			else {
+				ehci_dbg(ehci, "failed handover port %d: %x\n",
+						port + 1, status);
+				ehci_writel(ehci, status & ~PORT_PE, reg);
+			}
+		}
+	}
+
+	ehci->owned_ports = 0;
+}
+
+#else	/* CONFIG_USB_PERSIST */
+
+static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci)
+{ }
+
+#endif
+
 #ifdef	CONFIG_PM
 
 static int ehci_bus_suspend (struct usb_hcd *hcd)
@@ -60,14 +143,16 @@
 	 * then manually resume them in the bus_resume() routine.
 	 */
 	ehci->bus_suspended = 0;
+	ehci->owned_ports = 0;
 	while (port--) {
 		u32 __iomem	*reg = &ehci->regs->port_status [port];
 		u32		t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
 		u32		t2 = t1;
 
 		/* keep track of which ports we suspend */
-		if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) &&
-				!(t1 & PORT_SUSPEND)) {
+		if (t1 & PORT_OWNER)
+			set_bit(port, &ehci->owned_ports);
+		else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
 			t2 |= PORT_SUSPEND;
 			set_bit(port, &ehci->bus_suspended);
 		}
@@ -108,11 +193,16 @@
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	u32			temp;
+	u32			power_okay;
 	int			i;
 
 	if (time_before (jiffies, ehci->next_statechange))
 		msleep(5);
 	spin_lock_irq (&ehci->lock);
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+		spin_unlock_irq(&ehci->lock);
+		return -ESHUTDOWN;
+	}
 
 	/* Ideally and we've got a real resume here, and no port's power
 	 * was lost.  (For PCI, that means Vaux was maintained.)  But we
@@ -120,8 +210,9 @@
 	 * the last user of the controller, not reset/pm hardware keeping
 	 * state we gave to it.
 	 */
-	temp = ehci_readl(ehci, &ehci->regs->intr_enable);
-	ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss");
+	power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
+	ehci_dbg(ehci, "resume root hub%s\n",
+			power_okay ? "" : " after power loss");
 
 	/* at least some APM implementations will try to deliver
 	 * IRQs right away, so delay them until we're ready.
@@ -184,6 +275,9 @@
 	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
 
 	spin_unlock_irq (&ehci->lock);
+
+	if (!power_okay)
+		ehci_handover_companion_ports(ehci);
 	return 0;
 }
 
@@ -448,7 +542,8 @@
 ) {
 	struct ehci_hcd	*ehci = hcd_to_ehci (hcd);
 	int		ports = HCS_N_PORTS (ehci->hcs_params);
-	u32 __iomem	*status_reg = &ehci->regs->port_status[wIndex - 1];
+	u32 __iomem	*status_reg = &ehci->regs->port_status[
+				(wIndex & 0xff) - 1];
 	u32		temp, status;
 	unsigned long	flags;
 	int		retval = 0;
@@ -556,9 +651,24 @@
 			status |= 1 << USB_PORT_FEAT_C_CONNECTION;
 		if (temp & PORT_PEC)
 			status |= 1 << USB_PORT_FEAT_C_ENABLE;
-		if ((temp & PORT_OCC) && !ignore_oc)
+
+		if ((temp & PORT_OCC) && !ignore_oc){
 			status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
 
+			/*
+			 * Hubs should disable port power on over-current.
+			 * However, not all EHCI implementations do this
+			 * automatically, even if they _do_ support per-port
+			 * power switching; they're allowed to just limit the
+			 * current.  khubd will turn the power back on.
+			 */
+			if (HCS_PPC (ehci->hcs_params)){
+				ehci_writel(ehci,
+					temp & ~(PORT_RWC_BITS | PORT_POWER),
+					status_reg);
+			}
+		}
+
 		/* whoever resumes must GetPortStatus to complete it!! */
 		if (temp & PORT_RESUME) {
 
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index a8ba2e1..8816d09 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -27,7 +27,7 @@
  *	  need to use dma_pool or dma_alloc_coherent
  *	- driver buffers, read/written by HC ... single shot DMA mapped
  *
- * There's also PCI "register" data, which is memory mapped.
+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
  * No memory seen by this driver is pageable.
  */
 
@@ -35,13 +35,14 @@
 
 /* Allocate the key transfer structures from the previously allocated pool */
 
-static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
+static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd,
+				  dma_addr_t dma)
 {
 	memset (qtd, 0, sizeof *qtd);
 	qtd->qtd_dma = dma;
 	qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
-	qtd->hw_next = EHCI_LIST_END;
-	qtd->hw_alt_next = EHCI_LIST_END;
+	qtd->hw_next = EHCI_LIST_END(ehci);
+	qtd->hw_alt_next = EHCI_LIST_END(ehci);
 	INIT_LIST_HEAD (&qtd->qtd_list);
 }
 
@@ -52,7 +53,7 @@
 
 	qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
 	if (qtd != NULL) {
-		ehci_qtd_init (qtd, dma);
+		ehci_qtd_init(ehci, qtd, dma);
 	}
 	return qtd;
 }
@@ -63,9 +64,8 @@
 }
 
 
-static void qh_destroy (struct kref *kref)
+static void qh_destroy(struct ehci_qh *qh)
 {
-	struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
 	struct ehci_hcd *ehci = qh->ehci;
 
 	/* clean qtds first, and know this is not linked */
@@ -89,11 +89,14 @@
 		return qh;
 
 	memset (qh, 0, sizeof *qh);
-	kref_init(&qh->kref);
+	qh->refcount = 1;
 	qh->ehci = ehci;
 	qh->qh_dma = dma;
 	// INIT_LIST_HEAD (&qh->qh_list);
 	INIT_LIST_HEAD (&qh->qtd_list);
+#ifdef CONFIG_CPU_FREQ
+	INIT_LIST_HEAD (&qh->split_intr_qhs);
+#endif
 
 	/* dummy td enables safe urb queuing */
 	qh->dummy = ehci_qtd_alloc (ehci, flags);
@@ -108,13 +111,15 @@
 /* to share a qh (cpu threads, or hc) */
 static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
 {
-	kref_get(&qh->kref);
+	WARN_ON(!qh->refcount);
+	qh->refcount++;
 	return qh;
 }
 
 static inline void qh_put (struct ehci_qh *qh)
 {
-	kref_put(&qh->kref, qh_destroy);
+	if (!--qh->refcount)
+		qh_destroy(qh);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -217,7 +222,7 @@
 		goto fail;
 	}
 	for (i = 0; i < ehci->periodic_size; i++)
-		ehci->periodic [i] = EHCI_LIST_END;
+		ehci->periodic [i] = EHCI_LIST_END(ehci);
 
 	/* software shadow of hardware table */
 	ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 966965f..a7816e3 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -312,13 +312,14 @@
 	ehci_work(ehci);
 	spin_unlock_irq(&ehci->lock);
 
-	/* here we "know" root ports should always stay powered */
-	ehci_port_power(ehci, 1);
-
 	ehci_writel(ehci, ehci->command, &ehci->regs->command);
 	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
 	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
 
+	/* here we "know" root ports should always stay powered */
+	ehci_port_power(ehci, 1);
+	ehci_handover_companion_ports(ehci);
+
 	hcd->state = HC_STATE_SUSPENDED;
 	return 0;
 }
diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c
new file mode 100644
index 0000000..c2cedb0
--- /dev/null
+++ b/drivers/usb/host/ehci-ppc-soc.c
@@ -0,0 +1,182 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 2006-2007 Stefan Roese <sr@denx.de>, DENX Software Engineering
+ *
+ * Bus Glue for PPC On-Chip EHCI driver
+ * Tested on AMCC 440EPx
+ *
+ * Based on "ehci-au12xx.c" by David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/platform_device.h>
+
+extern int usb_disabled(void);
+
+/**
+ * usb_ehci_ppc_soc_probe - initialize PPC-SoC-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_ehci_ppc_soc_probe(const struct hc_driver *driver,
+			   struct usb_hcd **hcd_out,
+			   struct platform_device *dev)
+{
+	int retval;
+	struct usb_hcd *hcd;
+	struct ehci_hcd *ehci;
+
+	if (dev->resource[1].flags != IORESOURCE_IRQ) {
+		pr_debug("resource[1] is not IORESOURCE_IRQ");
+		retval = -ENOMEM;
+	}
+	hcd = usb_create_hcd(driver, &dev->dev, "PPC-SOC EHCI");
+	if (!hcd)
+		return -ENOMEM;
+	hcd->rsrc_start = dev->resource[0].start;
+	hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+		pr_debug("request_mem_region failed");
+		retval = -EBUSY;
+		goto err1;
+	}
+
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		pr_debug("ioremap failed");
+		retval = -ENOMEM;
+		goto err2;
+	}
+
+	ehci = hcd_to_ehci(hcd);
+	ehci->big_endian_mmio = 1;
+	ehci->big_endian_desc = 1;
+	ehci->caps = hcd->regs;
+	ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+	/* cache this readonly data; minimize chip reads */
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+#if defined(CONFIG_440EPX)
+	/*
+	 * 440EPx Errata USBH_3
+	 * Fix: Enable Break Memory Transfer (BMT) in INSNREG3
+	 */
+	out_be32((void *)((ulong)(&ehci->regs->command) + 0x8c), (1 << 0));
+	ehci_dbg(ehci, "Break Memory Transfer (BMT) has beed enabled!\n");
+#endif
+
+	retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
+	if (retval == 0)
+		return retval;
+
+	iounmap(hcd->regs);
+err2:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+	usb_put_hcd(hcd);
+	return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_ehci_hcd_ppc_soc_remove - shutdown processing for PPC-SoC-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_ehci_hcd_ppc_soc_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_ehci_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *dev)
+{
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+}
+
+static const struct hc_driver ehci_ppc_soc_hc_driver = {
+	.description = hcd_name,
+	.product_desc = "PPC-SOC EHCI",
+	.hcd_priv_size = sizeof(struct ehci_hcd),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.irq = ehci_irq,
+	.flags = HCD_MEMORY | HCD_USB2,
+
+	/*
+	 * basic lifecycle operations
+	 */
+	.reset = ehci_init,
+	.start = ehci_run,
+	.stop = ehci_stop,
+	.shutdown = ehci_shutdown,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue = ehci_urb_enqueue,
+	.urb_dequeue = ehci_urb_dequeue,
+	.endpoint_disable = ehci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number = ehci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data = ehci_hub_status_data,
+	.hub_control = ehci_hub_control,
+#ifdef	CONFIG_PM
+	.hub_suspend = ehci_hub_suspend,
+	.hub_resume = ehci_hub_resume,
+#endif
+};
+
+static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = NULL;
+	int ret;
+
+	pr_debug("In ehci_hcd_ppc_soc_drv_probe\n");
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	ret = usb_ehci_ppc_soc_probe(&ehci_ppc_soc_hc_driver, &hcd, pdev);
+	return ret;
+}
+
+static int ehci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_ehci_ppc_soc_remove(hcd, pdev);
+	return 0;
+}
+
+MODULE_ALIAS("ppc-soc-ehci");
+static struct platform_driver ehci_ppc_soc_driver = {
+	.probe = ehci_hcd_ppc_soc_drv_probe,
+	.remove = ehci_hcd_ppc_soc_drv_remove,
+	.shutdown = usb_hcd_platform_shutdown,
+	.driver = {
+		.name = "ppc-soc-ehci",
+		.bus = &platform_bus_type
+	}
+};
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 37b83ba..829fe64 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -18,6 +18,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <asm/firmware.h>
 #include <asm/ps3.h>
 
 static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
@@ -73,7 +74,7 @@
 #endif
 };
 
-static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
+static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
 {
 	int result;
 	struct usb_hcd *hcd;
@@ -85,13 +86,30 @@
 		goto fail_start;
 	}
 
+	result = ps3_open_hv_device(dev);
+
+	if (result) {
+		dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
+			__func__, __LINE__);
+		goto fail_open;
+	}
+
+	result = ps3_dma_region_create(dev->d_region);
+
+	if (result) {
+		dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+			"(%d)\n", __func__, __LINE__, result);
+		BUG_ON("check region type");
+		goto fail_dma_region;
+	}
+
 	result = ps3_mmio_region_create(dev->m_region);
 
 	if (result) {
 		dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
 			__func__, __LINE__);
 		result = -EPERM;
-		goto fail_mmio;
+		goto fail_mmio_region;
 	}
 
 	dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
@@ -120,6 +138,11 @@
 
 	hcd->rsrc_start = dev->m_region->lpar_addr;
 	hcd->rsrc_len = dev->m_region->len;
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+		dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+			__func__, __LINE__);
+
 	hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
 
 	if (!hcd->regs) {
@@ -153,34 +176,73 @@
 fail_add_hcd:
 	iounmap(hcd->regs);
 fail_ioremap:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	usb_put_hcd(hcd);
 fail_create_hcd:
 	ps3_io_irq_destroy(virq);
 fail_irq:
 	ps3_free_mmio_region(dev->m_region);
-fail_mmio:
+fail_mmio_region:
+	ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+	ps3_close_hv_device(dev);
+fail_open:
 fail_start:
 	return result;
 }
 
-static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev)
+static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
 {
+	unsigned int tmp;
 	struct usb_hcd *hcd =
 		(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
 
-	usb_put_hcd(hcd);
+	BUG_ON(!hcd);
+
+	dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+	dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+	tmp = hcd->irq;
+
+	usb_remove_hcd(hcd);
+
 	ps3_system_bus_set_driver_data(dev, NULL);
 
+	BUG_ON(!hcd->regs);
+	iounmap(hcd->regs);
+
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+
+	ps3_io_irq_destroy(tmp);
+	ps3_free_mmio_region(dev->m_region);
+
+	ps3_dma_region_free(dev->d_region);
+	ps3_close_hv_device(dev);
+
 	return 0;
 }
 
-MODULE_ALIAS("ps3-ehci");
+static int ps3_ehci_driver_register(struct ps3_system_bus_driver *drv)
+{
+	return firmware_has_feature(FW_FEATURE_PS3_LV1)
+		? ps3_system_bus_driver_register(drv)
+		: 0;
+}
 
-static struct ps3_system_bus_driver ps3_ehci_sb_driver = {
+static void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+		ps3_system_bus_driver_unregister(drv);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_EHCI);
+
+static struct ps3_system_bus_driver ps3_ehci_driver = {
+	.core.name = "ps3-ehci-driver",
+	.core.owner = THIS_MODULE,
 	.match_id = PS3_MATCH_ID_EHCI,
-	.core = {
-		.name = "ps3-ehci-driver",
-	},
-	.probe = ps3_ehci_sb_probe,
-	.remove = ps3_ehci_sb_remove,
+	.probe = ps3_ehci_probe,
+	.remove = ps3_ehci_remove,
+	.shutdown = ps3_ehci_remove,
 };
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index e7fbbd0..2284028 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -43,15 +43,15 @@
 /* fill a qtd, returning how much of the buffer we were able to queue up */
 
 static int
-qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
-		int token, int maxpacket)
+qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
+		  size_t len, int token, int maxpacket)
 {
 	int	i, count;
 	u64	addr = buf;
 
 	/* one buffer entry per 4K ... first might be short or unaligned */
-	qtd->hw_buf [0] = cpu_to_le32 ((u32)addr);
-	qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32));
+	qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32)addr);
+	qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32)(addr >> 32));
 	count = 0x1000 - (buf & 0x0fff);	/* rest of that page */
 	if (likely (len < count))		/* ... iff needed */
 		count = len;
@@ -62,8 +62,9 @@
 		/* per-qtd limit: from 16K to 20K (best alignment) */
 		for (i = 1; count < len && i < 5; i++) {
 			addr = buf;
-			qtd->hw_buf [i] = cpu_to_le32 ((u32)addr);
-			qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32));
+			qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr);
+			qtd->hw_buf_hi[i] = cpu_to_hc32(ehci,
+					(u32)(addr >> 32));
 			buf += 0x1000;
 			if ((count + 0x1000) < len)
 				count += 0x1000;
@@ -75,7 +76,7 @@
 		if (count != len)
 			count -= (count % maxpacket);
 	}
-	qtd->hw_token = cpu_to_le32 ((count << 16) | token);
+	qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token);
 	qtd->length = count;
 
 	return count;
@@ -89,28 +90,28 @@
 	/* writes to an active overlay are unsafe */
 	BUG_ON(qh->qh_state != QH_STATE_IDLE);
 
-	qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
-	qh->hw_alt_next = EHCI_LIST_END;
+	qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
+	qh->hw_alt_next = EHCI_LIST_END(ehci);
 
 	/* Except for control endpoints, we make hardware maintain data
 	 * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
 	 * and set the pseudo-toggle in udev. Only usb_clear_halt() will
 	 * ever clear it.
 	 */
-	if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) {
+	if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
 		unsigned	is_out, epnum;
 
-		is_out = !(qtd->hw_token & cpu_to_le32(1 << 8));
-		epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f;
+		is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
+		epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
 		if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
-			qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
+			qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
 			usb_settoggle (qh->dev, epnum, is_out, 1);
 		}
 	}
 
 	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
 	wmb ();
-	qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
+	qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
 }
 
 /* if it weren't for a common silicon quirk (writing the dummy into the qh
@@ -128,7 +129,7 @@
 		qtd = list_entry (qh->qtd_list.next,
 				struct ehci_qtd, qtd_list);
 		/* first qtd may already be partially processed */
-		if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current)
+		if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current)
 			qtd = NULL;
 	}
 
@@ -222,7 +223,7 @@
 		struct ehci_qh	*qh = (struct ehci_qh *) urb->hcpriv;
 
 		/* S-mask in a QH means it's an interrupt urb */
-		if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) {
+		if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
 
 			/* ... update hc-wide periodic stats (for usbfs) */
 			ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
@@ -277,7 +278,6 @@
  * Chases up to qh->hw_current.  Returns number of completions called,
  * indicating how much "real" work we did.
  */
-#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT)
 static unsigned
 qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
@@ -287,6 +287,7 @@
 	unsigned		count = 0;
 	int			do_status = 0;
 	u8			state;
+	u32			halt = HALT_BIT(ehci);
 
 	if (unlikely (list_empty (&qh->qtd_list)))
 		return count;
@@ -311,6 +312,10 @@
 		struct urb	*urb;
 		u32		token = 0;
 
+		/* ignore QHs that are currently inactive */
+		if (qh->hw_info1 & __constant_cpu_to_le32(QH_INACTIVATE))
+			break;
+
 		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
 		urb = qtd->urb;
 
@@ -330,7 +335,7 @@
 
 		/* hardware copies qtd out of qh overlay */
 		rmb ();
-		token = le32_to_cpu (qtd->hw_token);
+		token = hc32_to_cpu(ehci, qtd->hw_token);
 
 		/* always clean up qtds the hc de-activated */
 		if ((token & QTD_STS_ACTIVE) == 0) {
@@ -342,7 +347,8 @@
 			 * that silicon quirk can kick in with this dummy too.
 			 */
 			} else if (IS_SHORT_READ (token)
-					&& !(qtd->hw_alt_next & EHCI_LIST_END)) {
+					&& !(qtd->hw_alt_next
+						& EHCI_LIST_END(ehci))) {
 				stopped = 1;
 				goto halt;
 			}
@@ -374,17 +380,17 @@
 
 			/* token in overlay may be most current */
 			if (state == QH_STATE_IDLE
-					&& cpu_to_le32 (qtd->qtd_dma)
+					&& cpu_to_hc32(ehci, qtd->qtd_dma)
 						== qh->hw_current)
-				token = le32_to_cpu (qh->hw_token);
+				token = hc32_to_cpu(ehci, qh->hw_token);
 
 			/* force halt for unlinked or blocked qh, so we'll
 			 * patch the qh later and so that completions can't
 			 * activate it while we "know" it's stopped.
 			 */
-			if ((HALT_BIT & qh->hw_token) == 0) {
+			if ((halt & qh->hw_token) == 0) {
 halt:
-				qh->hw_token |= HALT_BIT;
+				qh->hw_token |= halt;
 				wmb ();
 			}
 		}
@@ -419,7 +425,7 @@
 	 * it after fault cleanup, or recovering from silicon wrongly
 	 * overlaying the dummy qtd (which reduces DMA chatter).
 	 */
-	if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) {
+	if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
 		switch (state) {
 		case QH_STATE_IDLE:
 			qh_refresh(ehci, qh);
@@ -428,7 +434,7 @@
 			/* should be rare for periodic transfers,
 			 * except maybe high bandwidth ...
 			 */
-			if ((__constant_cpu_to_le32 (QH_SMASK)
+			if ((cpu_to_hc32(ehci, QH_SMASK)
 					& qh->hw_info2) != 0) {
 				intr_deschedule (ehci, qh);
 				(void) qh_schedule (ehci, qh);
@@ -502,8 +508,9 @@
 	is_input = usb_pipein (urb->pipe);
 	if (usb_pipecontrol (urb->pipe)) {
 		/* SETUP pid */
-		qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
-			token | (2 /* "setup" */ << 8), 8);
+		qtd_fill(ehci, qtd, urb->setup_dma,
+				sizeof (struct usb_ctrlrequest),
+				token | (2 /* "setup" */ << 8), 8);
 
 		/* ... and always at least one more pid */
 		token ^= QTD_TOGGLE;
@@ -512,7 +519,7 @@
 		if (unlikely (!qtd))
 			goto cleanup;
 		qtd->urb = urb;
-		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+		qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
 		list_add_tail (&qtd->qtd_list, head);
 
 		/* for zero length DATA stages, STATUS is always IN */
@@ -539,7 +546,7 @@
 	for (;;) {
 		int this_qtd_len;
 
-		this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
+		this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket);
 		len -= this_qtd_len;
 		buf += this_qtd_len;
 		if (is_input)
@@ -557,7 +564,7 @@
 		if (unlikely (!qtd))
 			goto cleanup;
 		qtd->urb = urb;
-		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+		qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
 		list_add_tail (&qtd->qtd_list, head);
 	}
 
@@ -566,7 +573,7 @@
 	 */
 	if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
 				|| usb_pipecontrol (urb->pipe)))
-		qtd->hw_alt_next = EHCI_LIST_END;
+		qtd->hw_alt_next = EHCI_LIST_END(ehci);
 
 	/*
 	 * control requests may need a terminating data "status" ack;
@@ -590,17 +597,17 @@
 			if (unlikely (!qtd))
 				goto cleanup;
 			qtd->urb = urb;
-			qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
+			qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
 			list_add_tail (&qtd->qtd_list, head);
 
 			/* never any data in such packets */
-			qtd_fill (qtd, 0, 0, token, 0);
+			qtd_fill(ehci, qtd, 0, 0, token, 0);
 		}
 	}
 
 	/* by default, enable interrupt on urb completion */
 	if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
-		qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
+		qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
 	return head;
 
 cleanup:
@@ -769,8 +776,8 @@
 
 	/* init as live, toggle clear, advance to dummy */
 	qh->qh_state = QH_STATE_IDLE;
-	qh->hw_info1 = cpu_to_le32 (info1);
-	qh->hw_info2 = cpu_to_le32 (info2);
+	qh->hw_info1 = cpu_to_hc32(ehci, info1);
+	qh->hw_info2 = cpu_to_hc32(ehci, info2);
 	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
 	qh_refresh (ehci, qh);
 	return qh;
@@ -782,7 +789,7 @@
 
 static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	__le32		dma = QH_NEXT (qh->qh_dma);
+	__hc32		dma = QH_NEXT(ehci, qh->qh_dma);
 	struct ehci_qh	*head;
 
 	/* (re)start the async schedule? */
@@ -820,8 +827,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-#define	QH_ADDR_MASK	__constant_cpu_to_le32(0x7f)
-
 /*
  * For control/bulk/interrupt, return QH with these TDs appended.
  * Allocates and initializes the QH if necessary.
@@ -837,6 +842,7 @@
 )
 {
 	struct ehci_qh		*qh = NULL;
+	u32			qh_addr_mask = cpu_to_hc32(ehci, 0x7f);
 
 	qh = (struct ehci_qh *) *ptr;
 	if (unlikely (qh == NULL)) {
@@ -858,7 +864,7 @@
 
                         /* usb_reset_device() briefly reverts to address 0 */
                         if (usb_pipedevice (urb->pipe) == 0)
-                                qh->hw_info1 &= ~QH_ADDR_MASK;
+                                qh->hw_info1 &= ~qh_addr_mask;
 		}
 
 		/* just one way to queue requests: swap with the dummy qtd.
@@ -867,7 +873,7 @@
 		if (likely (qtd != NULL)) {
 			struct ehci_qtd		*dummy;
 			dma_addr_t		dma;
-			__le32			token;
+			__hc32			token;
 
 			/* to avoid racing the HC, use the dummy td instead of
 			 * the first td of our list (becomes new dummy).  both
@@ -875,7 +881,7 @@
 			 * HC is allowed to fetch the old dummy (4.10.2).
 			 */
 			token = qtd->hw_token;
-			qtd->hw_token = HALT_BIT;
+			qtd->hw_token = HALT_BIT(ehci);
 			wmb ();
 			dummy = qh->dummy;
 
@@ -887,14 +893,14 @@
 			list_add (&dummy->qtd_list, qtd_list);
 			__list_splice (qtd_list, qh->qtd_list.prev);
 
-			ehci_qtd_init (qtd, qtd->qtd_dma);
+			ehci_qtd_init(ehci, qtd, qtd->qtd_dma);
 			qh->dummy = qtd;
 
 			/* hc must see the new dummy at list end */
 			dma = qtd->qtd_dma;
 			qtd = list_entry (qh->qtd_list.prev,
 					struct ehci_qtd, qtd_list);
-			qtd->hw_next = QTD_NEXT (dma);
+			qtd->hw_next = QTD_NEXT(ehci, dma);
 
 			/* let the hc process these next qtds */
 			wmb ();
@@ -970,7 +976,7 @@
 
 	timer_action_done (ehci, TIMER_IAA_WATCHDOG);
 
-	// qh->hw_next = cpu_to_le32 (qh->qh_dma);
+	// qh->hw_next = cpu_to_hc32(qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
 	qh->qh_next.qh = NULL;
 	qh_put (qh);			// refcount from reclaim
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 7b5ae71..d4a8ace 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -44,9 +44,10 @@
  * @tag: hardware tag for type of this record
  */
 static union ehci_shadow *
-periodic_next_shadow (union ehci_shadow *periodic, __le32 tag)
+periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
+		__hc32 tag)
 {
-	switch (tag) {
+	switch (hc32_to_cpu(ehci, tag)) {
 	case Q_TYPE_QH:
 		return &periodic->qh->qh_next;
 	case Q_TYPE_FSTN:
@@ -62,13 +63,14 @@
 /* caller must hold ehci->lock */
 static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
 {
-	union ehci_shadow	*prev_p = &ehci->pshadow [frame];
-	__le32			*hw_p = &ehci->periodic [frame];
+	union ehci_shadow	*prev_p = &ehci->pshadow[frame];
+	__hc32			*hw_p = &ehci->periodic[frame];
 	union ehci_shadow	here = *prev_p;
 
 	/* find predecessor of "ptr"; hw and shadow lists are in sync */
 	while (here.ptr && here.ptr != ptr) {
-		prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p));
+		prev_p = periodic_next_shadow(ehci, prev_p,
+				Q_NEXT_TYPE(ehci, *hw_p));
 		hw_p = here.hw_next;
 		here = *prev_p;
 	}
@@ -79,7 +81,8 @@
 	/* update shadow and hardware lists ... the old "next" pointers
 	 * from ptr may still be in use, the caller updates them.
 	 */
-	*prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p));
+	*prev_p = *periodic_next_shadow(ehci, &here,
+			Q_NEXT_TYPE(ehci, *hw_p));
 	*hw_p = *here.hw_next;
 }
 
@@ -87,18 +90,19 @@
 static unsigned short
 periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
 {
-	__le32			*hw_p = &ehci->periodic [frame];
+	__hc32			*hw_p = &ehci->periodic [frame];
 	union ehci_shadow	*q = &ehci->pshadow [frame];
 	unsigned		usecs = 0;
 
 	while (q->ptr) {
-		switch (Q_NEXT_TYPE (*hw_p)) {
+		switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
 		case Q_TYPE_QH:
 			/* is it in the S-mask? */
-			if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe))
+			if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
 				usecs += q->qh->usecs;
 			/* ... or C-mask? */
-			if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe)))
+			if (q->qh->hw_info2 & cpu_to_hc32(ehci,
+					1 << (8 + uframe)))
 				usecs += q->qh->c_usecs;
 			hw_p = &q->qh->hw_next;
 			q = &q->qh->qh_next;
@@ -108,7 +112,7 @@
 			/* for "save place" FSTNs, count the relevant INTR
 			 * bandwidth from the previous frame
 			 */
-			if (q->fstn->hw_prev != EHCI_LIST_END) {
+			if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) {
 				ehci_dbg (ehci, "ignoring FSTN cost ...\n");
 			}
 			hw_p = &q->fstn->hw_next;
@@ -121,9 +125,10 @@
 			break;
 		case Q_TYPE_SITD:
 			/* is it in the S-mask?  (count SPLIT, DATA) */
-			if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) {
+			if (q->sitd->hw_uframe & cpu_to_hc32(ehci,
+					1 << uframe)) {
 				if (q->sitd->hw_fullspeed_ep &
-						__constant_cpu_to_le32 (1<<31))
+						cpu_to_hc32(ehci, 1<<31))
 					usecs += q->sitd->stream->usecs;
 				else	/* worst case for OUT start-split */
 					usecs += HS_USECS_ISO (188);
@@ -131,7 +136,7 @@
 
 			/* ... C-mask?  (count CSPLIT, DATA) */
 			if (q->sitd->hw_uframe &
-					cpu_to_le32 (1 << (8 + uframe))) {
+					cpu_to_hc32(ehci, 1 << (8 + uframe))) {
 				/* worst case for IN complete-split */
 				usecs += q->sitd->stream->c_usecs;
 			}
@@ -173,9 +178,9 @@
  * will cause a transfer in "B-frame" uframe 0.  "B-frames" lag
  * "H-frames" by 1 uframe.  See the EHCI spec sec 4.5 and figure 4.7.
  */
-static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __le32 mask)
+static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
 {
-	unsigned char smask = QH_SMASK & le32_to_cpu(mask);
+	unsigned char smask = QH_SMASK & hc32_to_cpu(ehci, mask);
 	if (!smask) {
 		ehci_err(ehci, "invalid empty smask!\n");
 		/* uframe 7 can't have bw so this will indicate failure */
@@ -217,14 +222,14 @@
 	unsigned short tt_usecs[8]
 )
 {
-	__le32			*hw_p = &ehci->periodic [frame];
+	__hc32			*hw_p = &ehci->periodic [frame];
 	union ehci_shadow	*q = &ehci->pshadow [frame];
 	unsigned char		uf;
 
 	memset(tt_usecs, 0, 16);
 
 	while (q->ptr) {
-		switch (Q_NEXT_TYPE(*hw_p)) {
+		switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
 		case Q_TYPE_ITD:
 			hw_p = &q->itd->hw_next;
 			q = &q->itd->itd_next;
@@ -247,8 +252,8 @@
 			continue;
 		// case Q_TYPE_FSTN:
 		default:
-			ehci_dbg(ehci,
-				  "ignoring periodic frame %d FSTN\n", frame);
+			ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n",
+					frame);
 			hw_p = &q->fstn->hw_next;
 			q = &q->fstn->fstn_next;
 		}
@@ -368,41 +373,42 @@
 	 */
 	for (; frame < ehci->periodic_size; frame += period) {
 		union ehci_shadow	here;
-		__le32			type;
+		__hc32			type;
 
 		here = ehci->pshadow [frame];
-		type = Q_NEXT_TYPE (ehci->periodic [frame]);
+		type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
 		while (here.ptr) {
-			switch (type) {
+			switch (hc32_to_cpu(ehci, type)) {
 			case Q_TYPE_ITD:
-				type = Q_NEXT_TYPE (here.itd->hw_next);
+				type = Q_NEXT_TYPE(ehci, here.itd->hw_next);
 				here = here.itd->itd_next;
 				continue;
 			case Q_TYPE_QH:
 				if (same_tt (dev, here.qh->dev)) {
 					u32		mask;
 
-					mask = le32_to_cpu (here.qh->hw_info2);
+					mask = hc32_to_cpu(ehci,
+							here.qh->hw_info2);
 					/* "knows" no gap is needed */
 					mask |= mask >> 8;
 					if (mask & uf_mask)
 						break;
 				}
-				type = Q_NEXT_TYPE (here.qh->hw_next);
+				type = Q_NEXT_TYPE(ehci, here.qh->hw_next);
 				here = here.qh->qh_next;
 				continue;
 			case Q_TYPE_SITD:
 				if (same_tt (dev, here.sitd->urb->dev)) {
 					u16		mask;
 
-					mask = le32_to_cpu (here.sitd
+					mask = hc32_to_cpu(ehci, here.sitd
 								->hw_uframe);
 					/* FIXME assumes no gap for IN! */
 					mask |= mask >> 8;
 					if (mask & uf_mask)
 						break;
 				}
-				type = Q_NEXT_TYPE (here.sitd->hw_next);
+				type = Q_NEXT_TYPE(ehci, here.sitd->hw_next);
 				here = here.sitd->sitd_next;
 				continue;
 			// case Q_TYPE_FSTN:
@@ -473,6 +479,109 @@
 }
 
 /*-------------------------------------------------------------------------*/
+#ifdef CONFIG_CPU_FREQ
+
+static int safe_to_modify_i (struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+	int now; /* current (frame * 8) + uframe */
+	int prev_start, next_start; /* uframes from/to split start */
+	int start_uframe = ffs(le32_to_cpup (&qh->hw_info2) & QH_SMASK);
+	int end_uframe = fls((le32_to_cpup (&qh->hw_info2) & QH_CMASK) >> 8);
+	int split_duration = end_uframe - start_uframe;
+
+	now = readl(&ehci->regs->frame_index) % (ehci->periodic_size << 3);
+
+	next_start = ((1024 << 3) + (qh->start << 3) + start_uframe - now)
+			% (qh->period << 3);
+	prev_start = (qh->period << 3) - next_start;
+
+	/*
+	 * Make sure there will be at least one uframe when qh is safe.
+	 */
+	if ((qh->period << 3) <= (ehci->i_thresh + 2 + split_duration))
+		/* never safe */
+		return -EINVAL;
+
+	/*
+	 * Wait 1 uframe after transaction should have started, to make
+	 * sure controller has time to write back overlay, so we can
+	 * check QTD_STS_STS to see if transaction is in progress.
+	 */
+	if ((next_start > ehci->i_thresh) && (prev_start > 1))
+		/* safe to set "i" bit if split isn't in progress */
+		return (qh->hw_token & STATUS_BIT(ehci)) ? 0 : 1;
+	else
+		return 0;
+}
+
+/* Set inactivate bit for all the split interrupt QHs. */
+static void qh_inactivate_split_intr_qhs (struct ehci_hcd *ehci)
+{
+	struct ehci_qh	*qh;
+	int		not_done, safe;
+	u32		inactivate = INACTIVATE_BIT(ehci);
+	u32		active = ACTIVE_BIT(ehci);
+
+	do {
+		not_done = 0;
+		list_for_each_entry(qh, &ehci->split_intr_qhs,
+				split_intr_qhs) {
+			if (qh->hw_info1 & inactivate)
+				/* already off */
+				continue;
+			/*
+			 * To avoid setting "I" after the start split happens,
+			 * don't set it if the QH might be cached in the
+			 * controller.  Some HCs (Broadcom/ServerWorks HT1000)
+			 * will stop in the middle of a split transaction when
+			 * the "I" bit is set.
+			 */
+			safe = safe_to_modify_i(ehci, qh);
+			if (safe == 0) {
+				not_done = 1;
+			} else if (safe > 0) {
+				qh->was_active = qh->hw_token & active;
+				qh->hw_info1 |= inactivate;
+			}
+		}
+	} while (not_done);
+	wmb();
+}
+
+static void qh_reactivate_split_intr_qhs (struct ehci_hcd *ehci)
+{
+	struct ehci_qh	*qh;
+	u32		token;
+	int		not_done, safe;
+	u32		inactivate = INACTIVATE_BIT(ehci);
+	u32		active = ACTIVE_BIT(ehci);
+	u32		halt = HALT_BIT(ehci);
+
+	do {
+		not_done = 0;
+		list_for_each_entry(qh, &ehci->split_intr_qhs, split_intr_qhs) {
+			if (!(qh->hw_info1 & inactivate)) /* already on */
+				continue;
+			/*
+			 * Don't reactivate if cached, or controller might
+			 * overwrite overlay after we modify it!
+			 */
+			safe = safe_to_modify_i(ehci, qh);
+			if (safe == 0) {
+				not_done = 1;
+			} else if (safe > 0) {
+				/* See EHCI 1.0 section 4.15.2.4. */
+				token = qh->hw_token;
+				qh->hw_token = (token | halt) & ~active;
+				wmb();
+				qh->hw_info1 &= ~inactivate;
+				wmb();
+				qh->hw_token = (token & ~halt) | qh->was_active;
+			}
+		}
+	} while (not_done);
+}
+#endif
 
 /* periodic schedule slots have iso tds (normal or split) first, then a
  * sparse tree for active interrupt transfers.
@@ -487,25 +596,36 @@
 
 	dev_dbg (&qh->dev->dev,
 		"link qh%d-%04x/%p start %d [%d/%d us]\n",
-		period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
+		period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
 		qh, qh->start, qh->usecs, qh->c_usecs);
 
+#ifdef CONFIG_CPU_FREQ
+	/*
+	 * If low/full speed interrupt QHs are inactive (because of
+	 * cpufreq changing processor speeds), start QH with I flag set--
+	 * it will automatically be cleared when cpufreq is done.
+	 */
+	if (ehci->cpufreq_changing)
+		if (!(qh->hw_info1 & (cpu_to_le32(1 << 13))))
+			qh->hw_info1 |= INACTIVATE_BIT(ehci);
+#endif
+
 	/* high bandwidth, or otherwise every microframe */
 	if (period == 0)
 		period = 1;
 
 	for (i = qh->start; i < ehci->periodic_size; i += period) {
-		union ehci_shadow	*prev = &ehci->pshadow [i];
-		__le32			*hw_p = &ehci->periodic [i];
+		union ehci_shadow	*prev = &ehci->pshadow[i];
+		__hc32			*hw_p = &ehci->periodic[i];
 		union ehci_shadow	here = *prev;
-		__le32			type = 0;
+		__hc32			type = 0;
 
 		/* skip the iso nodes at list head */
 		while (here.ptr) {
-			type = Q_NEXT_TYPE (*hw_p);
-			if (type == Q_TYPE_QH)
+			type = Q_NEXT_TYPE(ehci, *hw_p);
+			if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
 				break;
-			prev = periodic_next_shadow (prev, type);
+			prev = periodic_next_shadow(ehci, prev, type);
 			hw_p = &here.qh->hw_next;
 			here = *prev;
 		}
@@ -527,7 +647,7 @@
 				qh->hw_next = *hw_p;
 			wmb ();
 			prev->qh = qh;
-			*hw_p = QH_NEXT (qh->qh_dma);
+			*hw_p = QH_NEXT (ehci, qh->qh_dma);
 		}
 	}
 	qh->qh_state = QH_STATE_LINKED;
@@ -538,6 +658,12 @@
 		? ((qh->usecs + qh->c_usecs) / qh->period)
 		: (qh->usecs * 8);
 
+#ifdef CONFIG_CPU_FREQ
+	/* add qh to list of low/full speed interrupt QHs, if applicable */
+	if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) {
+		list_add(&qh->split_intr_qhs, &ehci->split_intr_qhs);
+	}
+#endif
 	/* maybe enable periodic schedule processing */
 	if (!ehci->periodic_sched++)
 		return enable_periodic (ehci);
@@ -555,7 +681,14 @@
 	//   and this qh is active in the current uframe
 	//   (and overlay token SplitXstate is false?)
 	// THEN
-	//   qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */);
+	//   qh->hw_info1 |= __constant_cpu_to_hc32(1 << 7 /* "ignore" */);
+
+#ifdef CONFIG_CPU_FREQ
+	/* remove qh from list of low/full speed interrupt QHs */
+	if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) {
+		list_del_init(&qh->split_intr_qhs);
+	}
+#endif
 
 	/* high bandwidth, or otherwise part of every microframe */
 	if ((period = qh->period) == 0)
@@ -572,7 +705,7 @@
 	dev_dbg (&qh->dev->dev,
 		"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
 		qh->period,
-		le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
+		hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
 		qh, qh->start, qh->usecs, qh->c_usecs);
 
 	/* qh->qh_next still "live" to HC */
@@ -598,7 +731,7 @@
 	 * active high speed queues may need bigger delays...
 	 */
 	if (list_empty (&qh->qtd_list)
-			|| (__constant_cpu_to_le32 (QH_CMASK)
+			|| (cpu_to_hc32(ehci, QH_CMASK)
 					& qh->hw_info2) != 0)
 		wait = 2;
 	else
@@ -606,7 +739,7 @@
 
 	udelay (wait);
 	qh->qh_state = QH_STATE_IDLE;
-	qh->hw_next = EHCI_LIST_END;
+	qh->hw_next = EHCI_LIST_END(ehci);
 	wmb ();
 }
 
@@ -663,7 +796,7 @@
 	unsigned		frame,
 	unsigned		uframe,
 	const struct ehci_qh	*qh,
-	__le32			*c_maskp
+	__hc32			*c_maskp
 )
 {
 	int		retval = -ENOSPC;
@@ -695,7 +828,7 @@
 
 		retval = 0;
 
-		*c_maskp = cpu_to_le32 (mask << 8);
+		*c_maskp = cpu_to_hc32(ehci, mask << 8);
 	}
 #else
 	/* Make sure this tt's buffer is also available for CSPLITs.
@@ -706,7 +839,7 @@
 	 * one smart pass...
 	 */
 	mask = 0x03 << (uframe + qh->gap_uf);
-	*c_maskp = cpu_to_le32 (mask << 8);
+	*c_maskp = cpu_to_hc32(ehci, mask << 8);
 
 	mask |= 1 << uframe;
 	if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
@@ -726,20 +859,20 @@
 /* "first fit" scheduling policy used the first time through,
  * or when the previous schedule slot can't be re-used.
  */
-static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	int		status;
 	unsigned	uframe;
-	__le32		c_mask;
+	__hc32		c_mask;
 	unsigned	frame;		/* 0..(qh->period - 1), or NO_FRAME */
 
 	qh_refresh(ehci, qh);
-	qh->hw_next = EHCI_LIST_END;
+	qh->hw_next = EHCI_LIST_END(ehci);
 	frame = qh->start;
 
 	/* reuse the previous schedule slots, if we can */
 	if (frame < qh->period) {
-		uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK);
+		uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK);
 		status = check_intr_schedule (ehci, frame, --uframe,
 				qh, &c_mask);
 	} else {
@@ -775,10 +908,10 @@
 		qh->start = frame;
 
 		/* reset S-frame and (maybe) C-frame masks */
-		qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK));
+		qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
 		qh->hw_info2 |= qh->period
-			? cpu_to_le32 (1 << uframe)
-			: __constant_cpu_to_le32 (QH_SMASK);
+			? cpu_to_hc32(ehci, 1 << uframe)
+			: cpu_to_hc32(ehci, QH_SMASK);
 		qh->hw_info2 |= c_mask;
 	} else
 		ehci_dbg (ehci, "reused qh %p schedule\n", qh);
@@ -808,7 +941,7 @@
 	spin_lock_irqsave (&ehci->lock, flags);
 
 	if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
-			       &ehci_to_hcd(ehci)->flags))) {
+			&ehci_to_hcd(ehci)->flags))) {
 		status = -ESHUTDOWN;
 		goto done;
 	}
@@ -898,9 +1031,9 @@
 		buf1 |= maxp;
 		maxp *= multi;
 
-		stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum);
-		stream->buf1 = cpu_to_le32 (buf1);
-		stream->buf2 = cpu_to_le32 (multi);
+		stream->buf0 = cpu_to_hc32(ehci, (epnum << 8) | dev->devnum);
+		stream->buf1 = cpu_to_hc32(ehci, buf1);
+		stream->buf2 = cpu_to_hc32(ehci, multi);
 
 		/* usbfs wants to report the average usecs per frame tied up
 		 * when transfers on this endpoint are scheduled ...
@@ -943,7 +1076,7 @@
 		bandwidth /= 1 << (interval + 2);
 
 		/* stream->splits gets created from raw_mask later */
-		stream->address = cpu_to_le32 (addr);
+		stream->address = cpu_to_hc32(ehci, addr);
 	}
 	stream->bandwidth = bandwidth;
 
@@ -1077,7 +1210,8 @@
 }
 
 static inline void
-itd_sched_init (
+itd_sched_init(
+	struct ehci_hcd		*ehci,
 	struct ehci_iso_sched	*iso_sched,
 	struct ehci_iso_stream	*stream,
 	struct urb		*urb
@@ -1107,7 +1241,7 @@
 				&& !(urb->transfer_flags & URB_NO_INTERRUPT))
 			trans |= EHCI_ITD_IOC;
 		trans |= length << 16;
-		uframe->transaction = cpu_to_le32 (trans);
+		uframe->transaction = cpu_to_hc32(ehci, trans);
 
 		/* might need to cross a buffer page within a uframe */
 		uframe->bufp = (buf & ~(u64)0x0fff);
@@ -1149,7 +1283,7 @@
 	if (unlikely (sched == NULL))
 		return -ENOMEM;
 
-	itd_sched_init (sched, stream, urb);
+	itd_sched_init(ehci, sched, stream, urb);
 
 	if (urb->interval < 8)
 		num_itds = 1 + (sched->span + 7) / 8;
@@ -1167,7 +1301,7 @@
 		/* prefer previously-allocated itds */
 		if (likely (!list_empty(&stream->free_list))) {
 			itd = list_entry (stream->free_list.prev,
-					 struct ehci_itd, itd_list);
+					struct ehci_itd, itd_list);
 			list_del (&itd->itd_list);
 			itd_dma = itd->itd_dma;
 		} else
@@ -1294,7 +1428,7 @@
 		uframe += period_uframes;
 	} while (uframe < mod);
 
-	stream->splits = cpu_to_le32(stream->raw_mask << (uframe & 7));
+	stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7));
 	return 1;
 }
 
@@ -1415,12 +1549,13 @@
 /*-------------------------------------------------------------------------*/
 
 static inline void
-itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd)
+itd_init(struct ehci_hcd *ehci, struct ehci_iso_stream *stream,
+		struct ehci_itd *itd)
 {
 	int i;
 
 	/* it's been recently zeroed */
-	itd->hw_next = EHCI_LIST_END;
+	itd->hw_next = EHCI_LIST_END(ehci);
 	itd->hw_bufp [0] = stream->buf0;
 	itd->hw_bufp [1] = stream->buf1;
 	itd->hw_bufp [2] = stream->buf2;
@@ -1432,7 +1567,8 @@
 }
 
 static inline void
-itd_patch (
+itd_patch(
+	struct ehci_hcd		*ehci,
 	struct ehci_itd		*itd,
 	struct ehci_iso_sched	*iso_sched,
 	unsigned		index,
@@ -1447,17 +1583,18 @@
 	uframe &= 0x07;
 	itd->index [uframe] = index;
 
-	itd->hw_transaction [uframe] = uf->transaction;
-	itd->hw_transaction [uframe] |= cpu_to_le32 (pg << 12);
-	itd->hw_bufp [pg] |= cpu_to_le32 (uf->bufp & ~(u32)0);
-	itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32));
+	itd->hw_transaction[uframe] = uf->transaction;
+	itd->hw_transaction[uframe] |= cpu_to_hc32(ehci, pg << 12);
+	itd->hw_bufp[pg] |= cpu_to_hc32(ehci, uf->bufp & ~(u32)0);
+	itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(uf->bufp >> 32));
 
 	/* iso_frame_desc[].offset must be strictly increasing */
 	if (unlikely (uf->cross)) {
 		u64	bufp = uf->bufp + 4096;
+
 		itd->pg = ++pg;
-		itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0);
-		itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(bufp >> 32));
+		itd->hw_bufp[pg] |= cpu_to_hc32(ehci, bufp & ~(u32)0);
+		itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(bufp >> 32));
 	}
 }
 
@@ -1470,7 +1607,7 @@
 	ehci->pshadow [frame].itd = itd;
 	itd->frame = frame;
 	wmb ();
-	ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
+	ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
 }
 
 /* fit urb's itds into the selected schedule slot; activate as needed */
@@ -1515,14 +1652,14 @@
 			list_move_tail (&itd->itd_list, &stream->td_list);
 			itd->stream = iso_stream_get (stream);
 			itd->urb = usb_get_urb (urb);
-			itd_init (stream, itd);
+			itd_init (ehci, stream, itd);
 		}
 
 		uframe = next_uframe & 0x07;
 		frame = next_uframe >> 3;
 
 		itd->usecs [uframe] = stream->usecs;
-		itd_patch (itd, iso_sched, packet, uframe);
+		itd_patch(ehci, itd, iso_sched, packet, uframe);
 
 		next_uframe += stream->interval;
 		stream->depth += stream->interval;
@@ -1570,7 +1707,7 @@
 		urb_index = itd->index[uframe];
 		desc = &urb->iso_frame_desc [urb_index];
 
-		t = le32_to_cpup (&itd->hw_transaction [uframe]);
+		t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
 		itd->hw_transaction [uframe] = 0;
 		stream->depth -= stream->interval;
 
@@ -1700,7 +1837,8 @@
  */
 
 static inline void
-sitd_sched_init (
+sitd_sched_init(
+	struct ehci_hcd		*ehci,
 	struct ehci_iso_sched	*iso_sched,
 	struct ehci_iso_stream	*stream,
 	struct urb		*urb
@@ -1729,7 +1867,7 @@
 				&& !(urb->transfer_flags & URB_NO_INTERRUPT))
 			trans |= SITD_IOC;
 		trans |= length << 16;
-		packet->transaction = cpu_to_le32 (trans);
+		packet->transaction = cpu_to_hc32(ehci, trans);
 
 		/* might need to cross a buffer page within a td */
 		packet->bufp = buf;
@@ -1765,7 +1903,7 @@
 	if (iso_sched == NULL)
 		return -ENOMEM;
 
-	sitd_sched_init (iso_sched, stream, urb);
+	sitd_sched_init(ehci, iso_sched, stream, urb);
 
 	/* allocate/init sITDs */
 	spin_lock_irqsave (&ehci->lock, flags);
@@ -1817,7 +1955,8 @@
 /*-------------------------------------------------------------------------*/
 
 static inline void
-sitd_patch (
+sitd_patch(
+	struct ehci_hcd		*ehci,
 	struct ehci_iso_stream	*stream,
 	struct ehci_sitd	*sitd,
 	struct ehci_iso_sched	*iso_sched,
@@ -1827,20 +1966,20 @@
 	struct ehci_iso_packet	*uf = &iso_sched->packet [index];
 	u64			bufp = uf->bufp;
 
-	sitd->hw_next = EHCI_LIST_END;
+	sitd->hw_next = EHCI_LIST_END(ehci);
 	sitd->hw_fullspeed_ep = stream->address;
 	sitd->hw_uframe = stream->splits;
 	sitd->hw_results = uf->transaction;
-	sitd->hw_backpointer = EHCI_LIST_END;
+	sitd->hw_backpointer = EHCI_LIST_END(ehci);
 
 	bufp = uf->bufp;
-	sitd->hw_buf [0] = cpu_to_le32 (bufp);
-	sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
+	sitd->hw_buf[0] = cpu_to_hc32(ehci, bufp);
+	sitd->hw_buf_hi[0] = cpu_to_hc32(ehci, bufp >> 32);
 
-	sitd->hw_buf [1] = cpu_to_le32 (uf->buf1);
+	sitd->hw_buf[1] = cpu_to_hc32(ehci, uf->buf1);
 	if (uf->cross)
 		bufp += 4096;
-	sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32);
+	sitd->hw_buf_hi[1] = cpu_to_hc32(ehci, bufp >> 32);
 	sitd->index = index;
 }
 
@@ -1853,7 +1992,7 @@
 	ehci->pshadow [frame].sitd = sitd;
 	sitd->frame = frame;
 	wmb ();
-	ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
+	ehci->periodic[frame] = cpu_to_hc32(ehci, sitd->sitd_dma | Q_TYPE_SITD);
 }
 
 /* fit urb's sitds into the selected schedule slot; activate as needed */
@@ -1881,7 +2020,7 @@
 			urb->dev->devpath, stream->bEndpointAddress & 0x0f,
 			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
 			(next_uframe >> 3) % ehci->periodic_size,
-			stream->interval, le32_to_cpu (stream->splits));
+			stream->interval, hc32_to_cpu(ehci, stream->splits));
 		stream->start = jiffies;
 	}
 	ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -1902,7 +2041,7 @@
 		sitd->stream = iso_stream_get (stream);
 		sitd->urb = usb_get_urb (urb);
 
-		sitd_patch (stream, sitd, sched, packet);
+		sitd_patch(ehci, stream, sitd, sched, packet);
 		sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
 				sitd);
 
@@ -1940,7 +2079,7 @@
 
 	urb_index = sitd->index;
 	desc = &urb->iso_frame_desc [urb_index];
-	t = le32_to_cpup (&sitd->hw_results);
+	t = hc32_to_cpup(ehci, &sitd->hw_results);
 
 	/* report transfer status */
 	if (t & SITD_ERRS) {
@@ -2095,7 +2234,7 @@
 
 	for (;;) {
 		union ehci_shadow	q, *q_p;
-		__le32			type, *hw_p;
+		__hc32			type, *hw_p;
 		unsigned		uframes;
 
 		/* don't scan past the live uframe */
@@ -2113,7 +2252,7 @@
 		q_p = &ehci->pshadow [frame];
 		hw_p = &ehci->periodic [frame];
 		q.ptr = q_p->ptr;
-		type = Q_NEXT_TYPE (*hw_p);
+		type = Q_NEXT_TYPE(ehci, *hw_p);
 		modified = 0;
 
 		while (q.ptr != NULL) {
@@ -2122,11 +2261,11 @@
 			int			live;
 
 			live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
-			switch (type) {
+			switch (hc32_to_cpu(ehci, type)) {
 			case Q_TYPE_QH:
 				/* handle any completions */
 				temp.qh = qh_get (q.qh);
-				type = Q_NEXT_TYPE (q.qh->hw_next);
+				type = Q_NEXT_TYPE(ehci, q.qh->hw_next);
 				q = q.qh->qh_next;
 				modified = qh_completions (ehci, temp.qh);
 				if (unlikely (list_empty (&temp.qh->qtd_list)))
@@ -2137,10 +2276,10 @@
 				/* for "save place" FSTNs, look at QH entries
 				 * in the previous frame for completions.
 				 */
-				if (q.fstn->hw_prev != EHCI_LIST_END) {
+				if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) {
 					dbg ("ignoring completions from FSTNs");
 				}
-				type = Q_NEXT_TYPE (q.fstn->hw_next);
+				type = Q_NEXT_TYPE(ehci, q.fstn->hw_next);
 				q = q.fstn->fstn_next;
 				break;
 			case Q_TYPE_ITD:
@@ -2148,11 +2287,12 @@
 				rmb ();
 				for (uf = live ? uframes : 8; uf < 8; uf++) {
 					if (0 == (q.itd->hw_transaction [uf]
-							& ITD_ACTIVE))
+							& ITD_ACTIVE(ehci)))
 						continue;
 					q_p = &q.itd->itd_next;
 					hw_p = &q.itd->hw_next;
-					type = Q_NEXT_TYPE (q.itd->hw_next);
+					type = Q_NEXT_TYPE(ehci,
+							q.itd->hw_next);
 					q = *q_p;
 					break;
 				}
@@ -2164,23 +2304,24 @@
 				 */
 				*q_p = q.itd->itd_next;
 				*hw_p = q.itd->hw_next;
-				type = Q_NEXT_TYPE (q.itd->hw_next);
+				type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
 				wmb();
 				modified = itd_complete (ehci, q.itd);
 				q = *q_p;
 				break;
 			case Q_TYPE_SITD:
-				if ((q.sitd->hw_results & SITD_ACTIVE)
+				if ((q.sitd->hw_results & SITD_ACTIVE(ehci))
 						&& live) {
 					q_p = &q.sitd->sitd_next;
 					hw_p = &q.sitd->hw_next;
-					type = Q_NEXT_TYPE (q.sitd->hw_next);
+					type = Q_NEXT_TYPE(ehci,
+							q.sitd->hw_next);
 					q = *q_p;
 					break;
 				}
 				*q_p = q.sitd->sitd_next;
 				*hw_p = q.sitd->hw_next;
-				type = Q_NEXT_TYPE (q.sitd->hw_next);
+				type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
 				wmb();
 				modified = sitd_complete (ehci, q.sitd);
 				q = *q_p;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 46fa57a..2c68a04 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -21,6 +21,22 @@
 
 /* definitions used for the EHCI driver */
 
+/*
+ * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
+ * __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on
+ * the host controller implementation.
+ *
+ * To facilitate the strongest possible byte-order checking from "sparse"
+ * and so on, we use __leXX unless that's not practical.
+ */
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+typedef __u32 __bitwise __hc32;
+typedef __u16 __bitwise __hc16;
+#else
+#define __hc32	__le32
+#define __hc16	__le16
+#endif
+
 /* statistics can be kept for for tuning/monitoring */
 struct ehci_stats {
 	/* irq usage */
@@ -55,6 +71,12 @@
 	__u32			hcs_params;	/* cached register copy */
 	spinlock_t		lock;
 
+#ifdef CONFIG_CPU_FREQ
+	struct notifier_block	cpufreq_transition;
+	int			cpufreq_changing;
+	struct list_head	split_intr_qhs;
+#endif
+
 	/* async schedule support */
 	struct ehci_qh		*async;
 	struct ehci_qh		*reclaim;
@@ -64,7 +86,7 @@
 	/* periodic schedule support */
 #define	DEFAULT_I_TDPS		1024		/* some HCs can do less */
 	unsigned		periodic_size;
-	__le32			*periodic;	/* hw periodic table */
+	__hc32			*periodic;	/* hw periodic table */
 	dma_addr_t		periodic_dma;
 	unsigned		i_thresh;	/* uframes HC might cache */
 
@@ -74,11 +96,14 @@
 
 	/* per root hub port */
 	unsigned long		reset_done [EHCI_MAX_ROOT_PORTS];
+
 	/* bit vectors (one bit per port) */
 	unsigned long		bus_suspended;		/* which ports were
 			already suspended at the start of a bus suspend */
 	unsigned long		companion_ports;	/* which ports are
 			dedicated to the companion controller */
+	unsigned long		owned_ports;		/* which ports are
+			owned by the companion during a bus suspend */
 
 	/* per-HC memory pools (could be per-bus, but ...) */
 	struct dma_pool		*qh_pool;	/* qh per active urb */
@@ -97,6 +122,7 @@
 	unsigned		no_selective_suspend:1;
 	unsigned		has_fsl_port_bug:1; /* FreeScale */
 	unsigned		big_endian_mmio:1;
+	unsigned		big_endian_desc:1;
 
 	u8			sbrn;		/* packed release number */
 
@@ -276,6 +302,12 @@
 #define PORT_RWC_BITS   (PORT_CSC | PORT_PEC | PORT_OCC)
 } __attribute__ ((packed));
 
+#define USBMODE		0x68		/* USB Device mode */
+#define USBMODE_SDIS	(1<<3)		/* Stream disable */
+#define USBMODE_BE	(1<<2)		/* BE/LE endianness select */
+#define USBMODE_CM_HC	(3<<0)		/* host controller mode */
+#define USBMODE_CM_IDLE	(0<<0)		/* idle state */
+
 /* Appendix C, Debug port ... intended for use with special "debug devices"
  * that can help if there's no serial console.  (nonstandard enumeration.)
  */
@@ -303,7 +335,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-#define	QTD_NEXT(dma)	cpu_to_le32((u32)dma)
+#define	QTD_NEXT(ehci, dma)	cpu_to_hc32(ehci, (u32)dma)
 
 /*
  * EHCI Specification 0.95 Section 3.5
@@ -315,9 +347,9 @@
  */
 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 */
+	__hc32			hw_next;	/* see EHCI 3.5.1 */
+	__hc32			hw_alt_next;    /* see EHCI 3.5.2 */
+	__hc32			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 */
@@ -331,8 +363,13 @@
 #define	QTD_STS_MMF	(1 << 2)	/* incomplete split transaction */
 #define	QTD_STS_STS	(1 << 1)	/* split transaction state */
 #define	QTD_STS_PING	(1 << 0)	/* issue PING? */
-	__le32			hw_buf [5];        /* see EHCI 3.5.4 */
-	__le32			hw_buf_hi [5];        /* Appendix B */
+
+#define ACTIVE_BIT(ehci)	cpu_to_hc32(ehci, QTD_STS_ACTIVE)
+#define HALT_BIT(ehci)		cpu_to_hc32(ehci, QTD_STS_HALT)
+#define STATUS_BIT(ehci)	cpu_to_hc32(ehci, QTD_STS_STS)
+
+	__hc32			hw_buf [5];        /* see EHCI 3.5.4 */
+	__hc32			hw_buf_hi [5];        /* Appendix B */
 
 	/* the rest is HCD-private */
 	dma_addr_t		qtd_dma;		/* qtd address */
@@ -342,26 +379,33 @@
 } __attribute__ ((aligned (32)));
 
 /* mask NakCnt+T in qh->hw_alt_next */
-#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
+#define QTD_MASK(ehci)	cpu_to_hc32 (ehci, ~0x1f)
 
 #define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
 
 /*-------------------------------------------------------------------------*/
 
 /* type tag from {qh,itd,sitd,fstn}->hw_next */
-#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
+#define Q_NEXT_TYPE(ehci,dma)	((dma) & cpu_to_hc32(ehci, 3 << 1))
 
+/*
+ * Now the following defines are not converted using the
+ * __constant_cpu_to_le32() macro anymore, since we have to support
+ * "dynamic" switching between be and le support, so that the driver
+ * can be used on one system with SoC EHCI controller using big-endian
+ * descriptors as well as a normal little-endian PCI EHCI controller.
+ */
 /* 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_ITD	(0 << 1)
+#define Q_TYPE_QH	(1 << 1)
+#define Q_TYPE_SITD	(2 << 1)
+#define Q_TYPE_FSTN	(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)
+#define QH_NEXT(ehci,dma)	(cpu_to_hc32(ehci, (((u32)dma)&~0x01f)|Q_TYPE_QH))
 
 /* for periodic/async schedules and qtd lists, mark end of list */
-#define	EHCI_LIST_END	__constant_cpu_to_le32(1) /* "null pointer" to hw */
+#define EHCI_LIST_END(ehci)	cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
 
 /*
  * Entries in periodic shadow table are pointers to one of four kinds
@@ -376,7 +420,7 @@
 	struct ehci_itd		*itd;		/* Q_TYPE_ITD */
 	struct ehci_sitd	*sitd;		/* Q_TYPE_SITD */
 	struct ehci_fstn	*fstn;		/* Q_TYPE_FSTN */
-	__le32			*hw_next;	/* (all types) */
+	__hc32			*hw_next;	/* (all types) */
 	void			*ptr;
 };
 
@@ -392,23 +436,27 @@
 
 struct ehci_qh {
 	/* first part defined by EHCI spec */
-	__le32			hw_next;	 /* see EHCI 3.6.1 */
-	__le32			hw_info1;        /* see EHCI 3.6.2 */
+	__hc32			hw_next;	/* see EHCI 3.6.1 */
+	__hc32			hw_info1;       /* see EHCI 3.6.2 */
 #define	QH_HEAD		0x00008000
-	__le32			hw_info2;        /* see EHCI 3.6.2 */
+#define	QH_INACTIVATE	0x00000080
+
+#define INACTIVATE_BIT(ehci)	cpu_to_hc32(ehci, QH_INACTIVATE)
+
+	__hc32			hw_info2;        /* see EHCI 3.6.2 */
 #define	QH_SMASK	0x000000ff
 #define	QH_CMASK	0x0000ff00
 #define	QH_HUBADDR	0x007f0000
 #define	QH_HUBPORT	0x3f800000
 #define	QH_MULT		0xc0000000
-	__le32			hw_current;	 /* qtd list - see EHCI 3.6.4 */
+	__hc32			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;
-	__le32			hw_token;
-	__le32			hw_buf [5];
-	__le32			hw_buf_hi [5];
+	__hc32			hw_qtd_next;
+	__hc32			hw_alt_next;
+	__hc32			hw_token;
+	__hc32			hw_buf [5];
+	__hc32			hw_buf_hi [5];
 
 	/* the rest is HCD-private */
 	dma_addr_t		qh_dma;		/* address of qh */
@@ -418,7 +466,14 @@
 	struct ehci_qh		*reclaim;	/* next to reclaim */
 
 	struct ehci_hcd		*ehci;
-	struct kref		kref;
+
+	/*
+	 * Do NOT use atomic operations for QH refcounting. On some CPUs
+	 * (PPC7448 for example), atomic operations cannot be performed on
+	 * memory that is cache-inhibited (i.e. being used for DMA).
+	 * Spinlocks are used to protect all QH fields.
+	 */
+	u32			refcount;
 	unsigned		stamp;
 
 	u8			qh_state;
@@ -437,6 +492,10 @@
 	unsigned short		start;		/* where polling starts */
 #define NO_FRAME ((unsigned short)~0)			/* pick new start */
 	struct usb_device	*dev;		/* access to TT */
+#ifdef CONFIG_CPU_FREQ
+	struct list_head	split_intr_qhs; /* list of split qhs */
+	__le32			was_active;	/* active bit before "i" set */
+#endif
 } __attribute__ ((aligned (32)));
 
 /*-------------------------------------------------------------------------*/
@@ -445,7 +504,7 @@
 struct ehci_iso_packet {
 	/* These will be copied to iTD when scheduling */
 	u64			bufp;		/* itd->hw_bufp{,_hi}[pg] |= */
-	__le32			transaction;	/* itd->hw_transaction[i] |= */
+	__hc32			transaction;	/* itd->hw_transaction[i] |= */
 	u8			cross;		/* buf crosses pages */
 	/* for full speed OUT splits */
 	u32			buf1;
@@ -467,8 +526,8 @@
  */
 struct ehci_iso_stream {
 	/* first two fields match QH, but info1 == 0 */
-	__le32			hw_next;
-	__le32			hw_info1;
+	__hc32			hw_next;
+	__hc32			hw_info1;
 
 	u32			refcount;
 	u8			bEndpointAddress;
@@ -483,7 +542,7 @@
 	unsigned long		start;		/* jiffies */
 	unsigned long		rescheduled;
 	int			next_uframe;
-	__le32			splits;
+	__hc32			splits;
 
 	/* the rest is derived from the endpoint descriptor,
 	 * trusting urb->interval == f(epdesc->bInterval) and
@@ -497,12 +556,12 @@
 	unsigned		bandwidth;
 
 	/* This is used to initialize iTD's hw_bufp fields */
-	__le32			buf0;
-	__le32			buf1;
-	__le32			buf2;
+	__hc32			buf0;
+	__hc32			buf1;
+	__hc32			buf2;
 
 	/* this is used to initialize sITD's tt info */
-	__le32			address;
+	__hc32			address;
 };
 
 /*-------------------------------------------------------------------------*/
@@ -515,8 +574,8 @@
  */
 struct ehci_itd {
 	/* first part defined by EHCI spec */
-	__le32			hw_next;           /* see EHCI 3.3.1 */
-	__le32			hw_transaction [8]; /* see EHCI 3.3.2 */
+	__hc32			hw_next;           /* see EHCI 3.3.1 */
+	__hc32			hw_transaction [8]; /* see EHCI 3.3.2 */
 #define EHCI_ISOC_ACTIVE        (1<<31)        /* activate transfer this slot */
 #define EHCI_ISOC_BUF_ERR       (1<<30)        /* Data buffer error */
 #define EHCI_ISOC_BABBLE        (1<<29)        /* babble detected */
@@ -524,10 +583,10 @@
 #define	EHCI_ITD_LENGTH(tok)	(((tok)>>16) & 0x0fff)
 #define	EHCI_ITD_IOC		(1 << 15)	/* interrupt on complete */
 
-#define ITD_ACTIVE	__constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
+#define ITD_ACTIVE(ehci)	cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
 
-	__le32			hw_bufp [7];	/* see EHCI 3.3.3 */
-	__le32			hw_bufp_hi [7];	/* Appendix B */
+	__hc32			hw_bufp [7];	/* see EHCI 3.3.3 */
+	__hc32			hw_bufp_hi [7];	/* Appendix B */
 
 	/* the rest is HCD-private */
 	dma_addr_t		itd_dma;	/* for this itd */
@@ -554,11 +613,11 @@
  */
 struct ehci_sitd {
 	/* first part defined by EHCI spec */
-	__le32			hw_next;
+	__hc32			hw_next;
 /* uses bit field macros above - see EHCI 0.95 Table 3-8 */
-	__le32			hw_fullspeed_ep;	/* EHCI table 3-9 */
-	__le32			hw_uframe;		/* EHCI table 3-10 */
-	__le32			hw_results;		/* EHCI table 3-11 */
+	__hc32			hw_fullspeed_ep;	/* EHCI table 3-9 */
+	__hc32			hw_uframe;		/* EHCI table 3-10 */
+	__hc32			hw_results;		/* EHCI table 3-11 */
 #define	SITD_IOC	(1 << 31)	/* interrupt on completion */
 #define	SITD_PAGE	(1 << 30)	/* buffer 0/1 */
 #define	SITD_LENGTH(x)	(0x3ff & ((x)>>16))
@@ -570,11 +629,11 @@
 #define	SITD_STS_MMF	(1 << 2)	/* incomplete split transaction */
 #define	SITD_STS_STS	(1 << 1)	/* split transaction state */
 
-#define SITD_ACTIVE	__constant_cpu_to_le32(SITD_STS_ACTIVE)
+#define SITD_ACTIVE(ehci)	cpu_to_hc32(ehci, SITD_STS_ACTIVE)
 
-	__le32			hw_buf [2];		/* EHCI table 3-12 */
-	__le32			hw_backpointer;		/* EHCI table 3-13 */
-	__le32			hw_buf_hi [2];		/* Appendix B */
+	__hc32			hw_buf [2];		/* EHCI table 3-12 */
+	__hc32			hw_backpointer;		/* EHCI table 3-13 */
+	__hc32			hw_buf_hi [2];		/* Appendix B */
 
 	/* the rest is HCD-private */
 	dma_addr_t		sitd_dma;
@@ -599,8 +658,8 @@
  * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
  */
 struct ehci_fstn {
-	__le32			hw_next;	/* any periodic q entry */
-	__le32			hw_prev;	/* qh or EHCI_LIST_END */
+	__hc32			hw_next;	/* any periodic q entry */
+	__hc32			hw_prev;	/* qh or EHCI_LIST_END */
 
 	/* the rest is HCD-private */
 	dma_addr_t		fstn_dma;
@@ -672,8 +731,21 @@
 #define ehci_big_endian_mmio(e)		0
 #endif
 
-static inline unsigned int ehci_readl (const struct ehci_hcd *ehci,
-				       __u32 __iomem * regs)
+/*
+ * Big-endian read/write functions are arch-specific.
+ * Other arches can be added if/when they're needed.
+ *
+ * REVISIT: arch/powerpc now has readl/writel_be, so the
+ * definition below can die once the 4xx support is
+ * finally ported over.
+ */
+#if defined(CONFIG_PPC)
+#define readl_be(addr)		in_be32((__force unsigned *)addr)
+#define writel_be(val, addr)	out_be32((__force unsigned *)addr, val)
+#endif
+
+static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
+		__u32 __iomem * regs)
 {
 #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
 	return ehci_big_endian_mmio(ehci) ?
@@ -684,8 +756,8 @@
 #endif
 }
 
-static inline void ehci_writel (const struct ehci_hcd *ehci,
-				const unsigned int val, __u32 __iomem *regs)
+static inline void ehci_writel(const struct ehci_hcd *ehci,
+		const unsigned int val, __u32 __iomem *regs)
 {
 #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
 	ehci_big_endian_mmio(ehci) ?
@@ -698,6 +770,62 @@
 
 /*-------------------------------------------------------------------------*/
 
+/*
+ * The AMCC 440EPx not only implements its EHCI registers in big-endian
+ * format, but also its DMA data structures (descriptors).
+ *
+ * EHCI controllers accessed through PCI work normally (little-endian
+ * everywhere), so we won't bother supporting a BE-only mode for now.
+ */
+#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+#define ehci_big_endian_desc(e)		((e)->big_endian_desc)
+
+/* cpu to ehci */
+static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
+{
+	return ehci_big_endian_desc(ehci)
+		? (__force __hc32)cpu_to_be32(x)
+		: (__force __hc32)cpu_to_le32(x);
+}
+
+/* ehci to cpu */
+static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
+{
+	return ehci_big_endian_desc(ehci)
+		? be32_to_cpu((__force __be32)x)
+		: le32_to_cpu((__force __le32)x);
+}
+
+static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
+{
+	return ehci_big_endian_desc(ehci)
+		? be32_to_cpup((__force __be32 *)x)
+		: le32_to_cpup((__force __le32 *)x);
+}
+
+#else
+
+/* cpu to ehci */
+static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x)
+{
+	return cpu_to_le32(x);
+}
+
+/* ehci to cpu */
+static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x)
+{
+	return le32_to_cpu(x);
+}
+
+static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
+{
+	return le32_to_cpup(x);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 #ifndef DEBUG
 #define STUB_DEBUG_FILES
 #endif	/* DEBUG */
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 273d5dd..6f9e43e 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -23,7 +23,7 @@
 /* debug| print the main components of an URB
  * small: 0) header + data packets 1) just header
  */
-static void __attribute__((unused))
+static void __maybe_unused
 urb_print (struct urb * urb, char * str, int small)
 {
 	unsigned int pipe= urb->pipe;
@@ -338,7 +338,7 @@
 }
 
 /* caller MUST own hcd spinlock if verbose is set! */
-static void __attribute__((unused))
+static void __maybe_unused
 ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
 		const struct ed *ed, int verbose)
 {
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index a66637e..2038125 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -35,15 +35,13 @@
 #include <linux/dma-mapping.h>
 #include <linux/dmapool.h>
 #include <linux/reboot.h>
+#include <linux/workqueue.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/unaligned.h>
 #include <asm/byteorder.h>
-#ifdef CONFIG_PPC_PS3
-#include <asm/firmware.h>
-#endif
 
 #include "../core/hcd.h"
 
@@ -82,6 +80,8 @@
 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_restart (struct ohci_hcd *ohci);
+static void ohci_quirk_nec_worker (struct work_struct *work);
 
 #include "ohci-hub.c"
 #include "ohci-dbg.c"
@@ -510,15 +510,7 @@
 	// flush the writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
 	msleep(temp);
-	temp = roothub_a (ohci);
-	if (!(temp & RH_A_NPS)) {
-		/* power down each port */
-		for (temp = 0; temp < ohci->num_ports; temp++)
-			ohci_writel (ohci, RH_PS_LSDA,
-				&ohci->regs->roothub.portstatus [temp]);
-	}
-	// flush those writes
-	(void) ohci_readl (ohci, &ohci->regs->control);
+
 	memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
 
 	/* 2msec timelimit here means no irqs/preempt */
@@ -659,9 +651,20 @@
 	}
 
 	if (ints & OHCI_INTR_UE) {
-		disable (ohci);
-		ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
 		// e.g. due to PCI Master/Target Abort
+		if (ohci->flags & OHCI_QUIRK_NEC) {
+			/* Workaround for a silicon bug in some NEC chips used
+			 * in Apple's PowerBooks. Adapted from Darwin code.
+			 */
+			ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n");
+
+			ohci_writel (ohci, OHCI_INTR_UE, &regs->intrdisable);
+
+			schedule_work (&ohci->nec_work);
+		} else {
+			disable (ohci);
+			ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
+		}
 
 		ohci_dump (ohci, 1);
 		ohci_usb_reset (ohci);
@@ -763,23 +766,16 @@
 /*-------------------------------------------------------------------------*/
 
 /* must not be called from interrupt context */
-
-#ifdef	CONFIG_PM
-
 static int ohci_restart (struct ohci_hcd *ohci)
 {
 	int temp;
 	int i;
 	struct urb_priv *priv;
 
-	/* mark any devices gone, so they do nothing till khubd disconnects.
-	 * recycle any "live" eds/tds (and urbs) right away.
-	 * later, khubd disconnect processing will recycle the other state,
-	 * (either as disconnect/reconnect, or maybe someday as a reset).
-	 */
 	spin_lock_irq(&ohci->lock);
 	disable (ohci);
-	usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub);
+
+	/* Recycle any "live" eds/tds (and urbs). */
 	if (!list_empty (&ohci->pending))
 		ohci_dbg(ohci, "abort schedule...\n");
 	list_for_each_entry (priv, &ohci->pending, pending) {
@@ -826,20 +822,31 @@
 	if ((temp = ohci_run (ohci)) < 0) {
 		ohci_err (ohci, "can't restart, %d\n", temp);
 		return temp;
-	} else {
-		/* here we "know" root ports should always stay powered,
-		 * and that if we try to turn them back on the root hub
-		 * will respond to CSC processing.
-		 */
-		i = ohci->num_ports;
-		while (i--)
-			ohci_writel (ohci, RH_PS_PSS,
-				&ohci->regs->roothub.portstatus [i]);
-		ohci_dbg (ohci, "restart complete\n");
 	}
+	ohci_dbg(ohci, "restart complete\n");
 	return 0;
 }
-#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* NEC workaround */
+static void ohci_quirk_nec_worker(struct work_struct *work)
+{
+	struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work);
+	int status;
+
+	status = ohci_init(ohci);
+	if (status != 0) {
+		ohci_err(ohci, "Restarting NEC controller failed "
+			 "in ohci_init, %d\n", status);
+		return;
+	}
+
+	status = ohci_restart(ohci);
+	if (status != 0)
+		ohci_err(ohci, "Restarting NEC controller failed "
+			 "in ohci_restart, %d\n", status);
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -917,7 +924,7 @@
 
 #ifdef CONFIG_PPC_PS3
 #include "ohci-ps3.c"
-#define PS3_SYSTEM_BUS_DRIVER	ps3_ohci_sb_driver
+#define PS3_SYSTEM_BUS_DRIVER	ps3_ohci_driver
 #endif
 
 #if	!defined(PCI_DRIVER) &&		\
@@ -940,12 +947,9 @@
 		sizeof (struct ed), sizeof (struct td));
 
 #ifdef PS3_SYSTEM_BUS_DRIVER
-	if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
-		retval = ps3_system_bus_driver_register(
-				&PS3_SYSTEM_BUS_DRIVER);
-		if (retval < 0)
-			goto error_ps3;
-	}
+	retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
+	if (retval < 0)
+		goto error_ps3;
 #endif
 
 #ifdef PLATFORM_DRIVER
@@ -991,8 +995,7 @@
  error_platform:
 #endif
 #ifdef PS3_SYSTEM_BUS_DRIVER
-	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
-		ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+	ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
  error_ps3:
 #endif
 	return retval;
@@ -1014,8 +1017,7 @@
 	platform_driver_unregister(&PLATFORM_DRIVER);
 #endif
 #ifdef PS3_SYSTEM_BUS_DRIVER
-	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
-		ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
+	ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
 #endif
 }
 module_exit(ohci_hcd_mod_exit);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index bb9cc59..48e4b11 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -55,8 +55,6 @@
 static void finish_unlinks (struct ohci_hcd *, u16);
 
 #ifdef	CONFIG_PM
-static int ohci_restart(struct ohci_hcd *ohci);
-
 static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
 __releases(ohci->lock)
 __acquires(ohci->lock)
@@ -191,6 +189,9 @@
 			spin_unlock_irq (&ohci->lock);
 			(void) ohci_init (ohci);
 			status = ohci_restart (ohci);
+
+			usb_root_hub_lost_power(hcd->self.root_hub);
+
 			spin_lock_irq (&ohci->lock);
 		}
 		return status;
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index 2f20d3d..450c7b4 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -28,6 +28,7 @@
 	ohci->next_statechange = jiffies;
 	spin_lock_init (&ohci->lock);
 	INIT_LIST_HEAD (&ohci->pending);
+	INIT_WORK (&ohci->nec_work, ohci_quirk_nec_worker);
 }
 
 /*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index ca62cb5..a5e2eb8 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -111,6 +111,18 @@
 #endif
 }
 
+/* Check for NEC chip and apply quirk for allegedly lost interrupts.
+ */
+static int ohci_quirk_nec(struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
+
+	ohci->flags |= OHCI_QUIRK_NEC;
+	ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n");
+
+	return 0;
+}
+
 /* List of quirks for OHCI */
 static const struct pci_device_id ohci_pci_quirks[] = {
 	{
@@ -134,6 +146,10 @@
 		.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
 	},
 	{
+		PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB),
+		.driver_data = (unsigned long)ohci_quirk_nec,
+	},
+	{
 		/* Toshiba portege 4000 */
 		.vendor		= PCI_VENDOR_ID_AL,
 		.device		= 0x5237,
@@ -202,6 +218,42 @@
 	return ret;
 }
 
+#if	defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \
+		defined(CONFIG_USB_EHCI_HCD_MODULE))
+
+/* Following a power loss, we must prepare to regain control of the ports
+ * we used to own.  This means turning on the port power before ehci-hcd
+ * tries to switch ownership.
+ *
+ * This isn't a 100% perfect solution.  On most systems the OHCI controllers
+ * lie at lower PCI addresses than the EHCI controller, so they will be
+ * discovered (and hence resumed) first.  But there is no guarantee things
+ * will always work this way.  If the EHCI controller is resumed first and
+ * the OHCI ports are unpowered, then the handover will fail.
+ */
+static void prepare_for_handover(struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
+	int		port;
+
+	/* Here we "know" root ports should always stay powered */
+	ohci_dbg(ohci, "powerup ports\n");
+	for (port = 0; port < ohci->num_ports; port++)
+		ohci_writel(ohci, RH_PS_PPS,
+				&ohci->regs->roothub.portstatus[port]);
+
+	/* Flush those writes */
+	ohci_readl(ohci, &ohci->regs->control);
+	msleep(20);
+}
+
+#else
+
+static inline void prepare_for_handover(struct usb_hcd *hcd)
+{ }
+
+#endif	/* CONFIG_USB_PERSIST etc. */
+
 #ifdef	CONFIG_PM
 
 static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
@@ -241,7 +293,10 @@
 static int ohci_pci_resume (struct usb_hcd *hcd)
 {
 	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-	usb_hcd_resume_root_hub(hcd);
+
+	/* FIXME: we should try to detect loss of VBUS power here */
+	prepare_for_handover(hcd);
+
 	return 0;
 }
 
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index d601bbb..ca2a6ab 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -134,7 +134,7 @@
 {
 	struct i2c_client *c;
 
-	c = (struct i2c_client *)kzalloc(sizeof(*c), GFP_KERNEL);
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
 
 	if (!c)
 		return -ENOMEM;
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index d7cf072..01a0cae 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -18,6 +18,7 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <asm/firmware.h>
 #include <asm/ps3.h>
 
 static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
@@ -75,7 +76,7 @@
 #endif
 };
 
-static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev)
+static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
 {
 	int result;
 	struct usb_hcd *hcd;
@@ -87,13 +88,31 @@
 		goto fail_start;
 	}
 
+	result = ps3_open_hv_device(dev);
+
+	if (result) {
+		dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n",
+			__func__, __LINE__, ps3_result(result));
+		result = -EPERM;
+		goto fail_open;
+	}
+
+	result = ps3_dma_region_create(dev->d_region);
+
+	if (result) {
+		dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
+			"(%d)\n", __func__, __LINE__, result);
+		BUG_ON("check region type");
+		goto fail_dma_region;
+	}
+
 	result = ps3_mmio_region_create(dev->m_region);
 
 	if (result) {
 		dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
 			__func__, __LINE__);
 		result = -EPERM;
-		goto fail_mmio;
+		goto fail_mmio_region;
 	}
 
 	dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
@@ -122,6 +141,11 @@
 
 	hcd->rsrc_start = dev->m_region->lpar_addr;
 	hcd->rsrc_len = dev->m_region->len;
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
+		dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
+			__func__, __LINE__);
+
 	hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
 
 	if (!hcd->regs) {
@@ -155,34 +179,73 @@
 fail_add_hcd:
 	iounmap(hcd->regs);
 fail_ioremap:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	usb_put_hcd(hcd);
 fail_create_hcd:
 	ps3_io_irq_destroy(virq);
 fail_irq:
 	ps3_free_mmio_region(dev->m_region);
-fail_mmio:
+fail_mmio_region:
+	ps3_dma_region_free(dev->d_region);
+fail_dma_region:
+	ps3_close_hv_device(dev);
+fail_open:
 fail_start:
 	return result;
 }
 
-static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev)
+static int ps3_ohci_remove (struct ps3_system_bus_device *dev)
 {
+	unsigned int tmp;
 	struct usb_hcd *hcd =
 		(struct usb_hcd *)ps3_system_bus_get_driver_data(dev);
 
-	usb_put_hcd(hcd);
+	BUG_ON(!hcd);
+
+	dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
+	dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
+
+	tmp = hcd->irq;
+
+	usb_remove_hcd(hcd);
+
 	ps3_system_bus_set_driver_data(dev, NULL);
 
+	BUG_ON(!hcd->regs);
+	iounmap(hcd->regs);
+
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+
+	ps3_io_irq_destroy(tmp);
+	ps3_free_mmio_region(dev->m_region);
+
+	ps3_dma_region_free(dev->d_region);
+	ps3_close_hv_device(dev);
+
 	return 0;
 }
 
-MODULE_ALIAS("ps3-ohci");
+static int ps3_ohci_driver_register(struct ps3_system_bus_driver *drv)
+{
+	return firmware_has_feature(FW_FEATURE_PS3_LV1)
+		? ps3_system_bus_driver_register(drv)
+		: 0;
+}
 
-static struct ps3_system_bus_driver ps3_ohci_sb_driver = {
+static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv)
+{
+	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
+		ps3_system_bus_driver_unregister(drv);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI);
+
+static struct ps3_system_bus_driver ps3_ohci_driver = {
+	.core.name = "ps3-ohci-driver",
+	.core.owner = THIS_MODULE,
 	.match_id = PS3_MATCH_ID_OHCI,
-	.core = {
-		.name = "ps3-ohci-driver",
-	},
-	.probe = ps3_ohci_sb_probe,
-	.remove = ps3_ohci_sb_remove,
+	.probe = ps3_ohci_probe,
+	.remove = ps3_ohci_remove,
+	.shutdown = ps3_ohci_remove,
 };
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index c2b5ecf..4ada43c 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -397,8 +397,10 @@
 #define	OHCI_QUIRK_BE_DESC	0x08			/* BE descriptors */
 #define	OHCI_QUIRK_BE_MMIO	0x10			/* BE registers */
 #define	OHCI_QUIRK_ZFMICRO	0x20			/* Compaq ZFMicro chipset*/
+#define	OHCI_QUIRK_NEC		0x40			/* lost interrupts */
 	// there are also chip quirks/bugs in init logic
 
+	struct work_struct	nec_work;	/* Worker for NEC quirk */
 };
 
 /* convert between an hcd pointer and the corresponding ohci_hcd */
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
new file mode 100644
index 0000000..a7a7070
--- /dev/null
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -0,0 +1,2244 @@
+/*
+ * R8A66597 HCD (Host Controller Driver)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Portions Copyright (C) 2004-2005 David Brownell
+ * Portions Copyright (C) 1999 Roman Weissgaerber
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "../core/hcd.h"
+#include "r8a66597.h"
+
+MODULE_DESCRIPTION("R8A66597 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+
+#define DRIVER_VERSION	"29 May 2007"
+
+static const char hcd_name[] = "r8a66597_hcd";
+
+/* module parameters */
+static unsigned short clock = XTAL12;
+module_param(clock, ushort, 0644);
+MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0(default=0)");
+static unsigned short vif = LDRV;
+module_param(vif, ushort, 0644);
+MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)");
+static unsigned short endian = 0;
+module_param(endian, ushort, 0644);
+MODULE_PARM_DESC(endian, "data endian: big=256, little=0(default=0)");
+static unsigned short irq_sense = INTL;
+module_param(irq_sense, ushort, 0644);
+MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0(default=32)");
+
+static void packet_write(struct r8a66597 *r8a66597, u16 pipenum);
+static int r8a66597_get_frame(struct usb_hcd *hcd);
+
+/* this function must be called with interrupt disabled */
+static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+			    unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, INTENB0);
+	r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+	r8a66597_bset(r8a66597, 1 << pipenum, reg);
+	r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+/* this function must be called with interrupt disabled */
+static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum,
+			     unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, INTENB0);
+	r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+	r8a66597_bclr(r8a66597, 1 << pipenum, reg);
+	r8a66597_write(r8a66597, tmp, INTENB0);
+}
+
+static void set_devadd_reg(struct r8a66597 *r8a66597, u8 r8a66597_address,
+			   u16 usbspd, u8 upphub, u8 hubport, int port)
+{
+	u16 val;
+	unsigned long devadd_reg = get_devadd_addr(r8a66597_address);
+
+	val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001);
+	r8a66597_write(r8a66597, val, devadd_reg);
+}
+
+static int enable_controller(struct r8a66597 *r8a66597)
+{
+	u16 tmp;
+	int i = 0;
+
+	do {
+		r8a66597_write(r8a66597, USBE, SYSCFG0);
+		tmp = r8a66597_read(r8a66597, SYSCFG0);
+		if (i++ > 1000) {
+			err("register access fail.");
+			return -ENXIO;
+		}
+	} while ((tmp & USBE) != USBE);
+	r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+	r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0);
+
+	i = 0;
+	r8a66597_bset(r8a66597, XCKE, SYSCFG0);
+	do {
+		msleep(1);
+		tmp = r8a66597_read(r8a66597, SYSCFG0);
+		if (i++ > 500) {
+			err("register access fail.");
+			return -ENXIO;
+		}
+	} while ((tmp & SCKE) != SCKE);
+
+	r8a66597_bset(r8a66597, DCFM | DRPD, SYSCFG0);
+	r8a66597_bset(r8a66597, DRPD, SYSCFG1);
+
+	r8a66597_bset(r8a66597, vif & LDRV, PINCFG);
+	r8a66597_bset(r8a66597, HSE, SYSCFG0);
+	r8a66597_bset(r8a66597, HSE, SYSCFG1);
+	r8a66597_bset(r8a66597, USBE, SYSCFG0);
+
+	r8a66597_bset(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
+	r8a66597_bset(r8a66597, irq_sense & INTL, SOFCFG);
+	r8a66597_bset(r8a66597, BRDY0, BRDYENB);
+	r8a66597_bset(r8a66597, BEMP0, BEMPENB);
+
+	r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA0CFG);
+	r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA1CFG);
+
+	r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL);
+	r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL);
+	r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL);
+
+	r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
+
+	r8a66597_bset(r8a66597, SIGNE | SACKE, INTENB1);
+	r8a66597_bclr(r8a66597, DTCHE, INTENB1);
+	r8a66597_bset(r8a66597, ATTCHE, INTENB1);
+	r8a66597_bclr(r8a66597, DTCHE, INTENB2);
+	r8a66597_bset(r8a66597, ATTCHE, INTENB2);
+
+	return 0;
+}
+
+static void disable_controller(struct r8a66597 *r8a66597)
+{
+	u16 tmp;
+
+	r8a66597_write(r8a66597, 0, INTENB0);
+	r8a66597_write(r8a66597, 0, INTENB1);
+	r8a66597_write(r8a66597, 0, INTENB2);
+	r8a66597_write(r8a66597, 0, INTSTS0);
+	r8a66597_write(r8a66597, 0, INTSTS1);
+	r8a66597_write(r8a66597, 0, INTSTS2);
+
+	r8a66597_port_power(r8a66597, 0, 0);
+	r8a66597_port_power(r8a66597, 1, 0);
+
+	do {
+		tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
+		udelay(640);
+	} while (tmp == EDGESTS);
+
+	r8a66597_bclr(r8a66597, DCFM | DRPD, SYSCFG0);
+	r8a66597_bclr(r8a66597, DRPD, SYSCFG1);
+	r8a66597_bclr(r8a66597, HSE, SYSCFG0);
+	r8a66597_bclr(r8a66597, HSE, SYSCFG1);
+
+	r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+	udelay(1);
+	r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+	r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+	r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+}
+
+static int get_parent_r8a66597_address(struct r8a66597 *r8a66597,
+				       struct usb_device *udev)
+{
+	struct r8a66597_device *dev;
+
+	if (udev->parent && udev->parent->devnum != 1)
+		udev = udev->parent;
+
+	dev = dev_get_drvdata(&udev->dev);
+	if (dev)
+		return dev->address;
+	else
+		return 0;
+}
+
+static int is_child_device(char *devpath)
+{
+	return (devpath[2] ? 1 : 0);
+}
+
+static int is_hub_limit(char *devpath)
+{
+	return ((strlen(devpath) >= 4) ? 1 : 0);
+}
+
+static void get_port_number(char *devpath, u16 *root_port, u16 *hub_port)
+{
+	if (root_port) {
+		*root_port = (devpath[0] & 0x0F) - 1;
+		if (*root_port >= R8A66597_MAX_ROOT_HUB)
+			err("illegal root port number");
+	}
+	if (hub_port)
+		*hub_port = devpath[2] & 0x0F;
+}
+
+static u16 get_r8a66597_usb_speed(enum usb_device_speed speed)
+{
+	u16 usbspd = 0;
+
+	switch (speed) {
+	case USB_SPEED_LOW:
+		usbspd = LSMODE;
+		break;
+	case USB_SPEED_FULL:
+		usbspd = FSMODE;
+		break;
+	case USB_SPEED_HIGH:
+		usbspd = HSMODE;
+		break;
+	default:
+		err("unknown speed");
+		break;
+	}
+
+	return usbspd;
+}
+
+static void set_child_connect_map(struct r8a66597 *r8a66597, int address)
+{
+	int idx;
+
+	idx = address / 32;
+	r8a66597->child_connect_map[idx] |= 1 << (address % 32);
+}
+
+static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
+{
+	int idx;
+
+	idx = address / 32;
+	r8a66597->child_connect_map[idx] &= ~(1 << (address % 32));
+}
+
+static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
+{
+	u16 pipenum = pipe->info.pipenum;
+	unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
+	unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
+	unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
+
+	if (dma_ch > R8A66597_PIPE_NO_DMA)	/* dma fifo not use? */
+		dma_ch = R8A66597_PIPE_NO_DMA;
+
+	pipe->fifoaddr = fifoaddr[dma_ch];
+	pipe->fifosel = fifosel[dma_ch];
+	pipe->fifoctr = fifoctr[dma_ch];
+
+	if (pipenum == 0)
+		pipe->pipectr = DCPCTR;
+	else
+		pipe->pipectr = get_pipectr_addr(pipenum);
+
+	if (check_bulk_or_isoc(pipenum)) {
+		pipe->pipetre = get_pipetre_addr(pipenum);
+		pipe->pipetrn = get_pipetrn_addr(pipenum);
+	} else {
+		pipe->pipetre = 0;
+		pipe->pipetrn = 0;
+	}
+}
+
+static struct r8a66597_device *
+get_urb_to_r8a66597_dev(struct r8a66597 *r8a66597, struct urb *urb)
+{
+	if (usb_pipedevice(urb->pipe) == 0)
+		return &r8a66597->device0;
+
+	return dev_get_drvdata(&urb->dev->dev);
+}
+
+static int make_r8a66597_device(struct r8a66597 *r8a66597,
+				struct urb *urb, u8 addr)
+{
+	struct r8a66597_device *dev;
+	int usb_address = urb->setup_packet[2];	/* urb->pipe is address 0 */
+
+	dev = kzalloc(sizeof(struct r8a66597_device), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+
+	dev_set_drvdata(&urb->dev->dev, dev);
+	dev->udev = urb->dev;
+	dev->address = addr;
+	dev->usb_address = usb_address;
+	dev->state = USB_STATE_ADDRESS;
+	dev->ep_in_toggle = 0;
+	dev->ep_out_toggle = 0;
+	INIT_LIST_HEAD(&dev->device_list);
+	list_add_tail(&dev->device_list, &r8a66597->child_device);
+
+	get_port_number(urb->dev->devpath, &dev->root_port, &dev->hub_port);
+	if (!is_child_device(urb->dev->devpath))
+		r8a66597->root_hub[dev->root_port].dev = dev;
+
+	set_devadd_reg(r8a66597, dev->address,
+		       get_r8a66597_usb_speed(urb->dev->speed),
+		       get_parent_r8a66597_address(r8a66597, urb->dev),
+		       dev->hub_port, dev->root_port);
+
+	return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static u8 alloc_usb_address(struct r8a66597 *r8a66597, struct urb *urb)
+{
+	u8 addr;	/* R8A66597's address */
+	struct r8a66597_device *dev;
+
+	if (is_hub_limit(urb->dev->devpath)) {
+		err("Externel hub limit reached.");
+		return 0;
+	}
+
+	dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+	if (dev && dev->state >= USB_STATE_ADDRESS)
+		return dev->address;
+
+	for (addr = 1; addr <= R8A66597_MAX_DEVICE; addr++) {
+		if (r8a66597->address_map & (1 << addr))
+			continue;
+
+		dbg("alloc_address: r8a66597_addr=%d", addr);
+		r8a66597->address_map |= 1 << addr;
+
+		if (make_r8a66597_device(r8a66597, urb, addr) < 0)
+			return 0;
+
+		return addr;
+	}
+
+	err("cannot communicate with a USB device more than 10.(%x)",
+	    r8a66597->address_map);
+
+	return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void free_usb_address(struct r8a66597 *r8a66597,
+			     struct r8a66597_device *dev)
+{
+	int port;
+
+	if (!dev)
+		return;
+
+	dbg("free_addr: addr=%d", dev->address);
+
+	dev->state = USB_STATE_DEFAULT;
+	r8a66597->address_map &= ~(1 << dev->address);
+	dev->address = 0;
+	dev_set_drvdata(&dev->udev->dev, NULL);
+	list_del(&dev->device_list);
+	kfree(dev);
+
+	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
+		if (r8a66597->root_hub[port].dev == dev) {
+			r8a66597->root_hub[port].dev = NULL;
+			break;
+		}
+	}
+}
+
+static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,
+			      u16 mask, u16 loop)
+{
+	u16 tmp;
+	int i = 0;
+
+	do {
+		tmp = r8a66597_read(r8a66597, reg);
+		if (i++ > 1000000) {
+			err("register%lx, loop %x is timeout", reg, loop);
+			break;
+		}
+		ndelay(1);
+	} while ((tmp & mask) != loop);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_start(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID;
+	if ((pipe->info.pipenum != 0) & ((tmp & PID_STALL) != 0)) /* stall? */
+		r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr);
+	r8a66597_mdfy(r8a66597, PID_BUF, PID, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_stop(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe)
+{
+	u16 tmp;
+
+	tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID;
+	if ((tmp & PID_STALL11) != PID_STALL11)	/* force stall? */
+		r8a66597_mdfy(r8a66597, PID_STALL, PID, pipe->pipectr);
+	r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr);
+	r8a66597_reg_wait(r8a66597, pipe->pipectr, PBUSY, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void clear_all_buffer(struct r8a66597 *r8a66597,
+			     struct r8a66597_pipe *pipe)
+{
+	u16 tmp;
+
+	if (!pipe || pipe->info.pipenum == 0)
+		return;
+
+	pipe_stop(r8a66597, pipe);
+	r8a66597_bset(r8a66597, ACLRM, pipe->pipectr);
+	tmp = r8a66597_read(r8a66597, pipe->pipectr);
+	tmp = r8a66597_read(r8a66597, pipe->pipectr);
+	tmp = r8a66597_read(r8a66597, pipe->pipectr);
+	r8a66597_bclr(r8a66597, ACLRM, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_pipe_toggle(struct r8a66597 *r8a66597,
+				 struct r8a66597_pipe *pipe, int toggle)
+{
+	if (toggle)
+		r8a66597_bset(r8a66597, SQSET, pipe->pipectr);
+	else
+		r8a66597_bset(r8a66597, SQCLR, pipe->pipectr);
+}
+
+/* this function must be called with interrupt disabled */
+static inline void cfifo_change(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL);
+	r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
+}
+
+/* this function must be called with interrupt disabled */
+static inline void fifo_change_from_pipe(struct r8a66597 *r8a66597,
+					 struct r8a66597_pipe *pipe)
+{
+	cfifo_change(r8a66597, 0);
+	r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D0FIFOSEL);
+	r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D1FIFOSEL);
+
+	r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum, MBW | CURPIPE,
+		      pipe->fifosel);
+	r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE, pipe->info.pipenum);
+}
+
+static u16 r8a66597_get_pipenum(struct urb *urb, struct usb_host_endpoint *hep)
+{
+	struct r8a66597_pipe *pipe = hep->hcpriv;
+
+	if (usb_pipeendpoint(urb->pipe) == 0)
+		return 0;
+	else
+		return pipe->info.pipenum;
+}
+
+static u16 get_urb_to_r8a66597_addr(struct r8a66597 *r8a66597, struct urb *urb)
+{
+	struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+
+	return (usb_pipedevice(urb->pipe) == 0) ? 0 : dev->address;
+}
+
+static unsigned short *get_toggle_pointer(struct r8a66597_device *dev,
+					  int urb_pipe)
+{
+	if (!dev)
+		return NULL;
+
+	return usb_pipein(urb_pipe) ? &dev->ep_in_toggle : &dev->ep_out_toggle;
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_set(struct r8a66597 *r8a66597,
+			    struct r8a66597_pipe *pipe,
+			    struct urb *urb, int set)
+{
+	struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+	unsigned char endpoint = usb_pipeendpoint(urb->pipe);
+	unsigned short *toggle = get_toggle_pointer(dev, urb->pipe);
+
+	if (!toggle)
+		return;
+
+	if (set)
+		*toggle |= 1 << endpoint;
+	else
+		*toggle &= ~(1 << endpoint);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_save(struct r8a66597 *r8a66597,
+			     struct r8a66597_pipe *pipe,
+			     struct urb *urb)
+{
+	if (r8a66597_read(r8a66597, pipe->pipectr) & SQMON)
+		pipe_toggle_set(r8a66597, pipe, urb, 1);
+	else
+		pipe_toggle_set(r8a66597, pipe, urb, 0);
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_toggle_restore(struct r8a66597 *r8a66597,
+				struct r8a66597_pipe *pipe,
+				struct urb *urb)
+{
+	struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+	unsigned char endpoint = usb_pipeendpoint(urb->pipe);
+	unsigned short *toggle = get_toggle_pointer(dev, urb->pipe);
+
+	if (!toggle)
+		return;
+
+	r8a66597_pipe_toggle(r8a66597, pipe, *toggle & (1 << endpoint));
+}
+
+/* this function must be called with interrupt disabled */
+static void pipe_buffer_setting(struct r8a66597 *r8a66597,
+				struct r8a66597_pipe_info *info)
+{
+	u16 val = 0;
+
+	if (info->pipenum == 0)
+		return;
+
+	r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(info->pipenum));
+	r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(info->pipenum));
+	r8a66597_write(r8a66597, info->pipenum, PIPESEL);
+	if (!info->dir_in)
+		val |= R8A66597_DIR;
+	if (info->type == R8A66597_BULK && info->dir_in)
+		val |= R8A66597_DBLB | R8A66597_SHTNAK;
+	val |= info->type | info->epnum;
+	r8a66597_write(r8a66597, val, PIPECFG);
+
+	r8a66597_write(r8a66597, (info->buf_bsize << 10) | (info->bufnum),
+		       PIPEBUF);
+	r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket,
+		       PIPEMAXP);
+	if (info->interval)
+		info->interval--;
+	r8a66597_write(r8a66597, info->interval, PIPEPERI);
+}
+
+
+
+/* this function must be called with interrupt disabled */
+static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+	struct r8a66597_pipe_info *info;
+	struct urb *urb = td->urb;
+
+	if (td->pipenum > 0) {
+		info = &td->pipe->info;
+		cfifo_change(r8a66597, 0);
+		pipe_buffer_setting(r8a66597, info);
+
+		if (!usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+				   usb_pipeout(urb->pipe)) &&
+		    !usb_pipecontrol(urb->pipe)) {
+			r8a66597_pipe_toggle(r8a66597, td->pipe, 0);
+			pipe_toggle_set(r8a66597, td->pipe, urb, 0);
+			clear_all_buffer(r8a66597, td->pipe);
+			usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+				      usb_pipeout(urb->pipe), 1);
+		}
+		pipe_toggle_restore(r8a66597, td->pipe, urb);
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static u16 get_empty_pipenum(struct r8a66597 *r8a66597,
+			     struct usb_endpoint_descriptor *ep)
+{
+	u16 array[R8A66597_MAX_NUM_PIPE], i = 0, min;
+
+	memset(array, 0, sizeof(array));
+        switch(ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+        case USB_ENDPOINT_XFER_BULK:
+		if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+			array[i++] = 4;
+		else {
+			array[i++] = 3;
+			array[i++] = 5;
+		}
+                break;
+        case USB_ENDPOINT_XFER_INT:
+		if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+			array[i++] = 6;
+			array[i++] = 7;
+			array[i++] = 8;
+		} else
+			array[i++] = 9;
+                break;
+        case USB_ENDPOINT_XFER_ISOC:
+		if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+			array[i++] = 2;
+		else
+			array[i++] = 1;
+                break;
+        default:
+                err("Illegal type");
+                return 0;
+        }
+
+	i = 1;
+	min = array[0];
+	while (array[i] != 0) {
+		if (r8a66597->pipe_cnt[min] > r8a66597->pipe_cnt[array[i]])
+			min = array[i];
+		i++;
+	}
+
+	return min;
+}
+
+static u16 get_r8a66597_type(__u8 type)
+{
+	u16 r8a66597_type;
+
+	switch(type) {
+	case USB_ENDPOINT_XFER_BULK:
+		r8a66597_type = R8A66597_BULK;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		r8a66597_type = R8A66597_INT;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		r8a66597_type = R8A66597_ISO;
+		break;
+	default:
+		err("Illegal type");
+		r8a66597_type = 0x0000;
+		break;
+	}
+
+	return r8a66597_type;
+}
+
+static u16 get_bufnum(u16 pipenum)
+{
+	u16 bufnum = 0;
+
+	if (pipenum == 0)
+		bufnum = 0;
+	else if (check_bulk_or_isoc(pipenum))
+		bufnum = 8 + (pipenum - 1) * R8A66597_BUF_BSIZE*2;
+	else if (check_interrupt(pipenum))
+		bufnum = 4 + (pipenum - 6);
+	else
+		err("Illegal pipenum (%d)", pipenum);
+
+	return bufnum;
+}
+
+static u16 get_buf_bsize(u16 pipenum)
+{
+	u16 buf_bsize = 0;
+
+	if (pipenum == 0)
+		buf_bsize = 3;
+	else if (check_bulk_or_isoc(pipenum))
+		buf_bsize = R8A66597_BUF_BSIZE - 1;
+	else if (check_interrupt(pipenum))
+		buf_bsize = 0;
+	else
+		err("Illegal pipenum (%d)", pipenum);
+
+	return buf_bsize;
+}
+
+/* this function must be called with interrupt disabled */
+static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
+				     struct r8a66597_device *dev,
+				     struct r8a66597_pipe *pipe,
+				     struct urb *urb)
+{
+	int i;
+	struct r8a66597_pipe_info *info = &pipe->info;
+
+	if ((pipe->info.pipenum != 0) && (info->type != R8A66597_INT)) {
+		for (i = 0; i < R8A66597_MAX_DMA_CHANNEL; i++) {
+			if ((r8a66597->dma_map & (1 << i)) != 0)
+				continue;
+
+			info("address %d, EndpointAddress 0x%02x use DMA FIFO",
+			     usb_pipedevice(urb->pipe),
+			     info->dir_in ? USB_ENDPOINT_DIR_MASK + info->epnum
+					    : info->epnum);
+
+			r8a66597->dma_map |= 1 << i;
+			dev->dma_map |= 1 << i;
+			set_pipe_reg_addr(pipe, i);
+
+			cfifo_change(r8a66597, 0);
+			r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum,
+				      MBW | CURPIPE, pipe->fifosel);
+
+			r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE,
+					  pipe->info.pipenum);
+			r8a66597_bset(r8a66597, BCLR, pipe->fifoctr);
+			break;
+		}
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static void enable_r8a66597_pipe(struct r8a66597 *r8a66597, struct urb *urb,
+				 struct usb_host_endpoint *hep,
+				 struct r8a66597_pipe_info *info)
+{
+	struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+	struct r8a66597_pipe *pipe = hep->hcpriv;
+
+	dbg("enable_pipe:");
+
+	pipe->info = *info;
+	set_pipe_reg_addr(pipe, R8A66597_PIPE_NO_DMA);
+	r8a66597->pipe_cnt[pipe->info.pipenum]++;
+	dev->pipe_cnt[pipe->info.pipenum]++;
+
+	enable_r8a66597_pipe_dma(r8a66597, dev, pipe, urb);
+}
+
+/* this function must be called with interrupt disabled */
+static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address)
+{
+	struct r8a66597_td *td, *next;
+	struct urb *urb;
+	struct list_head *list = &r8a66597->pipe_queue[pipenum];
+
+	if (list_empty(list))
+		return;
+
+	list_for_each_entry_safe(td, next, list, queue) {
+		if (!td)
+			continue;
+		if (td->address != address)
+			continue;
+
+		urb = td->urb;
+		list_del(&td->queue);
+		kfree(td);
+
+		if (urb) {
+			urb->status = -ENODEV;
+			urb->hcpriv = NULL;
+			spin_unlock(&r8a66597->lock);
+			usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb);
+			spin_lock(&r8a66597->lock);
+		}
+		break;
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597,
+				      struct r8a66597_device *dev)
+{
+	int check_ep0 = 0;
+	u16 pipenum;
+
+	if (!dev)
+		return;
+
+	for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+		if (!dev->pipe_cnt[pipenum])
+			continue;
+
+		if (!check_ep0) {
+			check_ep0 = 1;
+			force_dequeue(r8a66597, 0, dev->address);
+		}
+
+		r8a66597->pipe_cnt[pipenum] -= dev->pipe_cnt[pipenum];
+		dev->pipe_cnt[pipenum] = 0;
+		force_dequeue(r8a66597, pipenum, dev->address);
+	}
+
+	dbg("disable_pipe");
+
+	r8a66597->dma_map &= ~(dev->dma_map);
+	dev->dma_map = 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
+			   struct usb_host_endpoint *hep,
+			   struct usb_endpoint_descriptor *ep)
+{
+	struct r8a66597_pipe_info info;
+
+	info.pipenum = get_empty_pipenum(r8a66597, ep);
+	info.address = get_urb_to_r8a66597_addr(r8a66597, urb);
+	info.epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	info.maxpacket = ep->wMaxPacketSize;
+	info.type = get_r8a66597_type(ep->bmAttributes
+				      & USB_ENDPOINT_XFERTYPE_MASK);
+	info.bufnum = get_bufnum(info.pipenum);
+	info.buf_bsize = get_buf_bsize(info.pipenum);
+	info.interval = ep->bInterval;
+	if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		info.dir_in = 1;
+	else
+		info.dir_in = 0;
+
+	enable_r8a66597_pipe(r8a66597, urb, hep, &info);
+}
+
+static void init_pipe_config(struct r8a66597 *r8a66597, struct urb *urb)
+{
+	struct r8a66597_device *dev;
+
+	dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+	dev->state = USB_STATE_CONFIGURED;
+}
+
+static void pipe_irq_enable(struct r8a66597 *r8a66597, struct urb *urb,
+			    u16 pipenum)
+{
+	if (pipenum == 0 && usb_pipeout(urb->pipe))
+		enable_irq_empty(r8a66597, pipenum);
+	else
+		enable_irq_ready(r8a66597, pipenum);
+
+	if (!usb_pipeisoc(urb->pipe))
+		enable_irq_nrdy(r8a66597, pipenum);
+}
+
+static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	disable_irq_ready(r8a66597, pipenum);
+	disable_irq_nrdy(r8a66597, pipenum);
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_preconnect(struct r8a66597 *r8a66597, int port)
+{
+	r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_CONNECTION)
+					 | (1 << USB_PORT_FEAT_C_CONNECTION);
+	r8a66597_write(r8a66597, (u16)~DTCH, get_intsts_reg(port));
+	r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port));
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_connect(struct r8a66597 *r8a66597, int port)
+{
+	u16 speed = get_rh_usb_speed(r8a66597, port);
+	struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+	if (speed == HSMODE)
+		rh->port |= (1 << USB_PORT_FEAT_HIGHSPEED);
+	else if (speed == LSMODE)
+		rh->port |= (1 << USB_PORT_FEAT_LOWSPEED);
+
+	rh->port &= ~(1 << USB_PORT_FEAT_RESET);
+	rh->port |= 1 << USB_PORT_FEAT_ENABLE;
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597, int port)
+{
+	struct r8a66597_device *dev = r8a66597->root_hub[port].dev;
+
+	r8a66597->root_hub[port].port &= ~(1 << USB_PORT_FEAT_CONNECTION);
+	r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_C_CONNECTION);
+
+	disable_r8a66597_pipe_all(r8a66597, dev);
+	free_usb_address(r8a66597, dev);
+
+	r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_setup_packet(struct r8a66597 *r8a66597,
+				 struct r8a66597_td *td)
+{
+	int i;
+	u16 *p = (u16 *)td->urb->setup_packet;
+	unsigned long setup_addr = USBREQ;
+
+	r8a66597_write(r8a66597, make_devsel(td->address) | td->maxpacket,
+		       DCPMAXP);
+	r8a66597_write(r8a66597, (u16)~(SIGN | SACK), INTSTS1);
+
+	for (i = 0; i < 4; i++) {
+		r8a66597_write(r8a66597, p[i], setup_addr);
+		setup_addr += 2;
+	}
+	r8a66597_write(r8a66597, SUREQ, DCPCTR);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_packet_read(struct r8a66597 *r8a66597,
+				struct r8a66597_td *td)
+{
+	struct urb *urb = td->urb;
+
+	if (usb_pipecontrol(urb->pipe)) {
+		r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+		r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+		if (urb->actual_length == 0) {
+			r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+			r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+		}
+		pipe_irq_disable(r8a66597, td->pipenum);
+		pipe_start(r8a66597, td->pipe);
+		pipe_irq_enable(r8a66597, urb, td->pipenum);
+	} else {
+		if (urb->actual_length == 0) {
+			pipe_irq_disable(r8a66597, td->pipenum);
+			pipe_setting(r8a66597, td);
+			pipe_stop(r8a66597, td->pipe);
+			r8a66597_write(r8a66597, (u16)~(1 << td->pipenum),
+				       BRDYSTS);
+
+			if (td->pipe->pipetre) {
+				r8a66597_write(r8a66597, TRCLR,
+					        td->pipe->pipetre);
+				r8a66597_write(r8a66597,
+					       (urb->transfer_buffer_length
+					       + td->maxpacket - 1)
+					       / td->maxpacket,
+					       td->pipe->pipetrn);
+				r8a66597_bset(r8a66597, TRENB,
+					      td->pipe->pipetre);
+			}
+
+			pipe_start(r8a66597, td->pipe);
+			pipe_irq_enable(r8a66597, urb, td->pipenum);
+		}
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_packet_write(struct r8a66597 *r8a66597,
+				 struct r8a66597_td *td)
+{
+	u16 tmp;
+	struct urb *urb = td->urb;
+
+	if (usb_pipecontrol(urb->pipe)) {
+		pipe_stop(r8a66597, td->pipe);
+		r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+		r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+		if (urb->actual_length == 0) {
+			r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+			r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+		}
+	} else {
+		if (urb->actual_length == 0)
+			pipe_setting(r8a66597, td);
+		if (td->pipe->pipetre)
+			r8a66597_bclr(r8a66597, TRENB, td->pipe->pipetre);
+	}
+	r8a66597_write(r8a66597, (u16)~(1 << td->pipenum), BRDYSTS);
+
+	fifo_change_from_pipe(r8a66597, td->pipe);
+	tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+	if (unlikely((tmp & FRDY) == 0))
+		pipe_irq_enable(r8a66597, urb, td->pipenum);
+	else
+		packet_write(r8a66597, td->pipenum);
+	pipe_start(r8a66597, td->pipe);
+}
+
+/* this function must be called with interrupt disabled */
+static void prepare_status_packet(struct r8a66597 *r8a66597,
+				  struct r8a66597_td *td)
+{
+	struct urb *urb = td->urb;
+
+	r8a66597_pipe_toggle(r8a66597, td->pipe, 1);
+
+	if (urb->setup_packet[0] & USB_ENDPOINT_DIR_MASK) {
+		r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
+		r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
+		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+		r8a66597_write(r8a66597, BVAL | BCLR, CFIFOCTR);
+		r8a66597_write(r8a66597, (u16)~BEMP0, BEMPSTS);
+		enable_irq_empty(r8a66597, 0);
+	} else {
+		r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
+		r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
+		r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
+		r8a66597_write(r8a66597, BCLR, CFIFOCTR);
+		r8a66597_write(r8a66597, (u16)~BRDY0, BRDYSTS);
+		r8a66597_write(r8a66597, (u16)~BEMP0, BEMPSTS);
+		enable_irq_ready(r8a66597, 0);
+	}
+	enable_irq_nrdy(r8a66597, 0);
+	pipe_start(r8a66597, td->pipe);
+}
+
+/* this function must be called with interrupt disabled */
+static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+	BUG_ON(!td);
+
+	switch (td->type) {
+	case USB_PID_SETUP:
+		if (td->urb->setup_packet[1] == USB_REQ_SET_ADDRESS) {
+			td->set_address = 1;
+			td->urb->setup_packet[2] = alloc_usb_address(r8a66597,
+								     td->urb);
+			if (td->urb->setup_packet[2] == 0)
+				return -EPIPE;
+		}
+		prepare_setup_packet(r8a66597, td);
+		break;
+	case USB_PID_IN:
+		prepare_packet_read(r8a66597, td);
+		break;
+	case USB_PID_OUT:
+		prepare_packet_write(r8a66597, td);
+		break;
+	case USB_PID_ACK:
+		prepare_status_packet(r8a66597, td);
+		break;
+	default:
+		err("invalid type.");
+		break;
+	}
+
+	return 0;
+}
+
+static int check_transfer_finish(struct r8a66597_td *td, struct urb *urb)
+{
+	if (usb_pipeisoc(urb->pipe)) {
+		if (urb->number_of_packets == td->iso_cnt)
+			return 1;
+	}
+
+	/* control or bulk or interrupt */
+	if ((urb->transfer_buffer_length <= urb->actual_length) ||
+	    (td->short_packet) || (td->zero_packet))
+		return 1;
+
+	return 0;
+}
+
+/* this function must be called with interrupt disabled */
+static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td)
+{
+	unsigned long time;
+
+	BUG_ON(!td);
+
+	if (!list_empty(&r8a66597->pipe_queue[td->pipenum]) &&
+	    !usb_pipecontrol(td->urb->pipe) && usb_pipein(td->urb->pipe)) {
+		r8a66597->timeout_map |= 1 << td->pipenum;
+		switch (usb_pipetype(td->urb->pipe)) {
+		case PIPE_INTERRUPT:
+		case PIPE_ISOCHRONOUS:
+			time = 30;
+			break;
+		default:
+			time = 300;
+			break;
+		}
+
+		mod_timer(&r8a66597->td_timer[td->pipenum],
+			  jiffies + msecs_to_jiffies(time));
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td,
+		 u16 pipenum, struct urb *urb)
+{
+	int restart = 0;
+	struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
+
+	r8a66597->timeout_map &= ~(1 << pipenum);
+
+	if (likely(td)) {
+		if (td->set_address && urb->status != 0)
+			r8a66597->address_map &= ~(1 << urb->setup_packet[2]);
+
+		pipe_toggle_save(r8a66597, td->pipe, urb);
+		list_del(&td->queue);
+		kfree(td);
+	}
+
+	if (!list_empty(&r8a66597->pipe_queue[pipenum]))
+		restart = 1;
+
+	if (likely(urb)) {
+		if (usb_pipeisoc(urb->pipe))
+			urb->start_frame = r8a66597_get_frame(hcd);
+
+		urb->hcpriv = NULL;
+		spin_unlock(&r8a66597->lock);
+		usb_hcd_giveback_urb(hcd, urb);
+		spin_lock(&r8a66597->lock);
+	}
+
+	if (restart) {
+		td = r8a66597_get_td(r8a66597, pipenum);
+		if (unlikely(!td))
+			return;
+
+		start_transfer(r8a66597, td);
+		set_td_timer(r8a66597, td);
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td,
+			   u16 pipenum, struct urb *urb)
+__releases(r8a66597->lock) __acquires(r8a66597->lock)
+{
+	done(r8a66597, td, pipenum, urb);
+}
+
+static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	u16 tmp;
+	int rcv_len, bufsize, urb_len, size;
+	u16 *buf;
+	struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+	struct urb *urb;
+	int finish = 0;
+
+	if (unlikely(!td))
+		return;
+	urb = td->urb;
+
+	fifo_change_from_pipe(r8a66597, td->pipe);
+	tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+	if (unlikely((tmp & FRDY) == 0)) {
+		urb->status = -EPIPE;
+		pipe_stop(r8a66597, td->pipe);
+		pipe_irq_disable(r8a66597, pipenum);
+		err("in fifo not ready (%d)", pipenum);
+		finish_request(r8a66597, td, pipenum, td->urb);
+		return;
+	}
+
+	/* prepare parameters */
+	rcv_len = tmp & DTLN;
+	bufsize = td->maxpacket;
+	if (usb_pipeisoc(urb->pipe)) {
+		buf = (u16 *)(urb->transfer_buffer +
+				urb->iso_frame_desc[td->iso_cnt].offset);
+		urb_len = urb->iso_frame_desc[td->iso_cnt].length;
+	} else {
+		buf = (void *)urb->transfer_buffer + urb->actual_length;
+		urb_len = urb->transfer_buffer_length - urb->actual_length;
+	}
+	if (rcv_len < bufsize)
+		size = min(rcv_len, urb_len);
+	else
+		size = min(bufsize, urb_len);
+
+	/* update parameters */
+	urb->actual_length += size;
+	if (rcv_len == 0)
+		td->zero_packet = 1;
+	if ((size % td->maxpacket) > 0) {
+		td->short_packet = 1;
+		if (urb->transfer_buffer_length != urb->actual_length &&
+		    urb->transfer_flags & URB_SHORT_NOT_OK)
+			td->urb->status = -EREMOTEIO;
+	}
+	if (usb_pipeisoc(urb->pipe)) {
+		urb->iso_frame_desc[td->iso_cnt].actual_length = size;
+		urb->iso_frame_desc[td->iso_cnt].status = 0;
+		td->iso_cnt++;
+	}
+
+	/* check transfer finish */
+	if (check_transfer_finish(td, urb)) {
+		pipe_stop(r8a66597, td->pipe);
+		pipe_irq_disable(r8a66597, pipenum);
+		finish = 1;
+	}
+
+	/* read fifo */
+	if (urb->transfer_buffer) {
+		if (size == 0)
+			r8a66597_write(r8a66597, BCLR, td->pipe->fifoctr);
+		else
+			r8a66597_read_fifo(r8a66597, td->pipe->fifoaddr,
+					   buf, size);
+	}
+
+	if (finish && pipenum != 0) {
+		if (td->urb->status == -EINPROGRESS)
+			td->urb->status = 0;
+		finish_request(r8a66597, td, pipenum, urb);
+	}
+}
+
+static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	u16 tmp;
+	int bufsize, size;
+	u16 *buf;
+	struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+	struct urb *urb;
+
+	if (unlikely(!td))
+		return;
+	urb = td->urb;
+
+	fifo_change_from_pipe(r8a66597, td->pipe);
+	tmp = r8a66597_read(r8a66597, td->pipe->fifoctr);
+	if (unlikely((tmp & FRDY) == 0)) {
+		urb->status = -EPIPE;
+		pipe_stop(r8a66597, td->pipe);
+		pipe_irq_disable(r8a66597, pipenum);
+		err("out write fifo not ready. (%d)", pipenum);
+		finish_request(r8a66597, td, pipenum, td->urb);
+		return;
+	}
+
+	/* prepare parameters */
+	bufsize = td->maxpacket;
+	if (usb_pipeisoc(urb->pipe)) {
+		buf = (u16 *)(urb->transfer_buffer +
+				urb->iso_frame_desc[td->iso_cnt].offset);
+		size = min(bufsize,
+			   (int)urb->iso_frame_desc[td->iso_cnt].length);
+	} else {
+		buf = (u16 *)(urb->transfer_buffer + urb->actual_length);
+		size = min((int)bufsize,
+			   urb->transfer_buffer_length - urb->actual_length);
+	}
+
+	/* write fifo */
+	if (pipenum > 0)
+		r8a66597_write(r8a66597, (u16)~(1 << pipenum), BEMPSTS);
+	if (urb->transfer_buffer) {
+		r8a66597_write_fifo(r8a66597, td->pipe->fifoaddr, buf, size);
+		if (!usb_pipebulk(urb->pipe) || td->maxpacket != size)
+			r8a66597_write(r8a66597, BVAL, td->pipe->fifoctr);
+	}
+
+	/* update parameters */
+	urb->actual_length += size;
+	if (usb_pipeisoc(urb->pipe)) {
+		urb->iso_frame_desc[td->iso_cnt].actual_length = size;
+		urb->iso_frame_desc[td->iso_cnt].status = 0;
+		td->iso_cnt++;
+	}
+
+	/* check transfer finish */
+	if (check_transfer_finish(td, urb)) {
+		disable_irq_ready(r8a66597, pipenum);
+		enable_irq_empty(r8a66597, pipenum);
+		if (!usb_pipeisoc(urb->pipe))
+			enable_irq_nrdy(r8a66597, pipenum);
+	} else
+		pipe_irq_enable(r8a66597, urb, pipenum);
+}
+
+
+static void check_next_phase(struct r8a66597 *r8a66597)
+{
+	struct r8a66597_td *td = r8a66597_get_td(r8a66597, 0);
+	struct urb *urb;
+	u8 finish = 0;
+
+	if (unlikely(!td))
+		return;
+	urb = td->urb;
+
+	switch (td->type) {
+	case USB_PID_IN:
+	case USB_PID_OUT:
+		if (urb->status != -EINPROGRESS) {
+			finish = 1;
+			break;
+		}
+		if (check_transfer_finish(td, urb))
+			td->type = USB_PID_ACK;
+		break;
+	case USB_PID_SETUP:
+		if (urb->status != -EINPROGRESS)
+			finish = 1;
+		else if (urb->transfer_buffer_length == urb->actual_length) {
+			td->type = USB_PID_ACK;
+			urb->status = 0;
+		} else if (usb_pipeout(urb->pipe))
+			td->type = USB_PID_OUT;
+		else
+			td->type = USB_PID_IN;
+		break;
+	case USB_PID_ACK:
+		finish = 1;
+		if (urb->status == -EINPROGRESS)
+			urb->status = 0;
+		break;
+	}
+
+	if (finish)
+		finish_request(r8a66597, td, 0, urb);
+	else
+		start_transfer(r8a66597, td);
+}
+
+static void set_urb_error(struct r8a66597 *r8a66597, u16 pipenum)
+{
+	struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum);
+
+	if (td && td->urb) {
+		u16 pid = r8a66597_read(r8a66597, td->pipe->pipectr) & PID;
+
+		if (pid == PID_NAK)
+			td->urb->status = -ECONNRESET;
+		else
+			td->urb->status = -EPIPE;
+	}
+}
+
+static void irq_pipe_ready(struct r8a66597 *r8a66597)
+{
+	u16 check;
+	u16 pipenum;
+	u16 mask;
+	struct r8a66597_td *td;
+
+	mask = r8a66597_read(r8a66597, BRDYSTS)
+	       & r8a66597_read(r8a66597, BRDYENB);
+	r8a66597_write(r8a66597, (u16)~mask, BRDYSTS);
+	if (mask & BRDY0) {
+		td = r8a66597_get_td(r8a66597, 0);
+		if (td && td->type == USB_PID_IN)
+			packet_read(r8a66597, 0);
+		else
+			pipe_irq_disable(r8a66597, 0);
+		check_next_phase(r8a66597);
+	}
+
+	for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+		check = 1 << pipenum;
+		if (mask & check) {
+			td = r8a66597_get_td(r8a66597, pipenum);
+			if (unlikely(!td))
+				continue;
+
+			if (td->type == USB_PID_IN)
+				packet_read(r8a66597, pipenum);
+			else if (td->type == USB_PID_OUT)
+				packet_write(r8a66597, pipenum);
+		}
+	}
+}
+
+static void irq_pipe_empty(struct r8a66597 *r8a66597)
+{
+	u16 tmp;
+	u16 check;
+	u16 pipenum;
+	u16 mask;
+	struct r8a66597_td *td;
+
+	mask = r8a66597_read(r8a66597, BEMPSTS)
+	       & r8a66597_read(r8a66597, BEMPENB);
+	r8a66597_write(r8a66597, (u16)~mask, BEMPSTS);
+	if (mask & BEMP0) {
+		cfifo_change(r8a66597, 0);
+		td = r8a66597_get_td(r8a66597, 0);
+		if (td && td->type != USB_PID_OUT)
+			disable_irq_empty(r8a66597, 0);
+		check_next_phase(r8a66597);
+	}
+
+	for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+		check = 1 << pipenum;
+		if (mask &  check) {
+			struct r8a66597_td *td;
+			td = r8a66597_get_td(r8a66597, pipenum);
+			if (unlikely(!td))
+				continue;
+
+			tmp = r8a66597_read(r8a66597, td->pipe->pipectr);
+			if ((tmp & INBUFM) == 0) {
+				disable_irq_empty(r8a66597, pipenum);
+				pipe_irq_disable(r8a66597, pipenum);
+				if (td->urb->status == -EINPROGRESS)
+					td->urb->status = 0;
+				finish_request(r8a66597, td, pipenum, td->urb);
+			}
+		}
+	}
+}
+
+static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
+{
+	u16 check;
+	u16 pipenum;
+	u16 mask;
+
+	mask = r8a66597_read(r8a66597, NRDYSTS)
+	       & r8a66597_read(r8a66597, NRDYENB);
+	r8a66597_write(r8a66597, (u16)~mask, NRDYSTS);
+	if (mask & NRDY0) {
+		cfifo_change(r8a66597, 0);
+		set_urb_error(r8a66597, 0);
+		pipe_irq_disable(r8a66597, 0);
+		check_next_phase(r8a66597);
+	}
+
+	for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+		check = 1 << pipenum;
+		if (mask & check) {
+			struct r8a66597_td *td;
+			td = r8a66597_get_td(r8a66597, pipenum);
+			if (unlikely(!td))
+				continue;
+
+			set_urb_error(r8a66597, pipenum);
+			pipe_irq_disable(r8a66597, pipenum);
+			pipe_stop(r8a66597, td->pipe);
+			finish_request(r8a66597, td, pipenum, td->urb);
+		}
+	}
+}
+
+static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port)
+{
+	struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+	rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
+	rh->scount = R8A66597_MAX_SAMPLING;
+	mod_timer(&r8a66597->rh_timer, jiffies + msecs_to_jiffies(50));
+}
+
+static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	u16 intsts0, intsts1, intsts2;
+	u16 intenb0, intenb1, intenb2;
+	u16 mask0, mask1, mask2;
+
+	spin_lock(&r8a66597->lock);
+
+	intsts0 = r8a66597_read(r8a66597, INTSTS0);
+	intsts1 = r8a66597_read(r8a66597, INTSTS1);
+	intsts2 = r8a66597_read(r8a66597, INTSTS2);
+	intenb0 = r8a66597_read(r8a66597, INTENB0);
+	intenb1 = r8a66597_read(r8a66597, INTENB1);
+	intenb2 = r8a66597_read(r8a66597, INTENB2);
+
+	mask2 = intsts2 & intenb2;
+	mask1 = intsts1 & intenb1;
+	mask0 = intsts0 & intenb0 & (BEMP | NRDY | BRDY);
+	if (mask2) {
+		if (mask2 & ATTCH) {
+			r8a66597_write(r8a66597, (u16)~ATTCH, INTSTS2);
+			r8a66597_bclr(r8a66597, ATTCHE, INTENB2);
+
+			/* start usb bus sampling */
+			start_root_hub_sampling(r8a66597, 1);
+		}
+		if (mask2 & DTCH) {
+			r8a66597_write(r8a66597, (u16)~DTCH, INTSTS2);
+			r8a66597_bclr(r8a66597, DTCHE, INTENB2);
+			r8a66597_usb_disconnect(r8a66597, 1);
+		}
+	}
+
+	if (mask1) {
+		if (mask1 & ATTCH) {
+			r8a66597_write(r8a66597, (u16)~ATTCH, INTSTS1);
+			r8a66597_bclr(r8a66597, ATTCHE, INTENB1);
+
+			/* start usb bus sampling */
+			start_root_hub_sampling(r8a66597, 0);
+		}
+		if (mask1 & DTCH) {
+			r8a66597_write(r8a66597, (u16)~DTCH, INTSTS1);
+			r8a66597_bclr(r8a66597, DTCHE, INTENB1);
+			r8a66597_usb_disconnect(r8a66597, 0);
+		}
+		if (mask1 & SIGN) {
+			r8a66597_write(r8a66597, (u16)~SIGN, INTSTS1);
+			set_urb_error(r8a66597, 0);
+			check_next_phase(r8a66597);
+		}
+		if (mask1 & SACK) {
+			r8a66597_write(r8a66597, (u16)~SACK, INTSTS1);
+			check_next_phase(r8a66597);
+		}
+	}
+	if (mask0) {
+		if (mask0 & BRDY)
+			irq_pipe_ready(r8a66597);
+		if (mask0 & BEMP)
+			irq_pipe_empty(r8a66597);
+		if (mask0 & NRDY)
+			irq_pipe_nrdy(r8a66597);
+	}
+
+	spin_unlock(&r8a66597->lock);
+	return IRQ_HANDLED;
+}
+
+/* this function must be called with interrupt disabled */
+static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
+{
+	u16 tmp;
+	struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+
+	if (rh->port & (1 << USB_PORT_FEAT_RESET)) {
+		unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+		tmp = r8a66597_read(r8a66597, dvstctr_reg);
+		if ((tmp & USBRST) == USBRST) {
+			r8a66597_mdfy(r8a66597, UACT, USBRST | UACT,
+				      dvstctr_reg);
+			mod_timer(&r8a66597->rh_timer,
+				  jiffies + msecs_to_jiffies(50));
+		} else
+			r8a66597_usb_connect(r8a66597, port);
+	}
+
+	if (rh->scount > 0) {
+		tmp = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
+		if (tmp == rh->old_syssts) {
+			rh->scount--;
+			if (rh->scount == 0) {
+				if (tmp == FS_JSTS) {
+					r8a66597_bset(r8a66597, HSE,
+						      get_syscfg_reg(port));
+					r8a66597_usb_preconnect(r8a66597, port);
+				} else if (tmp == LS_JSTS) {
+					r8a66597_bclr(r8a66597, HSE,
+						      get_syscfg_reg(port));
+					r8a66597_usb_preconnect(r8a66597, port);
+				} else if (tmp == SE0)
+					r8a66597_bset(r8a66597, ATTCHE,
+						      get_intenb_reg(port));
+			} else {
+				mod_timer(&r8a66597->rh_timer,
+					  jiffies + msecs_to_jiffies(50));
+			}
+		} else {
+			rh->scount = R8A66597_MAX_SAMPLING;
+			rh->old_syssts = tmp;
+			mod_timer(&r8a66597->rh_timer,
+				  jiffies + msecs_to_jiffies(50));
+		}
+	}
+}
+
+static void r8a66597_td_timer(unsigned long _r8a66597)
+{
+	struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+	unsigned long flags;
+	u16 pipenum;
+	struct r8a66597_td *td, *new_td = NULL;
+	struct r8a66597_pipe *pipe;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	for (pipenum = 0; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+		if (!(r8a66597->timeout_map & (1 << pipenum)))
+			continue;
+		if (timer_pending(&r8a66597->td_timer[pipenum]))
+			continue;
+
+		td = r8a66597_get_td(r8a66597, pipenum);
+		if (!td) {
+			r8a66597->timeout_map &= ~(1 << pipenum);
+			continue;
+		}
+
+		if (td->urb->actual_length) {
+			set_td_timer(r8a66597, td);
+			break;
+		}
+
+		pipe = td->pipe;
+		pipe_stop(r8a66597, pipe);
+
+		new_td = td;
+		do {
+			list_move_tail(&new_td->queue,
+				       &r8a66597->pipe_queue[pipenum]);
+			new_td = r8a66597_get_td(r8a66597, pipenum);
+			if (!new_td) {
+				new_td = td;
+				break;
+			}
+		} while (td != new_td && td->address == new_td->address);
+
+		start_transfer(r8a66597, new_td);
+
+		if (td == new_td)
+			r8a66597->timeout_map &= ~(1 << pipenum);
+		else
+			set_td_timer(r8a66597, new_td);
+		break;
+	}
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static void r8a66597_timer(unsigned long _r8a66597)
+{
+	struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+	unsigned long flags;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+
+	r8a66597_root_hub_control(r8a66597, 0);
+	r8a66597_root_hub_control(r8a66597, 1);
+
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static int check_pipe_config(struct r8a66597 *r8a66597, struct urb *urb)
+{
+	struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb);
+
+	if (dev && dev->address && dev->state != USB_STATE_CONFIGURED &&
+	    (urb->dev->state == USB_STATE_CONFIGURED))
+		return 1;
+	else
+		return 0;
+}
+
+static int r8a66597_start(struct usb_hcd *hcd)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	int ret;
+
+	hcd->state = HC_STATE_RUNNING;
+	if ((ret = enable_controller(r8a66597)) < 0)
+		return ret;
+
+	return 0;
+}
+
+static void r8a66597_stop(struct usb_hcd *hcd)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+
+	disable_controller(r8a66597);
+}
+
+static void set_address_zero(struct r8a66597 *r8a66597, struct urb *urb)
+{
+	unsigned int usb_address = usb_pipedevice(urb->pipe);
+	u16 root_port, hub_port;
+
+	if (usb_address == 0) {
+		get_port_number(urb->dev->devpath,
+				&root_port, &hub_port);
+		set_devadd_reg(r8a66597, 0,
+			       get_r8a66597_usb_speed(urb->dev->speed),
+			       get_parent_r8a66597_address(r8a66597, urb->dev),
+			       hub_port, root_port);
+	}
+}
+
+static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597,
+					    struct urb *urb,
+					    struct usb_host_endpoint *hep,
+					    gfp_t mem_flags)
+{
+	struct r8a66597_td *td;
+	u16 pipenum;
+
+	td = kzalloc(sizeof(struct r8a66597_td), mem_flags);
+	if (td == NULL)
+		return NULL;
+
+	pipenum = r8a66597_get_pipenum(urb, hep);
+	td->pipenum = pipenum;
+	td->pipe = hep->hcpriv;
+	td->urb = urb;
+	td->address = get_urb_to_r8a66597_addr(r8a66597, urb);
+	td->maxpacket = usb_maxpacket(urb->dev, urb->pipe,
+				      !usb_pipein(urb->pipe));
+	if (usb_pipecontrol(urb->pipe))
+		td->type = USB_PID_SETUP;
+	else if (usb_pipein(urb->pipe))
+		td->type = USB_PID_IN;
+	else
+		td->type = USB_PID_OUT;
+	INIT_LIST_HEAD(&td->queue);
+
+	return td;
+}
+
+static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
+				struct usb_host_endpoint *hep,
+				struct urb *urb,
+				gfp_t mem_flags)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	struct r8a66597_td *td = NULL;
+	int ret = 0, request = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	if (!get_urb_to_r8a66597_dev(r8a66597, urb)) {
+		ret = -ENODEV;
+		goto error;
+	}
+
+	if (!hep->hcpriv) {
+		hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe), mem_flags);
+		if (!hep->hcpriv) {
+			ret = -ENOMEM;
+			goto error;
+		}
+		set_pipe_reg_addr(hep->hcpriv, R8A66597_PIPE_NO_DMA);
+		if (usb_pipeendpoint(urb->pipe))
+			init_pipe_info(r8a66597, urb, hep, &hep->desc);
+	}
+
+	if (unlikely(check_pipe_config(r8a66597, urb)))
+		init_pipe_config(r8a66597, urb);
+
+	set_address_zero(r8a66597, urb);
+	td = r8a66597_make_td(r8a66597, urb, hep, mem_flags);
+	if (td == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	if (list_empty(&r8a66597->pipe_queue[td->pipenum]))
+		request = 1;
+	list_add_tail(&td->queue, &r8a66597->pipe_queue[td->pipenum]);
+
+	spin_lock(&urb->lock);
+	if (urb->status != -EINPROGRESS) {
+		spin_unlock(&urb->lock);
+		ret = -EPIPE;
+		goto error;
+	}
+	urb->hcpriv = td;
+	spin_unlock(&urb->lock);
+
+	if (request) {
+		ret = start_transfer(r8a66597, td);
+		if (ret < 0) {
+			list_del(&td->queue);
+			kfree(td);
+		}
+	} else
+		set_td_timer(r8a66597, td);
+
+error:
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+	return ret;
+}
+
+static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	struct r8a66597_td *td;
+	unsigned long flags;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	if (urb->hcpriv) {
+		td = urb->hcpriv;
+		pipe_stop(r8a66597, td->pipe);
+		pipe_irq_disable(r8a66597, td->pipenum);
+		disable_irq_empty(r8a66597, td->pipenum);
+		done(r8a66597, td, td->pipenum, urb);
+	}
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+	return 0;
+}
+
+static void r8a66597_endpoint_disable(struct usb_hcd *hcd,
+				      struct usb_host_endpoint *hep)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	struct r8a66597_pipe *pipe = (struct r8a66597_pipe *)hep->hcpriv;
+	struct r8a66597_td *td;
+	struct urb *urb = NULL;
+	u16 pipenum;
+	unsigned long flags;
+
+	if (pipe == NULL)
+		return;
+	pipenum = pipe->info.pipenum;
+
+	if (pipenum == 0) {
+		kfree(hep->hcpriv);
+		hep->hcpriv = NULL;
+		return;
+	}
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	pipe_stop(r8a66597, pipe);
+	pipe_irq_disable(r8a66597, pipenum);
+	disable_irq_empty(r8a66597, pipenum);
+	td = r8a66597_get_td(r8a66597, pipenum);
+	if (td)
+		urb = td->urb;
+	done(r8a66597, td, pipenum, urb);
+	kfree(hep->hcpriv);
+	hep->hcpriv = NULL;
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
+static int r8a66597_get_frame(struct usb_hcd *hcd)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	return r8a66597_read(r8a66597, FRMNUM) & 0x03FF;
+}
+
+static void collect_usb_address_map(struct usb_device *udev, unsigned long *map)
+{
+	int chix;
+
+	if (udev->state == USB_STATE_CONFIGURED &&
+	    udev->parent && udev->parent->devnum > 1 &&
+	    udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB)
+		map[udev->devnum/32] |= (1 << (udev->devnum % 32));
+
+	for (chix = 0; chix < udev->maxchild; chix++) {
+		struct usb_device *childdev = udev->children[chix];
+
+		if (childdev)
+			collect_usb_address_map(childdev, map);
+	}
+}
+
+/* this function must be called with interrupt disabled */
+static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597,
+						   int addr)
+{
+	struct r8a66597_device *dev;
+	struct list_head *list = &r8a66597->child_device;
+
+	list_for_each_entry(dev, list, device_list) {
+		if (!dev)
+			continue;
+		if (dev->usb_address != addr)
+			continue;
+
+		return dev;
+	}
+
+	err("get_r8a66597_device fail.(%d)\n", addr);
+	return NULL;
+}
+
+static void update_usb_address_map(struct r8a66597 *r8a66597,
+				   struct usb_device *root_hub,
+				   unsigned long *map)
+{
+	int i, j, addr;
+	unsigned long diff;
+	unsigned long flags;
+
+	for (i = 0; i < 4; i++) {
+		diff = r8a66597->child_connect_map[i] ^ map[i];
+		if (!diff)
+			continue;
+
+		for (j = 0; j < 32; j++) {
+			if (!(diff & (1 << j)))
+				continue;
+
+			addr = i * 32 + j;
+			if (map[i] & (1 << j))
+				set_child_connect_map(r8a66597, addr);
+			else {
+				struct r8a66597_device *dev;
+
+				spin_lock_irqsave(&r8a66597->lock, flags);
+				dev = get_r8a66597_device(r8a66597, addr);
+				disable_r8a66597_pipe_all(r8a66597, dev);
+				free_usb_address(r8a66597, dev);
+				put_child_connect_map(r8a66597, addr);
+				spin_unlock_irqrestore(&r8a66597->lock, flags);
+			}
+		}
+	}
+}
+
+static void r8a66597_check_detect_child(struct r8a66597 *r8a66597,
+					struct usb_hcd *hcd)
+{
+	struct usb_bus *bus;
+	unsigned long now_map[4];
+
+	memset(now_map, 0, sizeof(now_map));
+
+	list_for_each_entry(bus, &usb_bus_list, bus_list) {
+		if (!bus->root_hub)
+			continue;
+
+		if (bus->busnum != hcd->self.busnum)
+			continue;
+
+		collect_usb_address_map(bus->root_hub, now_map);
+		update_usb_address_map(r8a66597, bus->root_hub, now_map);
+	}
+}
+
+static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	unsigned long flags;
+	int i;
+
+	r8a66597_check_detect_child(r8a66597, hcd);
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+
+	*buf = 0;	/* initialize (no change) */
+
+	for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) {
+		if (r8a66597->root_hub[i].port & 0xffff0000)
+			*buf |= 1 << (i + 1);
+	}
+
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+
+	return (*buf != 0);
+}
+
+static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
+				    struct usb_hub_descriptor *desc)
+{
+	desc->bDescriptorType = 0x29;
+	desc->bHubContrCurrent = 0;
+	desc->bNbrPorts = R8A66597_MAX_ROOT_HUB;
+	desc->bDescLength = 9;
+	desc->bPwrOn2PwrGood = 0;
+	desc->wHubCharacteristics = cpu_to_le16(0x0011);
+	desc->bitmap[0] = ((1 << R8A66597_MAX_ROOT_HUB) - 1) << 1;
+	desc->bitmap[1] = ~0;
+}
+
+static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+				u16 wIndex, char *buf, u16 wLength)
+{
+	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
+	int ret;
+	int port = (wIndex & 0x00FF) - 1;
+	struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
+	unsigned long flags;
+
+	ret = 0;
+
+	spin_lock_irqsave(&r8a66597->lock, flags);
+	switch (typeReq) {
+	case ClearHubFeature:
+	case SetHubFeature:
+		switch (wValue) {
+		case C_HUB_OVER_CURRENT:
+		case C_HUB_LOCAL_POWER:
+			break;
+		default:
+			goto error;
+		}
+		break;
+	case ClearPortFeature:
+		if (wIndex > R8A66597_MAX_ROOT_HUB)
+			goto error;
+		if (wLength != 0)
+			goto error;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			rh->port &= (1 << USB_PORT_FEAT_POWER);
+			break;
+		case USB_PORT_FEAT_SUSPEND:
+			break;
+		case USB_PORT_FEAT_POWER:
+			r8a66597_port_power(r8a66597, port, 0);
+			break;
+		case USB_PORT_FEAT_C_ENABLE:
+		case USB_PORT_FEAT_C_SUSPEND:
+		case USB_PORT_FEAT_C_CONNECTION:
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+		case USB_PORT_FEAT_C_RESET:
+			break;
+		default:
+			goto error;
+		}
+		rh->port &= ~(1 << wValue);
+		break;
+	case GetHubDescriptor:
+		r8a66597_hub_descriptor(r8a66597,
+					(struct usb_hub_descriptor *)buf);
+		break;
+	case GetHubStatus:
+		*buf = 0x00;
+		break;
+	case GetPortStatus:
+		if (wIndex > R8A66597_MAX_ROOT_HUB)
+			goto error;
+		*(u32 *)buf = rh->port;
+		break;
+	case SetPortFeature:
+		if (wIndex > R8A66597_MAX_ROOT_HUB)
+			goto error;
+		if (wLength != 0)
+			goto error;
+
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			break;
+		case USB_PORT_FEAT_POWER:
+			r8a66597_port_power(r8a66597, port, 1);
+			rh->port |= (1 << USB_PORT_FEAT_POWER);
+			break;
+		case USB_PORT_FEAT_RESET: {
+			struct r8a66597_device *dev = rh->dev;
+
+			rh->port |= (1 << USB_PORT_FEAT_RESET);
+
+			disable_r8a66597_pipe_all(r8a66597, dev);
+			free_usb_address(r8a66597, dev);
+
+			r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT,
+				      get_dvstctr_reg(port));
+			mod_timer(&r8a66597->rh_timer,
+				  jiffies + msecs_to_jiffies(50));
+			}
+			break;
+		default:
+			goto error;
+		}
+		rh->port |= 1 << wValue;
+		break;
+	default:
+error:
+		ret = -EPIPE;
+		break;
+	}
+
+	spin_unlock_irqrestore(&r8a66597->lock, flags);
+	return ret;
+}
+
+static struct hc_driver r8a66597_hc_driver = {
+	.description =		hcd_name,
+	.hcd_priv_size =	sizeof(struct r8a66597),
+	.irq =			r8a66597_irq,
+
+	/*
+	 * generic hardware linkage
+	 */
+	.flags =		HCD_USB2,
+
+	.start =		r8a66597_start,
+	.stop =			r8a66597_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue =		r8a66597_urb_enqueue,
+	.urb_dequeue =		r8a66597_urb_dequeue,
+	.endpoint_disable =	r8a66597_endpoint_disable,
+
+	/*
+	 * periodic schedule support
+	 */
+	.get_frame_number =	r8a66597_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data =	r8a66597_hub_status_data,
+	.hub_control =		r8a66597_hub_control,
+};
+
+#if defined(CONFIG_PM)
+static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	pdev->dev.power.power_state = state;
+	return 0;
+}
+
+static int r8a66597_resume(struct platform_device *pdev)
+{
+	pdev->dev.power.power_state = PMSG_ON;
+	return 0;
+}
+#else	/* if defined(CONFIG_PM) */
+#define r8a66597_suspend	NULL
+#define r8a66597_resume		NULL
+#endif
+
+static int __init_or_module r8a66597_remove(struct platform_device *pdev)
+{
+	struct r8a66597		*r8a66597 = dev_get_drvdata(&pdev->dev);
+	struct usb_hcd		*hcd = r8a66597_to_hcd(r8a66597);
+
+	del_timer_sync(&r8a66597->rh_timer);
+	iounmap((void *)r8a66597->reg);
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+	return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+static int __init r8a66597_probe(struct platform_device *pdev)
+{
+	struct resource *res = NULL;
+	int irq = -1;
+	void __iomem *reg = NULL;
+	struct usb_hcd *hcd = NULL;
+	struct r8a66597 *r8a66597;
+	int ret = 0;
+	int i;
+
+	if (pdev->dev.dma_mask) {
+		ret = -EINVAL;
+		err("dma not support");
+		goto clean_up;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   (char *)hcd_name);
+	if (!res) {
+		ret = -ENODEV;
+		err("platform_get_resource_byname error.");
+		goto clean_up;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = -ENODEV;
+		err("platform_get_irq error.");
+		goto clean_up;
+	}
+
+	reg = ioremap(res->start, resource_len(res));
+	if (reg == NULL) {
+		ret = -ENOMEM;
+		err("ioremap error.");
+		goto clean_up;
+	}
+
+	/* initialize hcd */
+	hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name);
+	if (!hcd) {
+		ret = -ENOMEM;
+		err("Failed to create hcd");
+		goto clean_up;
+	}
+	r8a66597 = hcd_to_r8a66597(hcd);
+	memset(r8a66597, 0, sizeof(struct r8a66597));
+	dev_set_drvdata(&pdev->dev, r8a66597);
+
+	spin_lock_init(&r8a66597->lock);
+	init_timer(&r8a66597->rh_timer);
+	r8a66597->rh_timer.function = r8a66597_timer;
+	r8a66597->rh_timer.data = (unsigned long)r8a66597;
+	r8a66597->reg = (unsigned long)reg;
+
+	for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) {
+		INIT_LIST_HEAD(&r8a66597->pipe_queue[i]);
+		init_timer(&r8a66597->td_timer[i]);
+		r8a66597->td_timer[i].function = r8a66597_td_timer;
+		r8a66597->td_timer[i].data = (unsigned long)r8a66597;
+	}
+	INIT_LIST_HEAD(&r8a66597->child_device);
+
+	hcd->rsrc_start = res->start;
+	ret = usb_add_hcd(hcd, irq, 0);
+	if (ret != 0) {
+		err("Failed to add hcd");
+		goto clean_up;
+	}
+
+	return 0;
+
+clean_up:
+	if (reg)
+		iounmap(reg);
+	if (res)
+		release_mem_region(res->start, 1);
+
+	return ret;
+}
+
+static struct platform_driver r8a66597_driver = {
+	.probe =	r8a66597_probe,
+	.remove =	r8a66597_remove,
+	.suspend =	r8a66597_suspend,
+	.resume =	r8a66597_resume,
+	.driver		= {
+		.name = (char *) hcd_name,
+	},
+};
+
+static int __init r8a66597_init(void)
+{
+	if (usb_disabled())
+		return -ENODEV;
+
+	info("driver %s, %s", hcd_name, DRIVER_VERSION);
+	return platform_driver_register(&r8a66597_driver);
+}
+module_init(r8a66597_init);
+
+static void __exit r8a66597_cleanup(void)
+{
+	platform_driver_unregister(&r8a66597_driver);
+}
+module_exit(r8a66597_cleanup);
+
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
new file mode 100644
index 0000000..97c2a71
--- /dev/null
+++ b/drivers/usb/host/r8a66597.h
@@ -0,0 +1,634 @@
+/*
+ * R8A66597 HCD (Host Controller Driver)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Portions Copyright (C) 2004-2005 David Brownell
+ * Portions Copyright (C) 1999 Roman Weissgaerber
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __R8A66597_H__
+#define __R8A66597_H__
+
+#define SYSCFG0		0x00
+#define SYSCFG1		0x02
+#define SYSSTS0		0x04
+#define SYSSTS1		0x06
+#define DVSTCTR0	0x08
+#define DVSTCTR1	0x0A
+#define TESTMODE	0x0C
+#define PINCFG		0x0E
+#define DMA0CFG		0x10
+#define DMA1CFG		0x12
+#define CFIFO		0x14
+#define D0FIFO		0x18
+#define D1FIFO		0x1C
+#define CFIFOSEL	0x20
+#define CFIFOCTR	0x22
+#define CFIFOSIE	0x24
+#define D0FIFOSEL	0x28
+#define D0FIFOCTR	0x2A
+#define D1FIFOSEL	0x2C
+#define D1FIFOCTR	0x2E
+#define INTENB0		0x30
+#define INTENB1		0x32
+#define INTENB2		0x34
+#define BRDYENB		0x36
+#define NRDYENB		0x38
+#define BEMPENB		0x3A
+#define SOFCFG		0x3C
+#define INTSTS0		0x40
+#define INTSTS1		0x42
+#define INTSTS2		0x44
+#define BRDYSTS		0x46
+#define NRDYSTS		0x48
+#define BEMPSTS		0x4A
+#define FRMNUM		0x4C
+#define UFRMNUM		0x4E
+#define USBADDR		0x50
+#define USBREQ		0x54
+#define USBVAL		0x56
+#define USBINDX		0x58
+#define USBLENG		0x5A
+#define DCPCFG		0x5C
+#define DCPMAXP		0x5E
+#define DCPCTR		0x60
+#define PIPESEL		0x64
+#define PIPECFG		0x68
+#define PIPEBUF		0x6A
+#define PIPEMAXP	0x6C
+#define PIPEPERI	0x6E
+#define PIPE1CTR	0x70
+#define PIPE2CTR	0x72
+#define PIPE3CTR	0x74
+#define PIPE4CTR	0x76
+#define PIPE5CTR	0x78
+#define PIPE6CTR	0x7A
+#define PIPE7CTR	0x7C
+#define PIPE8CTR	0x7E
+#define PIPE9CTR	0x80
+#define PIPE1TRE	0x90
+#define PIPE1TRN	0x92
+#define PIPE2TRE	0x94
+#define PIPE2TRN	0x96
+#define PIPE3TRE	0x98
+#define PIPE3TRN	0x9A
+#define PIPE4TRE	0x9C
+#define	PIPE4TRN	0x9E
+#define	PIPE5TRE	0xA0
+#define	PIPE5TRN	0xA2
+#define DEVADD0		0xD0
+#define DEVADD1		0xD2
+#define DEVADD2		0xD4
+#define DEVADD3		0xD6
+#define DEVADD4		0xD8
+#define DEVADD5		0xDA
+#define DEVADD6		0xDC
+#define DEVADD7		0xDE
+#define DEVADD8		0xE0
+#define DEVADD9		0xE2
+#define DEVADDA		0xE4
+
+/* System Configuration Control Register */
+#define	XTAL		0xC000	/* b15-14: Crystal selection */
+#define	  XTAL48	 0x8000	  /* 48MHz */
+#define	  XTAL24	 0x4000	  /* 24MHz */
+#define	  XTAL12	 0x0000	  /* 12MHz */
+#define	XCKE		0x2000	/* b13: External clock enable */
+#define	PLLC		0x0800	/* b11: PLL control */
+#define	SCKE		0x0400	/* b10: USB clock enable */
+#define	PCSDIS		0x0200	/* b9: not CS wakeup */
+#define	LPSME		0x0100	/* b8: Low power sleep mode */
+#define	HSE		0x0080	/* b7: Hi-speed enable */
+#define	DCFM		0x0040	/* b6: Controller function select  */
+#define	DRPD		0x0020	/* b5: D+/- pull down control */
+#define	DPRPU		0x0010	/* b4: D+ pull up control */
+#define	USBE		0x0001	/* b0: USB module operation enable */
+
+/* System Configuration Status Register */
+#define	OVCBIT		0x8000	/* b15-14: Over-current bit */
+#define	OVCMON		0xC000	/* b15-14: Over-current monitor */
+#define	SOFEA		0x0020	/* b5: SOF monitor */
+#define	IDMON		0x0004	/* b3: ID-pin monitor */
+#define	LNST		0x0003	/* b1-0: D+, D- line status */
+#define	  SE1		 0x0003	  /* SE1 */
+#define	  FS_KSTS	 0x0002	  /* Full-Speed K State */
+#define	  FS_JSTS	 0x0001	  /* Full-Speed J State */
+#define	  LS_JSTS	 0x0002	  /* Low-Speed J State */
+#define	  LS_KSTS	 0x0001	  /* Low-Speed K State */
+#define	  SE0		 0x0000	  /* SE0 */
+
+/* Device State Control Register */
+#define	EXTLP0		0x0400	/* b10: External port */
+#define	VBOUT		0x0200	/* b9: VBUS output */
+#define	WKUP		0x0100	/* b8: Remote wakeup */
+#define	RWUPE		0x0080	/* b7: Remote wakeup sense */
+#define	USBRST		0x0040	/* b6: USB reset enable */
+#define	RESUME		0x0020	/* b5: Resume enable */
+#define	UACT		0x0010	/* b4: USB bus enable */
+#define	RHST		0x0007	/* b1-0: Reset handshake status */
+#define	  HSPROC	 0x0004	  /* HS handshake is processing */
+#define	  HSMODE	 0x0003	  /* Hi-Speed mode */
+#define	  FSMODE	 0x0002	  /* Full-Speed mode */
+#define	  LSMODE	 0x0001	  /* Low-Speed mode */
+#define	  UNDECID	 0x0000	  /* Undecided */
+
+/* Test Mode Register */
+#define	UTST			0x000F	/* b3-0: Test select */
+#define	  H_TST_PACKET		 0x000C	  /* HOST TEST Packet */
+#define	  H_TST_SE0_NAK		 0x000B	  /* HOST TEST SE0 NAK */
+#define	  H_TST_K		 0x000A	  /* HOST TEST K */
+#define	  H_TST_J		 0x0009	  /* HOST TEST J */
+#define	  H_TST_NORMAL		 0x0000	  /* HOST Normal Mode */
+#define	  P_TST_PACKET		 0x0004	  /* PERI TEST Packet */
+#define	  P_TST_SE0_NAK		 0x0003	  /* PERI TEST SE0 NAK */
+#define	  P_TST_K		 0x0002	  /* PERI TEST K */
+#define	  P_TST_J		 0x0001	  /* PERI TEST J */
+#define	  P_TST_NORMAL		 0x0000	  /* PERI Normal Mode */
+
+/* Data Pin Configuration Register */
+#define	LDRV			0x8000	/* b15: Drive Current Adjust */
+#define	  VIF1			  0x0000		/* VIF = 1.8V */
+#define	  VIF3			  0x8000		/* VIF = 3.3V */
+#define	INTA			0x0001	/* b1: USB INT-pin active */
+
+/* DMAx Pin Configuration Register */
+#define	DREQA			0x4000	/* b14: Dreq active select */
+#define	BURST			0x2000	/* b13: Burst mode */
+#define	DACKA			0x0400	/* b10: Dack active select */
+#define	DFORM			0x0380	/* b9-7: DMA mode select */
+#define	  CPU_ADR_RD_WR		 0x0000	  /* Address + RD/WR mode (CPU bus) */
+#define	  CPU_DACK_RD_WR	 0x0100	  /* DACK + RD/WR mode (CPU bus) */
+#define	  CPU_DACK_ONLY		 0x0180	  /* DACK only mode (CPU bus) */
+#define	  SPLIT_DACK_ONLY	 0x0200	  /* DACK only mode (SPLIT bus) */
+#define	DENDA			0x0040	/* b6: Dend active select */
+#define	PKTM			0x0020	/* b5: Packet mode */
+#define	DENDE			0x0010	/* b4: Dend enable */
+#define	OBUS			0x0004	/* b2: OUTbus mode */
+
+/* CFIFO/DxFIFO Port Select Register */
+#define	RCNT		0x8000	/* b15: Read count mode */
+#define	REW		0x4000	/* b14: Buffer rewind */
+#define	DCLRM		0x2000	/* b13: DMA buffer clear mode */
+#define	DREQE		0x1000	/* b12: DREQ output enable */
+#define	MBW		0x0400	/* b10: Maximum bit width for FIFO access */
+#define	  MBW_8		 0x0000	  /*  8bit */
+#define	  MBW_16	 0x0400	  /* 16bit */
+#define	BIGEND		0x0100	/* b8: Big endian mode */
+#define	  BYTE_LITTLE	 0x0000		/* little dendian */
+#define	  BYTE_BIG	 0x0100		/* big endifan */
+#define	ISEL		0x0020	/* b5: DCP FIFO port direction select */
+#define	CURPIPE		0x000F	/* b2-0: PIPE select */
+
+/* CFIFO/DxFIFO Port Control Register */
+#define	BVAL		0x8000	/* b15: Buffer valid flag */
+#define	BCLR		0x4000	/* b14: Buffer clear */
+#define	FRDY		0x2000	/* b13: FIFO ready */
+#define	DTLN		0x0FFF	/* b11-0: FIFO received data length */
+
+/* Interrupt Enable Register 0 */
+#define	VBSE		0x8000	/* b15: VBUS interrupt */
+#define	RSME		0x4000	/* b14: Resume interrupt */
+#define	SOFE		0x2000	/* b13: Frame update interrupt */
+#define	DVSE		0x1000	/* b12: Device state transition interrupt */
+#define	CTRE		0x0800	/* b11: Control transfer stage transition interrupt */
+#define	BEMPE		0x0400	/* b10: Buffer empty interrupt */
+#define	NRDYE		0x0200	/* b9: Buffer not ready interrupt */
+#define	BRDYE		0x0100	/* b8: Buffer ready interrupt */
+
+/* Interrupt Enable Register 1 */
+#define	OVRCRE		0x8000	/* b15: Over-current interrupt */
+#define	BCHGE		0x4000	/* b14: USB us chenge interrupt */
+#define	DTCHE		0x1000	/* b12: Detach sense interrupt */
+#define	ATTCHE		0x0800	/* b11: Attach sense interrupt */
+#define	EOFERRE		0x0040	/* b6: EOF error interrupt */
+#define	SIGNE		0x0020	/* b5: SETUP IGNORE interrupt */
+#define	SACKE		0x0010	/* b4: SETUP ACK interrupt */
+
+/* BRDY Interrupt Enable/Status Register */
+#define	BRDY9		0x0200	/* b9: PIPE9 */
+#define	BRDY8		0x0100	/* b8: PIPE8 */
+#define	BRDY7		0x0080	/* b7: PIPE7 */
+#define	BRDY6		0x0040	/* b6: PIPE6 */
+#define	BRDY5		0x0020	/* b5: PIPE5 */
+#define	BRDY4		0x0010	/* b4: PIPE4 */
+#define	BRDY3		0x0008	/* b3: PIPE3 */
+#define	BRDY2		0x0004	/* b2: PIPE2 */
+#define	BRDY1		0x0002	/* b1: PIPE1 */
+#define	BRDY0		0x0001	/* b1: PIPE0 */
+
+/* NRDY Interrupt Enable/Status Register */
+#define	NRDY9		0x0200	/* b9: PIPE9 */
+#define	NRDY8		0x0100	/* b8: PIPE8 */
+#define	NRDY7		0x0080	/* b7: PIPE7 */
+#define	NRDY6		0x0040	/* b6: PIPE6 */
+#define	NRDY5		0x0020	/* b5: PIPE5 */
+#define	NRDY4		0x0010	/* b4: PIPE4 */
+#define	NRDY3		0x0008	/* b3: PIPE3 */
+#define	NRDY2		0x0004	/* b2: PIPE2 */
+#define	NRDY1		0x0002	/* b1: PIPE1 */
+#define	NRDY0		0x0001	/* b1: PIPE0 */
+
+/* BEMP Interrupt Enable/Status Register */
+#define	BEMP9		0x0200	/* b9: PIPE9 */
+#define	BEMP8		0x0100	/* b8: PIPE8 */
+#define	BEMP7		0x0080	/* b7: PIPE7 */
+#define	BEMP6		0x0040	/* b6: PIPE6 */
+#define	BEMP5		0x0020	/* b5: PIPE5 */
+#define	BEMP4		0x0010	/* b4: PIPE4 */
+#define	BEMP3		0x0008	/* b3: PIPE3 */
+#define	BEMP2		0x0004	/* b2: PIPE2 */
+#define	BEMP1		0x0002	/* b1: PIPE1 */
+#define	BEMP0		0x0001	/* b0: PIPE0 */
+
+/* SOF Pin Configuration Register */
+#define	TRNENSEL	0x0100	/* b8: Select transaction enable period */
+#define	BRDYM		0x0040	/* b6: BRDY clear timing */
+#define	INTL		0x0020	/* b5: Interrupt sense select */
+#define	EDGESTS		0x0010	/* b4:  */
+#define	SOFMODE		0x000C	/* b3-2: SOF pin select */
+#define	  SOF_125US	 0x0008	  /* SOF OUT 125us Frame Signal */
+#define	  SOF_1MS	 0x0004	  /* SOF OUT 1ms Frame Signal */
+#define	  SOF_DISABLE	 0x0000	  /* SOF OUT Disable */
+
+/* Interrupt Status Register 0 */
+#define	VBINT		0x8000	/* b15: VBUS interrupt */
+#define	RESM		0x4000	/* b14: Resume interrupt */
+#define	SOFR		0x2000	/* b13: SOF frame update interrupt */
+#define	DVST		0x1000	/* b12: Device state transition interrupt */
+#define	CTRT		0x0800	/* b11: Control transfer stage transition interrupt */
+#define	BEMP		0x0400	/* b10: Buffer empty interrupt */
+#define	NRDY		0x0200	/* b9: Buffer not ready interrupt */
+#define	BRDY		0x0100	/* b8: Buffer ready interrupt */
+#define	VBSTS		0x0080	/* b7: VBUS input port */
+#define	DVSQ		0x0070	/* b6-4: Device state */
+#define	  DS_SPD_CNFG	 0x0070	  /* Suspend Configured */
+#define	  DS_SPD_ADDR	 0x0060	  /* Suspend Address */
+#define	  DS_SPD_DFLT	 0x0050	  /* Suspend Default */
+#define	  DS_SPD_POWR	 0x0040	  /* Suspend Powered */
+#define	  DS_SUSP	 0x0040	  /* Suspend */
+#define	  DS_CNFG	 0x0030	  /* Configured */
+#define	  DS_ADDS	 0x0020	  /* Address */
+#define	  DS_DFLT	 0x0010	  /* Default */
+#define	  DS_POWR	 0x0000	  /* Powered */
+#define	DVSQS		0x0030	/* b5-4: Device state */
+#define	VALID		0x0008	/* b3: Setup packet detected flag */
+#define	CTSQ		0x0007	/* b2-0: Control transfer stage */
+#define	  CS_SQER	 0x0006	  /* Sequence error */
+#define	  CS_WRND	 0x0005	  /* Control write nodata status stage */
+#define	  CS_WRSS	 0x0004	  /* Control write status stage */
+#define	  CS_WRDS	 0x0003	  /* Control write data stage */
+#define	  CS_RDSS	 0x0002	  /* Control read status stage */
+#define	  CS_RDDS	 0x0001	  /* Control read data stage */
+#define	  CS_IDST	 0x0000	  /* Idle or setup stage */
+
+/* Interrupt Status Register 1 */
+#define	OVRCR		0x8000	/* b15: Over-current interrupt */
+#define	BCHG		0x4000	/* b14: USB bus chenge interrupt */
+#define	DTCH		0x1000	/* b12: Detach sense interrupt */
+#define	ATTCH		0x0800	/* b11: Attach sense interrupt */
+#define	EOFERR		0x0040	/* b6: EOF-error interrupt */
+#define	SIGN		0x0020	/* b5: Setup ignore interrupt */
+#define	SACK		0x0010	/* b4: Setup acknowledge interrupt */
+
+/* Frame Number Register */
+#define	OVRN		0x8000	/* b15: Overrun error */
+#define	CRCE		0x4000	/* b14: Received data error */
+#define	FRNM		0x07FF	/* b10-0: Frame number */
+
+/* Micro Frame Number Register */
+#define	UFRNM		0x0007	/* b2-0: Micro frame number */
+
+/* USB Address / Low Power Status Recovery Register */
+//#define	USBADDR		0x007F	/* b6-0: USB address */
+
+/* Default Control Pipe Maxpacket Size Register */
+/* Pipe Maxpacket Size Register */
+#define	DEVSEL		0xF000	/* b15-14: Device address select */
+#define	MAXP		0x007F	/* b6-0: Maxpacket size of default control pipe */
+
+/* Default Control Pipe Control Register */
+#define	BSTS		0x8000	/* b15: Buffer status */
+#define	SUREQ		0x4000	/* b14: Send USB request  */
+#define	CSCLR		0x2000	/* b13: complete-split status clear */
+#define	CSSTS		0x1000	/* b12: complete-split status */
+#define	SUREQCLR	0x0800	/* b11: stop setup request */
+#define	SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	PBUSY		0x0020	/* b5: pipe busy */
+#define	PINGE		0x0010	/* b4: ping enable */
+#define	CCPL		0x0004	/* b2: Enable control transfer complete */
+#define	PID		0x0003	/* b1-0: Response PID */
+#define	  PID_STALL11	 0x0003	  /* STALL */
+#define	  PID_STALL	 0x0002	  /* STALL */
+#define	  PID_BUF	 0x0001	  /* BUF */
+#define	  PID_NAK	 0x0000	  /* NAK */
+
+/* Pipe Window Select Register */
+#define	PIPENM		0x0007	/* b2-0: Pipe select */
+
+/* Pipe Configuration Register */
+#define	R8A66597_TYP	0xC000	/* b15-14: Transfer type */
+#define	  R8A66597_ISO	 0xC000		  /* Isochronous */
+#define	  R8A66597_INT	 0x8000		  /* Interrupt */
+#define	  R8A66597_BULK	 0x4000		  /* Bulk */
+#define	R8A66597_BFRE	0x0400	/* b10: Buffer ready interrupt mode select */
+#define	R8A66597_DBLB	0x0200	/* b9: Double buffer mode select */
+#define	R8A66597_CNTMD	0x0100	/* b8: Continuous transfer mode select */
+#define	R8A66597_SHTNAK	0x0080	/* b7: Transfer end NAK */
+#define	R8A66597_DIR	0x0010	/* b4: Transfer direction select */
+#define	R8A66597_EPNUM	0x000F	/* b3-0: Eendpoint number select */
+
+/* Pipe Buffer Configuration Register */
+#define	BUFSIZE		0x7C00	/* b14-10: Pipe buffer size */
+#define	BUFNMB		0x007F	/* b6-0: Pipe buffer number */
+#define	PIPE0BUF	256
+#define	PIPExBUF	64
+
+/* Pipe Maxpacket Size Register */
+#define	MXPS		0x07FF	/* b10-0: Maxpacket size */
+
+/* Pipe Cycle Configuration Register */
+#define	IFIS		0x1000	/* b12: Isochronous in-buffer flush mode select */
+#define	IITV		0x0007	/* b2-0: Isochronous interval */
+
+/* Pipex Control Register */
+#define	BSTS		0x8000	/* b15: Buffer status */
+#define	INBUFM		0x4000	/* b14: IN buffer monitor (Only for PIPE1 to 5) */
+#define	CSCLR		0x2000	/* b13: complete-split status clear */
+#define	CSSTS		0x1000	/* b12: complete-split status */
+#define	ATREPM		0x0400	/* b10: Auto repeat mode */
+#define	ACLRM		0x0200	/* b9: Out buffer auto clear mode */
+#define	SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	PBUSY		0x0020	/* b5: pipe busy */
+#define	PID		0x0003	/* b1-0: Response PID */
+
+/* PIPExTRE */
+#define	TRENB		0x0200	/* b9: Transaction counter enable */
+#define	TRCLR		0x0100	/* b8: Transaction counter clear */
+
+/* PIPExTRN */
+#define	TRNCNT		0xFFFF	/* b15-0: Transaction counter */
+
+/* DEVADDx */
+#define	UPPHUB		0x7800
+#define	HUBPORT		0x0700
+#define	USBSPD		0x00C0
+#define	RTPORT		0x0001
+
+#define R8A66597_MAX_NUM_PIPE		10
+#define R8A66597_BUF_BSIZE		8
+#define R8A66597_MAX_DEVICE		10
+#define R8A66597_MAX_ROOT_HUB		2
+#define R8A66597_MAX_SAMPLING		10
+#define R8A66597_MAX_DMA_CHANNEL	2
+#define R8A66597_PIPE_NO_DMA		R8A66597_MAX_DMA_CHANNEL
+#define check_bulk_or_isoc(pipenum)	((pipenum >= 1 && pipenum <= 5))
+#define check_interrupt(pipenum)	((pipenum >= 6 && pipenum <= 9))
+#define make_devsel(addr)		(addr << 12)
+
+struct r8a66597_pipe_info {
+        u16 pipenum;
+        u16 address;	/* R8A66597 HCD usb addres */
+        u16 epnum;
+        u16 maxpacket;
+        u16 type;
+        u16 bufnum;
+        u16 buf_bsize;
+        u16 interval;
+        u16 dir_in;
+};
+
+struct r8a66597_pipe {
+	struct r8a66597_pipe_info info;
+
+	unsigned long fifoaddr;
+	unsigned long fifosel;
+	unsigned long fifoctr;
+	unsigned long pipectr;
+	unsigned long pipetre;
+	unsigned long pipetrn;
+};
+
+struct r8a66597_td {
+	struct r8a66597_pipe *pipe;
+	struct urb *urb;
+	struct list_head queue;
+
+	u16 type;
+	u16 pipenum;
+	int iso_cnt;
+
+	u16 address;		/* R8A66597's USB address */
+	u16 maxpacket;
+
+	unsigned zero_packet:1;
+	unsigned short_packet:1;
+	unsigned set_address:1;
+};
+
+struct r8a66597_device {
+	u16	address;	/* R8A66597's USB address */
+	u16	hub_port;
+	u16	root_port;
+
+	unsigned short ep_in_toggle;
+	unsigned short ep_out_toggle;
+	unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
+	unsigned char dma_map;
+
+	enum usb_device_state state;
+
+	struct usb_device *udev;
+	int usb_address;
+	struct list_head device_list;
+};
+
+struct r8a66597_root_hub {
+	u32 port;
+	u16 old_syssts;
+	int scount;
+
+	struct r8a66597_device	*dev;
+};
+
+struct r8a66597 {
+	spinlock_t lock;
+	unsigned long reg;
+
+	struct r8a66597_device		device0;
+	struct r8a66597_root_hub	root_hub[R8A66597_MAX_ROOT_HUB];
+	struct list_head		pipe_queue[R8A66597_MAX_NUM_PIPE];
+
+	struct timer_list rh_timer;
+	struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
+
+	unsigned short address_map;
+	unsigned short timeout_map;
+	unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
+	unsigned char dma_map;
+
+	struct list_head child_device;
+	unsigned long child_connect_map[4];
+};
+
+static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)
+{
+	return (struct r8a66597 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *r8a66597_to_hcd(struct r8a66597 *r8a66597)
+{
+	return container_of((void *)r8a66597, struct usb_hcd, hcd_priv);
+}
+
+static inline struct r8a66597_td *r8a66597_get_td(struct r8a66597 *r8a66597,
+						  u16 pipenum)
+{
+	if (unlikely(list_empty(&r8a66597->pipe_queue[pipenum])))
+		return NULL;
+
+	return list_entry(r8a66597->pipe_queue[pipenum].next,
+			  struct r8a66597_td, queue);
+}
+
+static inline struct urb *r8a66597_get_urb(struct r8a66597 *r8a66597,
+					   u16 pipenum)
+{
+	struct r8a66597_td *td;
+
+	td = r8a66597_get_td(r8a66597, pipenum);
+	return (td ? td->urb : NULL);
+}
+
+static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
+{
+	return inw(r8a66597->reg + offset);
+}
+
+static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
+				      unsigned long offset, u16 *buf,
+				      int len)
+{
+	len = (len + 1) / 2;
+	insw(r8a66597->reg + offset, buf, len);
+}
+
+static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
+				  unsigned long offset)
+{
+	outw(val, r8a66597->reg + offset);
+}
+
+static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
+				       unsigned long offset, u16 *buf,
+				       int len)
+{
+	unsigned long fifoaddr = r8a66597->reg + offset;
+	int odd = len & 0x0001;
+
+	len = len / 2;
+	outsw(fifoaddr, buf, len);
+	if (unlikely(odd)) {
+		buf = &buf[len];
+		outb((unsigned char)*buf, fifoaddr);
+	}
+}
+
+static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
+				 u16 val, u16 pat, unsigned long offset)
+{
+	u16 tmp;
+	tmp = r8a66597_read(r8a66597, offset);
+	tmp = tmp & (~pat);
+	tmp = tmp | val;
+	r8a66597_write(r8a66597, tmp, offset);
+}
+
+#define r8a66597_bclr(r8a66597, val, offset)	\
+			r8a66597_mdfy(r8a66597, 0, val, offset)
+#define r8a66597_bset(r8a66597, val, offset)	\
+			r8a66597_mdfy(r8a66597, val, 0, offset)
+
+static inline unsigned long get_syscfg_reg(int port)
+{
+	return port == 0 ? SYSCFG0 : SYSCFG1;
+}
+
+static inline unsigned long get_syssts_reg(int port)
+{
+	return port == 0 ? SYSSTS0 : SYSSTS1;
+}
+
+static inline unsigned long get_dvstctr_reg(int port)
+{
+	return port == 0 ? DVSTCTR0 : DVSTCTR1;
+}
+
+static inline unsigned long get_intenb_reg(int port)
+{
+	return port == 0 ? INTENB1 : INTENB2;
+}
+
+static inline unsigned long get_intsts_reg(int port)
+{
+	return port == 0 ? INTSTS1 : INTSTS2;
+}
+
+static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
+{
+	unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+	return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
+}
+
+static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
+				       int power)
+{
+	unsigned long dvstctr_reg = get_dvstctr_reg(port);
+
+	if (power)
+		r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
+	else
+		r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
+}
+
+#define get_pipectr_addr(pipenum)	(PIPE1CTR + (pipenum - 1) * 2)
+#define get_pipetre_addr(pipenum)	(PIPE1TRE + (pipenum - 1) * 4)
+#define get_pipetrn_addr(pipenum)	(PIPE1TRN + (pipenum - 1) * 4)
+#define get_devadd_addr(address)	(DEVADD0 + address * 2)
+
+#define enable_irq_ready(r8a66597, pipenum)	\
+	enable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define disable_irq_ready(r8a66597, pipenum)	\
+	disable_pipe_irq(r8a66597, pipenum, BRDYENB)
+#define enable_irq_empty(r8a66597, pipenum)	\
+	enable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define disable_irq_empty(r8a66597, pipenum)	\
+	disable_pipe_irq(r8a66597, pipenum, BEMPENB)
+#define enable_irq_nrdy(r8a66597, pipenum)	\
+	enable_pipe_irq(r8a66597, pipenum, NRDYENB)
+#define disable_irq_nrdy(r8a66597, pipenum)	\
+	disable_pipe_irq(r8a66597, pipenum, NRDYENB)
+
+#endif	/* __R8A66597_H__ */
+
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index d22da26..76c555a 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -730,10 +730,9 @@
 	int rc = 0;
 
 	spin_lock_irq(&uhci->lock);
-	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
-		dev_warn(&hcd->self.root_hub->dev, "HC isn't running!\n");
+	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
 		rc = -ESHUTDOWN;
-	} else if (!uhci->dead)
+	else if (!uhci->dead)
 		wakeup_rh(uhci);
 	spin_unlock_irq(&uhci->lock);
 	return rc;
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 77145f9..d72c42e 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -108,8 +108,6 @@
 	struct urb*		interrupt_out_urb;
 };
 
-/* prevent races between open() and disconnect */
-static DEFINE_MUTEX(disconnect_mutex);
 static struct usb_driver adu_driver;
 
 static void adu_debug_data(int level, const char *function, int size,
@@ -256,8 +254,6 @@
 
 	subminor = iminor(inode);
 
-	mutex_lock(&disconnect_mutex);
-
 	interface = usb_find_interface(&adu_driver, subminor);
 	if (!interface) {
 		err("%s - error, can't find device for minor %d",
@@ -306,7 +302,6 @@
 	up(&dev->sem);
 
 exit_no_device:
-	mutex_unlock(&disconnect_mutex);
 	dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
 
 	return retval;
@@ -318,12 +313,6 @@
 
 	dbg(2," %s : enter", __FUNCTION__);
 
-	if (dev->udev == NULL) {
-		/* the device was unplugged before the file was released */
-		adu_delete(dev);
-		goto exit;
-	}
-
 	/* decrement our usage count for the device */
 	--dev->open_count;
 	dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
@@ -332,7 +321,6 @@
 		dev->open_count = 0;
 	}
 
-exit:
 	dbg(2," %s : leave", __FUNCTION__);
 	return retval;
 }
@@ -367,8 +355,15 @@
 		goto exit;
 	}
 
-	/* do the work */
-	retval = adu_release_internal(dev);
+	if (dev->udev == NULL) {
+		/* the device was unplugged before the file was released */
+		up(&dev->sem);
+		adu_delete(dev);
+		dev = NULL;
+	} else {
+		/* do the work */
+		retval = adu_release_internal(dev);
+	}
 
 exit:
 	if (dev)
@@ -831,19 +826,17 @@
 
 	dbg(2," %s : enter", __FUNCTION__);
 
-	mutex_lock(&disconnect_mutex); /* not interruptible */
-
 	dev = usb_get_intfdata(interface);
 	usb_set_intfdata(interface, NULL);
 
-	down(&dev->sem); /* not interruptible */
-
 	minor = dev->minor;
 
 	/* give back our minor */
 	usb_deregister_dev(interface, &adu_class);
 	dev->minor = 0;
 
+	down(&dev->sem); /* not interruptible */
+
 	/* if the device is not opened, then we clean up right now */
 	dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
 	if (!dev->open_count) {
@@ -854,8 +847,6 @@
 		up(&dev->sem);
 	}
 
-	mutex_unlock(&disconnect_mutex);
-
 	dev_info(&interface->dev, "ADU device adutux%d now disconnected",
 		 (minor - ADU_MINOR_BASE));
 
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index cac1500..1fd5fc2 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -2034,12 +2034,12 @@
 	if (!cp)
 		return;
 
-	down (&cp->mutex);
-	info ("device /dev/%s now disconnecting", cp->name);
-
 	/* give back our USB minor number */
 	usb_deregister_dev(intf, &auerswald_class);
 
+	down (&cp->mutex);
+	info ("device /dev/%s now disconnecting", cp->name);
+
 	/* Stop the interrupt endpoint */
 	auerswald_int_release (cp);
 
diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c
index b15f2fd..92c1d27 100644
--- a/drivers/usb/misc/berry_charge.c
+++ b/drivers/usb/misc/berry_charge.c
@@ -26,8 +26,11 @@
 
 #define RIM_VENDOR		0x0fca
 #define BLACKBERRY		0x0001
+#define BLACKBERRY_PEARL_DUAL   0x0004
+#define BLACKBERRY_PEARL        0x0006
 
 static int debug;
+static int pearl_dual_mode = 1;
 
 #ifdef dbg
 #undef dbg
@@ -38,6 +41,8 @@
 
 static struct usb_device_id id_table [] = {
 	{ USB_DEVICE(RIM_VENDOR, BLACKBERRY) },
+	{ USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL) },
+	{ USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL_DUAL) },
 	{ },					/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, id_table);
@@ -86,6 +91,30 @@
 	return retval;
 }
 
+static int magic_dual_mode(struct usb_device *udev)
+{
+	char *dummy_buffer = kzalloc(2, GFP_KERNEL);
+	int retval;
+
+	if (!dummy_buffer)
+		return -ENOMEM;
+
+	/* send magic command so that the Blackberry Pearl device exposes
+	 * two interfaces: both the USB mass-storage one and one which can
+	 * be used for database access. */
+	dbg(&udev->dev, "Sending magic pearl command\n");
+	retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				 0xa9, 0xc0, 1, 1, dummy_buffer, 2, 100);
+	dbg(&udev->dev, "Magic pearl command returned %d\n", retval);
+
+	dbg(&udev->dev, "Calling set_configuration\n");
+	retval = usb_driver_set_configuration(udev, 1);
+	if (retval)
+		dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval);
+
+	return retval;
+}
+
 static int berry_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
 {
@@ -105,6 +134,10 @@
 	/* turn the power on */
 	magic_charge(udev);
 
+	if ((le16_to_cpu(udev->descriptor.idProduct) == BLACKBERRY_PEARL) &&
+	    (pearl_dual_mode))
+		magic_dual_mode(udev);
+
 	/* we don't really want to bind to the device, userspace programs can
 	 * handle the syncing just fine, so get outta here. */
 	return -ENODEV;
@@ -138,3 +171,5 @@
 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
 module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(pearl_dual_mode, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pearl_dual_mode, "Change Blackberry Pearl to run in dual mode");
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 8d0e360..e6fd024 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -119,9 +119,6 @@
 	.id_table = idmouse_table,
 };
 
-/* prevent races between open() and disconnect() */
-static DEFINE_MUTEX(disconnect_mutex);
-
 static int idmouse_create_image(struct usb_idmouse *dev)
 {
 	int bytes_read;
@@ -211,21 +208,15 @@
 	struct usb_interface *interface;
 	int result;
 
-	/* prevent disconnects */
-	mutex_lock(&disconnect_mutex);
-
 	/* get the interface from minor number and driver information */
 	interface = usb_find_interface (&idmouse_driver, iminor (inode));
-	if (!interface) {
-		mutex_unlock(&disconnect_mutex);
+	if (!interface)
 		return -ENODEV;
-	}
+
 	/* get the device information block from the interface */
 	dev = usb_get_intfdata(interface);
-	if (!dev) {
-		mutex_unlock(&disconnect_mutex);
+	if (!dev)
 		return -ENODEV;
-	}
 
 	/* lock this device */
 	down(&dev->sem);
@@ -255,9 +246,6 @@
 
 	/* unlock this device */
 	up(&dev->sem);
-
-	/* unlock the disconnect semaphore */
-	mutex_unlock(&disconnect_mutex);
 	return result;
 }
 
@@ -265,15 +253,10 @@
 {
 	struct usb_idmouse *dev;
 
-	/* prevent a race condition with open() */
-	mutex_lock(&disconnect_mutex);
-
 	dev = file->private_data;
 
-	if (dev == NULL) {
-		mutex_unlock(&disconnect_mutex);
+	if (dev == NULL)
 		return -ENODEV;
-	}
 
 	/* lock our device */
 	down(&dev->sem);
@@ -281,7 +264,6 @@
 	/* are we really open? */
 	if (dev->open <= 0) {
 		up(&dev->sem);
-		mutex_unlock(&disconnect_mutex);
 		return -ENODEV;
 	}
 
@@ -291,12 +273,9 @@
 		/* the device was unplugged before the file was released */
 		up(&dev->sem);
 		idmouse_delete(dev);
-		mutex_unlock(&disconnect_mutex);
-		return 0;
+	} else {
+		up(&dev->sem);
 	}
-
-	up(&dev->sem);
-	mutex_unlock(&disconnect_mutex);
 	return 0;
 }
 
@@ -391,30 +370,27 @@
 {
 	struct usb_idmouse *dev;
 
-	/* prevent races with open() */
-	mutex_lock(&disconnect_mutex);
-
 	/* get device structure */
 	dev = usb_get_intfdata(interface);
 	usb_set_intfdata(interface, NULL);
 
-	/* lock it */
-	down(&dev->sem);
-
 	/* give back our minor */
 	usb_deregister_dev(interface, &idmouse_class);
 
+	/* lock it */
+	down(&dev->sem);
+
 	/* prevent device read, write and ioctl */
 	dev->present = 0;
 
-	/* unlock */
-	up(&dev->sem);
-
 	/* if the device is opened, idmouse_release will clean this up */
-	if (!dev->open)
+	if (!dev->open) {
+		up(&dev->sem);
 		idmouse_delete(dev);
-
-	mutex_unlock(&disconnect_mutex);
+	} else {
+		/* unlock */
+		up(&dev->sem);
+	}
 
 	info("%s disconnected", DRIVER_DESC);
 }
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 3bb33f7..28548d1 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -100,8 +100,6 @@
 /*--------------*/
 /*    globals   */
 /*--------------*/
-/* prevent races between open() and disconnect() */
-static DECLARE_MUTEX(disconnect_sem);
 
 /*
  *  USB spec identifies 5 second timeouts.
@@ -600,22 +598,18 @@
 
 	subminor = iminor(inode);
 
-	/* prevent disconnects */
-	down(&disconnect_sem);
-
 	interface = usb_find_interface(&iowarrior_driver, subminor);
 	if (!interface) {
 		err("%s - error, can't find device for minor %d", __FUNCTION__,
 		    subminor);
-		retval = -ENODEV;
-		goto out;
+		return -ENODEV;
 	}
 
 	dev = usb_get_intfdata(interface);
-	if (!dev) {
-		retval = -ENODEV;
-		goto out;
-	}
+	if (!dev)
+		return -ENODEV;
+
+	mutex_lock(&dev->mutex);
 
 	/* Only one process can open each device, no sharing. */
 	if (dev->opened) {
@@ -636,7 +630,7 @@
 	retval = 0;
 
 out:
-	up(&disconnect_sem);
+	mutex_unlock(&dev->mutex);
 	return retval;
 }
 
@@ -868,19 +862,16 @@
 	struct iowarrior *dev;
 	int minor;
 
-	/* prevent races with open() */
-	down(&disconnect_sem);
-
 	dev = usb_get_intfdata(interface);
 	usb_set_intfdata(interface, NULL);
 
-	mutex_lock(&dev->mutex);
-
 	minor = dev->minor;
 
 	/* give back our minor */
 	usb_deregister_dev(interface, &iowarrior_class);
 
+	mutex_lock(&dev->mutex);
+
 	/* prevent device read, write and ioctl */
 	dev->present = 0;
 
@@ -898,7 +889,6 @@
 		/* no process is using the device, cleanup now */
 		iowarrior_delete(dev);
 	}
-	up(&disconnect_sem);
 
 	dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
 		 minor - IOWARRIOR_MINOR_BASE);
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 7bad494..5e950b9 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -176,9 +176,6 @@
 	int			interrupt_out_busy;
 };
 
-/* prevent races between open() and disconnect() */
-static DEFINE_MUTEX(disconnect_mutex);
-
 static struct usb_driver ld_usb_driver;
 
 /**
@@ -298,35 +295,28 @@
 {
 	struct ld_usb *dev;
 	int subminor;
-	int retval = 0;
+	int retval;
 	struct usb_interface *interface;
 
 	nonseekable_open(inode, file);
 	subminor = iminor(inode);
 
-	mutex_lock(&disconnect_mutex);
-
 	interface = usb_find_interface(&ld_usb_driver, subminor);
 
 	if (!interface) {
 		err("%s - error, can't find device for minor %d\n",
 		     __FUNCTION__, subminor);
-		retval = -ENODEV;
-		goto unlock_disconnect_exit;
+		return -ENODEV;
 	}
 
 	dev = usb_get_intfdata(interface);
 
-	if (!dev) {
-		retval = -ENODEV;
-		goto unlock_disconnect_exit;
-	}
+	if (!dev)
+		return -ENODEV;
 
 	/* lock this device */
-	if (down_interruptible(&dev->sem)) {
-		retval = -ERESTARTSYS;
-		goto unlock_disconnect_exit;
-	}
+	if (down_interruptible(&dev->sem))
+		return -ERESTARTSYS;
 
 	/* allow opening only once */
 	if (dev->open_count) {
@@ -366,9 +356,6 @@
 unlock_exit:
 	up(&dev->sem);
 
-unlock_disconnect_exit:
-	mutex_unlock(&disconnect_mutex);
-
 	return retval;
 }
 
@@ -766,18 +753,16 @@
 	struct ld_usb *dev;
 	int minor;
 
-	mutex_lock(&disconnect_mutex);
-
 	dev = usb_get_intfdata(intf);
 	usb_set_intfdata(intf, NULL);
 
-	down(&dev->sem);
-
 	minor = intf->minor;
 
 	/* give back our minor */
 	usb_deregister_dev(intf, &ld_usb_class);
 
+	down(&dev->sem);
+
 	/* if the device is not opened, then we clean up right now */
 	if (!dev->open_count) {
 		up(&dev->sem);
@@ -787,8 +772,6 @@
 		up(&dev->sem);
 	}
 
-	mutex_unlock(&disconnect_mutex);
-
 	dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
 		 (minor - USB_LD_MINOR_BASE));
 }
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 1713e19..2ed0dae 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -254,9 +254,6 @@
 static void tower_disconnect	(struct usb_interface *interface);
 
 
-/* prevent races between open() and disconnect */
-static DEFINE_MUTEX (disconnect_mutex);
-
 /* file operations needed when we register this driver */
 static const struct file_operations tower_fops = {
 	.owner =	THIS_MODULE,
@@ -344,28 +341,26 @@
 	nonseekable_open(inode, file);
 	subminor = iminor(inode);
 
-	mutex_lock (&disconnect_mutex);
-
 	interface = usb_find_interface (&tower_driver, subminor);
 
 	if (!interface) {
 		err ("%s - error, can't find device for minor %d",
 		     __FUNCTION__, subminor);
 		retval = -ENODEV;
-		goto unlock_disconnect_exit;
+		goto exit;
 	}
 
 	dev = usb_get_intfdata(interface);
 
 	if (!dev) {
 		retval = -ENODEV;
-		goto unlock_disconnect_exit;
+		goto exit;
 	}
 
 	/* lock this device */
 	if (down_interruptible (&dev->sem)) {
 	        retval = -ERESTARTSYS;
-		goto unlock_disconnect_exit;
+		goto exit;
 	}
 
 	/* allow opening only once */
@@ -421,9 +416,7 @@
 unlock_exit:
 	up (&dev->sem);
 
-unlock_disconnect_exit:
-	mutex_unlock (&disconnect_mutex);
-
+exit:
 	dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval);
 
 	return retval;
@@ -993,19 +986,16 @@
 
 	dbg(2, "%s: enter", __FUNCTION__);
 
-	mutex_lock (&disconnect_mutex);
-
 	dev = usb_get_intfdata (interface);
 	usb_set_intfdata (interface, NULL);
 
-
-	down (&dev->sem);
-
 	minor = dev->minor;
 
 	/* give back our minor */
 	usb_deregister_dev (interface, &tower_class);
 
+	down (&dev->sem);
+
 	/* if the device is not opened, then we clean up right now */
 	if (!dev->open_count) {
 		up (&dev->sem);
@@ -1015,8 +1005,6 @@
 		up (&dev->sem);
 	}
 
-	mutex_unlock (&disconnect_mutex);
-
 	info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
 
 	dbg(2, "%s: leave", __FUNCTION__);
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 6f8b134..9f37ba4 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -72,8 +72,6 @@
 
 static struct usb_driver sisusb_driver;
 
-DEFINE_MUTEX(disconnect_mutex);
-
 static void
 sisusb_free_buffers(struct sisusb_usb_data *sisusb)
 {
@@ -2511,31 +2509,24 @@
 	struct usb_interface *interface;
 	int subminor = iminor(inode);
 
-	mutex_lock(&disconnect_mutex);
-
 	if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
 		printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
 				subminor);
-		mutex_unlock(&disconnect_mutex);
 		return -ENODEV;
 	}
 
-	if (!(sisusb = usb_get_intfdata(interface))) {
-		mutex_unlock(&disconnect_mutex);
+	if (!(sisusb = usb_get_intfdata(interface)))
 		return -ENODEV;
-	}
 
 	mutex_lock(&sisusb->lock);
 
 	if (!sisusb->present || !sisusb->ready) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		return -ENODEV;
 	}
 
 	if (sisusb->isopen) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		return -EBUSY;
 	}
 
@@ -2543,7 +2534,6 @@
 		if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
 			if (sisusb_init_gfxdevice(sisusb, 0)) {
 				mutex_unlock(&sisusb->lock);
-				mutex_unlock(&disconnect_mutex);
 				printk(KERN_ERR
 					"sisusbvga[%d]: Failed to initialize "
 					"device\n",
@@ -2552,7 +2542,6 @@
 			}
 		} else {
 			mutex_unlock(&sisusb->lock);
-			mutex_unlock(&disconnect_mutex);
 			printk(KERN_ERR
 				"sisusbvga[%d]: Device not attached to "
 				"USB 2.0 hub\n",
@@ -2570,8 +2559,6 @@
 
 	mutex_unlock(&sisusb->lock);
 
-	mutex_unlock(&disconnect_mutex);
-
 	return 0;
 }
 
@@ -2601,12 +2588,8 @@
 	struct sisusb_usb_data *sisusb;
 	int myminor;
 
-	mutex_lock(&disconnect_mutex);
-
-	if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
-		mutex_unlock(&disconnect_mutex);
+	if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
 		return -ENODEV;
-	}
 
 	mutex_lock(&sisusb->lock);
 
@@ -2626,8 +2609,6 @@
 	/* decrement the usage count on our device */
 	kref_put(&sisusb->kref, sisusb_delete);
 
-	mutex_unlock(&disconnect_mutex);
-
 	return 0;
 }
 
@@ -3383,12 +3364,9 @@
 	sisusb_console_exit(sisusb);
 #endif
 
-	/* The above code doesn't need the disconnect
-	 * semaphore to be down; its meaning is to
-	 * protect all other routines from the disconnect
-	 * case, not the other way round.
-	 */
-	mutex_lock(&disconnect_mutex);
+	minor = sisusb->minor;
+
+	usb_deregister_dev(intf, &usb_sisusb_class);
 
 	mutex_lock(&sisusb->lock);
 
@@ -3396,12 +3374,8 @@
 	if (!sisusb_wait_all_out_complete(sisusb))
 		sisusb_kill_all_busy(sisusb);
 
-	minor = sisusb->minor;
-
 	usb_set_intfdata(intf, NULL);
 
-	usb_deregister_dev(intf, &usb_sisusb_class);
-
 #ifdef SISUSB_OLD_CONFIG_COMPAT
 	if (sisusb->ioctl32registered) {
 		int ret;
@@ -3426,8 +3400,6 @@
 	/* decrement our usage count */
 	kref_put(&sisusb->kref, sisusb_delete);
 
-	mutex_unlock(&disconnect_mutex);
-
 	printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
 }
 
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index 5947afb..8d0edc8 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -214,18 +214,13 @@
 	 * are set up/restored.
 	 */
 
-	mutex_lock(&disconnect_mutex);
-
-	if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
-		mutex_unlock(&disconnect_mutex);
+	if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
 		return;
-	}
 
 	mutex_lock(&sisusb->lock);
 
 	if (!sisusb_sisusb_valid(sisusb)) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		return;
 	}
 
@@ -264,8 +259,6 @@
 
 	mutex_unlock(&sisusb->lock);
 
-	mutex_unlock(&disconnect_mutex);
-
 	if (init) {
 		c->vc_cols = cols;
 		c->vc_rows = rows;
@@ -284,12 +277,8 @@
 	 * and others, ie not under our control.
 	 */
 
-	mutex_lock(&disconnect_mutex);
-
-	if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
-		mutex_unlock(&disconnect_mutex);
+	if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
 		return;
-	}
 
 	mutex_lock(&sisusb->lock);
 
@@ -314,8 +303,6 @@
 
 	/* decrement the usage count on our sisusb */
 	kref_put(&sisusb->kref, sisusb_delete);
-
-	mutex_unlock(&disconnect_mutex);
 }
 
 /* interface routine */
@@ -1490,14 +1477,11 @@
 {
 	int i, ret, minor = sisusb->minor;
 
-	mutex_lock(&disconnect_mutex);
-
 	mutex_lock(&sisusb->lock);
 
 	/* Erm.. that should not happen */
 	if (sisusb->haveconsole || !sisusb->SiS_Pr) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		return 1;
 	}
 
@@ -1508,14 +1492,12 @@
 	    first > MAX_NR_CONSOLES ||
 	    last > MAX_NR_CONSOLES) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		return 1;
 	}
 
 	/* If gfxcore not initialized or no consoles given, quit graciously */
 	if (!sisusb->gfxinit || first < 1 || last < 1) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		return 0;
 	}
 
@@ -1526,7 +1508,6 @@
 	/* Set up text mode (and upload  default font) */
 	if (sisusb_reset_text_mode(sisusb, 1)) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		printk(KERN_ERR
 			"sisusbvga[%d]: Failed to set up text mode\n",
 			minor);
@@ -1550,7 +1531,6 @@
 	/* Allocate screen buffer */
 	if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
 		mutex_unlock(&sisusb->lock);
-		mutex_unlock(&disconnect_mutex);
 		printk(KERN_ERR
 			"sisusbvga[%d]: Failed to allocate screen buffer\n",
 			minor);
@@ -1558,7 +1538,6 @@
 	}
 
 	mutex_unlock(&sisusb->lock);
-	mutex_unlock(&disconnect_mutex);
 
 	/* Now grab the desired console(s) */
 	ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h
index f05f832..864bc0e 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_init.h
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.h
@@ -808,8 +808,6 @@
 	{ 0x2b,0xc2, 35}  /* 0x71 768@576@60 */
 };
 
-extern struct mutex disconnect_mutex;
-
 int		SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
 int		SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
 
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 12bad8a..504f722 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -45,13 +45,13 @@
 	struct kref		kref;
 	struct semaphore	limit_sem;		/* to stop writes at full throttle from
 							 * using up all RAM */
+	struct usb_anchor	submitted;		/* URBs to wait for before suspend */
 };
 #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref)
 
 #define USB_LCD_CONCURRENT_WRITES	5
 
 static struct usb_driver lcd_driver;
-static DEFINE_MUTEX(usb_lcd_open_mutex);
 
 
 static void lcd_delete(struct kref *kref)
@@ -68,35 +68,35 @@
 {
 	struct usb_lcd *dev;
 	struct usb_interface *interface;
-	int subminor;
-	int retval = 0;
+	int subminor, r;
 
 	subminor = iminor(inode);
 
-	mutex_lock(&usb_lcd_open_mutex);
 	interface = usb_find_interface(&lcd_driver, subminor);
 	if (!interface) {
 		err ("USBLCD: %s - error, can't find device for minor %d",
 		     __FUNCTION__, subminor);
-		retval = -ENODEV;
-		goto exit;
+		return -ENODEV;
 	}
 
 	dev = usb_get_intfdata(interface);
-	if (!dev) {
-		retval = -ENODEV;
-		goto exit;
-	}
+	if (!dev)
+		return -ENODEV;
 
 	/* increment our usage count for the device */
 	kref_get(&dev->kref);
 
+	/* grab a power reference */
+	r = usb_autopm_get_interface(interface);
+	if (r < 0) {
+		kref_put(&dev->kref, lcd_delete);
+		return r;
+	}
+
 	/* save our object in the file's private structure */
 	file->private_data = dev;
 
-exit:
-	mutex_unlock(&usb_lcd_open_mutex);
-	return retval;
+	return 0;
 }
 
 static int lcd_release(struct inode *inode, struct file *file)
@@ -108,6 +108,7 @@
 		return -ENODEV;
 
 	/* decrement the count on our device */
+	usb_autopm_put_interface(dev->interface);
 	kref_put(&dev->kref, lcd_delete);
 	return 0;
 }
@@ -233,12 +234,14 @@
 			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
 			  buf, count, lcd_write_bulk_callback, dev);
 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_anchor_urb(urb, &dev->submitted);
 	
 	/* send the data out the bulk port */
 	retval = usb_submit_urb(urb, GFP_KERNEL);
 	if (retval) {
 		err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval);
-		goto error;
+		goto error_unanchor;
 	}
 	
 	/* release our reference to this urb, the USB core will eventually free it entirely */
@@ -246,7 +249,8 @@
 
 exit:
 	return count;
-
+error_unanchor:
+	usb_unanchor_urb(urb);
 error:
 	usb_buffer_free(dev->udev, count, buf, urb->transfer_dma);
 	usb_free_urb(urb);
@@ -291,6 +295,7 @@
 	}
 	kref_init(&dev->kref);
 	sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
+	init_usb_anchor(&dev->submitted);
 
 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
 	dev->interface = interface;
@@ -358,22 +363,41 @@
 	return retval;
 }
 
+static void lcd_draw_down(struct usb_lcd *dev)
+{
+	int time;
+
+	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
+	if (!time)
+		usb_kill_anchored_urbs(&dev->submitted);
+}
+
+static int lcd_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_lcd *dev = usb_get_intfdata(intf);
+
+	if (!dev)
+		return 0;
+	lcd_draw_down(dev);
+	return 0;
+}
+
+static int lcd_resume (struct usb_interface *intf)
+{
+	return 0;
+}
+
 static void lcd_disconnect(struct usb_interface *interface)
 {
 	struct usb_lcd *dev;
         int minor = interface->minor;
 
-        /* prevent skel_open() from racing skel_disconnect() */
-        mutex_lock(&usb_lcd_open_mutex);
-
         dev = usb_get_intfdata(interface);
         usb_set_intfdata(interface, NULL);
 
         /* give back our minor */
         usb_deregister_dev(interface, &lcd_class);
  
-	mutex_unlock(&usb_lcd_open_mutex);
-
 	/* decrement our usage count */
 	kref_put(&dev->kref, lcd_delete);
 
@@ -384,7 +408,10 @@
 	.name =		"usblcd",
 	.probe =	lcd_probe,
 	.disconnect =	lcd_disconnect,
+	.suspend =	lcd_suspend,
+	.resume =	lcd_resume,
 	.id_table =	id_table,
+	.supports_autosuspend = 1,
 };
 
 static int __init usb_lcd_init(void)
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 0af11a6..c03dfd7 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -4,7 +4,7 @@
  * This is a binary format reader.
  *
  * Copyright (C) 2006 Paolo Abeni (paolo.abeni@email.it)
- * Copyright (C) 2006 Pete Zaitcev (zaitcev@redhat.com)
+ * Copyright (C) 2006,2007 Pete Zaitcev (zaitcev@redhat.com)
  */
 
 #include <linux/kernel.h>
@@ -172,6 +172,7 @@
 
 #define MON_RING_EMPTY(rp)	((rp)->b_cnt == 0)
 
+static struct class *mon_bin_class;
 static dev_t mon_bin_dev0;
 static struct cdev mon_bin_cdev;
 
@@ -1144,10 +1145,38 @@
 		free_page((unsigned long) map[n].ptr);
 }
 
+int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus)
+{
+	struct device *dev;
+	unsigned minor = ubus? ubus->busnum: 0;
+
+	if (minor >= MON_BIN_MAX_MINOR)
+		return 0;
+
+	dev = device_create(mon_bin_class, ubus? ubus->controller: NULL,
+			MKDEV(MAJOR(mon_bin_dev0), minor), "usbmon%d", minor);
+	if (IS_ERR(dev))
+		return 0;
+
+	mbus->classdev = dev;
+	return 1;
+}
+
+void mon_bin_del(struct mon_bus *mbus)
+{
+	device_destroy(mon_bin_class, mbus->classdev->devt);
+}
+
 int __init mon_bin_init(void)
 {
 	int rc;
 
+	mon_bin_class = class_create(THIS_MODULE, "usbmon");
+	if (IS_ERR(mon_bin_class)) {
+		rc = PTR_ERR(mon_bin_class);
+		goto err_class;
+	}
+
 	rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon");
 	if (rc < 0)
 		goto err_dev;
@@ -1164,6 +1193,8 @@
 err_add:
 	unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
 err_dev:
+	class_destroy(mon_bin_class);
+err_class:
 	return rc;
 }
 
@@ -1171,4 +1202,5 @@
 {
 	cdev_del(&mon_bin_cdev);
 	unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
+	class_destroy(mon_bin_class);
 }
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 8977ec0..ce61d8b 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -220,6 +220,8 @@
 	list_del(&mbus->bus_link);
 	if (mbus->text_inited)
 		mon_text_del(mbus);
+	if (mbus->bin_inited)
+		mon_bin_del(mbus);
 
 	mon_dissolve(mbus, ubus);
 	kref_put(&mbus->ref, mon_bus_drop);
@@ -301,8 +303,8 @@
 	mbus->u_bus = ubus;
 	ubus->mon_bus = mbus;
 
-	mbus->text_inited = mon_text_add(mbus, ubus->busnum);
-	// mon_bin_add(...)
+	mbus->text_inited = mon_text_add(mbus, ubus);
+	mbus->bin_inited = mon_bin_add(mbus, ubus);
 
 	mutex_lock(&mon_lock);
 	list_add_tail(&mbus->bus_link, &mon_buses);
@@ -321,8 +323,8 @@
 	spin_lock_init(&mbus->lock);
 	INIT_LIST_HEAD(&mbus->r_list);
 
-	mbus->text_inited = mon_text_add(mbus, 0);
-	// mbus->bin_inited = mon_bin_add(mbus, 0);
+	mbus->text_inited = mon_text_add(mbus, NULL);
+	mbus->bin_inited = mon_bin_add(mbus, NULL);
 }
 
 /*
@@ -403,6 +405,8 @@
 
 		if (mbus->text_inited)
 			mon_text_del(mbus);
+		if (mbus->bin_inited)
+			mon_bin_del(mbus);
 
 		/*
 		 * This never happens, because the open/close paths in
@@ -423,6 +427,8 @@
 	mbus = &mon_bus0;
 	if (mbus->text_inited)
 		mon_text_del(mbus);
+	if (mbus->bin_inited)
+		mon_bin_del(mbus);
 
 	mutex_unlock(&mon_lock);
 
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index ec0cc51..982b773 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -655,20 +655,24 @@
 	.release =	mon_text_release,
 };
 
-int mon_text_add(struct mon_bus *mbus, int busnum)
+int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
 {
 	struct dentry *d;
 	enum { NAMESZ = 10 };
 	char name[NAMESZ];
+	int busnum = ubus? ubus->busnum: 0;
 	int rc;
 
-	rc = snprintf(name, NAMESZ, "%dt", busnum);
-	if (rc <= 0 || rc >= NAMESZ)
-		goto err_print_t;
-	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t);
-	if (d == NULL)
-		goto err_create_t;
-	mbus->dent_t = d;
+	if (ubus != NULL) {
+		rc = snprintf(name, NAMESZ, "%dt", busnum);
+		if (rc <= 0 || rc >= NAMESZ)
+			goto err_print_t;
+		d = debugfs_create_file(name, 0600, mon_dir, mbus,
+							     &mon_fops_text_t);
+		if (d == NULL)
+			goto err_create_t;
+		mbus->dent_t = d;
+	}
 
 	rc = snprintf(name, NAMESZ, "%du", busnum);
 	if (rc <= 0 || rc >= NAMESZ)
@@ -694,8 +698,10 @@
 	mbus->dent_u = NULL;
 err_create_u:
 err_print_u:
-	debugfs_remove(mbus->dent_t);
-	mbus->dent_t = NULL;
+	if (ubus != NULL) {
+		debugfs_remove(mbus->dent_t);
+		mbus->dent_t = NULL;
+	}
 err_create_t:
 err_print_t:
 	return 0;
@@ -704,7 +710,8 @@
 void mon_text_del(struct mon_bus *mbus)
 {
 	debugfs_remove(mbus->dent_u);
-	debugfs_remove(mbus->dent_t);
+	if (mbus->dent_t != NULL)
+		debugfs_remove(mbus->dent_t);
 	debugfs_remove(mbus->dent_s);
 }
 
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index 13d6325..f68ad6d 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -20,9 +20,11 @@
 	struct usb_bus *u_bus;
 
 	int text_inited;
+	int bin_inited;
 	struct dentry *dent_s;		/* Debugging file */
 	struct dentry *dent_t;		/* Text interface file */
 	struct dentry *dent_u;		/* Second text interface file */
+	struct device *classdev;	/* Device in usbmon class */
 
 	/* Ref */
 	int nreaders;			/* Under mon_lock AND mbus->lock */
@@ -52,9 +54,10 @@
 
 struct mon_bus *mon_bus_lookup(unsigned int num);
 
-int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum);
+int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
 void mon_text_del(struct mon_bus *mbus);
-// void mon_bin_add(struct mon_bus *);
+int /*bool*/ mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus);
+void mon_bin_del(struct mon_bus *mbus);
 
 int __init mon_text_init(void);
 void mon_text_exit(void);
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 3efe670..43d6db6 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -464,6 +464,16 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called pl2303.
 
+config USB_SERIAL_OTI6858
+	tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller (EXPERIMENTAL)"
+	depends on USB_SERIAL
+	help
+	  Say Y here if you want to use the OTi-6858 single port USB to serial
+          converter device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called oti6858.
+
 config USB_SERIAL_HP4X
         tristate "USB HP4x Calculators support"
         depends on USB_SERIAL
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 61166ad..07a976e 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -40,6 +40,7 @@
 obj-$(CONFIG_USB_SERIAL_NAVMAN)			+= navman.o
 obj-$(CONFIG_USB_SERIAL_OMNINET)		+= omninet.o
 obj-$(CONFIG_USB_SERIAL_OPTION)			+= option.o
+obj-$(CONFIG_USB_SERIAL_OTI6858)		+= oti6858.o
 obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
 obj-$(CONFIG_USB_SERIAL_SAFE)			+= safe_serial.o
 obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS)		+= sierra.o
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index fbc8c27..1cd29cd 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -411,12 +411,13 @@
 static void aircable_write_bulk_callback(struct urb *urb)
 {
 	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
 	int result;
 
-	dbg("%s - urb->status: %d", __FUNCTION__ , urb->status);
+	dbg("%s - urb status: %d", __FUNCTION__ , status);
 
 	/* This has been taken from cypress_m8.c cypress_write_int_callback */
-	switch (urb->status) {
+	switch (status) {
 		case 0:
 			/* success */
 			break;
@@ -425,14 +426,14 @@
 		case -ESHUTDOWN:
 			/* this urb is terminated, clean up */
 			dbg("%s - urb shutting down with status: %d",
-			    __FUNCTION__, urb->status);
+			    __FUNCTION__, status);
 			port->write_urb_busy = 0;
 			return;
 		default:
 			/* error in the urb, so we have to resubmit it */
 			dbg("%s - Overflow in write", __FUNCTION__);
 			dbg("%s - nonzero write bulk status received: %d",
-			    __FUNCTION__, urb->status);
+			    __FUNCTION__, status);
 			port->write_urb->transfer_buffer_length = 1;
 			port->write_urb->dev = port->serial->dev;
 			result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
@@ -457,16 +458,17 @@
 	unsigned long no_packages, remaining, package_length, i;
 	int result, shift = 0;
 	unsigned char *temp;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - urb status = %d", __FUNCTION__, status);
 		if (!port->open_count) {
 			dbg("%s - port is closed, exiting.", __FUNCTION__);
 			return;
 		}
-		if (urb->status == -EPROTO) {
+		if (status == -EPROTO) {
 			dbg("%s - caught -EPROTO, resubmitting the urb",
 			    __FUNCTION__);
 			usb_fill_bulk_urb(port->read_urb, port->serial->dev,
diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c
index 39a4983..cff6fd1 100644
--- a/drivers/usb/serial/airprime.c
+++ b/drivers/usb/serial/airprime.c
@@ -82,12 +82,13 @@
 	unsigned char *data = urb->transfer_buffer;
 	struct tty_struct *tty;
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s - nonzero read bulk status received: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 		return;
 	}
 	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
@@ -109,6 +110,7 @@
 {
 	struct usb_serial_port *port = urb->context;
 	struct airprime_private *priv = usb_get_serial_port_data(port);
+	int status = urb->status;
 	unsigned long flags;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
@@ -116,9 +118,9 @@
 	/* free up the transfer buffer, as usb_free_urb() does not do this */
 	kfree (urb->transfer_buffer);
 
-	if (urb->status)
+	if (status)
 		dbg("%s - nonzero write bulk status received: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 	spin_lock_irqsave(&priv->lock, flags);
 	--priv->outstanding_urbs;
 	spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index fe43712..c9fd486 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -172,7 +172,7 @@
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if ((!port->tty) || (!port->tty->termios)) {
+	if (!port->tty || !port->tty->termios) {
 		dbg("%s - no tty structures", __FUNCTION__);
 		return;
 	}
@@ -188,16 +188,6 @@
 
 	cflag = port->tty->termios->c_cflag;
 
-	/* check that they really want us to change something: */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) ==
-		     RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("%s - nothing to change...", __FUNCTION__);
-			return;
-		}
-	}
-
 	buf = kmalloc(1, GFP_KERNEL);
 	if (!buf) {
 		dbg("error kmalloc");
@@ -220,7 +210,7 @@
 			dbg("setting CS7");
 			break;
 		default:
-			err("CSIZE was set but not CS5-CS8, using CS8!");
+			dbg("CSIZE was set but not CS5-CS8, using CS8!");
 			/* fall through */
 		case CS8:
 			config |= 0x03;
@@ -251,38 +241,33 @@
 	}
 
 	/* set baudrate */
-	baud = 0;
-	switch (cflag & CBAUD) {
-		case B0:
-			err("can't set 0 baud, using 9600 instead");
-			break;
-		case B75:	baud = 75;	break;
-		case B150:	baud = 150;	break;
-		case B300:	baud = 300;	break;
-		case B600:	baud = 600;	break;
-		case B1200:	baud = 1200;	break;
-		case B1800:	baud = 1800;	break;
-		case B2400:	baud = 2400;	break;
-		case B4800:	baud = 4800;	break;
-		case B9600:	baud = 9600;	break;
-		case B19200:	baud = 19200;	break;
-		case B38400:	baud = 38400;	break;
-		case B57600:	baud = 57600;	break;
-		case B115200:	baud = 115200;	break;
-		case B230400:	baud = 230400;	break;
-		case B460800:	baud = 460800;	break;
-		default:
-			dbg("does not support the baudrate requested (fix it)");
-			break;
-	}
+	baud = tty_get_baud_rate(port->tty);
 
-	/* set 9600 as default (if given baudrate is invalid for example) */
-	if (baud == 0)
-		baud = 9600;
+	switch (baud) {
+		case 75:
+		case 150:
+		case 300:
+		case 600:
+		case 1200:
+		case 1800:
+		case 2400:
+		case 4800:
+		case 9600:
+		case 19200:
+		case 38400:
+		case 57600:
+		case 115200:
+		case 230400:
+		case 460800:
+			break;
+		/* set 9600 as default (if given baudrate is invalid for example) */
+		default:
+			baud = 9600;
+	}
 
 	/*
 	 * found by try'n'error, be careful, maybe there are other options
-	 * for multiplicator etc!
+	 * for multiplicator etc! (3.5 for example)
 	 */
 	if (baud == 460800)
 		/* strange, for 460800 the formula is wrong
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index 3b800d2..e67ce25 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -255,9 +255,10 @@
 	struct belkin_sa_private *priv;
 	unsigned char *data = urb->transfer_buffer;
 	int retval;
+	int status = urb->status;
 	unsigned long flags;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -265,10 +266,12 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
-		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, status);
 		goto exit;
 	}
 
@@ -346,6 +349,7 @@
 	unsigned long flags;
 	unsigned long control_state;
 	int bad_flow_control;
+	speed_t baud;
 	
 	if ((!port->tty) || (!port->tty->termios)) {
 		dbg ("%s - no tty or termios structure", __FUNCTION__);
@@ -361,16 +365,8 @@
 	bad_flow_control = priv->bad_flow_control;
 	spin_unlock_irqrestore(&priv->lock, flags);
 	
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("%s - nothing to change...", __FUNCTION__);
-			return;
-		}
-		old_iflag = old_termios->c_iflag;
-		old_cflag = old_termios->c_cflag;
-	}
+	old_iflag = old_termios->c_iflag;
+	old_cflag = old_termios->c_cflag;
 
 	/* Set the baud rate */
 	if( (cflag&CBAUD) != (old_cflag&CBAUD) ) {
@@ -384,38 +380,30 @@
 				if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 1) < 0)
 					err("Set RTS error");
 		}
+	}
 
-		switch(cflag & CBAUD) {
-			case B0: /* handled below */ break;
-			case B300: urb_value = BELKIN_SA_BAUD(300); break;
-			case B600: urb_value = BELKIN_SA_BAUD(600); break;
-			case B1200: urb_value = BELKIN_SA_BAUD(1200); break;
-			case B2400: urb_value = BELKIN_SA_BAUD(2400); break;
-			case B4800: urb_value = BELKIN_SA_BAUD(4800); break;
-			case B9600: urb_value = BELKIN_SA_BAUD(9600); break;
-			case B19200: urb_value = BELKIN_SA_BAUD(19200); break;
-			case B38400: urb_value = BELKIN_SA_BAUD(38400); break;
-			case B57600: urb_value = BELKIN_SA_BAUD(57600); break;
-			case B115200: urb_value = BELKIN_SA_BAUD(115200); break;
-			case B230400: urb_value = BELKIN_SA_BAUD(230400); break;
-			default: err("BELKIN USB Serial Adapter: unsupported baudrate request, using default of 9600");
-				urb_value = BELKIN_SA_BAUD(9600); break;
-		}
-		if ((cflag & CBAUD) != B0 ) {
-			if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
-				err("Set baudrate error");
-		} else {
-			/* Disable flow control */
-			if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0)
-				err("Disable flowcontrol error");
+	baud = tty_get_baud_rate(port->tty);
+	urb_value = BELKIN_SA_BAUD(baud);
+	/* Clip to maximum speed */
+	if (urb_value == 0)
+		urb_value = 1;
+	/* Turn it back into a resulting real baud rate */
+	baud = BELKIN_SA_BAUD(urb_value);
+	/* FIXME: Once the tty updates are done then push this back to the tty */
 
-			/* Drop RTS and DTR */
-			control_state &= ~(TIOCM_DTR | TIOCM_RTS);
-			if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
-				err("DTR LOW error");
-			if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
-				err("RTS LOW error");
-		}
+	if ((cflag & CBAUD) != B0 ) {
+		if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
+			err("Set baudrate error");
+	} else {
+		/* Disable flow control */
+		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0)
+			err("Disable flowcontrol error");
+		/* Drop RTS and DTR */
+		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
+			err("DTR LOW error");
+		if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
+			err("RTS LOW error");
 	}
 
 	/* set the parity */
@@ -435,7 +423,7 @@
 			case CS6: urb_value = BELKIN_SA_DATA_BITS(6); break;
 			case CS7: urb_value = BELKIN_SA_DATA_BITS(7); break;
 			case CS8: urb_value = BELKIN_SA_DATA_BITS(8); break;
-			default: err("CSIZE was not CS5-CS8, using default of 8");
+			default: dbg("CSIZE was not CS5-CS8, using default of 8");
 				urb_value = BELKIN_SA_DATA_BITS(8);
 				break;
 		}
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 4167753..4353df9 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -305,12 +305,13 @@
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct cyberjack_private *priv = usb_get_serial_port_data(port);
 	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
 	int result;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	/* the urb might have been killed. */
-	if (urb->status)
+	if (status)
 		return;
 
 	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
@@ -365,12 +366,14 @@
 	unsigned char *data = urb->transfer_buffer;
 	short todo;
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
-	
+
 	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -411,12 +414,14 @@
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct cyberjack_private *priv = usb_get_serial_port_data(port);
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	port->write_urb_busy = 0;
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 57b8e27..1633863 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -1275,10 +1275,11 @@
 	int bytes = 0;
 	int result;
 	int i = 0;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0: /* success */
 		break;
 	case -ECONNRESET:
@@ -1292,7 +1293,7 @@
 	default:
 		/* something ugly is going on... */
 		dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n",
-			__FUNCTION__,urb->status);
+			__FUNCTION__, status);
 		cypress_set_dead(port);
 		return;
 	}
@@ -1419,10 +1420,11 @@
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct cypress_private *priv = usb_get_serial_port_data(port);
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
-	
-	switch (urb->status) {
+
+	switch (status) {
 		case 0:
 			/* success */
 			break;
@@ -1430,7 +1432,8 @@
 		case -ENOENT:
 		case -ESHUTDOWN:
 			/* this urb is terminated, clean up */
-			dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+			dbg("%s - urb shutting down with status: %d",
+			    __FUNCTION__, status);
 			priv->write_urb_in_use = 0;
 			return;
 		case -EPIPE: /* no break needed; clear halt and resubmit */
@@ -1438,7 +1441,8 @@
 				break;
 			usb_clear_halt(port->serial->dev, 0x02);
 			/* error in the urb, so we have to resubmit it */
-			dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+			dbg("%s - nonzero write bulk status received: %d",
+			    __FUNCTION__, status);
 			port->interrupt_out_urb->transfer_buffer_length = 1;
 			port->interrupt_out_urb->dev = port->serial->dev;
 			result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
@@ -1450,7 +1454,7 @@
 			break;
 		default:
 			dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n",
-				__FUNCTION__,urb->status);
+				__FUNCTION__, status);
 			cypress_set_dead(port);
 			break;
 	}
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index d78692c..976f54e 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -416,9 +416,6 @@
 	int dp_port_num;
 	int dp_out_buf_len;
 	unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE];
-	int dp_in_buf_len;
-	unsigned char dp_in_buf[DIGI_IN_BUF_SIZE];
-	unsigned char dp_in_flag_buf[DIGI_IN_BUF_SIZE];
 	int dp_write_urb_in_use;
 	unsigned int dp_modem_signals;
 	wait_queue_head_t dp_modem_change_wait;
@@ -920,7 +917,6 @@
 	spin_lock_irqsave( &priv->dp_port_lock, flags );
 	priv->dp_throttled = 1;
 	priv->dp_throttle_restart = 0;
-	priv->dp_in_buf_len = 0;
 	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
 
 }
@@ -930,23 +926,16 @@
 {
 
 	int ret = 0;
-	int len;
 	unsigned long flags;
 	struct digi_port *priv = usb_get_serial_port_data(port);
-	struct tty_struct *tty = port->tty;
-
 
 dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num );
 
 	spin_lock_irqsave( &priv->dp_port_lock, flags );
 
-	/* send any buffered chars from throttle time on to tty subsystem */
-
-	len = tty_buffer_request_room(tty, priv->dp_in_buf_len);
-	if( len > 0 ) {
-		tty_insert_flip_string_flags(tty, priv->dp_in_buf, priv->dp_in_flag_buf, len);
-		tty_flip_buffer_push( tty );
-	}
+	/* turn throttle off */
+	priv->dp_throttled = 0;
+	priv->dp_throttle_restart = 0;
 
 	/* restart read chain */
 	if( priv->dp_throttle_restart ) {
@@ -954,11 +943,6 @@
 		ret = usb_submit_urb( port->read_urb, GFP_ATOMIC );
 	}
 
-	/* turn throttle off */
-	priv->dp_throttled = 0;
-	priv->dp_in_buf_len = 0;
-	priv->dp_throttle_restart = 0;
-
 	spin_unlock_irqrestore( &priv->dp_port_lock, flags );
 
 	if( ret ) {
@@ -1340,19 +1324,21 @@
 	struct digi_port *priv;
 	struct digi_serial *serial_priv;
 	int ret = 0;
+	int status = urb->status;
 
 
-dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status );
+	dbg("digi_write_bulk_callback: TOP, urb status=%d", status);
 
 	/* port and serial sanity check */
 	if( port == NULL || (priv=usb_get_serial_port_data(port)) == NULL ) {
-		err("%s: port or port->private is NULL, status=%d", __FUNCTION__,
-			urb->status );
+		err("%s: port or port->private is NULL, status=%d",
+		    __FUNCTION__, status);
 		return;
 	}
 	serial = port->serial;
 	if( serial == NULL || (serial_priv=usb_get_serial_data(serial)) == NULL ) {
-		err("%s: serial or serial->private is NULL, status=%d", __FUNCTION__, urb->status );
+		err("%s: serial or serial->private is NULL, status=%d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -1687,7 +1673,6 @@
 		spin_lock_init( &priv->dp_port_lock );
 		priv->dp_port_num = i;
 		priv->dp_out_buf_len = 0;
-		priv->dp_in_buf_len = 0;
 		priv->dp_write_urb_in_use = 0;
 		priv->dp_modem_signals = 0;
 		init_waitqueue_head( &priv->dp_modem_change_wait );
@@ -1757,25 +1742,28 @@
 	struct digi_port *priv;
 	struct digi_serial *serial_priv;
 	int ret;
+	int status = urb->status;
 
 
 dbg( "digi_read_bulk_callback: TOP" );
 
 	/* port sanity check, do not resubmit if port is not valid */
 	if( port == NULL || (priv=usb_get_serial_port_data(port)) == NULL ) {
-		err("%s: port or port->private is NULL, status=%d", __FUNCTION__,
-			urb->status );
+		err("%s: port or port->private is NULL, status=%d",
+		    __FUNCTION__, status);
 		return;
 	}
 	if( port->serial == NULL
 	|| (serial_priv=usb_get_serial_data(port->serial)) == NULL ) {
-		err("%s: serial is bad or serial->private is NULL, status=%d", __FUNCTION__, urb->status );
+		err("%s: serial is bad or serial->private is NULL, status=%d",
+		    __FUNCTION__, status);
 		return;
 	}
 
 	/* do not resubmit urb if it has any status error */
-	if( urb->status ) {
-		err("%s: nonzero read bulk status: status=%d, port=%d", __FUNCTION__, urb->status, priv->dp_port_num );
+	if (status) {
+		err("%s: nonzero read bulk status: status=%d, port=%d",
+		    __FUNCTION__, status, priv->dp_port_num);
 		return;
 	}
 
@@ -1816,10 +1804,11 @@
 	struct digi_port *priv = usb_get_serial_port_data(port);
 	int opcode = ((unsigned char *)urb->transfer_buffer)[0];
 	int len = ((unsigned char *)urb->transfer_buffer)[1];
-	int status = ((unsigned char *)urb->transfer_buffer)[2];
+	int port_status = ((unsigned char *)urb->transfer_buffer)[2];
 	unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3;
 	int flag,throttled;
 	int i;
+	int status = urb->status;
 
 	/* do not process callbacks on closed ports */
 	/* but do continue the read chain */
@@ -1828,7 +1817,10 @@
 
 	/* short/multiple packet check */
 	if( urb->actual_length != len + 2 ) {
-     		err("%s: INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", __FUNCTION__, urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status );
+		err("%s: INCOMPLETE OR MULTIPLE PACKET, urb status=%d, "
+		    "port=%d, opcode=%d, len=%d, actual_length=%d, "
+		    "port_status=%d", __FUNCTION__, status, priv->dp_port_num,
+		    opcode, len, urb->actual_length, port_status);
 		return( -1 );
 	}
 
@@ -1843,52 +1835,37 @@
 	/* receive data */
 	if( opcode == DIGI_CMD_RECEIVE_DATA ) {
 
-		/* get flag from status */
+		/* get flag from port_status */
 		flag = 0;
 
 		/* overrun is special, not associated with a char */
-		if( status & DIGI_OVERRUN_ERROR ) {
+		if (port_status & DIGI_OVERRUN_ERROR) {
 			tty_insert_flip_char( tty, 0, TTY_OVERRUN );
 		}
 
 		/* break takes precedence over parity, */
 		/* which takes precedence over framing errors */
-		if( status & DIGI_BREAK_ERROR ) {
+		if (port_status & DIGI_BREAK_ERROR) {
 			flag = TTY_BREAK;
-		} else if( status & DIGI_PARITY_ERROR ) {
+		} else if (port_status & DIGI_PARITY_ERROR) {
 			flag = TTY_PARITY;
-		} else if( status & DIGI_FRAMING_ERROR ) {
+		} else if (port_status & DIGI_FRAMING_ERROR) {
 			flag = TTY_FRAME;
 		}
 
-		/* data length is len-1 (one byte of len is status) */
+		/* data length is len-1 (one byte of len is port_status) */
 		--len;
 
-		if( throttled ) {
-
-			len = min( len,
-				DIGI_IN_BUF_SIZE - priv->dp_in_buf_len );
-
-			if( len > 0 ) {
-				memcpy( priv->dp_in_buf + priv->dp_in_buf_len,
-					data, len );
-				memset( priv->dp_in_flag_buf
-					+ priv->dp_in_buf_len, flag, len );
-				priv->dp_in_buf_len += len;
+		len = tty_buffer_request_room(tty, len);
+		if( len > 0 ) {
+			/* Hot path */
+			if(flag == TTY_NORMAL)
+				tty_insert_flip_string(tty, data, len);
+			else {
+				for(i = 0; i < len; i++)
+					tty_insert_flip_char(tty, data[i], flag);
 			}
-
-		} else {
-			len = tty_buffer_request_room(tty, len);
-			if( len > 0 ) {
-				/* Hot path */
-				if(flag == TTY_NORMAL)
-					tty_insert_flip_string(tty, data, len);
-				else {
-					for(i = 0; i < len; i++)
-						tty_insert_flip_char(tty, data[i], flag);
-				}
-				tty_flip_buffer_push( tty );
-			}
+			tty_flip_buffer_push( tty );
 		}
 	}
 
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index 4703c8f..050fcc9 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -326,12 +326,14 @@
 
 static void empeg_write_bulk_callback (struct urb *urb)
 {
-	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -345,11 +347,13 @@
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index da1c6f7..7b1673a 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -271,26 +271,58 @@
 static __u16 vendor = FTDI_VID;
 static __u16 product;
 
+struct ftdi_private {
+	ftdi_chip_type_t chip_type;
+				/* type of the device, either SIO or FT8U232AM */
+	int baud_base;		/* baud base clock for divisor setting */
+	int custom_divisor;	/* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
+	__u16 last_set_data_urb_value ;
+				/* the last data state set - needed for doing a break */
+        int write_offset;       /* This is the offset in the usb data block to write the serial data -
+				 * it is different between devices
+				 */
+	int flags;		/* some ASYNC_xxxx flags are supported */
+	unsigned long last_dtr_rts;	/* saved modem control outputs */
+        wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
+	char prev_status, diff_status;        /* Used for TIOCMIWAIT */
+	__u8 rx_flags;		/* receive state flags (throttling) */
+	spinlock_t rx_lock;	/* spinlock for receive state */
+	struct delayed_work rx_work;
+	struct usb_serial_port *port;
+	int rx_processed;
+	unsigned long rx_bytes;
+
+	__u16 interface;	/* FT2232C port interface (0 for FT232/245) */
+
+	int force_baud;		/* if non-zero, force the baud rate to this value */
+	int force_rtscts;	/* if non-zero, force RTS-CTS to always be enabled */
+
+	spinlock_t tx_lock;	/* spinlock for transmit state */
+	unsigned long tx_bytes;
+	unsigned long tx_outstanding_bytes;
+	unsigned long tx_outstanding_urbs;
+};
+
 /* struct ftdi_sio_quirk is used by devices requiring special attention. */
 struct ftdi_sio_quirk {
 	int (*probe)(struct usb_serial *);
-	void (*setup)(struct usb_serial *); /* Special settings during startup. */
+	void (*port_probe)(struct ftdi_private *); /* Special settings for probed ports. */
 };
 
 static int   ftdi_olimex_probe		(struct usb_serial *serial);
-static void  ftdi_USB_UIRT_setup	(struct usb_serial *serial);
-static void  ftdi_HE_TIRA1_setup	(struct usb_serial *serial);
+static void  ftdi_USB_UIRT_setup	(struct ftdi_private *priv);
+static void  ftdi_HE_TIRA1_setup	(struct ftdi_private *priv);
 
 static struct ftdi_sio_quirk ftdi_olimex_quirk = {
 	.probe	= ftdi_olimex_probe,
 };
 
 static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
-	.setup = ftdi_USB_UIRT_setup,
+	.port_probe = ftdi_USB_UIRT_setup,
 };
 
 static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
-	.setup = ftdi_HE_TIRA1_setup,
+	.port_probe = ftdi_HE_TIRA1_setup,
 };
 
 /*
@@ -567,38 +599,6 @@
 #define THROTTLED		0x01
 #define ACTUALLY_THROTTLED	0x02
 
-struct ftdi_private {
-	ftdi_chip_type_t chip_type;
-				/* type of the device, either SIO or FT8U232AM */
-	int baud_base;		/* baud base clock for divisor setting */
-	int custom_divisor;	/* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
-	__u16 last_set_data_urb_value ;
-				/* the last data state set - needed for doing a break */
-        int write_offset;       /* This is the offset in the usb data block to write the serial data -
-				 * it is different between devices
-				 */
-	int flags;		/* some ASYNC_xxxx flags are supported */
-	unsigned long last_dtr_rts;	/* saved modem control outputs */
-        wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
-	char prev_status, diff_status;        /* Used for TIOCMIWAIT */
-	__u8 rx_flags;		/* receive state flags (throttling) */
-	spinlock_t rx_lock;	/* spinlock for receive state */
-	struct delayed_work rx_work;
-	struct usb_serial_port *port;
-	int rx_processed;
-	unsigned long rx_bytes;
-
-	__u16 interface;	/* FT2232C port interface (0 for FT232/245) */
-
-	int force_baud;		/* if non-zero, force the baud rate to this value */
-	int force_rtscts;	/* if non-zero, force RTS-CTS to always be enabled */
-
-	spinlock_t tx_lock;	/* spinlock for transmit state */
-	unsigned long tx_bytes;
-	unsigned long tx_outstanding_bytes;
-	unsigned long tx_outstanding_urbs;
-};
-
 /* Used for TIOCMIWAIT */
 #define FTDI_STATUS_B0_MASK	(FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)
 #define FTDI_STATUS_B1_MASK	(FTDI_RS_BI)
@@ -609,7 +609,6 @@
 
 /* function prototypes for a FTDI serial converter */
 static int  ftdi_sio_probe	(struct usb_serial *serial, const struct usb_device_id *id);
-static int  ftdi_sio_attach		(struct usb_serial *serial);
 static void ftdi_shutdown		(struct usb_serial *serial);
 static int  ftdi_sio_port_probe	(struct usb_serial_port *port);
 static int  ftdi_sio_port_remove	(struct usb_serial_port *port);
@@ -663,7 +662,6 @@
 	.ioctl =		ftdi_ioctl,
 	.set_termios =		ftdi_set_termios,
 	.break_ctl =		ftdi_break_ctl,
-	.attach =		ftdi_sio_attach,
 	.shutdown =		ftdi_shutdown,
 };
 
@@ -1149,7 +1147,9 @@
 		dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
 		retval = device_create_file(&port->dev, &dev_attr_event_char);
 		if ((!retval) &&
-		    (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
+		    (priv->chip_type == FT232BM ||
+		     priv->chip_type == FT2232C ||
+		     priv->chip_type == FT232RL)) {
 			retval = device_create_file(&port->dev,
 						    &dev_attr_latency_timer);
 		}
@@ -1198,6 +1198,8 @@
 static int ftdi_sio_port_probe(struct usb_serial_port *port)
 {
 	struct ftdi_private *priv;
+	struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
+
 
 	dbg("%s",__FUNCTION__);
 
@@ -1214,6 +1216,9 @@
 	   than queue a task to deliver them */
 	priv->flags = ASYNC_LOW_LATENCY;
 
+	if (quirk && quirk->port_probe)
+		quirk->port_probe(priv);
+
 	/* Increase the size of read buffers */
 	kfree(port->bulk_in_buffer);
 	port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL);
@@ -1244,29 +1249,13 @@
 	return 0;
 }
 
-/* attach subroutine */
-static int ftdi_sio_attach (struct usb_serial *serial)
-{
-	/* Check for device requiring special set up. */
-	struct ftdi_sio_quirk *quirk = usb_get_serial_data(serial);
-
-	if (quirk && quirk->setup)
-		quirk->setup(serial);
-
-	return 0;
-} /* ftdi_sio_attach */
-
-
 /* Setup for the USB-UIRT device, which requires hardwired
  * baudrate (38400 gets mapped to 312500) */
 /* Called from usbserial:serial_probe */
-static void ftdi_USB_UIRT_setup (struct usb_serial *serial)
+static void ftdi_USB_UIRT_setup (struct ftdi_private *priv)
 {
-	struct ftdi_private *priv;
-
 	dbg("%s",__FUNCTION__);
 
-	priv = usb_get_serial_port_data(serial->port[0]);
 	priv->flags |= ASYNC_SPD_CUST;
 	priv->custom_divisor = 77;
 	priv->force_baud = B38400;
@@ -1274,13 +1263,10 @@
 
 /* Setup for the HE-TIRA1 device, which requires hardwired
  * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled.  */
-static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
+static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv)
 {
-	struct ftdi_private *priv;
-
 	dbg("%s",__FUNCTION__);
 
-	priv = usb_get_serial_port_data(serial->port[0]);
 	priv->flags |= ASYNC_SPD_CUST;
 	priv->custom_divisor = 240;
 	priv->force_baud = B38400;
@@ -1574,14 +1560,15 @@
 	struct ftdi_private *priv;
 	int data_offset;       /* will be 1 for the SIO and 0 otherwise */
 	unsigned long countback;
+	int status = urb->status;
 
 	/* free up the transfer buffer, as usb_free_urb() does not do this */
 	kfree (urb->transfer_buffer);
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("nonzero write bulk status received: %d", urb->status);
+	if (status) {
+		dbg("nonzero write bulk status received: %d", status);
 		return;
 	}
 
@@ -1657,6 +1644,7 @@
 	struct ftdi_private *priv;
 	unsigned long countread;
 	unsigned long flags;
+	int status = urb->status;
 
 	if (urb->number_of_packets > 0) {
 		err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
@@ -1685,9 +1673,10 @@
 		err("%s - Not my urb!", __FUNCTION__);
 	}
 
-	if (urb->status) {
+	if (status) {
 		/* This will happen at close every time so it is a dbg not an err */
-		dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status);
+		dbg("(this is ok on close) nonzero read bulk status received: "
+		    "%d", status);
 		return;
 	}
 
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 74660a3..04bd3b7 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1036,15 +1036,16 @@
 	unsigned long flags;
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+	int status = urb->status;
 
 	/* free up the transfer buffer, as usb_free_urb() does not do this */
 	kfree (urb->transfer_buffer);
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s - nonzero write bulk status received: %d",
-			__FUNCTION__, urb->status);
+			__FUNCTION__, status);
 		spin_lock_irqsave(&garmin_data_p->lock, flags);
 		garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
 		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
@@ -1281,7 +1282,8 @@
 	struct usb_serial *serial =  port->serial;
 	struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 	unsigned char *data = urb->transfer_buffer;
-	int status;
+	int status = urb->status;
+	int retval;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -1290,9 +1292,9 @@
 		return;
 	}
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s - nonzero read bulk status received: %d",
-			__FUNCTION__, urb->status);
+			__FUNCTION__, status);
 		return;
 	}
 
@@ -1306,19 +1308,19 @@
 		spin_lock_irqsave(&garmin_data_p->lock, flags);
 		garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART;
 		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
-		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-		if (status)
+		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (retval)
 			dev_err(&port->dev,
 				"%s - failed resubmitting read urb, error %d\n",
-			        __FUNCTION__, status);
+				__FUNCTION__, retval);
 	} else if (urb->actual_length > 0) {
 		/* Continue trying to read until nothing more is received  */
 		if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) {
-			status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-			if (status)
+			retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+			if (retval)
 				dev_err(&port->dev,
-					"%s - failed resubmitting read urb, error %d\n",
-			        	__FUNCTION__, status);
+					"%s - failed resubmitting read urb, "
+					"error %d\n", __FUNCTION__, retval);
 		}
 	} else {
 		dbg("%s - end of bulk data", __FUNCTION__);
@@ -1333,13 +1335,14 @@
 static void garmin_read_int_callback (struct urb *urb)
 {
 	unsigned long flags;
-	int status;
+	int retval;
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct usb_serial *serial = port->serial;
 	struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
 	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -1348,11 +1351,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d",
-			__FUNCTION__, urb->status);
+			__FUNCTION__, status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d",
-			__FUNCTION__, urb->status);
+			__FUNCTION__, status);
 		return;
 	}
 
@@ -1374,11 +1377,11 @@
 					port->read_urb->transfer_buffer,
 					port->read_urb->transfer_buffer_length,
 					garmin_read_bulk_callback, port);
-			status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-			if (status) {
+			retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+			if (retval) {
 				dev_err(&port->dev,
 					"%s - failed submitting read urb, error %d\n",
-				__FUNCTION__, status);
+				__FUNCTION__, retval);
 			} else {
 				spin_lock_irqsave(&garmin_data_p->lock, flags);
 				garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
@@ -1422,11 +1425,11 @@
 	}
 
 	port->interrupt_in_urb->dev = port->serial->dev;
-	status = usb_submit_urb (urb, GFP_ATOMIC);
-	if (status)
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
 		dev_err(&urb->dev->dev,
 			"%s - Error %d submitting interrupt urb\n",
-			__FUNCTION__, status);
+			__FUNCTION__, retval);
 }
 
 
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 4f8282a..88a2c7d 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -69,6 +69,7 @@
 	.shutdown =		usb_serial_generic_shutdown,
 	.throttle =		usb_serial_generic_throttle,
 	.unthrottle =		usb_serial_generic_unthrottle,
+	.resume =		usb_serial_generic_resume,
 };
 
 static int generic_probe(struct usb_interface *interface,
@@ -169,6 +170,23 @@
 	}
 }
 
+int usb_serial_generic_resume(struct usb_serial *serial)
+{
+	struct usb_serial_port *port;
+	int i, c = 0, r;
+
+	for (i = 0; i < serial->num_ports; i++) {
+		port = serial->port[i];
+		if (port->open_count && port->read_urb) {
+			r = usb_submit_urb(port->read_urb, GFP_NOIO);
+			if (r < 0)
+				c++;
+		}
+	}
+
+	return c ? -EIO : 0;
+}
+
 void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp)
 {
 	dbg("%s - port %d", __FUNCTION__, port->number);
@@ -263,79 +281,82 @@
 	return (chars);
 }
 
-/* Push data to tty layer and resubmit the bulk read URB */
-static void flush_and_resubmit_read_urb (struct usb_serial_port *port)
+
+static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags)
 {
-	struct usb_serial *serial = port->serial;
 	struct urb *urb = port->read_urb;
-	struct tty_struct *tty = port->tty;
+	struct usb_serial *serial = port->serial;
 	int result;
 
-	/* Push data to tty */
-	if (tty && urb->actual_length) {
-		tty_buffer_request_room(tty, urb->actual_length);
-		tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
-	  	tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */
-	}
-
 	/* Continue reading from device */
-	usb_fill_bulk_urb (port->read_urb, serial->dev,
+	usb_fill_bulk_urb (urb, serial->dev,
 			   usb_rcvbulkpipe (serial->dev,
 				   	    port->bulk_in_endpointAddress),
-			   port->read_urb->transfer_buffer,
-			   port->read_urb->transfer_buffer_length,
+			   urb->transfer_buffer,
+			   urb->transfer_buffer_length,
 			   ((serial->type->read_bulk_callback) ? 
 			     serial->type->read_bulk_callback : 
 			     usb_serial_generic_read_bulk_callback), port);
-	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+	result = usb_submit_urb(urb, mem_flags);
 	if (result)
 		dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
 }
 
+/* Push data to tty layer and resubmit the bulk read URB */
+static void flush_and_resubmit_read_urb (struct usb_serial_port *port)
+{
+	struct urb *urb = port->read_urb;
+	struct tty_struct *tty = port->tty;
+	int room;
+
+	/* Push data to tty */
+	if (tty && urb->actual_length) {
+		room = tty_buffer_request_room(tty, urb->actual_length);
+		if (room) {
+			tty_insert_flip_string(tty, urb->transfer_buffer, room);
+			tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */
+		}
+	}
+
+	resubmit_read_urb(port, GFP_ATOMIC);
+}
+
 void usb_serial_generic_read_bulk_callback (struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	unsigned char *data = urb->transfer_buffer;
-	int is_throttled;
-	unsigned long flags;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (unlikely(status != 0)) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
 	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
 
 	/* Throttle the device if requested by tty */
-	if (urb->actual_length) {
-		spin_lock_irqsave(&port->lock, flags);
-		is_throttled = port->throttled = port->throttle_req;
-		spin_unlock_irqrestore(&port->lock, flags);
-		if (is_throttled) {
-			/* Let the received data linger in the read URB;
-			 * usb_serial_generic_unthrottle() will pick it
-			 * up later. */
-			dbg("%s - throttling device", __FUNCTION__);
-			return;
-		}
-	}
-
-	/* Handle data and continue reading from device */
-	flush_and_resubmit_read_urb(port);
+	spin_lock(&port->lock);
+	if (!(port->throttled = port->throttle_req))
+		/* Handle data and continue reading from device */
+		flush_and_resubmit_read_urb(port);
+	spin_unlock(&port->lock);
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
 
 void usb_serial_generic_write_bulk_callback (struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	port->write_urb_busy = 0;
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -370,8 +391,8 @@
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	if (was_throttled) {
-		/* Handle pending data and resume reading from device */
-		flush_and_resubmit_read_urb(port);
+		/* Resume reading from device */
+		resubmit_read_urb(port, GFP_KERNEL);
 	}
 }
 
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 056e192..dd42f57 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -599,10 +599,11 @@
 	int txCredits;
 	int portNumber;
 	int result;
+	int status = urb->status;
 
 	dbg("%s", __FUNCTION__);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -610,10 +611,12 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
-		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, status);
 		goto exit;
 	}
 
@@ -688,13 +691,15 @@
 {
 	struct edgeport_serial	*edge_serial = (struct edgeport_serial *)urb->context;
 	unsigned char		*data = urb->transfer_buffer;
-	int			status;
+	int			retval;
 	__u16			raw_data_length;
+	int status = urb->status;
 
 	dbg("%s", __FUNCTION__);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		edge_serial->read_in_progress = false;
 		return;
 	}
@@ -722,9 +727,11 @@
 	if (edge_serial->rxBytesAvail > 0) {
 		dbg("%s - posting a read", __FUNCTION__);
 		edge_serial->read_urb->dev = edge_serial->serial->dev;
-		status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
-		if (status) {
-			dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status);
+		retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
+		if (retval) {
+			dev_err(&urb->dev->dev,
+				"%s - usb_submit_urb(read bulk) failed, "
+				"retval = %d\n", __FUNCTION__, retval);
 			edge_serial->read_in_progress = false;
 		}
 	} else {
@@ -744,11 +751,13 @@
 {
 	struct edgeport_port *edge_port = (struct edgeport_port *)urb->context;
 	struct tty_struct *tty;
+	int status = urb->status;
 
 	dbg("%s", __FUNCTION__);
 
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 	}
 
 	tty = edge_port->port->tty;
@@ -1504,15 +1513,6 @@
 	}
 
 	cflag = tty->termios->c_cflag;
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if (cflag == old_termios->c_cflag &&
-		    tty->termios->c_iflag == old_termios->c_iflag) {
-			dbg("%s - nothing to change", __FUNCTION__);
-			return;
-		}
-	}
-
 	dbg("%s - clfag %08x iflag %08x", __FUNCTION__, 
 	    tty->termios->c_cflag, tty->termios->c_iflag);
 	if (old_termios) {
diff --git a/drivers/usb/serial/io_fw_down3.h b/drivers/usb/serial/io_fw_down3.h
index 93b56d6..4496b06 100644
--- a/drivers/usb/serial/io_fw_down3.h
+++ b/drivers/usb/serial/io_fw_down3.h
@@ -5,7 +5,7 @@
 //**************************************************************
 
 
-static int IMAGE_SIZE = 12749;
+static int IMAGE_SIZE = 12938;
 
 struct EDGE_FIRMWARE_VERSION_INFO
 {
@@ -16,7 +16,7 @@
 
 static struct EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME =
 {
-	4, 10, 0		// Major, Minor, Build
+	4, 80, 0		// Major, Minor, Build
 
 };
 
@@ -27,16 +27,16 @@
 //      WORD    Length;
 //      BYTE    CheckSum;
 //  };
-0xca, 0x31, 
-0xa8, 
+0x87, 0x32,
+0x9a,
 
-0x02, 0x26, 0xfe, 0x02, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 
+0x02, 0x27, 0xbf, 0x02, 0x21, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x1a, 0x85, 0x3f, 
 0x8c, 0x85, 0x40, 0x8a, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 
 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xe5, 0x3e, 
 0x24, 0x08, 0xf8, 0xe6, 0x60, 0x2b, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0xa6, 0x81, 0xe5, 0x3e, 0x75, 
 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0x78, 0x8c, 0xe5, 0x81, 
-0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0x94, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, 
+0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0xdc, 0xe6, 0xf0, 0x08, 0xa3, 0xd9,
 0xfa, 0x74, 0x08, 0x25, 0x3e, 0xf8, 0x05, 0x3e, 0x08, 0xe6, 0x54, 0x80, 0x70, 0x0c, 0xe5, 0x3e, 
 0xb4, 0x07, 0xf3, 0x78, 0x08, 0x75, 0x3e, 0x00, 0x80, 0xef, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0x86, 
 0x81, 0xe5, 0x3e, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 
@@ -49,387 +49,398 @@
 0xc9, 0xf0, 0x69, 0x60, 0x02, 0x7e, 0x04, 0xa3, 0xe0, 0xca, 0xf0, 0x6a, 0x60, 0x02, 0x7e, 0x04, 
 0xa3, 0xe0, 0xcb, 0xf0, 0x6b, 0x60, 0x02, 0x7e, 0x04, 0x22, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 
 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 
-0xc0, 0x06, 0xc0, 0x07, 0x90, 0xff, 0x93, 0x74, 0x01, 0xf0, 0xe5, 0x81, 0x94, 0xfd, 0x40, 0x03, 
-0x02, 0x11, 0x94, 0x85, 0x41, 0x8d, 0x85, 0x42, 0x8b, 0x74, 0xaf, 0xf5, 0x82, 0x74, 0xfa, 0xf5, 
-0x83, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x4a, 0xe0, 0x30, 0xe7, 0x2c, 
-0x90, 0xff, 0x4e, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, 
-0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, 
-0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, 
-0xa3, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x52, 0xe0, 0x30, 0xe7, 0x2c, 
-0x90, 0xff, 0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x25, 
-0xb4, 0x02, 0x22, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, 
-0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 
-0x80, 0x03, 0x02, 0x02, 0x62, 0x74, 0x15, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x20, 0x04, 
-0xf1, 0x20, 0x02, 0x03, 0x30, 0x01, 0xeb, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 
-0x14, 0xfc, 0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, 
-0x7e, 0x01, 0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x33, 0x12, 0x00, 0xc9, 0xee, 
-0x64, 0x04, 0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, 
-0x02, 0x60, 0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x33, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, 
-0x33, 0x7c, 0x0a, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 
-0xa3, 0xee, 0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, 
-0x80, 0x10, 0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, 
-0xd2, 0xb1, 0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, 
-0x60, 0x08, 0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x3d, 
-0x60, 0x13, 0x14, 0xf5, 0x3d, 0x70, 0x0e, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 
-0x0f, 0xd2, 0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 
-0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 
-0x90, 0xff, 0x04, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 
-0xec, 0xff, 0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, 
-0x01, 0x7f, 0x08, 0x8e, 0x3b, 0x8f, 0x3c, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 
-0xff, 0xea, 0x90, 0xfa, 0xba, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x1c, 0x30, 0xe4, 0xf5, 0x4d, 0xe5, 
-0x4d, 0xc3, 0x94, 0x02, 0x50, 0x0f, 0x12, 0x1c, 0x11, 0xe4, 0x12, 0x1a, 0x38, 0x05, 0x4d, 0x04, 
-0x12, 0x1c, 0x02, 0x80, 0xea, 0x12, 0x1c, 0x30, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, 
-0xc0, 0x70, 0x03, 0x02, 0x08, 0xc5, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 
-0xe0, 0xfe, 0x54, 0x0f, 0xf5, 0x4d, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, 
-0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 0x4c, 0x03, 0x56, 0x00, 0x04, 0x29, 0x01, 0x05, 0x3c, 0x03, 
-0x06, 0x03, 0x05, 0x06, 0x45, 0x06, 0x07, 0xa7, 0x08, 0x07, 0xef, 0x09, 0x08, 0x4b, 0x0a, 0x08, 
-0x8b, 0x0b, 0x00, 0x00, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 
-0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x02, 0x45, 
-0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, 
-0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xee, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x11, 0x74, 
-0x01, 0x12, 0x1a, 0x38, 0x78, 0x67, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x1c, 0x11, 0x74, 0x02, 0x12, 
-0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb6, 0xe0, 
-0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 
-0x26, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 
-0x60, 0x07, 0x64, 0x80, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, 
-0x26, 0xe5, 0x4d, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x1c, 0x0e, 0x12, 0x1a, 
-0x38, 0x80, 0x24, 0x90, 0xff, 0x82, 0x12, 0x1c, 0x0e, 0x12, 0x1a, 0x38, 0x80, 0x19, 0x15, 0x4d, 
-0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x80, 0x09, 0x12, 0x1c, 
-0xb3, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x12, 0x1c, 0x11, 0x12, 0x19, 0xf2, 0x60, 0x05, 0x74, 
-0x01, 0x12, 0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 
-0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x14, 0x60, 0x2d, 
-0x14, 0x60, 0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x04, 
-0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 
-0x0f, 0x26, 0x78, 0x67, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 
-0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb6, 
-0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, 
-0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, 
-0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, 
-0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 
-0x07, 0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x4d, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, 
-0x54, 0xf7, 0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x4d, 0x24, 0xfe, 0x60, 
-0x20, 0x24, 0xfb, 0x60, 0x34, 0x24, 0x06, 0x70, 0x35, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 
-0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, 0x26, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, 
-0x1d, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x0e, 
-0xe4, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x05, 0x7f, 0x87, 0x12, 0x31, 0x32, 0x15, 0x4d, 
-0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x09, 0x12, 0x1c, 
-0xb3, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 
-0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 
-0x14, 0x60, 0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 
-0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 
-0x60, 0x03, 0x02, 0x0f, 0x26, 0x78, 0x67, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, 
-0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x07, 
-0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0a, 0xe5, 0x4d, 0xd3, 0x94, 
-0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, 0x70, 
-0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x12, 0x31, 0x82, 
-0x40, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 
-0xe5, 0x4d, 0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x1d, 0x64, 0x02, 0x1d, 0x2f, 0xe5, 0x35, 0x20, 
-0xe1, 0x03, 0x02, 0x0f, 0x26, 0x15, 0x4d, 0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, 
-0x44, 0x08, 0xf0, 0x80, 0x09, 0x12, 0x1c, 0xb3, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0xe4, 0xff, 
-0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 
-0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 
-0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xbb, 
-0xe0, 0x90, 0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x35, 0x01, 0x80, 0x03, 0x53, 0x35, 0xfe, 
-0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 
-0x3b, 0x70, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 
-0xba, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, 0x75, 0x24, 0x02, 
-0x60, 0x03, 0x02, 0x0f, 0x26, 0xed, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x30, 0x12, 0x1d, 
-0x5d, 0x7d, 0x03, 0x12, 0x0f, 0x6d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, 
-0xb3, 0xe0, 0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 
-0xaf, 0x3c, 0x02, 0x0f, 0xba, 0x12, 0x1c, 0x30, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x0d, 0x12, 
-0x1d, 0x5d, 0x7d, 0x14, 0x12, 0x0f, 0x6d, 0x60, 0x10, 0x02, 0x0f, 0x26, 0x12, 0x1d, 0x5d, 0x7d, 
-0x04, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, 0xb3, 0xe0, 
-0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 
-0x02, 0x0f, 0xba, 0x12, 0x1d, 0x5d, 0x7d, 0x05, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, 
-0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 0x12, 0x1c, 0x78, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 
-0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb2, 0xf0, 
-0xe4, 0xf5, 0x4c, 0x90, 0xfa, 0xb2, 0xe0, 0xff, 0xe5, 0x4c, 0xc3, 0x9f, 0x50, 0x24, 0x12, 0x1c, 
-0x72, 0x12, 0x0f, 0xcc, 0xff, 0xfd, 0x90, 0xfa, 0xb4, 0xe4, 0x8d, 0xf0, 0x12, 0x1a, 0x6c, 0x90, 
-0xfa, 0xb3, 0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0f, 0x26, 0x05, 0x4c, 
-0x80, 0xd1, 0x12, 0x1c, 0x72, 0x12, 0x0f, 0xcc, 0x24, 0xfe, 0xff, 0x90, 0xfa, 0xb3, 0xf0, 0xfd, 
-0xa3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0x7a, 0xf9, 0x79, 0x6f, 0x7b, 0x01, 0x8b, 0x36, 
-0x8a, 0x37, 0x89, 0x38, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x1c, 0x78, 0x12, 0x25, 
-0xd7, 0x8f, 0x4c, 0x05, 0x4c, 0x05, 0x4c, 0x12, 0x1c, 0x11, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x12, 
-0x1c, 0x11, 0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x1a, 0x4a, 0xaf, 0x4c, 0x7e, 0x00, 0xc3, 0xef, 
-0x95, 0x3c, 0xee, 0x95, 0x3b, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x8e, 0x39, 0x8f, 
-0x3a, 0x02, 0x2c, 0x07, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 
-0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 
-0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 
-0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 
-0x02, 0x0f, 0x26, 0x75, 0x36, 0x00, 0x75, 0x37, 0x00, 0x75, 0x38, 0x32, 0x02, 0x0f, 0xa9, 0xe5, 
-0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 
-0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, 0xe0, 0x94, 0x01, 
-0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, 
-0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 
-0xbb, 0xe0, 0xf5, 0x32, 0xe5, 0x32, 0x70, 0x08, 0x43, 0x35, 0x01, 0x53, 0x35, 0xfd, 0x80, 0x06, 
-0x53, 0x35, 0xfe, 0x43, 0x35, 0x02, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, 
-0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 
-0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 
-0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 
-0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x7f, 0x01, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 
-0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, 
-0xe0, 0x94, 0x00, 0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 
-0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 
-0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xff, 0x01, 0x12, 0x1d, 0x74, 0xef, 0x12, 0x1a, 0x38, 0x90, 
-0xfa, 0xb6, 0x12, 0x1d, 0x74, 0x90, 0x00, 0x01, 0xef, 0x12, 0x1a, 0x4a, 0x90, 0x00, 0x02, 0xe4, 
-0x12, 0x1a, 0x4a, 0x74, 0x03, 0x12, 0x1c, 0x02, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0xa3, 0xe0, 0x85, 
-0x38, 0x82, 0x85, 0x37, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 
-0x4c, 0x09, 0x4a, 0x02, 0x09, 0x6c, 0x04, 0x09, 0x8e, 0x05, 0x09, 0xba, 0x06, 0x09, 0xd8, 0x07, 
-0x09, 0xf6, 0x08, 0x0a, 0x14, 0x09, 0x0a, 0x32, 0x0b, 0x0a, 0xe7, 0x80, 0x0d, 0x6f, 0x81, 0x0d, 
-0xa0, 0x82, 0x0b, 0x2e, 0x83, 0x0b, 0x77, 0x84, 0x0b, 0x96, 0x85, 0x0b, 0xdb, 0x86, 0x0c, 0x26, 
-0x87, 0x0c, 0xb7, 0x88, 0x0d, 0x42, 0x89, 0x0a, 0x50, 0x92, 0x0a, 0x50, 0x93, 0x0e, 0x53, 0xc0, 
-0x0e, 0x7f, 0xc1, 0x0e, 0x90, 0xc2, 0x00, 0x00, 0x0f, 0x15, 0xe5, 0x35, 0x20, 0xe7, 0x05, 0x7f, 
-0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 
-0x7f, 0x07, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, 0x20, 0xe7, 
-0x05, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 
-0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, 
-0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1d, 0x92, 0x50, 0x06, 0xe5, 0x3c, 0x45, 0x3b, 0x70, 
-0x05, 0x7f, 0x02, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfe, 0x24, 0xfd, 0x50, 0x02, 
-0x80, 0x03, 0x02, 0x31, 0x6f, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 
-0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x08, 
-0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 
-0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x09, 0x02, 0x11, 
-0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 
-0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, 0x11, 0x16, 0x7f, 
-0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 
-0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 
-0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 
-0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 
-0xe5, 0x35, 0x30, 0xe7, 0x56, 0x12, 0x1c, 0xc9, 0x70, 0x4a, 0x90, 0xff, 0x02, 0xe0, 0xf5, 0x4c, 
-0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 0x4c, 0xb4, 0x83, 0x05, 0x75, 
-0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x4c, 0x12, 0x1b, 0x72, 0x12, 
-0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, 0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x05, 0x12, 0x31, 0xbd, 
-0x80, 0x06, 0x85, 0x33, 0x39, 0x85, 0x34, 0x3a, 0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 
-0x72, 0x02, 0x2c, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0x18, 0x12, 0x1c, 0xc9, 0x60, 0x05, 
-0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1d, 0x92, 0x40, 0x05, 0x7f, 0x03, 0x02, 0x30, 0xec, 0x90, 
-0xff, 0x02, 0xe0, 0xf5, 0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 
-0x4c, 0xb4, 0x83, 0x05, 0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 
-0x4c, 0x12, 0x1b, 0x72, 0x02, 0x31, 0x6f, 0x12, 0x1d, 0x9c, 0x12, 0x2a, 0x06, 0x12, 0x1c, 0x83, 
-0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x78, 0x68, 0x12, 0x1b, 
-0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, 0xe4, 0x12, 0x1a, 
-0x4a, 0x90, 0xfa, 0xb7, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x7c, 0xe6, 0xfc, 0x08, 0xe6, 0x8c, 
-0x83, 0x12, 0x1c, 0x8b, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, 
-0xb6, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, 
-0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, 0xbb, 0xe0, 0x42, 
-0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, 0xe0, 
-0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xbb, 0xe0, 0x42, 0xb0, 
-0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0xb4, 0x01, 
-0x0a, 0x12, 0x1c, 0x11, 0xe5, 0x90, 0x12, 0x1a, 0x38, 0x80, 0x08, 0x12, 0x1c, 0x11, 0xe5, 0xb0, 
-0x12, 0x1a, 0x38, 0x02, 0x0f, 0xa9, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0x12, 0x12, 0x1c, 0x41, 
-0x20, 0xe1, 0x33, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, 0xfa, 
-0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x1d, 0xa6, 
-0xf0, 0x80, 0x13, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0xf0, 
-0x80, 0x04, 0x12, 0x1d, 0xad, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 
-0x24, 0x12, 0x12, 0x1c, 0x41, 0x20, 0xe1, 0x39, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x1b, 
-0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, 
-0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 
-0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 
-0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x12, 0x1c, 0xc1, 0x60, 0x4d, 0x04, 0x60, 
-0x03, 0x02, 0x0c, 0xb2, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x0f, 0x90, 0xff, 0xa4, 0x12, 0x1c, 0x3a, 
-0x30, 0xe1, 0x6f, 0x12, 0x1d, 0x7c, 0x02, 0x0c, 0xb2, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfb, 0x12, 
-0x1c, 0x3d, 0xfe, 0x30, 0xe1, 0x5c, 0x30, 0xe2, 0x11, 0x30, 0xb4, 0x05, 0x12, 0x1d, 0x7c, 0x80, 
-0x51, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x48, 0x30, 0x95, 0x05, 0x12, 0x1d, 0x7c, 
-0x80, 0x40, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x37, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 
-0x12, 0x90, 0xff, 0xb4, 0x12, 0x1c, 0x3a, 0x30, 0xe1, 0x28, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 
-0xf0, 0x80, 0x1f, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfb, 0x12, 0x1c, 0x3d, 0x30, 0xe1, 0x13, 0x30, 
-0x93, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 
-0xfd, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, 
-0x60, 0x40, 0x04, 0x70, 0x78, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xa2, 0xe0, 0x44, 
-0x40, 0xf0, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x65, 0xd2, 0x03, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 
-0xff, 0xa3, 0xef, 0x54, 0x7f, 0xf0, 0x80, 0x55, 0x30, 0x03, 0x0e, 0x90, 0xff, 0xa3, 0xe0, 0x44, 
-0x80, 0xf0, 0xc2, 0x03, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x54, 0xbf, 0xf0, 
-0x80, 0x3b, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xb2, 0xe0, 0x44, 0x40, 0xf0, 0xa3, 
-0xe0, 0xff, 0x30, 0xe7, 0x28, 0xd2, 0x04, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0xff, 0xb3, 0xef, 
-0x54, 0x7f, 0xf0, 0x80, 0x18, 0x30, 0x04, 0x0e, 0x90, 0xff, 0xb3, 0xe0, 0x44, 0x80, 0xf0, 0xc2, 
-0x04, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, 0x02, 
-0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, 0x60, 0x0f, 0x04, 0x70, 0x16, 
-0x90, 0xff, 0xa6, 0xe0, 0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x80, 0x0a, 0x90, 0xff, 0xb6, 0xe0, 
-0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0x07, 0xe4, 
-0xff, 0x12, 0x30, 0xec, 0x12, 0x1d, 0x37, 0x7f, 0x03, 0x12, 0x12, 0x19, 0x90, 0xf9, 0x15, 0xe0, 
-0x30, 0xe4, 0x08, 0x90, 0xff, 0x93, 0x74, 0x80, 0xf0, 0x80, 0x10, 0x90, 0xff, 0xfc, 0xe0, 0x54, 
-0x7f, 0xf0, 0x7f, 0xff, 0x7e, 0x00, 0x12, 0x30, 0x16, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, 
-0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0x90, 0xfa, 0xbc, 0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, 
-0xb4, 0xf0, 0xa3, 0x74, 0x15, 0xf0, 0xe0, 0x54, 0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, 
-0xb9, 0xf0, 0xd3, 0x94, 0x00, 0xe4, 0x94, 0x3e, 0x40, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, 
-0xb9, 0xf0, 0x12, 0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x73, 0x12, 0x1c, 0x4a, 0x90, 0xfa, 
-0xbc, 0x12, 0x1d, 0x56, 0x60, 0x27, 0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, 
-0xfa, 0xb9, 0x74, 0x40, 0xf0, 0x80, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, 0xb9, 0xf0, 0x12, 
-0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x46, 0x12, 0x1c, 0x4a, 0x80, 0xd1, 0x75, 0x4c, 0x02, 
-0x90, 0xfa, 0xbc, 0xe4, 0xf0, 0xa3, 0x04, 0xf0, 0x90, 0xfa, 0xb4, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, 
-0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0x90, 0xfa, 0xbd, 0xe0, 0xf5, 0x4a, 0x7d, 0x0f, 0x7c, 
-0x00, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0xe4, 
-0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xaf, 0x31, 
-0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x30, 0xe7, 0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x64, 0xf0, 
-0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, 0x11, 0x90, 0xfa, 0xb7, 0xe0, 0x54, 0x0f, 0x90, 
-0xf9, 0x63, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, 0xc2, 0x94, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 
-0x1d, 0x9c, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, 0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x30, 0xec, 
-0x12, 0x1c, 0xd0, 0x54, 0x03, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 
-0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, 0x80, 0x23, 0x12, 0x1d, 0xa6, 0x12, 0x0f, 0x78, 
-0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0x78, 0xff, 
-0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, 0x91, 0x12, 0x1d, 0xa6, 0xf0, 0x90, 0xfa, 0xb7, 
-0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 
-0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, 0x27, 0xc2, 0x92, 0x80, 0x23, 0x12, 0x1d, 0xad, 
-0x12, 0x0f, 0x98, 0x60, 0x04, 0xd2, 0x92, 0x80, 0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, 
-0x0f, 0x98, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, 0x80, 0x02, 0xd2, 0x92, 0x12, 0x1d, 0xad, 0xf0, 
-0xe4, 0xff, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 
-0x18, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x31, 0xbd, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 
-0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 
-0xfa, 0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 
-0xaa, 0x4e, 0xa9, 0x4f, 0x7b, 0xff, 0x90, 0xfa, 0xb4, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 
-0xb9, 0xe0, 0xf5, 0x4a, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x22, 0x12, 0x22, 0xa0, 
-0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, 
-0xa6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x12, 0x25, 0xd7, 0x8f, 0x4c, 0x7e, 0x00, 
-0xc3, 0xef, 0x95, 0x3c, 0xee, 0x95, 0x3b, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, 
-0xb6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 
-0x2c, 0x07, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x02, 0x31, 0x82, 0x8e, 0x39, 0x8f, 0x3a, 0x02, 0x2c, 
-0x07, 0x12, 0x22, 0xa0, 0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0x7d, 0x01, 0x12, 0x25, 
-0xd7, 0x90, 0xfa, 0xb1, 0xe0, 0x22, 0xef, 0x90, 0xf8, 0x04, 0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 
-0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, 0xfe, 0xde, 0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, 
-0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, 0x03, 0xd0, 0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, 
-0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x05, 0xe5, 0x3e, 0x24, 
-0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, 0xff, 0x12, 0x10, 0x78, 0x7f, 0x00, 0x7e, 0x00, 
-0xe5, 0x43, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, 
-0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 
-0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, 
-0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 
-0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x12, 0x11, 0x0f, 0xd0, 0x05, 0xd0, 
-0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x85, 0xa8, 0x44, 0x75, 0xa8, 0x88, 0xec, 0x70, 
-0x02, 0x7c, 0x3f, 0x8c, 0x3d, 0x22, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0x66, 
-0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x06, 0x7c, 0xff, 0x12, 0x10, 
-0x78, 0xe5, 0x43, 0x60, 0x42, 0xfe, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, 
-0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, 
-0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0xd8, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 
-0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 
-0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, 0x08, 0x08, 0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, 
-0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, 0x00, 0x80, 0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, 
-0xee, 0x12, 0x11, 0x0f, 0xd0, 0x06, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, 
-0x3d, 0x00, 0x85, 0x44, 0xa8, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc3, 0xe5, 0x43, 0x24, 
-0xe8, 0x50, 0x05, 0x12, 0x11, 0x66, 0x80, 0xf4, 0xef, 0x60, 0x31, 0x90, 0x30, 0x54, 0xe4, 0x93, 
-0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, 0x12, 0x10, 0x78, 0xd0, 0x04, 0x43, 0x07, 0x80, 
-0xe5, 0x43, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, 
-0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, 0x43, 0x12, 0x11, 0x0f, 0xd0, 0x83, 0xd0, 0x82, 
-0xd0, 0xf0, 0x22, 0x02, 0x11, 0x94, 0xc0, 0x04, 0x7c, 0x20, 0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, 
-0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, 0x88, 0x00, 0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, 
-0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, 0xf0, 0x90, 0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, 
-0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, 0xff, 0x48, 0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, 
-0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, 
-0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 
-0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0xd2, 0xb0, 0xd2, 0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, 
-0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 
-0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x03, 0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, 
-0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x42, 0x8e, 0x41, 0x22, 0xc3, 0xef, 0x94, 0xbc, 
-0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, 0xd0, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 
-0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x40, 0x8e, 0x3f, 0x22, 0xef, 0x70, 0x01, 0x22, 0xc0, 0x00, 0xc0, 
-0xa8, 0xc2, 0xaf, 0xe5, 0x3e, 0x24, 0x18, 0xf8, 0xa6, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xc6, 
-0x54, 0x7f, 0xf6, 0xd0, 0xa8, 0xe6, 0x30, 0xe7, 0x03, 0xd0, 0x00, 0x22, 0x12, 0x11, 0x66, 0x80, 
-0xf4, 0xc0, 0x00, 0x7f, 0x01, 0xef, 0x24, 0x08, 0xf8, 0xe6, 0x60, 0x09, 0x0f, 0xbf, 0x08, 0xf5, 
-0x12, 0x11, 0x66, 0x80, 0xee, 0xd0, 0x00, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 
-0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, 0x76, 0x9a, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, 
-0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0xc0, 0x82, 0xc0, 0x83, 0xa3, 0xa3, 0xe4, 0x78, 
-0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, 0x75, 0xf0, 0x02, 0xa4, 0x24, 0x36, 0xf5, 0x82, 
-0xe5, 0xf0, 0x34, 0x30, 0xf5, 0x83, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, 
-0x82, 0xec, 0xf0, 0xa3, 0xee, 0xf0, 0xed, 0x24, 0x08, 0xf8, 0xef, 0x44, 0x80, 0xf6, 0xd0, 0x04, 
-0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x75, 0x3e, 0x00, 0x75, 0x43, 
-0x00, 0x7a, 0x08, 0x79, 0x18, 0x78, 0x08, 0x76, 0x00, 0x77, 0x00, 0x08, 0x09, 0xda, 0xf8, 0x90, 
-0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x30, 0x54, 0xe4, 0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, 
-0x04, 0xf0, 0x78, 0x08, 0x74, 0x80, 0x44, 0x7f, 0xf6, 0x74, 0x01, 0x44, 0x10, 0xf5, 0x89, 0x75, 
-0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, 0x81, 0x8b, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, 
-0xe5, 0x43, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, 
-0x08, 0xe0, 0x54, 0x7f, 0xfa, 0x7b, 0x00, 0xe6, 0x54, 0x7f, 0xb5, 0x02, 0x02, 0x7b, 0xff, 0x08, 
-0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, 0x07, 0x12, 0x12, 0x41, 0xad, 0x07, 0xaf, 0x02, 
-0x12, 0x12, 0x58, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, 0xdf, 0xce, 0x12, 0x11, 0x66, 0x80, 0xc1, 0x8f, 
-0x24, 0x12, 0x2a, 0x06, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, 
-0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xed, 0xf0, 
-0x12, 0x22, 0x56, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x12, 0x22, 0x99, 0x24, 0x09, 0x12, 
-0x21, 0xf3, 0xef, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x22, 0x09, 0xc0, 
-0x83, 0xc0, 0x82, 0xa3, 0xe0, 0x25, 0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, 
-0x82, 0xe0, 0x33, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x78, 0x80, 0x12, 0x22, 0x09, 
-0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0xff, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x21, 
-0xf3, 0xef, 0xf0, 0xed, 0x12, 0x22, 0x99, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xed, 0xf0, 0x12, 0x21, 
-0xfb, 0xe0, 0xff, 0x30, 0xe7, 0x19, 0x12, 0x22, 0x6e, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x09, 0x12, 
-0x21, 0xfb, 0xef, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x12, 0x21, 0xfb, 0xef, 0x54, 0xfd, 0xf0, 0x78, 
-0x7e, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, 
-0xfd, 0x12, 0x22, 0x43, 0xa3, 0xe0, 0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 
-0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x31, 0x94, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x78, 0x80, 
-0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x06, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x03, 0x43, 0x07, 
-0x04, 0x53, 0x07, 0xfc, 0x78, 0x80, 0x12, 0x22, 0x7a, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x42, 
-0x07, 0x43, 0x07, 0x80, 0x12, 0x22, 0x8a, 0xf5, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xef, 0xf0, 0x12, 
-0x22, 0x99, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xe0, 
-0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, 0x80, 0x03, 0x43, 0x07, 0x20, 0xec, 
-0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, 0x10, 0x12, 0x21, 0xfb, 0xe0, 0xfe, 
-0x54, 0x03, 0x60, 0x73, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, 0x69, 0x78, 0x80, 0x12, 0x22, 0x6f, 
-0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x14, 0xa6, 0x00, 0x14, 0xda, 0x01, 0x14, 0xdf, 0x03, 
-0x14, 0xda, 0x05, 0x14, 0xdf, 0x07, 0x14, 0xda, 0x09, 0x14, 0xdf, 0x0b, 0x14, 0xda, 0x0d, 0x14, 
-0xdf, 0x0f, 0x00, 0x00, 0x14, 0xe7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x21, 0x90, 0xf9, 0x15, 0xe0, 
-0x30, 0xe2, 0x0d, 0x30, 0xb4, 0x05, 0x43, 0x07, 0x02, 0x80, 0x2c, 0x53, 0x07, 0xfd, 0x80, 0x27, 
-0x30, 0x95, 0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, 0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, 
-0x43, 0x07, 0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, 0x43, 0x07, 0x02, 0x80, 0x08, 0x53, 
-0x07, 0xfd, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x22, 0x78, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xef, 
-0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, 0x12, 0x21, 0xfb, 0xe0, 0xfe, 0x54, 
-0x03, 0x70, 0x03, 0x02, 0x15, 0xd7, 0xee, 0x20, 0xe1, 0x03, 0x02, 0x15, 0xd4, 0x12, 0x22, 0x6e, 
-0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x15, 0x36, 0x00, 0x15, 0x6c, 0x01, 0x15, 0x6c, 0x03, 
-0x15, 0xa0, 0x05, 0x15, 0xa0, 0x07, 0x15, 0x86, 0x09, 0x15, 0x86, 0x0b, 0x15, 0xba, 0x0d, 0x15, 
-0xba, 0x0f, 0x00, 0x00, 0x15, 0xd7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x23, 0x90, 0xf9, 0x15, 0xe0, 
-0x30, 0xe2, 0x0f, 0x30, 0xb1, 0x06, 0x53, 0x07, 0x7f, 0x02, 0x15, 0xd7, 0x43, 0x07, 0x80, 0x02, 
-0x15, 0xd7, 0x30, 0x94, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x7d, 0x43, 0x07, 0x80, 0x80, 0x78, 0x30, 
-0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x70, 0x43, 0x07, 0x80, 0x80, 0x6b, 0xe5, 0x24, 0xb4, 0x03, 
-0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, 
-0xf0, 0x53, 0x07, 0x7f, 0x80, 0x51, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, 
-0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x37, 
-0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 
-0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x43, 0x07, 0x80, 0x80, 0x1d, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 
-0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x43, 
-0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, 0x78, 0x80, 0x12, 0x22, 0x3f, 0xe0, 0xfc, 0xa3, 0xe0, 
-0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, 0x53, 0x07, 0xdf, 0xec, 0x30, 0xe3, 0x05, 
-0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x10, 0x80, 
-0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, 0x07, 0x08, 0x80, 0x03, 0x53, 0x07, 0xf7, 
-0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, 0x53, 0x07, 0xfb, 0xed, 0x30, 0xe6, 0x05, 
-0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, 0x30, 0xe7, 0x05, 0x43, 0x07, 0x02, 0x80, 
-0x03, 0x53, 0x07, 0xfd, 0x78, 0x7e, 0x12, 0x22, 0x3f, 0xa3, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0x7f, 
-0x00, 0x22, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, 0x74, 0x16, 0xf0, 0x90, 0xff, 0xf9, 0x74, 
-0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, 
-0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x12, 0x18, 0xe2, 0xe5, 0x23, 0x30, 0xe7, 0x02, 0xd2, 
-0x02, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x24, 0x90, 0xfa, 0xcc, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 
-0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, 
-0x00, 0x7a, 0x00, 0x79, 0x23, 0x75, 0x2d, 0x00, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe5, 
-0x23, 0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x23, 0x64, 0x07, 0x60, 0x1e, 0xe5, 0x23, 0x64, 
-0x06, 0x60, 0x18, 0xe5, 0x23, 0x64, 0x14, 0x60, 0x12, 0xe5, 0x23, 0x64, 0x41, 0x60, 0x0c, 0xe5, 
-0x23, 0x64, 0x1a, 0x70, 0x46, 0xe5, 0x24, 0x64, 0x02, 0x70, 0x40, 0xe5, 0x23, 0xb4, 0x07, 0x16, 
-0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x02, 0xf0, 0xa3, 
-0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1e, 0xe5, 0x23, 0xb4, 0x41, 0x12, 0x90, 0xf9, 0x15, 0xe0, 0x44, 
-0x06, 0xf0, 0xa3, 0xe0, 0x44, 0x06, 0xf0, 0xd2, 0xb1, 0xd2, 0xb4, 0x80, 0x07, 0x90, 0xf9, 0x15, 
-0xe0, 0x44, 0x01, 0xf0, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x23, 0x64, 0x42, 0x60, 
-0x05, 0xe5, 0x23, 0xb4, 0x43, 0x0c, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x80, 0xf0, 0xa3, 0xe0, 0x44, 
-0x80, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x18, 0xe2, 0x90, 0xff, 
-0xf5, 0xe5, 0x23, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x33, 0xf5, 0x34, 0xf5, 0x32, 0x12, 0x1d, 0x84, 
-0x12, 0x1c, 0x30, 0x12, 0x1d, 0x8b, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x43, 0x90, 0xf9, 0x6c, 0x12, 
-0x1b, 0x43, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, 
+0xc0, 0x06, 0xc0, 0x07, 0x74, 0x15, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x60, 0x23, 0x74,
+0x66, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x14, 0xf0, 0x70, 0x16, 0x74, 0xff, 0xf0, 0x74,
+0x1c, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x60, 0x04, 0x14, 0xf0, 0x70, 0x04, 0xc2, 0x90,
+0x80, 0xfc, 0x90, 0xff, 0x93, 0x74, 0x81, 0xf0, 0xe5, 0x81, 0x94, 0xfd, 0x40, 0x03, 0x02, 0x11,
+0xdc, 0x85, 0x41, 0x8d, 0x85, 0x42, 0x8b, 0x74, 0xb2, 0xf5, 0x82, 0x74, 0xfa, 0xf5, 0x83, 0xe0,
+0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x4a, 0xe0, 0x30, 0xe7, 0x2c, 0x90, 0xff,
+0x4e, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, 0xb4, 0x02,
+0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x28, 0x4e, 0x80,
+0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, 0xa3, 0xe0,
+0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x52, 0xe0, 0x30, 0xe7, 0x2c, 0x90, 0xff,
+0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x25, 0xb4, 0x02,
+0x22, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x28, 0x4e, 0x80,
+0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x80, 0x03,
+0x02, 0x02, 0x90, 0x74, 0x16, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x20, 0x04, 0xf1, 0x20,
+0x02, 0x03, 0x30, 0x01, 0xeb, 0x74, 0x19, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x14, 0xfc,
+0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, 0x7e, 0x01,
+0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x61, 0x12, 0x00, 0xc9, 0xee, 0x64, 0x04,
+0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, 0x02, 0x60,
+0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x61, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, 0x61, 0x7c,
+0x0a, 0x74, 0x19, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, 0xee,
+0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, 0x80, 0x10,
+0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, 0xd2, 0xb1,
+0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, 0x60, 0x08,
+0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x3d, 0x60, 0x13,
+0x14, 0xf5, 0x3d, 0x70, 0x0e, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0x57, 0xd2,
+0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0,
+0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 0x90, 0xff,
+0x04, 0xe0, 0x90, 0xfa, 0xb9, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 0xff,
+0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, 0x01, 0x7f,
+0x08, 0x8e, 0x3b, 0x8f, 0x3c, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 0xff, 0xea,
+0x90, 0xfa, 0xbd, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x1c, 0xe0, 0xe4, 0xf5, 0x4d, 0xe5, 0x4d, 0xc3,
+0x94, 0x02, 0x50, 0x0f, 0x12, 0x1c, 0xc1, 0xe4, 0x12, 0x1a, 0xe8, 0x05, 0x4d, 0x04, 0x12, 0x1c,
+0xb2, 0x80, 0xea, 0x12, 0x1c, 0xe0, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, 0xc0, 0x70,
+0x03, 0x02, 0x08, 0xf3, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0xfe,
+0x54, 0x0f, 0xf5, 0x4d, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, 0x90, 0xff,
+0x01, 0xe0, 0x12, 0x1b, 0xfc, 0x03, 0x84, 0x00, 0x04, 0x57, 0x01, 0x05, 0x6a, 0x03, 0x06, 0x31,
+0x05, 0x06, 0x73, 0x06, 0x07, 0xd5, 0x08, 0x08, 0x1d, 0x09, 0x08, 0x79, 0x0a, 0x08, 0xb9, 0x0b,
+0x00, 0x00, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0,
+0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x64, 0x02, 0x45, 0x3b, 0x60,
+0x03, 0x02, 0x0f, 0x6e, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, 0x02, 0x60,
+0x03, 0x02, 0x0f, 0x6e, 0xee, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1c, 0xc1, 0x74, 0x01, 0x12,
+0x1a, 0xe8, 0x78, 0x67, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x1c, 0xc1, 0x74, 0x02, 0x12, 0x1a, 0xe8,
+0x7f, 0x02, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03,
+0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0x7f,
+0x02, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x60, 0x07,
+0x64, 0x80, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0xfa, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0xe5,
+0x4d, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x1c, 0xbe, 0x12, 0x1a, 0xe8, 0x80,
+0x24, 0x90, 0xff, 0x82, 0x12, 0x1c, 0xbe, 0x12, 0x1a, 0xe8, 0x80, 0x19, 0x15, 0x4d, 0x30, 0x0a,
+0x0b, 0x12, 0x1d, 0x55, 0x12, 0x1c, 0xbc, 0x12, 0x1a, 0xe8, 0x80, 0x09, 0x12, 0x1d, 0x63, 0x12,
+0x1c, 0xbc, 0x12, 0x1a, 0xe8, 0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xa2, 0x60, 0x05, 0x74, 0x01, 0x12,
+0x1a, 0xe8, 0x7f, 0x02, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5,
+0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x14, 0x60, 0x2d, 0x14, 0x60,
+0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x04, 0xa3, 0xe0,
+0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e,
+0x78, 0x67, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x06,
+0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb9, 0xe0, 0x60,
+0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb9, 0xe0, 0xd3, 0x94, 0x01,
+0x40, 0x03, 0x02, 0x0f, 0x6e, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02,
+0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0xfa, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0xe5,
+0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe0, 0x07, 0xe5,
+0x4d, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x4d, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7,
+0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x4d, 0x24, 0xfe, 0x60, 0x20, 0x24,
+0xfb, 0x60, 0x34, 0x24, 0x06, 0x70, 0x35, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f,
+0x03, 0x12, 0x2e, 0x79, 0x80, 0x26, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2e, 0x79, 0x80, 0x1d, 0x30,
+0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2e, 0x79, 0x80, 0x0e, 0xe4, 0xfd,
+0x7f, 0x04, 0x12, 0x2e, 0x79, 0x80, 0x05, 0x7f, 0x87, 0x12, 0x31, 0xef, 0x15, 0x4d, 0x30, 0x0a,
+0x0b, 0x12, 0x1d, 0x55, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x09, 0x12, 0x1d, 0x63, 0xf5,
+0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02,
+0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x14, 0x60,
+0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70,
+0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03,
+0x02, 0x0f, 0x6e, 0x78, 0x67, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35,
+0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe0, 0x07, 0xe5, 0x4d,
+0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe1, 0x0a, 0xe5, 0x4d, 0xd3, 0x94, 0x01, 0x40,
+0x03, 0x02, 0x0f, 0x6e, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, 0xa3,
+0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x12, 0x32, 0x3f, 0x40, 0x03,
+0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x4d,
+0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x1e, 0x14, 0x02, 0x1d, 0xdf, 0xe5, 0x35, 0x20, 0xe1, 0x03,
+0x02, 0x0f, 0x6e, 0x15, 0x4d, 0x30, 0x0a, 0x0b, 0x12, 0x1d, 0x55, 0xf5, 0x83, 0xe0, 0x44, 0x08,
+0xf0, 0x80, 0x09, 0x12, 0x1d, 0x63, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0xe4, 0xff, 0x02, 0x32,
+0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02,
+0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x60, 0x03,
+0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe1, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbe, 0xe0, 0x90,
+0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x35, 0x01, 0x80, 0x03, 0x53, 0x35, 0xfe, 0xe4, 0xff,
+0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x70,
+0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0,
+0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, 0x75, 0x24, 0x02, 0x60, 0x03,
+0x02, 0x0f, 0x6e, 0xed, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1c, 0xe0, 0x12, 0x1e, 0x0d, 0x7d,
+0x03, 0x12, 0x0f, 0xb5, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0x72, 0x90, 0xfa, 0xb6, 0xe0,
+0xfd, 0xa3, 0x12, 0x1d, 0x2b, 0x12, 0x0f, 0xd1, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c,
+0x02, 0x10, 0x02, 0x12, 0x1c, 0xe0, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe4, 0x0d, 0x12, 0x1e, 0x0d,
+0x7d, 0x14, 0x12, 0x0f, 0xb5, 0x60, 0x10, 0x02, 0x0f, 0x6e, 0x12, 0x1e, 0x0d, 0x7d, 0x04, 0x12,
+0x10, 0x09, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0x72, 0x90, 0xfa, 0xb6, 0xe0, 0xfd, 0xa3,
+0x12, 0x1d, 0x2b, 0x12, 0x0f, 0xd1, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x02, 0x10,
+0x02, 0x12, 0x1e, 0x0d, 0x7d, 0x05, 0x12, 0x10, 0x09, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x7b, 0x01,
+0x7a, 0xfa, 0x79, 0xb6, 0x12, 0x1d, 0x28, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xb7, 0xe4,
+0x75, 0xf0, 0x03, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xbe, 0xe0, 0x90, 0xfa, 0xb5, 0xf0, 0xe4, 0xf5,
+0x4c, 0x90, 0xfa, 0xb5, 0xe0, 0xff, 0xe5, 0x4c, 0xc3, 0x9f, 0x50, 0x24, 0x12, 0x1d, 0x22, 0x12,
+0x10, 0x14, 0xff, 0xfd, 0x90, 0xfa, 0xb7, 0xe4, 0x8d, 0xf0, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xb6,
+0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0f, 0x6e, 0x05, 0x4c, 0x80, 0xd1,
+0x12, 0x1d, 0x22, 0x12, 0x10, 0x14, 0x24, 0xfe, 0xff, 0x90, 0xfa, 0xb6, 0xf0, 0xfd, 0xa3, 0xe4,
+0x75, 0xf0, 0x02, 0x12, 0x1b, 0x1c, 0x7a, 0xf9, 0x79, 0x72, 0x7b, 0x01, 0x8b, 0x36, 0x8a, 0x37,
+0x89, 0x38, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x1d, 0x28, 0x12, 0x26, 0x98, 0x8f,
+0x4c, 0x05, 0x4c, 0x05, 0x4c, 0x12, 0x1c, 0xc1, 0xe5, 0x4c, 0x12, 0x1a, 0xe8, 0x12, 0x1c, 0xc1,
+0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x1a, 0xfa, 0xaf, 0x4c, 0x7e, 0x00, 0xc3, 0xef, 0x95, 0x3c,
+0xee, 0x95, 0x3b, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x8e, 0x39, 0x8f, 0x3a, 0x02,
+0x2c, 0xd8, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x64,
+0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f,
+0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d,
+0x79, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f,
+0x6e, 0x75, 0x36, 0x00, 0x75, 0x37, 0x00, 0x75, 0x38, 0x32, 0x02, 0x0f, 0xf1, 0xe5, 0x35, 0x30,
+0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa,
+0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xd3, 0x90, 0xfa, 0xbe, 0xe0, 0x94, 0x01, 0x90, 0xfa,
+0xbd, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x60, 0x03, 0x02, 0x0f,
+0x6e, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbe, 0xe0,
+0xf5, 0x32, 0xe5, 0x32, 0x70, 0x08, 0x43, 0x35, 0x01, 0x53, 0x35, 0xfd, 0x80, 0x06, 0x53, 0x35,
+0xfe, 0x43, 0x35, 0x02, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f,
+0x6e, 0xe5, 0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0,
+0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02,
+0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe1,
+0x03, 0x02, 0x0f, 0x6e, 0x7f, 0x01, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f,
+0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xd3, 0x90, 0xfa, 0xbe, 0xe0, 0x94,
+0x00, 0x90, 0xfa, 0xbd, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x64,
+0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x6e, 0xe4, 0xff,
+0x02, 0x32, 0x6e, 0x90, 0xff, 0x01, 0x12, 0x1e, 0x24, 0xef, 0x12, 0x1a, 0xe8, 0x90, 0xfa, 0xb9,
+0x12, 0x1e, 0x24, 0x90, 0x00, 0x01, 0xef, 0x12, 0x1a, 0xfa, 0x90, 0x00, 0x02, 0xe4, 0x12, 0x1a,
+0xfa, 0x74, 0x03, 0x12, 0x1c, 0xb2, 0x90, 0xfa, 0xbd, 0xe0, 0xff, 0xa3, 0xe0, 0x85, 0x38, 0x82,
+0x85, 0x37, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 0xfc, 0x09,
+0x7b, 0x02, 0x09, 0x9d, 0x04, 0x09, 0xbf, 0x05, 0x09, 0xeb, 0x06, 0x0a, 0x09, 0x07, 0x0a, 0x27,
+0x08, 0x0a, 0x45, 0x09, 0x0a, 0x63, 0x0b, 0x0b, 0x18, 0x80, 0x0d, 0xb7, 0x81, 0x0d, 0xe8, 0x82,
+0x0b, 0x5f, 0x83, 0x0b, 0xa8, 0x84, 0x0b, 0xc7, 0x85, 0x0c, 0x0c, 0x86, 0x0c, 0x57, 0x87, 0x0c,
+0xe8, 0x88, 0x0d, 0x73, 0x89, 0x0a, 0x81, 0x92, 0x0a, 0x81, 0x93, 0x0d, 0xa0, 0xb0, 0x0e, 0x9b,
+0xc0, 0x0e, 0xc7, 0xc1, 0x0e, 0xd8, 0xc2, 0x00, 0x00, 0x0f, 0x5d, 0xe5, 0x35, 0x20, 0xe7, 0x05,
+0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c,
+0x00, 0x7f, 0x07, 0x02, 0x11, 0x5e, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0xb4, 0xe5, 0x35, 0x20,
+0xe7, 0x05, 0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef,
+0xfd, 0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x11, 0x5e, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0xb4, 0xe5,
+0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, 0x1e, 0x42, 0x50, 0x06, 0xe5, 0x3c, 0x45, 0x3b,
+0x70, 0x05, 0x7f, 0x02, 0x02, 0x31, 0xa9, 0x90, 0xfa, 0xb9, 0xe0, 0x24, 0xfe, 0x24, 0xfd, 0x50,
+0x02, 0x80, 0x03, 0x02, 0x32, 0x2c, 0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03,
+0x02, 0x0f, 0x71, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f,
+0x08, 0x02, 0x11, 0x5e, 0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f,
+0x71, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x09, 0x02,
+0x11, 0x5e, 0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12,
+0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, 0x11, 0x5e,
+0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, 0x1d, 0x71,
+0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x11, 0x5e, 0x7f, 0x07,
+0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, 0x1d, 0x71, 0x60, 0x03,
+0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x11, 0x5e, 0x7f, 0x07, 0x02, 0x31,
+0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x56, 0x12, 0x1d, 0x79, 0x70, 0x4a, 0x90, 0xff, 0x02, 0xe0, 0xf5,
+0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 0x4c, 0xb4, 0x83, 0x05,
+0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x4c, 0x12, 0x1c, 0x22,
+0x12, 0x1e, 0x3b, 0x12, 0x25, 0xfa, 0x12, 0x1d, 0x89, 0x12, 0x1a, 0xbb, 0x60, 0x05, 0x12, 0x32,
+0x7a, 0x80, 0x06, 0x85, 0x33, 0x39, 0x85, 0x34, 0x3a, 0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75,
+0x38, 0x75, 0x02, 0x2c, 0xd8, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0xb4, 0x12, 0x1d, 0x79, 0x60,
+0x05, 0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x1e, 0x42, 0x40, 0x05, 0x7f, 0x03, 0x02, 0x31, 0xa9,
+0x90, 0xff, 0x02, 0xe0, 0xf5, 0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12,
+0xe5, 0x4c, 0xb4, 0x83, 0x05, 0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04,
+0xf5, 0x4c, 0x12, 0x1c, 0x22, 0x02, 0x32, 0x2c, 0x12, 0x1e, 0x4c, 0x12, 0x2a, 0xc7, 0x12, 0x1d,
+0x33, 0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xba, 0xf0, 0x78, 0x68, 0x12,
+0x1b, 0xd8, 0x90, 0x00, 0x02, 0x12, 0x1a, 0xbb, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, 0xe4, 0x12,
+0x1a, 0xfa, 0x90, 0xfa, 0xba, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x7c, 0xe6, 0xfc, 0x08, 0xe6,
+0x8c, 0x83, 0x12, 0x1d, 0x3b, 0xef, 0xf0, 0x12, 0x32, 0x84, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x90,
+0xfa, 0xb9, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xbd, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06,
+0xa3, 0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, 0xbe, 0xe0,
+0x42, 0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xbd, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3,
+0xe0, 0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xbe, 0xe0, 0x42,
+0xb0, 0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1c, 0xe0, 0x90, 0xfa, 0xb9, 0xe0, 0xb4,
+0x01, 0x0a, 0x12, 0x1c, 0xc1, 0xe5, 0x90, 0x12, 0x1a, 0xe8, 0x80, 0x08, 0x12, 0x1c, 0xc1, 0xe5,
+0xb0, 0x12, 0x1a, 0xe8, 0x02, 0x0f, 0xf1, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x24, 0x13, 0x12, 0x1c,
+0xf1, 0x20, 0xe1, 0x33, 0x12, 0x1d, 0x80, 0xef, 0x24, 0xfc, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90,
+0xfa, 0xba, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x1e,
+0x56, 0xf0, 0x80, 0x13, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10,
+0xf0, 0x80, 0x04, 0x12, 0x1e, 0x5d, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x90, 0xfa, 0xb9, 0xe0,
+0xff, 0x24, 0x13, 0x12, 0x1c, 0xf1, 0x20, 0xe1, 0x39, 0x12, 0x1d, 0x80, 0xef, 0x24, 0xfc, 0x60,
+0x1b, 0x04, 0x70, 0x2e, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20,
+0xf0, 0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xba, 0xe0,
+0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54,
+0xdf, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x12, 0x1d, 0x71, 0x60, 0x4d, 0x04,
+0x60, 0x03, 0x02, 0x0c, 0xe3, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x0f, 0x90, 0xff, 0xa4, 0x12, 0x1c,
+0xea, 0x30, 0xe1, 0x6f, 0x12, 0x1e, 0x2c, 0x02, 0x0c, 0xe3, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfb,
+0x12, 0x1c, 0xed, 0xfe, 0x30, 0xe1, 0x5c, 0x30, 0xe2, 0x11, 0x30, 0xb4, 0x05, 0x12, 0x1e, 0x2c,
+0x80, 0x51, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x48, 0x30, 0x95, 0x05, 0x12, 0x1e,
+0x2c, 0x80, 0x40, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x37, 0x90, 0xfa, 0xba, 0xe0,
+0x60, 0x12, 0x90, 0xff, 0xb4, 0x12, 0x1c, 0xea, 0x30, 0xe1, 0x28, 0x90, 0xff, 0xb4, 0xe0, 0x44,
+0x02, 0xf0, 0x80, 0x1f, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfb, 0x12, 0x1c, 0xed, 0x30, 0xe1, 0x13,
+0x30, 0x93, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0,
+0x54, 0xfd, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x90, 0xfa, 0xb9, 0xe0, 0x24,
+0xfc, 0x60, 0x40, 0x04, 0x70, 0x78, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xa2, 0xe0,
+0x44, 0x40, 0xf0, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x65, 0xd2, 0x03, 0xa3, 0xe0, 0x54, 0xdf, 0xf0,
+0x90, 0xff, 0xa3, 0xef, 0x54, 0x7f, 0xf0, 0x80, 0x55, 0x30, 0x03, 0x0e, 0x90, 0xff, 0xa3, 0xe0,
+0x44, 0x80, 0xf0, 0xc2, 0x03, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x54, 0xbf,
+0xf0, 0x80, 0x3b, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xb2, 0xe0, 0x44, 0x40, 0xf0,
+0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x28, 0xd2, 0x04, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0xff, 0xb3,
+0xef, 0x54, 0x7f, 0xf0, 0x80, 0x18, 0x30, 0x04, 0x0e, 0x90, 0xff, 0xb3, 0xe0, 0x44, 0x80, 0xf0,
+0xc2, 0x04, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff,
+0x02, 0x31, 0xa9, 0x12, 0x1c, 0xe0, 0x90, 0xfa, 0xb9, 0xe0, 0x24, 0xfc, 0x60, 0x0f, 0x04, 0x70,
+0x16, 0x90, 0xff, 0xa6, 0xe0, 0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xe8, 0x80, 0x0a, 0x90, 0xff, 0xb6,
+0xe0, 0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xe8, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0xd8,
+0x90, 0xf9, 0x15, 0x74, 0x01, 0xf0, 0x90, 0xf9, 0x1c, 0x74, 0x19, 0xf0, 0x90, 0xf9, 0x66, 0x74,
+0xff, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0xe4, 0xff, 0x12, 0x31, 0xa9, 0x12, 0x1d, 0xe7, 0x7f,
+0x03, 0x12, 0x12, 0x61, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe4, 0x08, 0x90, 0xff, 0x93, 0x74, 0x80,
+0xf0, 0x80, 0x10, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0xff, 0x7e, 0x00, 0x12, 0x30,
+0xd3, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, 0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0x90, 0xfa, 0xbf,
+0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, 0xb7, 0xf0, 0xa3, 0x74, 0x15, 0xf0, 0xe0, 0x54,
+0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, 0xbc, 0xf0, 0xd3, 0x94, 0x00, 0xe4, 0x94, 0x3e,
+0x40, 0x08, 0x90, 0xfa, 0xc0, 0xe0, 0x90, 0xfa, 0xbc, 0xf0, 0x12, 0x0f, 0x98, 0xe5, 0x31, 0x45,
+0x30, 0x70, 0x73, 0x12, 0x1c, 0xfa, 0x90, 0xfa, 0xbf, 0x12, 0x1e, 0x06, 0x60, 0x27, 0xd3, 0xef,
+0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, 0xfa, 0xbc, 0x74, 0x40, 0xf0, 0x80, 0x08, 0x90,
+0xfa, 0xc0, 0xe0, 0x90, 0xfa, 0xbc, 0xf0, 0x12, 0x0f, 0x98, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x46,
+0x12, 0x1c, 0xfa, 0x80, 0xd1, 0x75, 0x4c, 0x02, 0x90, 0xfa, 0xbf, 0xe4, 0xf0, 0xa3, 0x04, 0xf0,
+0x90, 0xfa, 0xb7, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0x90,
+0xfa, 0xc0, 0xe0, 0xf5, 0x4a, 0x7d, 0x0f, 0x7c, 0x00, 0x12, 0x29, 0x60, 0x75, 0x30, 0x00, 0x8f,
+0x31, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0xe4, 0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26,
+0x98, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xaf, 0x31, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x30, 0xe7,
+0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x67, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80,
+0x11, 0x90, 0xfa, 0xba, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x65, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02,
+0xc2, 0x94, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1e, 0x4c, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80,
+0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x54, 0x03, 0x14, 0x60, 0x0a,
+0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91,
+0x80, 0x23, 0x12, 0x1e, 0x56, 0x12, 0x0f, 0xc0, 0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff,
+0xa4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0xc0, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2,
+0x91, 0x12, 0x1e, 0x56, 0xf0, 0x90, 0xfa, 0xba, 0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f,
+0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80,
+0x27, 0xc2, 0x92, 0x80, 0x23, 0x12, 0x1e, 0x5d, 0x12, 0x0f, 0xe0, 0x60, 0x04, 0xd2, 0x92, 0x80,
+0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0xe0, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92,
+0x80, 0x02, 0xd2, 0x92, 0x12, 0x1e, 0x5d, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30,
+0xe7, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0xb4, 0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x32,
+0x7a, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb6, 0x90, 0xfa, 0xb7, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0,
+0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xb7, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1b,
+0x1c, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 0xaa, 0x4e, 0xa9, 0x4f, 0x7b, 0xff, 0x90, 0xfa,
+0xb7, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xbc, 0xe0, 0xf5, 0x4a, 0x12, 0x29, 0x60, 0x75,
+0x30, 0x00, 0x8f, 0x31, 0x22, 0x12, 0x23, 0x61, 0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22,
+0xf0, 0x7f, 0x01, 0x12, 0x12, 0x61, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xbb, 0xf0, 0x54, 0xa0,
+0x22, 0x12, 0x26, 0x98, 0x8f, 0x4c, 0x7e, 0x00, 0xc3, 0xef, 0x95, 0x3c, 0xee, 0x95, 0x3b, 0x22,
+0xf0, 0x7f, 0x01, 0x12, 0x12, 0x61, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xbb, 0xf0, 0x54, 0xa0,
+0x22, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0xd8, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x02,
+0x32, 0x3f, 0x8e, 0x39, 0x8f, 0x3a, 0x02, 0x2c, 0xd8, 0x12, 0x23, 0x61, 0x7e, 0x00, 0x8e, 0x30,
+0x8f, 0x31, 0xef, 0x22, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xb4, 0xe0, 0x22, 0xef, 0x90,
+0xf8, 0x04, 0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd,
+0xfe, 0xde, 0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, 0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70,
+0x03, 0xd0, 0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, 0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0,
+0x02, 0xc0, 0x04, 0xc0, 0x05, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c,
+0xff, 0x12, 0x10, 0xc0, 0x7f, 0x00, 0x7e, 0x00, 0xe5, 0x43, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x1d,
+0xe0, 0x54, 0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff,
+0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82,
+0xd0, 0x83, 0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83,
+0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3,
+0x80, 0xda, 0x12, 0x11, 0x57, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22,
+0x85, 0xa8, 0x44, 0x75, 0xa8, 0x88, 0xec, 0x70, 0x02, 0x7c, 0x3f, 0x8c, 0x3d, 0x22, 0xe5, 0x3e,
+0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0xae, 0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02,
+0xc0, 0x04, 0xc0, 0x06, 0x7c, 0xff, 0x12, 0x10, 0xc0, 0xe5, 0x43, 0x60, 0x42, 0xfe, 0x90, 0xf9,
+0x1d, 0xe0, 0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x43,
+0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, 0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80,
+0xd8, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3,
+0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78,
+0x08, 0x08, 0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, 0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77,
+0x00, 0x80, 0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, 0xee, 0x12, 0x11, 0x57, 0xd0, 0x06, 0xd0, 0x04,
+0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, 0x3d, 0x00, 0x85, 0x44, 0xa8, 0x22, 0xc0, 0xf0,
+0xc0, 0x82, 0xc0, 0x83, 0xc3, 0xe5, 0x43, 0x24, 0xe8, 0x50, 0x05, 0x12, 0x11, 0xae, 0x80, 0xf4,
+0xef, 0x60, 0x31, 0x90, 0x31, 0x11, 0xe4, 0x93, 0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff,
+0x12, 0x10, 0xc0, 0xd0, 0x04, 0x43, 0x07, 0x80, 0xe5, 0x43, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x1d,
+0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, 0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05,
+0x43, 0x12, 0x11, 0x57, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x02, 0x11, 0xdc, 0xc0, 0x04,
+0x7c, 0x20, 0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, 0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75,
+0x88, 0x00, 0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, 0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04,
+0xf0, 0x90, 0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, 0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90,
+0xff, 0x48, 0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, 0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90,
+0xff, 0x80, 0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, 0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10,
+0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0xd2,
+0xb0, 0xd2, 0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10,
+0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04,
+0x7e, 0x03, 0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f,
+0x42, 0x8e, 0x41, 0x22, 0xc3, 0xef, 0x94, 0xbc, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f,
+0xd0, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x40, 0x8e, 0x3f,
+0x22, 0xef, 0x70, 0x01, 0x22, 0xc0, 0x00, 0xc0, 0xa8, 0xc2, 0xaf, 0xe5, 0x3e, 0x24, 0x18, 0xf8,
+0xa6, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xc6, 0x54, 0x7f, 0xf6, 0xd0, 0xa8, 0xe6, 0x30, 0xe7,
+0x03, 0xd0, 0x00, 0x22, 0x12, 0x11, 0xae, 0x80, 0xf4, 0xc0, 0x00, 0x7f, 0x01, 0xef, 0x24, 0x08,
+0xf8, 0xe6, 0x60, 0x09, 0x0f, 0xbf, 0x08, 0xf5, 0x12, 0x11, 0xae, 0x80, 0xee, 0xd0, 0x00, 0x22,
+0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8,
+0x76, 0x9a, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83,
+0xc0, 0x82, 0xc0, 0x83, 0xa3, 0xa3, 0xe4, 0x78, 0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f,
+0x75, 0xf0, 0x02, 0xa4, 0x24, 0xf3, 0xf5, 0x82, 0xe5, 0xf0, 0x34, 0x30, 0xf5, 0x83, 0xe4, 0x93,
+0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, 0x82, 0xec, 0xf0, 0xa3, 0xee, 0xf0, 0xed, 0x24,
+0x08, 0xf8, 0xef, 0x44, 0x80, 0xf6, 0xd0, 0x04, 0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82,
+0xd0, 0xf0, 0x22, 0x75, 0x3e, 0x00, 0x75, 0x43, 0x00, 0x7a, 0x08, 0x79, 0x18, 0x78, 0x08, 0x76,
+0x00, 0x77, 0x00, 0x08, 0x09, 0xda, 0xf8, 0x90, 0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x31, 0x11, 0xe4,
+0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, 0x04, 0xf0, 0x78, 0x08, 0x74, 0x80, 0x44, 0x7f,
+0xf6, 0x74, 0x01, 0x44, 0x10, 0xf5, 0x89, 0x75, 0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75,
+0x81, 0x8b, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, 0xe5, 0x43, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x1d,
+0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, 0x08, 0xe0, 0x54, 0x7f, 0xfa, 0x7b, 0x00, 0xe6,
+0x54, 0x7f, 0xb5, 0x02, 0x02, 0x7b, 0xff, 0x08, 0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0,
+0x07, 0x12, 0x12, 0x89, 0xad, 0x07, 0xaf, 0x02, 0x12, 0x12, 0xa0, 0xd0, 0x07, 0xa3, 0xa3, 0xa3,
+0xdf, 0xce, 0x12, 0x11, 0xae, 0x80, 0xc1, 0x8f, 0x24, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xb5, 0xa3,
+0xa3, 0xe0, 0xa3, 0x30, 0xe7, 0x28, 0x78, 0x7e, 0x12, 0x22, 0x99, 0xe0, 0x44, 0x01, 0xf0, 0x12,
+0x22, 0xfa, 0x12, 0x22, 0x9d, 0xe0, 0x20, 0xe0, 0xf6, 0x12, 0x23, 0x50, 0x74, 0x02, 0xf0, 0x12,
+0x22, 0xda, 0xe0, 0xa3, 0x30, 0xe5, 0x07, 0x12, 0x23, 0x50, 0xe0, 0x44, 0x01, 0xf0, 0x78, 0x80,
+0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, 0x22, 0xa1, 0xe0, 0xfd, 0x12, 0x23,
+0x39, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x22, 0xa1, 0xed, 0xf0, 0x12, 0x23, 0x06, 0x24, 0x07, 0x12,
+0x22, 0xa1, 0xe0, 0xff, 0x12, 0x23, 0x5a, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xef, 0xf0, 0x90, 0xf9,
+0x16, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x22, 0xb7, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xe0, 0x25,
+0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, 0x82, 0xe0, 0x33, 0xd0, 0x82, 0xd0,
+0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x22, 0xb5, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0xff, 0x12,
+0x23, 0x39, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x22, 0xa1, 0xef, 0xf0, 0xed, 0x12, 0x23, 0x5a, 0x24,
+0x07, 0x12, 0x22, 0xa1, 0xed, 0xf0, 0x12, 0x22, 0xa9, 0xe0, 0x30, 0xe6, 0x0a, 0x12, 0x23, 0x41,
+0x24, 0x09, 0x12, 0x22, 0xa1, 0xe4, 0xf0, 0x12, 0x22, 0xa9, 0xe0, 0xff, 0x30, 0xe7, 0x1b, 0x12,
+0x23, 0x1e, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0x60, 0x09, 0x12, 0x22, 0xa9, 0xef, 0x44, 0x02,
+0xf0, 0x80, 0x07, 0x12, 0x22, 0xa9, 0xef, 0x54, 0xfd, 0xf0, 0x78, 0x7e, 0x12, 0x22, 0xb7, 0xa3,
+0xa3, 0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x12, 0x22, 0xe0, 0xa3,
+0xe0, 0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x05, 0x12, 0x22, 0xa1, 0xe0,
+0x90, 0x32, 0x51, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x12, 0x23, 0x1e, 0x24, 0x06, 0x12, 0x22,
+0xa1, 0xe0, 0x60, 0x03, 0x43, 0x07, 0x04, 0x53, 0x07, 0xfc, 0x78, 0x80, 0x12, 0x23, 0x29, 0x24,
+0x04, 0x12, 0x22, 0xa1, 0xe0, 0x42, 0x07, 0x43, 0x07, 0x80, 0x12, 0x23, 0x39, 0xf5, 0x82, 0x8a,
+0x83, 0xa3, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0x5a, 0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, 0xff, 0x8d,
+0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf,
+0x80, 0x03, 0x43, 0x07, 0x20, 0xec, 0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07,
+0x10, 0x12, 0x22, 0xa9, 0xe0, 0xfe, 0x54, 0x03, 0x60, 0x73, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1,
+0x69, 0x12, 0x23, 0x1e, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0x12, 0x1b, 0xfc, 0x15, 0x2c, 0x00,
+0x15, 0x60, 0x01, 0x15, 0x65, 0x03, 0x15, 0x60, 0x05, 0x15, 0x65, 0x07, 0x15, 0x60, 0x09, 0x15,
+0x65, 0x0b, 0x15, 0x60, 0x0d, 0x15, 0x65, 0x0f, 0x00, 0x00, 0x15, 0x6d, 0xe5, 0x24, 0x64, 0x03,
+0x70, 0x21, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe2, 0x0d, 0x30, 0xb4, 0x05, 0x43, 0x07, 0x02, 0x80,
+0x2c, 0x53, 0x07, 0xfd, 0x80, 0x27, 0x30, 0x95, 0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07,
+0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, 0x43, 0x07, 0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d,
+0x43, 0x07, 0x02, 0x80, 0x08, 0x53, 0x07, 0xfd, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x23, 0x27,
+0x24, 0x04, 0x12, 0x22, 0xa1, 0xef, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff,
+0x12, 0x22, 0xa9, 0xe0, 0xfe, 0x54, 0x03, 0x70, 0x03, 0x02, 0x16, 0x60, 0xee, 0x20, 0xe1, 0x03,
+0x02, 0x16, 0x5d, 0x08, 0x12, 0x23, 0x20, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0x12, 0x1b, 0xfc,
+0x15, 0xbf, 0x00, 0x15, 0xf5, 0x01, 0x15, 0xf5, 0x03, 0x16, 0x29, 0x05, 0x16, 0x29, 0x07, 0x16,
+0x0f, 0x09, 0x16, 0x0f, 0x0b, 0x16, 0x43, 0x0d, 0x16, 0x43, 0x0f, 0x00, 0x00, 0x16, 0x60, 0xe5,
+0x24, 0x64, 0x03, 0x70, 0x23, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe2, 0x0f, 0x30, 0xb1, 0x06, 0x53,
+0x07, 0x7f, 0x02, 0x16, 0x60, 0x43, 0x07, 0x80, 0x02, 0x16, 0x60, 0x30, 0x94, 0x05, 0x53, 0x07,
+0x7f, 0x80, 0x7d, 0x43, 0x07, 0x80, 0x80, 0x78, 0x30, 0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x70,
+0x43, 0x07, 0x80, 0x80, 0x6b, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef,
+0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x51, 0xe5,
+0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e,
+0xe0, 0x44, 0x20, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x37, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff,
+0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x43, 0x07,
+0x80, 0x80, 0x1d, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80,
+0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x43, 0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f,
+0x12, 0x22, 0xda, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03,
+0x53, 0x07, 0xdf, 0xec, 0x30, 0xe3, 0x05, 0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec,
+0x30, 0xe0, 0x05, 0x43, 0x07, 0x10, 0x80, 0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43,
+0x07, 0x08, 0x80, 0x03, 0x53, 0x07, 0xf7, 0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03,
+0x53, 0x07, 0xfb, 0xed, 0x30, 0xe6, 0x05, 0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed,
+0x30, 0xe7, 0x05, 0x43, 0x07, 0x02, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x78, 0x7e, 0x12, 0x22, 0xdc,
+0xa3, 0xef, 0xf0, 0x12, 0x32, 0x84, 0x7f, 0x00, 0x22, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3,
+0x74, 0x16, 0xf0, 0x90, 0xff, 0xf9, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcf, 0xe4,
+0xfd, 0x12, 0x23, 0x61, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1b, 0x1c, 0x12, 0x19,
+0x92, 0xe5, 0x23, 0x30, 0xe7, 0x02, 0xd2, 0x02, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x24, 0x90, 0xfa,
+0xcf, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xcf,
+0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x75, 0x2d, 0x00, 0xf5,
+0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0xe5, 0x23, 0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x23,
+0x64, 0x07, 0x60, 0x1e, 0xe5, 0x23, 0x64, 0x06, 0x60, 0x18, 0xe5, 0x23, 0x64, 0x14, 0x60, 0x12,
+0xe5, 0x23, 0x64, 0x41, 0x60, 0x0c, 0xe5, 0x23, 0x64, 0x1a, 0x70, 0x46, 0xe5, 0x24, 0x64, 0x02,
+0x70, 0x40, 0xe5, 0x23, 0xb4, 0x07, 0x16, 0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0x90,
+0xf9, 0x16, 0xe0, 0x44, 0x02, 0xf0, 0xa3, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1e, 0xe5, 0x23, 0xb4,
+0x41, 0x12, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x06, 0xf0, 0xa3, 0xe0, 0x44, 0x06, 0xf0, 0xd2, 0xb1,
+0xd2, 0xb4, 0x80, 0x07, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x44,
+0x01, 0xf0, 0xe5, 0x23, 0x64, 0x42, 0x60, 0x0c, 0xe5, 0x23, 0x64, 0x43, 0x60, 0x06, 0xe5, 0x23,
+0x64, 0x44, 0x70, 0x2e, 0x90, 0xf9, 0x16, 0xe0, 0xff, 0xe5, 0x23, 0xb4, 0x44, 0x04, 0x7e, 0x40,
+0x80, 0x02, 0x7e, 0x00, 0xee, 0x24, 0x80, 0x4f, 0x90, 0xf9, 0x16, 0xf0, 0xa3, 0xe0, 0xff, 0xe5,
+0x23, 0xb4, 0x44, 0x04, 0x7e, 0x40, 0x80, 0x02, 0x7e, 0x00, 0xee, 0x24, 0x80, 0x4f, 0x90, 0xf9,
+0x17, 0xf0, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x19, 0x92, 0x90, 0xff,
+0xf5, 0xe5, 0x23, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x33, 0xf5, 0x34, 0xf5, 0x32, 0x12, 0x1e, 0x34,
+0x12, 0x1c, 0xe0, 0x12, 0x1e, 0x3b, 0x90, 0xf9, 0x6a, 0x12, 0x1b, 0xf3, 0x90, 0xf9, 0x6f, 0x12,
+0x1b, 0xf3, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81,
 0x74, 0x80, 0xf0, 0xa3, 0x74, 0x84, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xe4, 0xf5, 0x23, 0xe5, 0x23, 
-0x12, 0x1c, 0xa7, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x23, 0x12, 0x1c, 0xb5, 0xf5, 0x83, 0xe4, 0xf0, 
-0x05, 0x23, 0xe5, 0x23, 0xb4, 0x07, 0xe7, 0x78, 0x7a, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x31, 
-0x4d, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x31, 0x5a, 0xe4, 0x93, 0xff, 
-0x08, 0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x1c, 0x97, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, 
+0x12, 0x1d, 0x57, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x23, 0x12, 0x1d, 0x65, 0xf5, 0x83, 0xe4, 0xf0,
+0x05, 0x23, 0xe5, 0x23, 0xb4, 0x07, 0xe7, 0x78, 0x7a, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x32,
+0x0a, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x32, 0x17, 0xe4, 0x93, 0xff,
+0x08, 0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x1d, 0x47, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0,
 0x08, 0xa4, 0x24, 0x47, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xc3, 0x74, 0xf0, 
-0x9f, 0x78, 0x7b, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x1c, 0x28, 0xce, 0xc3, 0x13, 0xce, 
-0x13, 0xd8, 0xf9, 0xff, 0xed, 0x12, 0x1c, 0xf8, 0xef, 0xf0, 0xed, 0x12, 0x1d, 0x1e, 0xe4, 0xf5, 
-0x23, 0xe5, 0x23, 0x90, 0x31, 0x47, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xe5, 0x23, 0x25, 0xe0, 
-0x24, 0x4e, 0xf5, 0x82, 0xe4, 0x34, 0x31, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, 
-0x53, 0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0x97, 0x12, 0x1d, 0x06, 0x24, 0x47, 0xf5, 0x82, 
-0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, 
-0x1c, 0xf8, 0xef, 0xf0, 0x12, 0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 
-0x0b, 0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x1d, 0x1e, 
+0x9f, 0x78, 0x7b, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x1c, 0xd8, 0xce, 0xc3, 0x13, 0xce,
+0x13, 0xd8, 0xf9, 0xff, 0xed, 0x12, 0x1d, 0xa8, 0xef, 0xf0, 0xed, 0x12, 0x1d, 0xce, 0xe4, 0xf5,
+0x23, 0xe5, 0x23, 0x90, 0x32, 0x04, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xe5, 0x23, 0x25, 0xe0,
+0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x34, 0x32, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7,
+0x53, 0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1d, 0x47, 0x12, 0x1d, 0xb6, 0x24, 0x47, 0xf5, 0x82,
+0xe4, 0x34, 0xff, 0x12, 0x1c, 0xc8, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12,
+0x1d, 0xa8, 0xef, 0xf0, 0x12, 0x1c, 0xcf, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d,
+0xbb, 0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x1d, 0xce,
 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x46, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 
-0xf0, 0x02, 0x18, 0xb7, 0x78, 0x78, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0xea, 0x12, 0x1d, 0x06, 
-0x24, 0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 
-0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, 
-0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x05, 0xf5, 0x82, 
+0xf0, 0x02, 0x19, 0x67, 0x78, 0x78, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1d, 0x9a, 0x12, 0x1d, 0xb6,
+0x24, 0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x1c, 0xc8, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8,
+0xf9, 0x12, 0x1d, 0xbb, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12,
+0x1c, 0xcf, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 0xbb, 0x24, 0x05, 0xf5, 0x82,
 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 
 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 
 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x23, 0xe5, 0x23, 0x64, 0x04, 0x60, 0x03, 0x02, 
-0x17, 0xe1, 0x90, 0x31, 0x4c, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0x12, 0x1c, 0xe8, 0xe4, 0xf0, 
-0x90, 0x31, 0x4b, 0x93, 0xff, 0xf6, 0x12, 0x1c, 0x95, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, 
-0xf0, 0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, 
-0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0xe7, 0x09, 0xf6, 0x08, 
+0x18, 0x91, 0x90, 0x32, 0x09, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0x12, 0x1d, 0x98, 0xe4, 0xf0,
+0x90, 0x32, 0x08, 0x93, 0xff, 0xf6, 0x12, 0x1d, 0x45, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05,
+0xf0, 0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12,
+0x1b, 0x32, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x26, 0x98, 0xe7, 0x09, 0xf6, 0x08,
 0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e, 0x88, 0x82, 0x8c, 0x83, 
 0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x78, 
 0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83, 0xe3, 0x09, 0xf0, 0xa3, 
@@ -445,7 +456,7 @@
 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc, 0x88, 0xf0, 0xef, 0x60, 
 0x01, 0x0e, 0x4e, 0x60, 0xc3, 0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xb9, 0xf5, 
 0x82, 0xeb, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xaf, 0x23, 0x23, 0x45, 0x82, 0x23, 0x90, 0x19, 
-0x4c, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 
+0xfc, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb,
 0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, 
 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xf8, 
 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, 
@@ -469,364 +480,365 @@
 0xe0, 0xf9, 0x22, 0xeb, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xe9, 0xf0, 0x22, 0xd0, 0x83, 0xd0, 0x82, 
 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, 
 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3, 
-0x80, 0xdf, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 
+0x80, 0xdf, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0xe5, 0x4c, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25,
 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0x74, 0x11, 0x12, 
-0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x06, 
-0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 
-0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 0x38, 0x04, 0x25, 
+0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x06,
+0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38,
+0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 0xe8, 0x04, 0x25,
 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 
-0x38, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x04, 0xe0, 0xab, 
-0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 
-0x37, 0xf5, 0x37, 0x90, 0xff, 0x05, 0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 
+0xe8, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x04, 0xe0, 0xab,
+0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35,
+0x37, 0xf5, 0x37, 0x90, 0xff, 0x05, 0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0xe8,
 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x22, 0xf5, 0x83, 0xe0, 0x54, 
 0x08, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 0xf5, 0x83, 0xef, 0xf0, 0xfd, 0x7c, 0x00, 0xc3, 
 0x78, 0x7b, 0xe6, 0x9d, 0xf6, 0x18, 0xe6, 0x9c, 0xf6, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x03, 0x22, 
-0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 0x6f, 0x22, 0xe0, 0x44, 0x04, 0xf0, 0x74, 0x12, 
-0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xe0, 0x22, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, 
-0x00, 0xc3, 0x90, 0xfa, 0xbd, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xbc, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, 
-0xb4, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x4f, 0xf5, 0x4f, 0xee, 0x35, 0x4e, 0xf5, 
-0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb1, 0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 
+0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 0x72, 0x22, 0xe0, 0x44, 0x04, 0xf0, 0x74, 0x13,
+0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xe0, 0x22, 0x90, 0xfa, 0xbc, 0xe0, 0xff, 0x7e,
+0x00, 0xc3, 0x90, 0xfa, 0xc0, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xbf, 0xe0, 0x9e, 0xf0, 0x90, 0xfa,
+0xb7, 0xee, 0x8f, 0xf0, 0x12, 0x1b, 0x1c, 0xef, 0x25, 0x4f, 0xf5, 0x4f, 0xee, 0x35, 0x4e, 0xf5,
+0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb4, 0x90, 0xfa, 0xb7, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0,
 0xf5, 0x2e, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x04, 0xf5, 0x82, 0xe4, 
 0x35, 0x83, 0xf5, 0x83, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe4, 
 0x34, 0xff, 0xf5, 0x83, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x48, 0xf5, 0x82, 0xe4, 
 0x34, 0xff, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x34, 0xff, 
-0x22, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, 
-0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x75, 0x33, 0x00, 0x8f, 0x34, 0x90, 0xf9, 
-0x6c, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 
+0x22, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22,
+0x90, 0xfa, 0xbe, 0xe0, 0x90, 0xfa, 0xba, 0xf0, 0x22, 0x75, 0x33, 0x00, 0x8f, 0x34, 0x90, 0xf9,
+0x6f, 0x12, 0x1b, 0xea, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00,
 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x41, 0xf5, 0x82, 
 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x74, 0x80, 0xf0, 0x08, 0xe6, 0xff, 0xe9, 0x75, 0xf0, 0x08, 
-0xa4, 0x22, 0x74, 0xaf, 0x25, 0x22, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, 
+0xa4, 0x22, 0x74, 0xb2, 0x25, 0x22, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0,
 0x08, 0xa4, 0x24, 0x42, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x22, 0x90, 
 0xff, 0x82, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x03, 0xf0, 0x90, 0xff, 
 0xfc, 0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x78, 0x67, 0xe6, 0x54, 0xfd, 0xf6, 0x90, 0xff, 0xfd, 0x74, 
-0x65, 0xf0, 0x22, 0x12, 0x1b, 0x1c, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, 
-0xfa, 0x79, 0xb4, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, 
-0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x67, 0x02, 0x1b, 0x3a, 0x90, 0xff, 0xa4, 0xe0, 
+0x65, 0xf0, 0x22, 0x12, 0x1b, 0xcc, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a,
+0xfa, 0x79, 0xb7, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0,
+0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x6a, 0x02, 0x1b, 0xea, 0x90, 0xff, 0xa4, 0xe0,
 0x44, 0x02, 0xf0, 0x22, 0x75, 0x39, 0x01, 0x75, 0x3a, 0x09, 0x22, 0x7b, 0x01, 0x7a, 0xf9, 0x79, 
-0x6f, 0x22, 0xd3, 0xe5, 0x3c, 0x94, 0x08, 0xe5, 0x3b, 0x94, 0x01, 0x22, 0x90, 0xfa, 0xbb, 0xe0, 
-0xff, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, 
-0xe0, 0x54, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x88, 0xef, 0xf6, 0x12, 0x2a, 0x06, 0x12, 0x22, 
-0x4a, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x0a, 
-0x12, 0x22, 0x52, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x1a, 0x4a, 0x12, 
-0x22, 0x4a, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x53, 0x12, 0x22, 0x56, 0x24, 
-0x04, 0x12, 0x21, 0xf3, 0xe0, 0xf5, 0x54, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x55, 
+0x72, 0x22, 0xd3, 0xe5, 0x3c, 0x94, 0x08, 0xe5, 0x3b, 0x94, 0x01, 0x22, 0x90, 0xfa, 0xbe, 0xe0,
+0xff, 0x90, 0xfa, 0xba, 0xf0, 0x22, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4,
+0xe0, 0x54, 0xef, 0x22, 0x12, 0x10, 0x4b, 0x78, 0x88, 0xef, 0xf6, 0x12, 0x2a, 0xc7, 0x12, 0x22,
+0xfa, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0xfd, 0x12, 0x22, 0xe8, 0x90, 0x00, 0x0a,
+0x12, 0x23, 0x02, 0x24, 0x0a, 0x12, 0x22, 0xa1, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x1a, 0xfa, 0x12,
+0x22, 0xfa, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x53, 0x12, 0x23, 0x06, 0x24,
+0x04, 0x12, 0x22, 0xa1, 0xe0, 0xf5, 0x54, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x55,
 0xe5, 0x53, 0xc4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x78, 0x88, 0xf6, 0xd3, 0x94, 0x00, 0x40, 0x06, 
-0xe5, 0x54, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x88, 0xe6, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x0c, 0xef, 
-0x12, 0x1a, 0x4a, 0x78, 0x80, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x53, 
-0x07, 0x0c, 0x53, 0x06, 0xe6, 0xe5, 0x53, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, 0x54, 0x20, 
-0xe5, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, 0x07, 0x02, 
-0xe5, 0x53, 0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x53, 0x30, 0xe2, 0x03, 0x43, 0x07, 0x20, 
-0xe5, 0x53, 0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x53, 0x30, 0xe1, 0x03, 0x43, 0x07, 
-0x80, 0xe5, 0x53, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x53, 0x30, 0xe6, 0x03, 0x43, 0x06, 
-0x08, 0xe5, 0x54, 0x20, 0xe4, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 
-0x03, 0x43, 0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, 0x8f, 0xf0, 
-0x12, 0x1a, 0xef, 0xe5, 0x55, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, 0x12, 0x22, 
-0x2c, 0x90, 0x00, 0x08, 0xef, 0x12, 0x1a, 0x4a, 0x80, 0x0a, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x08, 
-0xe4, 0x12, 0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x03, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x07, 0xef, 0x12, 
-0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x1a, 0x4a, 0x90, 
-0x00, 0x07, 0x12, 0x1a, 0x0b, 0x70, 0x13, 0x12, 0x22, 0x2d, 0xe9, 0x24, 0x09, 0xf9, 0xe4, 0x3a, 
-0xfa, 0x12, 0x19, 0xf2, 0xff, 0xc3, 0x13, 0x12, 0x1a, 0x38, 0x12, 0x22, 0x78, 0x24, 0x08, 0x12, 
-0x21, 0xf3, 0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, 
-0xfd, 0xee, 0xed, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0xef, 0x12, 
-0x31, 0xc7, 0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0x90, 0xfa, 0xe3, 0xe0, 
-0xb4, 0x03, 0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, 0xfa, 0xd7, 
-0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xff, 0x7e, 0x00, 0x90, 0xfa, 
-0xd3, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, 0x12, 0x1a, 
-0x98, 0xff, 0x90, 0xfa, 0xd5, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, 0x01, 0x8e, 
-0x50, 0x8f, 0x51, 0x74, 0x0a, 0x25, 0x51, 0xf5, 0x51, 0xe4, 0x35, 0x50, 0xf5, 0x50, 0x90, 0xfa, 
-0xd8, 0xe0, 0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd6, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, 0xff, 0x90, 
-0xfa, 0xda, 0xf0, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0x90, 0xfa, 0xd3, 0xe0, 0x94, 0x00, 0x50, 
-0x06, 0xa3, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, 
-0x2d, 0x5a, 0x90, 0xfa, 0xd3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, 0xfa, 0xd7, 
-0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, 0xfa, 0xda, 
-0xf0, 0x80, 0x08, 0x90, 0xfa, 0xd4, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, 
-0xe0, 0xff, 0x22, 0x12, 0x2d, 0x5a, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, 
-0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7f, 0x00, 0x22, 0xaa, 0x50, 0xa9, 0x51, 0x7b, 
-0x01, 0x90, 0xfa, 0xd5, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xda, 0xe0, 0xf5, 0x4a, 0x12, 
-0x28, 0x9f, 0x90, 0xfa, 0xd9, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, 0xfe, 0x60, 
-0x2e, 0x24, 0xfe, 0x70, 0x03, 0x02, 0x20, 0xbb, 0x24, 0x06, 0x60, 0x03, 0x02, 0x21, 0x03, 0x78, 
-0x71, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x33, 
-0x90, 0xfa, 0x91, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xaf, 0x74, 0x01, 0xf0, 0x22, 0x78, 
-0x72, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x43, 
-0x90, 0xfa, 0x93, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb0, 0x74, 0x01, 0xf0, 0x22, 0x90, 
-0xfa, 0x9d, 0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x21, 0x03, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 
-0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 
-0xa6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 
-0xcb, 0xe0, 0xff, 0x74, 0x34, 0xfe, 0x12, 0x2c, 0xb4, 0xef, 0x70, 0x57, 0x90, 0xfa, 0xcb, 0xe0, 
-0xff, 0x74, 0x34, 0x90, 0xfa, 0x95, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xa7, 0xe0, 0xa3, 
-0x30, 0xe5, 0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, 
-0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xb6, 
-0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0xfe, 0x12, 
-0x2c, 0xb4, 0xef, 0x70, 0x0e, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, 0x97, 0xf0, 
-0xef, 0xa3, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 
-0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 
-0x07, 0x90, 0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xc9, 0xf0, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0xef, 
-0x12, 0x1b, 0x4c, 0x21, 0xbb, 0x26, 0x21, 0xbb, 0x2e, 0x21, 0x5e, 0x30, 0x21, 0x5e, 0x32, 0x21, 
-0x6c, 0x38, 0x21, 0x7e, 0x3a, 0x21, 0xb0, 0x3e, 0x21, 0x9b, 0x44, 0x21, 0x90, 0x46, 0x21, 0xa6, 
-0x50, 0x21, 0xa6, 0x52, 0x21, 0xa6, 0x54, 0x21, 0xa6, 0x56, 0x00, 0x00, 0x21, 0xc0, 0x90, 0xfa, 
-0xc9, 0xe0, 0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x11, 0x16, 0x80, 0x62, 0x7c, 0x00, 0x7d, 0x01, 
-0x7f, 0x03, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, 0x7c, 0x00, 
-0x7d, 0x01, 0x7f, 0x02, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x3e, 
-0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x05, 0x12, 0x11, 0x16, 0x80, 0x33, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 
-0x06, 0x12, 0x11, 0x16, 0x80, 0x28, 0x90, 0xfa, 0xc9, 0xe0, 0xff, 0x12, 0x20, 0x18, 0x80, 0x1e, 
-0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x04, 0x12, 0x11, 0x16, 0x80, 0x13, 0x12, 0x27, 0x8d, 0x80, 0x0e, 
-0x90, 0xfa, 0xc9, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x2c, 0xb4, 0xd0, 0x07, 
-0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 
-0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x24, 
-0x04, 0x8e, 0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x74, 0x12, 0x25, 0x24, 0xf5, 
-0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 
-0x83, 0x22, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, 
-0xff, 0x7a, 0x31, 0x79, 0x99, 0x7e, 0x00, 0x7f, 0x0a, 0x02, 0x19, 0xcc, 0xff, 0x90, 0xf9, 0x6c, 
-0x02, 0x1b, 0x3a, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x04, 0x02, 0x1a, 0x0b, 0xe6, 
-0xfc, 0x08, 0xe6, 0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, 0x78, 0x7e, 0xe6, 0xfe, 0x08, 0xe6, 
-0xff, 0x22, 0xed, 0x12, 0x1a, 0x4a, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, 
-0xfa, 0xcb, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, 0xf0, 0x4e, 0xf0, 0x22, 0x08, 0xe6, 
-0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x09, 0x22, 0x78, 0x7e, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, 
-0x83, 0x22, 0xa6, 0x07, 0xe6, 0x24, 0x6e, 0xf8, 0xe6, 0x22, 0x78, 0x7e, 0xe6, 0xfa, 0x08, 0xe6, 
-0xfb, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, 0x8b, 0x82, 0x8a, 0x83, 0xe5, 0x82, 0x22, 
-0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0x8d, 0x28, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, 0xa3, 0x74, 0x02, 
-0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 
-0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xce, 0xe0, 0x65, 0x28, 0x60, 0x46, 0xa3, 0xe0, 
-0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xce, 0xe0, 
-0xff, 0x90, 0xfa, 0xd1, 0xe4, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xd1, 
-0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xcf, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xfa, 0xce, 0xe0, 
-0xa3, 0x75, 0xf0, 0x00, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x04, 0x12, 0x1a, 
-0x6c, 0x02, 0x22, 0xb1, 0x90, 0xfa, 0xd0, 0xe0, 0x24, 0x01, 0xff, 0x90, 0xfa, 0xcf, 0xe0, 0x34, 
-0x00, 0xab, 0x25, 0xaa, 0x26, 0xa9, 0x27, 0x8f, 0xf0, 0x12, 0x1a, 0xd0, 0x7f, 0x00, 0x22, 0x7b, 
-0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x6c, 0x85, 
-0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0x8f, 0x62, 0x12, 0x2a, 0x06, 0x12, 0x22, 
-0x4a, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xfb, 0xf0, 0x44, 0x02, 0xf0, 0x08, 
-0x12, 0x22, 0x3f, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x22, 0x56, 0x24, 0x0b, 0x12, 0x21, 0xf3, 
-0xe0, 0x44, 0x01, 0xf0, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xf5, 0x82, 0x8e, 0x83, 0xe0, 
-0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x62, 0x24, 0xfe, 0x44, 0x20, 0xfc, 0x4d, 0xf0, 0xe5, 0x82, 0x24, 
-0x04, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0x74, 
-0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x05, 0x12, 0x21, 0xf3, 0xc0, 
-0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x96, 0x25, 0x62, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 
-0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 
-0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, 
-0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x62, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, 
-0x10, 0x03, 0x7f, 0x02, 0x12, 0x12, 0x19, 0x78, 0x67, 0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, 0xd2, 
-0xb1, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe7, 0x07, 0x90, 0xff, 0x9e, 0xe4, 0xf0, 0x80, 0x36, 0xd2, 
-0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, 0x7b, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, 0x7c, 
-0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, 0x79, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, 0x7a, 
-0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, 0x40, 
-0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, 0xe4, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, 0xff, 
-0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, 0x87, 
-0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7b, 0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7c, 0xe0, 
-0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x79, 0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x7a, 0xe0, 
-0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe4, 0xe0, 
-0xf5, 0xa8, 0x02, 0x10, 0x86, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x12, 0x2d, 0x3c, 0x90, 0xfa, 
-0xc0, 0x12, 0x1b, 0x43, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x43, 0x90, 0xfa, 
-0xc4, 0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x3a, 0xe9, 0x24, 
-0x01, 0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xab, 0x5c, 0xaa, 0x5d, 0xa9, 
-0x5e, 0x12, 0x2d, 0x48, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x82, 0xf6, 0x90, 0xfa, 0xbe, 
-0xe0, 0xff, 0x78, 0x82, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xc0, 0x12, 0x2d, 0x1d, 0xff, 
-0x78, 0x83, 0xf6, 0x90, 0xfa, 0xc3, 0x12, 0x2d, 0x1d, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x83, 0xf6, 
-0x12, 0x2d, 0x1a, 0x5e, 0x4f, 0xff, 0x78, 0x83, 0xf6, 0x12, 0x2d, 0x23, 0x75, 0xf0, 0x02, 0x12, 
-0x1a, 0x6c, 0x90, 0xfa, 0xc4, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0xab, 0x5c, 0xaa, 0x5d, 
-0xa9, 0x5e, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x0b, 0x30, 0xe4, 0x03, 0x12, 0x2d, 0x32, 0x78, 0x82, 
-0x06, 0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xbf, 0xf0, 0x22, 0x8b, 0x56, 0x8a, 0x57, 0x89, 0x58, 0x90, 
-0xfa, 0xbf, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, 
-0x26, 0x14, 0x70, 0x70, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x24, 0x95, 
-0x80, 0x62, 0x12, 0x2d, 0x53, 0x12, 0x1f, 0x2c, 0x90, 0xfa, 0xbf, 0xef, 0xf0, 0x80, 0x55, 0x90, 
-0xfa, 0xbf, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x3e, 
-0x12, 0x2c, 0x5f, 0x80, 0x3f, 0xe5, 0x58, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x57, 0xfa, 0x7b, 0x01, 
-0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xfd, 
-0x90, 0x00, 0x08, 0x12, 0x1a, 0x98, 0xf5, 0x2e, 0x85, 0xf0, 0x2d, 0xd0, 0x01, 0xd0, 0x02, 0xd0, 
-0x03, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xbe, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, 0xfa, 
-0xbf, 0x74, 0x81, 0xf0, 0x90, 0xfa, 0xbf, 0xe0, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x02, 0x12, 0x1a, 
-0x4a, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x22, 0x8b, 0x29, 0x8a, 0x2a, 0x89, 0x2b, 0x8d, 0x2c, 0xe5, 
-0x2c, 0x70, 0x03, 0xaf, 0x2c, 0x22, 0x12, 0x2d, 0x82, 0x70, 0x16, 0x12, 0x2d, 0xa1, 0xe5, 0x2d, 
-0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x26, 0x64, 0x40, 0x0b, 0x7f, 0x00, 
-0x22, 0x12, 0x2d, 0xa1, 0x12, 0x26, 0x64, 0x50, 0xf8, 0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, 0xe5, 
-0x2c, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, 0xf0, 
-0xf5, 0x2f, 0xe5, 0x2c, 0x14, 0xff, 0xe5, 0x2f, 0xc3, 0x9f, 0x50, 0x2a, 0x12, 0x31, 0x04, 0x40, 
-0x03, 0xaf, 0x2f, 0x22, 0xc3, 0xe5, 0x2c, 0x95, 0x2f, 0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, 0xf0, 
-0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2d, 0x94, 0x05, 0x2f, 0x74, 0x01, 0x25, 0x2b, 0xf5, 0x2b, 0xe4, 
-0x35, 0x2a, 0xf5, 0x2a, 0x80, 0xcc, 0x12, 0x31, 0x04, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, 0x2d, 
-0x94, 0xaf, 0x2c, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x2e, 0xf0, 0x02, 0x31, 0x1b, 0x12, 0x10, 0x03, 
-0x78, 0x84, 0x12, 0x22, 0x82, 0x30, 0xe1, 0x08, 0x7f, 0x13, 0x12, 0x30, 0xec, 0x02, 0x26, 0xfb, 
-0x78, 0x84, 0xe6, 0xf9, 0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xff, 0x30, 0xe7, 0x40, 0x54, 0x03, 
-0x60, 0x1e, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x44, 0x04, 
-0xf0, 0x80, 0x46, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x80, 0x39, 
-0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x80, 
-0x28, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1b, 0xef, 0x54, 
-0x03, 0x60, 0x14, 0xe9, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x07, 
-0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x17, 0xe0, 0x04, 0xf0, 0xaf, 
-0x01, 0x12, 0x22, 0x33, 0xfd, 0x12, 0x2f, 0x49, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x75, 0xa8, 
-0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x8b, 0x02, 0x27, 0x48, 0x02, 0x30, 0xcf, 
-0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, 
-0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 
-0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, 
-0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x2b, 0x4c, 0xe4, 0x7e, 0x01, 0x93, 0x60, 
-0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, 
-0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 
-0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, 
-0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xe4, 0xf5, 0x22, 
-0x12, 0x1d, 0x12, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x22, 0x24, 0x03, 0xff, 0x12, 0x2f, 0x77, 0x12, 
-0x1d, 0x12, 0xe4, 0xf0, 0x05, 0x22, 0xe5, 0x22, 0xc3, 0x94, 0x02, 0x40, 0xe3, 0xe4, 0xf5, 0x22, 
-0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1d, 0x53, 0x60, 0x2c, 0x12, 0x2c, 0xb4, 
-0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, 
-0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x22, 0x90, 0xfa, 0x9d, 0x12, 0x1b, 0x1c, 0xe0, 0xa3, 0x30, 
-0xe6, 0x33, 0x12, 0x1d, 0x12, 0x74, 0x04, 0xf0, 0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 
-0x95, 0x12, 0x1d, 0x53, 0x60, 0x16, 0x12, 0x2c, 0xb4, 0xef, 0x60, 0x19, 0x75, 0xf0, 0x02, 0xe5, 
-0x22, 0x90, 0xfa, 0x95, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, 0xa3, 0xf0, 0x22, 0x05, 0x22, 0xe5, 0x22, 
-0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xfe, 0xef, 
-0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xe0, 0x12, 
-0x1c, 0x11, 0x12, 0x1a, 0x38, 0x0f, 0x12, 0x1c, 0x00, 0x80, 0xdd, 0xef, 0xfd, 0xc3, 0xe5, 0x3a, 
-0x9d, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 0xd3, 0xe5, 0x3a, 0x94, 0x00, 0xe5, 0x39, 
-0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, 0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x84, 
-0x12, 0x1d, 0x76, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, 0x1e, 0x14, 0x60, 0x1b, 0x24, 0x8e, 0x70, 
-0x2d, 0x90, 0x00, 0x01, 0x12, 0x1a, 0x0b, 0xff, 0x24, 0xfc, 0x60, 0x03, 0x04, 0x70, 0x1f, 0xef, 
-0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x11, 0x16, 0x12, 0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, 
-0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, 0x12, 0x31, 0xb1, 0x22, 0x8b, 
-0x45, 0x8a, 0x46, 0x89, 0x47, 0x8c, 0x48, 0x8d, 0x49, 0xd2, 0x00, 0x12, 0x2d, 0x82, 0x70, 0x16, 
-0x12, 0x2d, 0xa1, 0xe5, 0x48, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x29, 
-0x14, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2d, 0xa1, 0x12, 0x29, 0x14, 0x50, 0xf8, 0xe4, 0xf5, 
-0x4b, 0xe5, 0x4a, 0x14, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, 0x17, 0x12, 0x29, 0x04, 0x40, 0x03, 
-0x7f, 0x18, 0x22, 0x05, 0x4b, 0x74, 0x01, 0x25, 0x47, 0xf5, 0x47, 0xe4, 0x35, 0x46, 0xf5, 0x46, 
-0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x29, 0x04, 0x40, 0x03, 0x7f, 0x18, 
-0x22, 0x7f, 0x00, 0x22, 0xab, 0x45, 0xaa, 0x46, 0xa9, 0x47, 0x12, 0x19, 0xf2, 0x90, 0xff, 0xf1, 
-0xf0, 0x02, 0x31, 0x1b, 0x90, 0xff, 0xf1, 0xe5, 0x49, 0xf0, 0x02, 0x31, 0x1b, 0x7b, 0x01, 0x7a, 
-0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x09, 0x12, 
-0x1a, 0x6c, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, 
-0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xff, 0xf7, 0xe5, 
-0x23, 0x12, 0x29, 0x78, 0x90, 0xff, 0xf6, 0xe5, 0x23, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 
-0x74, 0x06, 0x12, 0x29, 0x78, 0xe5, 0x23, 0x30, 0xe0, 0x07, 0x90, 0xff, 0xfc, 0x74, 0x94, 0xf0, 
-0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 
-0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 
-0x02, 0x25, 0xd7, 0x90, 0xff, 0x93, 0x74, 0x2a, 0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, 0x90, 
-0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, 0xe0, 0x44, 0x90, 0xf0, 0xe4, 0x90, 0xf9, 0x15, 
-0xf0, 0xa3, 0xf0, 0x12, 0x2a, 0x78, 0x12, 0x16, 0x42, 0x12, 0x2f, 0xcd, 0x7e, 0x07, 0x7f, 0xd0, 
-0x12, 0x11, 0xe2, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x11, 0xfc, 0xe4, 0x78, 0x77, 0xf6, 0x78, 0x77, 
-0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x6e, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x77, 0x06, 
-0x80, 0xec, 0x7f, 0x03, 0x12, 0x2e, 0xb3, 0x90, 0xf9, 0x15, 0xe0, 0x20, 0xe4, 0x05, 0x7f, 0x04, 
-0x12, 0x2e, 0xb3, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, 0xe0, 
-0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x15, 0x65, 0xa8, 0x65, 0xa6, 0x07, 0x30, 0x08, 0x05, 0x12, 
-0x11, 0x66, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x65, 0xe6, 0xff, 0xb4, 0x03, 0x0f, 0x78, 0x7c, 0x76, 
-0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, 0x7c, 0x76, 0xff, 
-0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x80, 0x76, 0xfa, 0x08, 0x76, 0x9b, 
-0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x22, 0x92, 0x7b, 0x01, 0x7a, 0xff, 
-0x79, 0x48, 0x78, 0x68, 0x12, 0x1b, 0x31, 0xa8, 0x65, 0xe6, 0x24, 0xfd, 0x75, 0xf0, 0x08, 0xa4, 
-0xff, 0xae, 0xf0, 0x78, 0x6a, 0x12, 0x22, 0x92, 0x79, 0x08, 0x78, 0x6b, 0x12, 0x1b, 0x31, 0x78, 
-0x6d, 0xef, 0x12, 0x22, 0x92, 0x05, 0x65, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xab, 0xf0, 0xe0, 
-0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe3, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, 
-0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7e, 0x00, 0x90, 0xfa, 0xe1, 0xee, 0xf0, 
-0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x52, 0x09, 0x90, 0xf9, 
-0x15, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe1, 0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 
-0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x10, 
-0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe3, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x54, 0xef, 0xf0, 
-0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x12, 0x10, 0x03, 0x78, 0x8a, 0xef, 0xf6, 0x12, 
-0x2a, 0x06, 0x12, 0x22, 0x33, 0x30, 0xe0, 0x25, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x7f, 0xf0, 0x78, 
-0x6b, 0x12, 0x1b, 0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0x09, 0x90, 0x00, 0x02, 
-0xe4, 0x12, 0x1a, 0x4a, 0x80, 0xe9, 0x12, 0x22, 0x07, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x22, 0x33, 
-0x30, 0xe1, 0x1e, 0x12, 0x21, 0xe9, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x31, 0x5c, 0x78, 0x68, 0x12, 
-0x1b, 0x28, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x1a, 0x4a, 0x12, 0x21, 0xe9, 0xe0, 0x44, 0x80, 
-0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x03, 0x68, 0x01, 0xff, 
-0x48, 0x03, 0x6b, 0x01, 0xff, 0x08, 0x02, 0x66, 0x00, 0x00, 0x44, 0xfa, 0x95, 0x00, 0x00, 0x00, 
-0x00, 0x44, 0xfa, 0x91, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xaf, 0x00, 0x00, 0x42, 0xfa, 0x7b, 
-0x00, 0x00, 0x42, 0xfa, 0x79, 0x00, 0x00, 0x42, 0xf9, 0x6a, 0xff, 0xff, 0x42, 0xfa, 0x77, 0x00, 
-0x00, 0x43, 0xf9, 0x18, 0x0a, 0x32, 0x02, 0x41, 0xf9, 0x65, 0x20, 0x41, 0xf9, 0x66, 0x20, 0x41, 
-0xf9, 0x63, 0x00, 0x41, 0xf9, 0x64, 0x00, 0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf9, 
-0x15, 0x00, 0x00, 0x41, 0xf9, 0x17, 0x00, 0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, 
-0x10, 0x03, 0x78, 0x85, 0xef, 0xf6, 0x12, 0x30, 0x93, 0x12, 0x30, 0xec, 0x78, 0x85, 0xe6, 0xff, 
-0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xfe, 0x30, 0xe7, 0x16, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 
-0x9e, 0xe0, 0x54, 0xfa, 0xf0, 0x80, 0x22, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf5, 0xf0, 0x80, 0x19, 
-0xee, 0x54, 0x03, 0x60, 0x14, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, 
-0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x14, 0xf0, 0xe0, 
-0x70, 0x02, 0xd2, 0xb3, 0x02, 0x10, 0x86, 0x12, 0x1d, 0x6c, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 
-0xe5, 0x39, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x3a, 0x94, 0x08, 0xe5, 0x39, 0x94, 0x00, 0x40, 
-0x11, 0x7f, 0x08, 0xef, 0xe5, 0x3a, 0x94, 0x08, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 
-0x80, 0x05, 0xaf, 0x3a, 0x12, 0x1d, 0x84, 0xe4, 0xfe, 0xee, 0xc3, 0x9f, 0x50, 0x19, 0x12, 0x1c, 
-0x11, 0x12, 0x19, 0xf2, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xed, 
-0xf0, 0x0e, 0x12, 0x1c, 0x00, 0x80, 0xe2, 0xef, 0x54, 0x7f, 0x90, 0xff, 0x81, 0xf0, 0x22, 0x8b, 
-0x59, 0x8a, 0x5a, 0x89, 0x5b, 0x12, 0x2d, 0x48, 0x70, 0x05, 0xa3, 0x74, 0x08, 0xf0, 0x22, 0xab, 
-0x59, 0xaa, 0x5a, 0xa9, 0x5b, 0x12, 0x2d, 0x3c, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xe5, 0x5b, 
-0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5a, 0xfa, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x43, 0xe4, 0x90, 0xfa, 
-0xbf, 0xf0, 0x78, 0x8b, 0xf6, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x78, 0x8b, 0xe6, 0xc3, 0x9f, 0x50, 
-0x12, 0x12, 0x2d, 0x1a, 0xff, 0x12, 0x2d, 0x23, 0x12, 0x2d, 0x36, 0x78, 0x8b, 0x06, 0x12, 0x2d, 
-0x32, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x31, 0x4d, 0xe4, 0x93, 0xff, 0x78, 0x74, 
-0xf6, 0x54, 0x0f, 0x12, 0x1c, 0xf8, 0xe0, 0x08, 0x76, 0x00, 0x08, 0xf6, 0x18, 0x12, 0x1c, 0x29, 
-0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x75, 0xee, 0xf6, 0x08, 0xef, 0xf6, 0xee, 
-0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, 0xe0, 0x20, 0xe7, 0x03, 0x7f, 0x00, 
-0x22, 0x78, 0x75, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 
-0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x90, 
-0x00, 0x03, 0x12, 0x1a, 0x0b, 0x54, 0xf0, 0x24, 0xa0, 0x22, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x3a, 
-0x02, 0x19, 0xf2, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x3a, 0xef, 0x12, 0x1a, 0x38, 0x90, 0xfa, 0xc7, 
-0xe4, 0x22, 0x90, 0xfa, 0xc1, 0xe4, 0x75, 0xf0, 0x01, 0x02, 0x1a, 0x6c, 0x90, 0x00, 0x08, 0x12, 
-0x1a, 0x98, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0x90, 0xfa, 
-0xbe, 0xf0, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x22, 0x90, 0xfa, 0xda, 0xe0, 0xff, 0x7e, 
-0x00, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xd3, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, 
-0xd5, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x51, 0xf5, 0x51, 0xee, 0x35, 0x50, 0xf5, 
-0x50, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0xfa, 0xe3, 
-0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x29, 0xaa, 0x2a, 0xa9, 0x2b, 0x02, 0x1a, 
-0x38, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x64, 0xed, 0x70, 0x0f, 0xe5, 0x64, 0xb4, 
-0x03, 0x05, 0x7f, 0x01, 0x02, 0x31, 0x32, 0x7f, 0x02, 0x02, 0x31, 0x32, 0xaf, 0x64, 0x12, 0x2a, 
-0x06, 0x74, 0x6e, 0x25, 0x64, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, 0xd2, 0x09, 0x12, 0x1c, 0x83, 0xe0, 
-0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x64, 0xb4, 0x03, 0x07, 0x7f, 0x81, 0x12, 0x31, 
-0x32, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x31, 0x32, 0x30, 0x09, 0x07, 0x12, 0x1c, 0x83, 0xe0, 0x44, 
-0x80, 0xf0, 0x12, 0x31, 0xc7, 0x22, 0x12, 0x10, 0x03, 0x90, 0xff, 0xfd, 0xe0, 0x44, 0x60, 0xf0, 
-0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0x00, 0xe0, 0x30, 0xe7, 0x13, 
-0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x35, 0x80, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x01, 
-0xf0, 0x80, 0x0d, 0x12, 0x1d, 0x2f, 0x53, 0x35, 0x7f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfe, 0xf0, 
-0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0xb0, 0x12, 0x1d, 0x37, 0x02, 0x10, 0x86, 
-0x12, 0x10, 0x03, 0x78, 0x89, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x2a, 0x06, 0x90, 0xf9, 0x67, 0x12, 
-0x1b, 0x3a, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x80, 0xe6, 0xfe, 0x08, 
-0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x22, 0x25, 0x12, 0x31, 0xc7, 
-0x78, 0x89, 0xe6, 0xff, 0x12, 0x13, 0x3f, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x8f, 0x63, 0x12, 
-0x2a, 0x06, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x3f, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, 
-0xe0, 0x54, 0x3f, 0xf0, 0x08, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, 
-0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x63, 0xf8, 0x74, 0xfb, 0x56, 0xf6, 
-0x7f, 0x00, 0x22, 0x8f, 0x23, 0xc2, 0x08, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, 0x78, 0x7e, 0x12, 
-0x21, 0xeb, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x22, 0x4a, 0x12, 0x21, 0xef, 0xe0, 0x20, 0xe0, 0xf6, 
-0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, 
-0xaf, 0x23, 0x12, 0x13, 0x3f, 0x22, 0x12, 0x10, 0x03, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x4a, 0x24, 
-0x06, 0x12, 0x21, 0xf1, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x03, 0x12, 0x22, 0x52, 0x24, 
-0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x4a, 0x12, 0x31, 0xc7, 0x7d, 0x02, 
-0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0xae, 0x05, 0x12, 0x1c, 0xde, 0xef, 0x12, 0x1a, 
-0x4a, 0x0e, 0x0e, 0x0e, 0xee, 0xd3, 0x95, 0x3c, 0xe4, 0x95, 0x3b, 0x40, 0x02, 0xae, 0x3c, 0xee, 
-0xd3, 0x94, 0x08, 0x74, 0x80, 0x94, 0x81, 0x40, 0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, 
-0x12, 0x1a, 0x4a, 0xaf, 0x06, 0x12, 0x31, 0xb1, 0x22, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, 
-0x60, 0x03, 0x7f, 0x10, 0x22, 0xed, 0x54, 0x7c, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, 
-0x74, 0x6e, 0x2e, 0xf8, 0x74, 0x02, 0x46, 0xf6, 0x74, 0x96, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 
-0xf5, 0x83, 0xed, 0xf0, 0x7f, 0x00, 0x22, 0xbf, 0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, 
-0x7c, 0xff, 0x7d, 0xe2, 0x8d, 0x82, 0x8c, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 
-0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, 0x74, 0x6e, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 
-0x00, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 
-0x16, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xff, 0xc3, 0xe5, 0x3a, 0x9f, 0xe5, 0x39, 0x94, 0x00, 
-0x40, 0x05, 0x12, 0x28, 0x16, 0x80, 0x03, 0x12, 0x31, 0xbd, 0x02, 0x10, 0x86, 0x90, 0xff, 0xfc, 
-0xe0, 0x20, 0xe7, 0x1f, 0xc2, 0xaf, 0x7d, 0xff, 0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, 
-0x7f, 0x00, 0xef, 0x1f, 0xaa, 0x06, 0x70, 0x01, 0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, 
-0xf0, 0x80, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x66, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, 
-0x12, 0x30, 0xe1, 0x0f, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x12, 0x19, 
-0x12, 0x1d, 0x46, 0x02, 0x10, 0x86, 0x8e, 0x5f, 0x8f, 0x60, 0xe5, 0x60, 0x15, 0x60, 0xae, 0x5f, 
-0x70, 0x02, 0x15, 0x5f, 0xd3, 0x94, 0x00, 0xee, 0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, 
-0x12, 0x0f, 0xdc, 0x80, 0xe5, 0x22, 0x11, 0x94, 0x2d, 0xf6, 0x23, 0xef, 0x31, 0xa3, 0x2f, 0xf4, 
-0x2f, 0xa2, 0x30, 0xb2, 0x2e, 0xe6, 0x26, 0x6d, 0x2b, 0xaf, 0x30, 0x55, 0x30, 0x74, 0x1d, 0xb4, 
-0x2e, 0x40, 0x2a, 0xe8, 0x0e, 0x12, 0x10, 0x03, 0x78, 0x86, 0x12, 0x22, 0x82, 0x20, 0xe1, 0x07, 
-0x7f, 0x12, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x86, 0xe6, 0xff, 0x12, 0x23, 0x49, 0x12, 0x30, 
-0xec, 0x02, 0x10, 0x86, 0x12, 0x10, 0x03, 0x78, 0x87, 0x12, 0x22, 0x82, 0x20, 0xe2, 0x07, 0x7f, 
-0x11, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x87, 0xe6, 0xff, 0x12, 0x2e, 0x7d, 0x12, 0x30, 0xec, 
-0x02, 0x10, 0x86, 0x8f, 0x61, 0x12, 0x2e, 0x7d, 0xaf, 0x61, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, 
-0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x61, 0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x61, 0x12, 0x13, 
-0x3f, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 
-0x05, 0x12, 0x2c, 0x07, 0x80, 0x06, 0x12, 0x1d, 0x64, 0x12, 0x1d, 0x6c, 0x02, 0x10, 0x86, 0x12, 
-0x29, 0x93, 0x12, 0x12, 0xbb, 0x90, 0xf8, 0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x12, 
-0x58, 0x12, 0x29, 0x1d, 0x12, 0x12, 0xf7, 0x12, 0x11, 0x74, 0x80, 0xe3, 0x12, 0x1c, 0xde, 0xef, 
-0x12, 0x1a, 0x4a, 0xe4, 0xf5, 0x33, 0xf5, 0x34, 0xef, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, 
-0x12, 0x31, 0xb1, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, 
-0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 
-0x54, 0x28, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 
-0xd3, 0x22, 0xef, 0x30, 0xe7, 0x08, 0x12, 0x1c, 0x95, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, 
-0x1c, 0xe8, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, 
-0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x7e, 0x12, 0x22, 
-0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, 
-0x3b, 0x39, 0x85, 0x3c, 0x3a, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, 
-0xf0, 0x22, 0xe4, 0xfe, 0xee, 0x90, 0x31, 0x47, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, 
-0x07, 0xf2, 0xc3, 0x22, 0x00, 0x08, 0x18, 0x28, 0x38, 0x01, 0x81, 0x10, 0x0a, 0x02, 0x00, 0x00, 
-0x00, 0x00, 0x00, 0x12, 0x10, 0x03, 0x7f, 0x02, 0x12, 0x10, 0x92, 0x12, 0x1d, 0x46, 0x02, 0x10, 
-0x86, 0x75, 0x39, 0x00, 0x8f, 0x3a, 0x12, 0x1c, 0x30, 0x12, 0x2c, 0x07, 0x22, 0x12, 0x1d, 0x6c, 
-0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x64, 0x22, 0xc2, 0x08, 0x22, 
+0xe5, 0x54, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x88, 0xe6, 0x12, 0x22, 0xe7, 0x90, 0x00, 0x0c, 0xef,
+0x12, 0x1a, 0xfa, 0x12, 0x22, 0xb5, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x53, 0x07, 0x0c,
+0x53, 0x06, 0xe6, 0xe5, 0x53, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, 0x54, 0x20, 0xe5, 0x0e,
+0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, 0x07, 0x02, 0xe5, 0x53,
+0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x53, 0x30, 0xe2, 0x03, 0x43, 0x07, 0x20, 0xe5, 0x53,
+0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x53, 0x30, 0xe1, 0x03, 0x43, 0x07, 0x80, 0xe5,
+0x53, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x53, 0x30, 0xe6, 0x03, 0x43, 0x06, 0x08, 0xe5,
+0x54, 0x20, 0xe4, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43,
+0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, 0x8f, 0xf0, 0x12, 0x1b,
+0x9f, 0xe5, 0x55, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, 0x12, 0x22, 0xe7, 0x90,
+0x00, 0x08, 0xef, 0x12, 0x1a, 0xfa, 0x80, 0x0a, 0x12, 0x22, 0xe8, 0x90, 0x00, 0x08, 0xe4, 0x12,
+0x1a, 0xfa, 0xe5, 0x55, 0x54, 0x03, 0x12, 0x22, 0xe7, 0x90, 0x00, 0x07, 0xef, 0x12, 0x1a, 0xfa,
+0xe5, 0x55, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x1a, 0xfa, 0x90, 0x00, 0x07,
+0x12, 0x1a, 0xbb, 0x70, 0x13, 0x12, 0x22, 0xe8, 0xe9, 0x24, 0x09, 0xf9, 0xe4, 0x3a, 0xfa, 0x12,
+0x1a, 0xa2, 0xff, 0xc3, 0x13, 0x12, 0x1a, 0xe8, 0x12, 0x23, 0x27, 0x24, 0x08, 0x12, 0x22, 0xa1,
+0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x22, 0xa1, 0xe0, 0xfd, 0xee,
+0xed, 0x12, 0x22, 0xe7, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x1b, 0x9f, 0x12, 0x32, 0x84,
+0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2f, 0xb4, 0x02, 0x10, 0xce, 0x90, 0xfa, 0xe6, 0xe0, 0xb4, 0x03,
+0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, 0xfa, 0xda, 0xee, 0xf0,
+0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x1a, 0xbb, 0xff, 0x7e, 0x00, 0x90, 0xfa, 0xd6, 0xee,
+0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, 0x12, 0x1b, 0x48, 0xff,
+0x90, 0xfa, 0xd8, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, 0x01, 0x8e, 0x50, 0x8f,
+0x51, 0x74, 0x0a, 0x25, 0x51, 0xf5, 0x51, 0xe4, 0x35, 0x50, 0xf5, 0x50, 0x90, 0xfa, 0xdb, 0xe0,
+0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd9, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, 0xff, 0x90, 0xfa, 0xdd,
+0xf0, 0xc3, 0x90, 0xfa, 0xd7, 0xe0, 0x9f, 0x90, 0xfa, 0xd6, 0xe0, 0x94, 0x00, 0x50, 0x06, 0xa3,
+0xe0, 0x90, 0xfa, 0xdd, 0xf0, 0x12, 0x20, 0xa9, 0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, 0x2e, 0x2b,
+0x90, 0xfa, 0xd6, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, 0xfa, 0xda, 0xe0, 0xfc,
+0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, 0xfa, 0xdd, 0xf0, 0x80,
+0x08, 0x90, 0xfa, 0xd7, 0xe0, 0x90, 0xfa, 0xdd, 0xf0, 0x12, 0x20, 0xa9, 0x60, 0x03, 0xe0, 0xff,
+0x22, 0x12, 0x2e, 0x2b, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, 0x2d, 0xf5,
+0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x7f, 0x00, 0x22, 0xaa, 0x50, 0xa9, 0x51, 0x7b, 0x01, 0x90,
+0xfa, 0xd8, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xdd, 0xe0, 0xf5, 0x4a, 0x12, 0x29, 0x60,
+0x90, 0xfa, 0xdc, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, 0xfe, 0x60, 0x2e, 0x24,
+0xfe, 0x70, 0x03, 0x02, 0x21, 0x69, 0x24, 0x06, 0x60, 0x03, 0x02, 0x21, 0xb1, 0x78, 0x71, 0xe6,
+0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x33, 0x90, 0xfa,
+0x94, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb2, 0x74, 0x01, 0xf0, 0x22, 0x78, 0x72, 0xe6,
+0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x43, 0x90, 0xfa,
+0x96, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb3, 0x74, 0x01, 0xf0, 0x22, 0x90, 0xfa, 0xa0,
+0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x21, 0xb1, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xcd, 0xf0,
+0xa3, 0xf0, 0x90, 0xfa, 0xcd, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xa6, 0x12,
+0x23, 0x0d, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xcd, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xce, 0xe0,
+0xff, 0x74, 0x34, 0xfe, 0x12, 0x2d, 0x85, 0xef, 0x70, 0x57, 0x90, 0xfa, 0xce, 0xe0, 0xff, 0x74,
+0x34, 0x90, 0xfa, 0x98, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xaa, 0xe0, 0xa3, 0x30, 0xe5,
+0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xcd, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xcd, 0xe0, 0xff,
+0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x23, 0x0d, 0x90, 0xff, 0xb6, 0xe0, 0x90,
+0xfa, 0xcd, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xce, 0xe0, 0xff, 0x74, 0x44, 0xfe, 0x12, 0x2d, 0x85,
+0xef, 0x70, 0x0e, 0x90, 0xfa, 0xce, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, 0x9a, 0xf0, 0xef, 0xa3,
+0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0xc0,
+0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0x90,
+0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xcc, 0xf0, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0xef, 0x12, 0x1b,
+0xfc, 0x22, 0x69, 0x26, 0x22, 0x69, 0x2e, 0x22, 0x0c, 0x30, 0x22, 0x0c, 0x32, 0x22, 0x1a, 0x38,
+0x22, 0x2c, 0x3a, 0x22, 0x5e, 0x3e, 0x22, 0x49, 0x44, 0x22, 0x3e, 0x46, 0x22, 0x54, 0x50, 0x22,
+0x54, 0x52, 0x22, 0x54, 0x54, 0x22, 0x54, 0x56, 0x00, 0x00, 0x22, 0x6e, 0x90, 0xfa, 0xcc, 0xe0,
+0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x11, 0x5e, 0x80, 0x62, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x03,
+0x12, 0x11, 0x5e, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, 0x7c, 0x00, 0x7d, 0x01,
+0x7f, 0x02, 0x12, 0x11, 0x5e, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x3e, 0x7c, 0x00,
+0x7d, 0x01, 0x7f, 0x05, 0x12, 0x11, 0x5e, 0x80, 0x33, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x06, 0x12,
+0x11, 0x5e, 0x80, 0x28, 0x90, 0xfa, 0xcc, 0xe0, 0xff, 0x12, 0x20, 0xc6, 0x80, 0x1e, 0x7c, 0x00,
+0x7d, 0x01, 0x7f, 0x04, 0x12, 0x11, 0x5e, 0x80, 0x13, 0x12, 0x28, 0x4e, 0x80, 0x0e, 0x90, 0xfa,
+0xcc, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x2d, 0x85, 0xd0, 0x07, 0xd0, 0x06,
+0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x82,
+0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x24, 0x04, 0x8e,
+0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x74, 0x13, 0x25, 0x24, 0xf5, 0x82, 0xe4,
+0x34, 0xf9, 0xf5, 0x83, 0x22, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0x22,
+0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, 0xff, 0x7a,
+0x32, 0x79, 0x56, 0x7e, 0x00, 0x7f, 0x0a, 0x02, 0x1a, 0x7c, 0x78, 0x80, 0xe6, 0xfc, 0x08, 0xe6,
+0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, 0xff, 0x90, 0xf9, 0x6f, 0x02, 0x1b, 0xea, 0x90, 0xf9,
+0x6a, 0x12, 0x1b, 0xea, 0x90, 0x00, 0x04, 0x02, 0x1a, 0xbb, 0x78, 0x7e, 0xe6, 0xfe, 0x08, 0xe6,
+0xff, 0x22, 0xed, 0x12, 0x1a, 0xfa, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90,
+0xfa, 0xce, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, 0xf0, 0x4e, 0xf0, 0x22, 0x78, 0x80,
+0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x22, 0x78, 0x7e, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, 0x83,
+0x22, 0xa6, 0x07, 0xe6, 0x24, 0x6e, 0xf8, 0xe6, 0x22, 0x78, 0x7e, 0xe6, 0xfa, 0x08, 0xe6, 0xfb,
+0x22, 0x08, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22,
+0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, 0x83, 0x22, 0x8b, 0x82, 0x8a, 0x83, 0xe5, 0x82,
+0x22, 0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0x8d, 0x28, 0x90, 0xfa, 0xd2, 0xe4, 0xf0, 0xa3, 0x74,
+0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xd1, 0x90, 0xfa, 0xd2, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0,
+0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xd1, 0xe0, 0x65, 0x28, 0x60, 0x46, 0xa3,
+0xe0, 0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0xf0, 0x90, 0xfa, 0xd1,
+0xe0, 0xff, 0x90, 0xfa, 0xd4, 0xe4, 0x8f, 0xf0, 0x12, 0x1b, 0x1c, 0x12, 0x23, 0xf0, 0x90, 0xfa,
+0xd4, 0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xd2, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xfa, 0xd1,
+0xe0, 0xa3, 0x75, 0xf0, 0x00, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xd2, 0xe4, 0x75, 0xf0, 0x04, 0x12,
+0x1b, 0x1c, 0x02, 0x23, 0x72, 0x90, 0xfa, 0xd3, 0xe0, 0x24, 0x01, 0xff, 0x90, 0xfa, 0xd2, 0xe0,
+0x34, 0x00, 0xab, 0x25, 0xaa, 0x26, 0xa9, 0x27, 0x8f, 0xf0, 0x12, 0x1b, 0x80, 0x7f, 0x00, 0x22,
+0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xd1, 0x90, 0xfa, 0xd2, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1b, 0x1c,
+0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x26, 0x98, 0x8f, 0x62, 0x12, 0x2a, 0xc7, 0x12,
+0x22, 0xfa, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x22, 0xa1, 0xe0, 0x54, 0xfb, 0xf0, 0x44, 0x02, 0xf0,
+0x08, 0x12, 0x22, 0xdc, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x23, 0x06, 0x24, 0x0b, 0x12, 0x22,
+0xa1, 0xe0, 0x44, 0x01, 0xf0, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xf5, 0x82, 0x8e, 0x83,
+0xe0, 0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x62, 0x24, 0xfe, 0x44, 0x20, 0xfc, 0x4d, 0xf0, 0xe5, 0x82,
+0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3,
+0x74, 0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x05, 0x12, 0x22, 0xa1,
+0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x99, 0x25, 0x62, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5,
+0x83, 0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82,
+0x8e, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, 0x44, 0x80,
+0xf0, 0x12, 0x32, 0x84, 0x74, 0x6e, 0x25, 0x62, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22,
+0x12, 0x10, 0x4b, 0x7f, 0x02, 0x12, 0x12, 0x61, 0x78, 0x67, 0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0,
+0xd2, 0xb1, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe7, 0x07, 0x90, 0xff, 0x9e, 0xe4, 0xf0, 0x80, 0x36,
+0xd2, 0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, 0x7e, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa,
+0x7f, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, 0x7c, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa,
+0x7d, 0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74,
+0x40, 0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, 0xe7, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90,
+0xff, 0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43,
+0x87, 0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7e, 0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7f,
+0xe0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x7c, 0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x7d,
+0xe0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x18, 0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe7,
+0xe0, 0xf5, 0xa8, 0x02, 0x10, 0xce, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x12, 0x2e, 0x0d, 0x90,
+0xfa, 0xc3, 0x12, 0x1b, 0xf3, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0xf3, 0x90,
+0xfa, 0xc7, 0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0xea, 0xe9,
+0x24, 0x01, 0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc9, 0x12, 0x1b, 0xf3, 0xab, 0x5c, 0xaa, 0x5d,
+0xa9, 0x5e, 0x12, 0x2e, 0x19, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x82, 0xf6, 0x90, 0xfa,
+0xc1, 0xe0, 0xff, 0x78, 0x82, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xc3, 0x12, 0x2d, 0xee,
+0xff, 0x78, 0x83, 0xf6, 0x90, 0xfa, 0xc6, 0x12, 0x2d, 0xee, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x83,
+0xf6, 0x12, 0x2d, 0xeb, 0x5e, 0x4f, 0xff, 0x78, 0x83, 0xf6, 0x12, 0x2d, 0xf4, 0x75, 0xf0, 0x02,
+0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xc7, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1b, 0x1c, 0xab, 0x5c, 0xaa,
+0x5d, 0xa9, 0x5e, 0x90, 0x00, 0x04, 0x12, 0x1a, 0xbb, 0x30, 0xe4, 0x03, 0x12, 0x2e, 0x03, 0x78,
+0x82, 0x06, 0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xc2, 0xf0, 0x22, 0x8b, 0x56, 0x8a, 0x57, 0x89, 0x58,
+0x90, 0xfa, 0xc2, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xc1, 0xf0, 0x12, 0x1a, 0xa2, 0x24, 0x6e,
+0x60, 0x26, 0x14, 0x70, 0x70, 0x12, 0x2d, 0xda, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x25,
+0x56, 0x80, 0x62, 0x12, 0x2e, 0x24, 0x12, 0x1f, 0xda, 0x90, 0xfa, 0xc2, 0xef, 0xf0, 0x80, 0x55,
+0x90, 0xfa, 0xc2, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2d, 0xda, 0x60, 0x09, 0x24, 0x30, 0x70,
+0x3e, 0x12, 0x2d, 0x30, 0x80, 0x3f, 0xe5, 0x58, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x57, 0xfa, 0x7b,
+0x01, 0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2e, 0x24, 0x90, 0x00, 0x05, 0x12, 0x1a, 0xbb,
+0xfd, 0x90, 0x00, 0x08, 0x12, 0x1b, 0x48, 0xf5, 0x2e, 0x85, 0xf0, 0x2d, 0xd0, 0x01, 0xd0, 0x02,
+0xd0, 0x03, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xc1, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90,
+0xfa, 0xc2, 0x74, 0x81, 0xf0, 0x90, 0xfa, 0xc2, 0xe0, 0x12, 0x2e, 0x24, 0x90, 0x00, 0x02, 0x12,
+0x1a, 0xfa, 0x90, 0xfa, 0xc1, 0xe0, 0xff, 0x22, 0x8b, 0x29, 0x8a, 0x2a, 0x89, 0x2b, 0x8d, 0x2c,
+0xe5, 0x2c, 0x70, 0x03, 0xaf, 0x2c, 0x22, 0x12, 0x2e, 0x53, 0x70, 0x16, 0x12, 0x2e, 0x72, 0xe5,
+0x2d, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0xd8, 0x50, 0xf2, 0x12, 0x27, 0x25, 0x40, 0x0b, 0x7f,
+0x00, 0x22, 0x12, 0x2e, 0x72, 0x12, 0x27, 0x25, 0x50, 0xf8, 0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0,
+0xe5, 0x2c, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4,
+0xf0, 0xf5, 0x2f, 0xe5, 0x2c, 0x14, 0xff, 0xe5, 0x2f, 0xc3, 0x9f, 0x50, 0x2a, 0x12, 0x31, 0xc1,
+0x40, 0x03, 0xaf, 0x2f, 0x22, 0xc3, 0xe5, 0x2c, 0x95, 0x2f, 0xff, 0xbf, 0x02, 0x07, 0x90, 0xff,
+0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2e, 0x65, 0x05, 0x2f, 0x74, 0x01, 0x25, 0x2b, 0xf5, 0x2b,
+0xe4, 0x35, 0x2a, 0xf5, 0x2a, 0x80, 0xcc, 0x12, 0x31, 0xc1, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x12,
+0x2e, 0x65, 0xaf, 0x2c, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x2e, 0xf0, 0x02, 0x31, 0xd8, 0x12, 0x10,
+0x4b, 0x78, 0x84, 0x12, 0x23, 0x31, 0x30, 0xe1, 0x08, 0x7f, 0x13, 0x12, 0x31, 0xa9, 0x02, 0x27,
+0xbc, 0x78, 0x84, 0xe6, 0xf9, 0x24, 0x13, 0x12, 0x22, 0xad, 0xe0, 0xff, 0x30, 0xe7, 0x40, 0x54,
+0x03, 0x60, 0x1e, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x44,
+0x04, 0xf0, 0x80, 0x46, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x80,
+0x39, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x01, 0xf0,
+0x80, 0x28, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1b, 0xef,
+0x54, 0x03, 0x60, 0x14, 0xe9, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80,
+0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x18, 0xe0, 0x04, 0xf0,
+0xaf, 0x01, 0x12, 0x22, 0xee, 0xfd, 0x12, 0x2f, 0xe5, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce, 0x75,
+0xa8, 0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x8b, 0x02, 0x28, 0x09, 0x02, 0x31,
+0x8c, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf,
+0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54,
+0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80,
+0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x2b, 0xa9, 0xe4, 0x7e, 0x01, 0x93,
+0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60,
+0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4,
+0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3,
+0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xe4, 0xf5,
+0x22, 0x12, 0x1d, 0xc2, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x22, 0x24, 0x03, 0xff, 0x12, 0x30, 0x13,
+0x12, 0x1d, 0xc2, 0xe4, 0xf0, 0x05, 0x22, 0xe5, 0x22, 0xc3, 0x94, 0x02, 0x40, 0xe3, 0xe4, 0xf5,
+0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x94, 0x12, 0x1e, 0x03, 0x60, 0x2c, 0x12, 0x2d,
+0x85, 0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x94, 0x12, 0x1b, 0xcc, 0xe4,
+0xf0, 0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x22, 0x90, 0xfa, 0xa0, 0x12, 0x1b, 0xcc, 0xe0, 0xa3,
+0x30, 0xe6, 0x33, 0x12, 0x1d, 0xc2, 0x74, 0x04, 0xf0, 0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90,
+0xfa, 0x98, 0x12, 0x1e, 0x03, 0x60, 0x16, 0x12, 0x2d, 0x85, 0xef, 0x60, 0x19, 0x75, 0xf0, 0x02,
+0xe5, 0x22, 0x90, 0xfa, 0x98, 0x12, 0x1b, 0xcc, 0xe4, 0xf0, 0xa3, 0xf0, 0x22, 0x05, 0x22, 0xe5,
+0x22, 0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xfe,
+0xef, 0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xe0,
+0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xe8, 0x0f, 0x12, 0x1c, 0xb0, 0x80, 0xdd, 0xef, 0xfd, 0xc3, 0xe5,
+0x3a, 0x9d, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 0xd3, 0xe5, 0x3a, 0x94, 0x00, 0xe5,
+0x39, 0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, 0x12, 0x1d, 0xdf, 0x12, 0x1e,
+0x34, 0x12, 0x1e, 0x26, 0x12, 0x1a, 0xa2, 0x24, 0x6e, 0x60, 0x1e, 0x14, 0x60, 0x1b, 0x24, 0x8e,
+0x70, 0x2d, 0x90, 0x00, 0x01, 0x12, 0x1a, 0xbb, 0xff, 0x24, 0xfc, 0x60, 0x03, 0x04, 0x70, 0x1f,
+0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x11, 0x5e, 0x12, 0x1e, 0x3b, 0x12, 0x25, 0xfa, 0x12,
+0x1d, 0x89, 0x12, 0x1a, 0xbb, 0x60, 0x03, 0x02, 0x32, 0x7a, 0xe4, 0xff, 0x12, 0x32, 0x6e, 0x22,
+0x8b, 0x45, 0x8a, 0x46, 0x89, 0x47, 0x8c, 0x48, 0x8d, 0x49, 0xd2, 0x00, 0x12, 0x2e, 0x53, 0x70,
+0x16, 0x12, 0x2e, 0x72, 0xe5, 0x48, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0xd8, 0x50, 0xf2, 0x12,
+0x29, 0xd5, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2e, 0x72, 0x12, 0x29, 0xd5, 0x50, 0xf8, 0xe4,
+0xf5, 0x4b, 0xe5, 0x4a, 0x14, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, 0x17, 0x12, 0x29, 0xc5, 0x40,
+0x03, 0x7f, 0x18, 0x22, 0x05, 0x4b, 0x74, 0x01, 0x25, 0x47, 0xf5, 0x47, 0xe4, 0x35, 0x46, 0xf5,
+0x46, 0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x29, 0xc5, 0x40, 0x03, 0x7f,
+0x18, 0x22, 0x7f, 0x00, 0x22, 0xab, 0x45, 0xaa, 0x46, 0xa9, 0x47, 0x12, 0x1a, 0xa2, 0x90, 0xff,
+0xf1, 0xf0, 0x02, 0x31, 0xd8, 0x90, 0xff, 0xf1, 0xe5, 0x49, 0xf0, 0x02, 0x31, 0xd8, 0x7b, 0x01,
+0x7a, 0xfa, 0x79, 0xcf, 0xe4, 0xfd, 0x12, 0x23, 0x61, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x09,
+0x12, 0x1b, 0x1c, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01,
+0x12, 0x1b, 0x32, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xff, 0xf7,
+0xe5, 0x23, 0x12, 0x2a, 0x39, 0x90, 0xff, 0xf6, 0xe5, 0x23, 0xf0, 0x90, 0xfa, 0xcf, 0xe4, 0xf0,
+0xa3, 0x74, 0x06, 0x12, 0x2a, 0x39, 0xe5, 0x23, 0x30, 0xe0, 0x07, 0x90, 0xff, 0xfc, 0x74, 0x94,
+0xf0, 0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23,
+0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1b, 0x32, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d,
+0x01, 0x02, 0x26, 0x98, 0x90, 0xff, 0x93, 0x74, 0x81, 0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06,
+0x90, 0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, 0xe0, 0x44, 0x90, 0xf0, 0xe4, 0x90, 0xf9,
+0x16, 0xf0, 0xa3, 0xf0, 0x12, 0x2b, 0x39, 0x12, 0x16, 0xc9, 0x12, 0x30, 0x69, 0x7e, 0x07, 0x7f,
+0xd0, 0x12, 0x12, 0x2a, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x12, 0x44, 0xe4, 0x78, 0x77, 0xf6, 0x78,
+0x77, 0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x6e, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x77,
+0x06, 0x80, 0xec, 0x7f, 0x03, 0x12, 0x30, 0xb2, 0x90, 0xf9, 0x16, 0xe0, 0x20, 0xe4, 0x05, 0x7f,
+0x04, 0x12, 0x30, 0xb2, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8,
+0xe0, 0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x15, 0x65, 0xa8, 0x65, 0xa6, 0x07, 0x30, 0x08, 0x05,
+0x12, 0x11, 0xae, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x65, 0xe6, 0xff, 0xb4, 0x03, 0x0f, 0x78, 0x7c,
+0x76, 0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, 0x7c, 0x76,
+0xff, 0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x80, 0x76, 0xfa, 0x08, 0x76,
+0x9e, 0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x23, 0x49, 0x7b, 0x01, 0x7a,
+0xff, 0x79, 0x48, 0x78, 0x68, 0x12, 0x1b, 0xe1, 0xa8, 0x65, 0xe6, 0x24, 0xfd, 0x75, 0xf0, 0x08,
+0xa4, 0xff, 0xae, 0xf0, 0x78, 0x6a, 0x12, 0x23, 0x49, 0x79, 0x08, 0x78, 0x6b, 0x12, 0x1b, 0xe1,
+0x78, 0x6d, 0xef, 0x12, 0x23, 0x49, 0x05, 0x65, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xab, 0xf0,
+0xe0, 0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe6, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcf,
+0xe4, 0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x7e, 0x00, 0x90, 0xfa, 0xe4, 0xee,
+0xf0, 0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcf, 0xe0, 0xb4, 0x52, 0x09, 0x90,
+0xf9, 0x16, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe4, 0xe0, 0x70, 0x04, 0xa3, 0xe0,
+0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcf, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x16, 0xe0, 0x44,
+0x10, 0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe6, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x16, 0xe0, 0x54, 0xef,
+0xf0, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x03, 0x68, 0x01, 0xff, 0x48, 0x03, 0x6b,
+0x01, 0xff, 0x08, 0x02, 0x66, 0x00, 0x00, 0x44, 0xfa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x44, 0xfa,
+0x94, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xb2, 0x00, 0x00, 0x42, 0xfa, 0x7e, 0x00, 0x00, 0x42,
+0xfa, 0x7c, 0x00, 0x00, 0x42, 0xf9, 0x6d, 0xff, 0xff, 0x42, 0xfa, 0x7a, 0x00, 0x00, 0x41, 0xf9,
+0x66, 0xff, 0x41, 0xf9, 0x1c, 0x19, 0x41, 0xf9, 0x15, 0x00, 0x43, 0xf9, 0x19, 0x0a, 0x32, 0x02,
+0x41, 0xf9, 0x68, 0x20, 0x41, 0xf9, 0x69, 0x20, 0x41, 0xf9, 0x65, 0x00, 0x41, 0xf9, 0x67, 0x00,
+0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf9, 0x16, 0x00, 0x00, 0x41, 0xf9, 0x18, 0x00,
+0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, 0x10, 0x4b, 0x78, 0x8a, 0xef, 0xf6, 0x12,
+0x2a, 0xc7, 0x12, 0x22, 0xee, 0x30, 0xe0, 0x29, 0x78, 0x7c, 0x12, 0x22, 0xb7, 0xe0, 0x54, 0x7f,
+0xf0, 0x78, 0x6b, 0x12, 0x1b, 0xd8, 0x90, 0x00, 0x02, 0x12, 0x1a, 0xbb, 0x30, 0xe7, 0x09, 0x90,
+0x00, 0x02, 0xe4, 0x12, 0x1a, 0xfa, 0x80, 0xe9, 0x78, 0x7c, 0x12, 0x22, 0xb7, 0xe0, 0x44, 0x80,
+0xf0, 0x12, 0x22, 0xee, 0x30, 0xe1, 0x1e, 0x12, 0x22, 0x97, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x32,
+0x19, 0x78, 0x68, 0x12, 0x1b, 0xd8, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x1a, 0xfa, 0x12, 0x22,
+0x97, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x32, 0x84, 0xe4, 0xff, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce,
+0x12, 0x10, 0x4b, 0x78, 0x85, 0xef, 0xf6, 0x12, 0x31, 0x50, 0x12, 0x31, 0xa9, 0x78, 0x85, 0xe6,
+0xff, 0x24, 0x13, 0x12, 0x22, 0xad, 0xe0, 0xfe, 0x30, 0xe7, 0x16, 0xef, 0xb4, 0x03, 0x09, 0x90,
+0xff, 0x9e, 0xe0, 0x54, 0xfa, 0xf0, 0x80, 0x22, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf5, 0xf0, 0x80,
+0x19, 0xee, 0x54, 0x03, 0x60, 0x14, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20,
+0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x18, 0xe0, 0x14, 0xf0,
+0xe0, 0x70, 0x02, 0xd2, 0xb3, 0x02, 0x10, 0xce, 0x12, 0x1e, 0x1c, 0xe5, 0x3a, 0x64, 0x09, 0x70,
+0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x3a, 0x94, 0x08, 0xe5, 0x39, 0x94, 0x00,
+0x40, 0x11, 0x7f, 0x08, 0xef, 0xe5, 0x3a, 0x94, 0x08, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5,
+0x39, 0x80, 0x05, 0xaf, 0x3a, 0x12, 0x1e, 0x34, 0xe4, 0xfe, 0xee, 0xc3, 0x9f, 0x50, 0x19, 0x12,
+0x1c, 0xc1, 0x12, 0x1a, 0xa2, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83,
+0xed, 0xf0, 0x0e, 0x12, 0x1c, 0xb0, 0x80, 0xe2, 0xef, 0x54, 0x7f, 0x90, 0xff, 0x81, 0xf0, 0x22,
+0x8b, 0x59, 0x8a, 0x5a, 0x89, 0x5b, 0x12, 0x2e, 0x19, 0x70, 0x05, 0xa3, 0x74, 0x08, 0xf0, 0x22,
+0xab, 0x59, 0xaa, 0x5a, 0xa9, 0x5b, 0x12, 0x2e, 0x0d, 0x90, 0xfa, 0xc9, 0x12, 0x1b, 0xf3, 0xe5,
+0x5b, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5a, 0xfa, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0xf3, 0xe4, 0x90,
+0xfa, 0xc2, 0xf0, 0x78, 0x8b, 0xf6, 0x90, 0xfa, 0xc1, 0xe0, 0xff, 0x78, 0x8b, 0xe6, 0xc3, 0x9f,
+0x50, 0x12, 0x12, 0x2d, 0xeb, 0xff, 0x12, 0x2d, 0xf4, 0x12, 0x2e, 0x07, 0x78, 0x8b, 0x06, 0x12,
+0x2e, 0x03, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x32, 0x0a, 0xe4, 0x93, 0xff, 0x78,
+0x74, 0xf6, 0x54, 0x0f, 0x12, 0x1d, 0xa8, 0xe0, 0x08, 0x76, 0x00, 0x08, 0xf6, 0x18, 0x12, 0x1c,
+0xd9, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x75, 0xee, 0xf6, 0x08, 0xef, 0xf6,
+0xee, 0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, 0xe0, 0x20, 0xe7, 0x03, 0x7f,
+0x00, 0x22, 0x78, 0x75, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0xec, 0xf0, 0xa3, 0xed,
+0xf0, 0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58,
+0x90, 0x00, 0x03, 0x12, 0x1a, 0xbb, 0x54, 0xf0, 0x24, 0xa0, 0x22, 0x90, 0xfa, 0xc9, 0x12, 0x1b,
+0xea, 0x02, 0x1a, 0xa2, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0xea, 0xef, 0x12, 0x1a, 0xe8, 0x90, 0xfa,
+0xca, 0xe4, 0x22, 0x90, 0xfa, 0xc4, 0xe4, 0x75, 0xf0, 0x01, 0x02, 0x1b, 0x1c, 0x90, 0x00, 0x08,
+0x12, 0x1b, 0x48, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, 0x05, 0x12, 0x1a, 0xbb, 0x90,
+0xfa, 0xc1, 0xf0, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x22, 0x90, 0xfa, 0xdd, 0xe0, 0xff,
+0x7e, 0x00, 0xc3, 0x90, 0xfa, 0xd7, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xd6, 0xe0, 0x9e, 0xf0, 0x90,
+0xfa, 0xd8, 0xee, 0x8f, 0xf0, 0x12, 0x1b, 0x1c, 0xef, 0x25, 0x51, 0xf5, 0x51, 0xee, 0x35, 0x50,
+0xf5, 0x50, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0xfa,
+0xe6, 0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x29, 0xaa, 0x2a, 0xa9, 0x2b, 0x02,
+0x1a, 0xe8, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x64, 0xed, 0x70, 0x0f, 0xe5, 0x64,
+0xb4, 0x03, 0x05, 0x7f, 0x01, 0x02, 0x31, 0xef, 0x7f, 0x02, 0x02, 0x31, 0xef, 0xaf, 0x64, 0x12,
+0x2a, 0xc7, 0x74, 0x6e, 0x25, 0x64, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, 0xd2, 0x09, 0x12, 0x1d, 0x33,
+0xe0, 0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x64, 0xb4, 0x03, 0x07, 0x7f, 0x81, 0x12,
+0x31, 0xef, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x31, 0xef, 0x30, 0x09, 0x07, 0x12, 0x1d, 0x33, 0xe0,
+0x44, 0x80, 0xf0, 0x12, 0x32, 0x84, 0x22, 0x12, 0x10, 0x4b, 0x90, 0xff, 0xfd, 0xe0, 0x44, 0x60,
+0xf0, 0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0x00, 0xe0, 0x30, 0xe7,
+0x13, 0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x35, 0x80, 0x90, 0xff, 0xfc, 0xe0, 0x44,
+0x01, 0xf0, 0x80, 0x0d, 0x12, 0x1d, 0xdf, 0x53, 0x35, 0x7f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfe,
+0xf0, 0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0xde, 0x12, 0x1d, 0xe7, 0x02, 0x10,
+0xce, 0x12, 0x10, 0x4b, 0x78, 0x89, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x2a, 0xc7, 0x90, 0xf9, 0x6a,
+0x12, 0x1b, 0xea, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x80, 0xe6, 0xfe,
+0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x22, 0xd3, 0x12, 0x32,
+0x84, 0x78, 0x89, 0xe6, 0xff, 0x12, 0x13, 0x87, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce, 0x8f, 0x63,
+0x12, 0x2a, 0xc7, 0x78, 0x7c, 0x12, 0x22, 0xb7, 0xe0, 0x54, 0x3f, 0xf0, 0xe5, 0x82, 0x24, 0x04,
+0x12, 0x22, 0xa1, 0xe0, 0x54, 0x3f, 0xf0, 0x12, 0x23, 0x41, 0x24, 0x0b, 0x12, 0x22, 0xa1, 0xe0,
+0x54, 0xf8, 0xf0, 0x12, 0x32, 0x84, 0x74, 0x6e, 0x25, 0x63, 0xf8, 0x74, 0xfb, 0x56, 0xf6, 0x7f,
+0x00, 0x22, 0x12, 0x10, 0x4b, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xfa, 0x24, 0x06, 0x12, 0x22, 0x9f,
+0xe0, 0xfd, 0x12, 0x22, 0xe8, 0x90, 0x00, 0x03, 0x12, 0x23, 0x02, 0x24, 0x05, 0x12, 0x22, 0xa1,
+0xe0, 0x90, 0x00, 0x04, 0x12, 0x1a, 0xfa, 0x12, 0x32, 0x84, 0x7d, 0x02, 0xe4, 0xff, 0x12, 0x2f,
+0xb4, 0x02, 0x10, 0xce, 0xae, 0x05, 0x12, 0x1d, 0x8e, 0xef, 0x12, 0x1a, 0xfa, 0x0e, 0x0e, 0x0e,
+0xee, 0xd3, 0x95, 0x3c, 0xe4, 0x95, 0x3b, 0x40, 0x02, 0xae, 0x3c, 0xee, 0xd3, 0x94, 0x08, 0x74,
+0x80, 0x94, 0x81, 0x40, 0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, 0x12, 0x1a, 0xfa, 0xaf,
+0x06, 0x12, 0x32, 0x6e, 0x22, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, 0x60, 0x03, 0x7f, 0x10,
+0x22, 0xed, 0x54, 0x7c, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, 0x74, 0x6e, 0x2e, 0xf8,
+0x74, 0x02, 0x46, 0xf6, 0x74, 0x99, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0xed, 0xf0,
+0x7f, 0x00, 0x22, 0xbf, 0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, 0x7c, 0xff, 0x7d, 0xe2,
+0x8d, 0x82, 0x8c, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0,
+0x44, 0x80, 0xf0, 0x74, 0x6e, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, 0x10,
+0x4b, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 0x16, 0x90, 0xff, 0x83,
+0xe0, 0x54, 0x0f, 0xff, 0xc3, 0xe5, 0x3a, 0x9f, 0xe5, 0x39, 0x94, 0x00, 0x40, 0x05, 0x12, 0x28,
+0xd7, 0x80, 0x03, 0x12, 0x32, 0x7a, 0x02, 0x10, 0xce, 0x90, 0xff, 0xfc, 0xe0, 0x20, 0xe7, 0x1f,
+0xc2, 0xaf, 0x7d, 0xff, 0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, 0x7f, 0x00, 0xef, 0x1f,
+0xaa, 0x06, 0x70, 0x01, 0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0x80, 0xef, 0x22,
+0x12, 0x10, 0x4b, 0x78, 0x66, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, 0x12, 0x30, 0xe1, 0x0f,
+0x90, 0xff, 0xfc, 0xe0, 0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x12, 0x61, 0x12, 0x1d, 0xf6, 0x02,
+0x10, 0xce, 0x8f, 0x23, 0xc2, 0x08, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xc0, 0x78, 0x7e, 0x12, 0x23,
+0x42, 0x24, 0x0b, 0x12, 0x22, 0xa1, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x32, 0x84, 0xaf, 0x23, 0x12,
+0x13, 0x87, 0x22, 0x8e, 0x5f, 0x8f, 0x60, 0xe5, 0x60, 0x15, 0x60, 0xae, 0x5f, 0x70, 0x02, 0x15,
+0x5f, 0xd3, 0x94, 0x00, 0xee, 0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, 0x12, 0x10, 0x24,
+0x80, 0xe5, 0x22, 0x11, 0xdc, 0x2e, 0xc7, 0x24, 0xb0, 0x32, 0x60, 0x30, 0x90, 0x30, 0x3e, 0x31,
+0x6f, 0x2f, 0x82, 0x27, 0x2e, 0x2c, 0x80, 0x31, 0x12, 0x31, 0x31, 0x1e, 0x64, 0x2f, 0x11, 0x2c,
+0x18, 0x0e, 0x12, 0x10, 0x4b, 0x78, 0x86, 0x12, 0x23, 0x31, 0x20, 0xe1, 0x07, 0x7f, 0x12, 0x12,
+0x31, 0xa9, 0x80, 0x0a, 0x78, 0x86, 0xe6, 0xff, 0x12, 0x24, 0x0a, 0x12, 0x31, 0xa9, 0x02, 0x10,
+0xce, 0x12, 0x10, 0x4b, 0x78, 0x87, 0x12, 0x23, 0x31, 0x20, 0xe2, 0x07, 0x7f, 0x11, 0x12, 0x31,
+0xa9, 0x80, 0x0a, 0x78, 0x87, 0xe6, 0xff, 0x12, 0x2f, 0x4e, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce,
+0x8f, 0x61, 0x12, 0x2f, 0x4e, 0xaf, 0x61, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xc0, 0x12, 0x32, 0x84,
+0x74, 0x6e, 0x25, 0x61, 0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x61, 0x12, 0x13, 0x87, 0x22, 0x12,
+0x10, 0x4b, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 0x05, 0x12, 0x2c,
+0xd8, 0x80, 0x06, 0x12, 0x1e, 0x14, 0x12, 0x1e, 0x1c, 0x02, 0x10, 0xce, 0x12, 0x2a, 0x54, 0x12,
+0x13, 0x03, 0x90, 0xf8, 0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x12, 0xa0, 0x12, 0x29,
+0xde, 0x12, 0x13, 0x3f, 0x12, 0x11, 0xbc, 0x80, 0xe3, 0x12, 0x1d, 0x8e, 0xef, 0x12, 0x1a, 0xfa,
+0xe4, 0xf5, 0x33, 0xf5, 0x34, 0xef, 0x60, 0x03, 0x02, 0x32, 0x7a, 0xe4, 0xff, 0x12, 0x32, 0x6e,
+0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff,
+0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0x28, 0x60,
+0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0xef,
+0x30, 0xe7, 0x08, 0x12, 0x1d, 0x45, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, 0x1d, 0x98, 0xe0,
+0x54, 0xdf, 0xf0, 0x22, 0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, 0x40, 0x00, 0x40,
+0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x7e, 0x12, 0x22, 0xb7, 0xa3, 0xa3,
+0xe0, 0xff, 0x30, 0xe7, 0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, 0x3b, 0x39, 0x85,
+0x3c, 0x3a, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xe4,
+0xfe, 0xee, 0x90, 0x32, 0x04, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, 0x07, 0xf2, 0xc3,
+0x22, 0x00, 0x08, 0x18, 0x28, 0x38, 0x01, 0x81, 0x90, 0x0a, 0x02, 0x00, 0x00, 0x11, 0x13, 0x00,
+0x12, 0x10, 0x4b, 0x7f, 0x02, 0x12, 0x10, 0xda, 0x12, 0x1d, 0xf6, 0x02, 0x10, 0xce, 0x75, 0x39,
+0x00, 0x8f, 0x3a, 0x12, 0x1c, 0xe0, 0x12, 0x2c, 0xd8, 0x22, 0x12, 0x1e, 0x1c, 0x12, 0x1d, 0xdf,
+0x12, 0x1e, 0x14, 0x22, 0xc2, 0x08, 0x22,
 };
 
 #undef IMAGE_VERSION_NAME
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 544098d..0d39036 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -48,7 +48,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.7"
+#define DRIVER_VERSION "v0.7mode043006"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
 #define DRIVER_DESC "Edgeport USB Serial Driver"
 
@@ -173,8 +173,12 @@
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) },
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) },
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) },
-// The 4-port shows up as two 2-port devices
+	/* The 4, 8 and 16 port devices show up as multiple 2 port devices */
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) },
 	{ }
 };
 
@@ -209,6 +213,10 @@
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) },
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) },
 	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) },
+	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) },
 	{ }
 };
 
@@ -231,6 +239,7 @@
 static int low_latency = EDGE_LOW_LATENCY;
 static int closing_wait = EDGE_CLOSING_WAIT;
 static int ignore_cpu_rev = 0;
+static int default_uart_mode = 0;	/* RS232 */
 
 
 static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length);
@@ -241,6 +250,10 @@
 static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios);
 static void edge_send(struct usb_serial_port *port);
 
+/* sysfs attributes */
+static int edge_create_sysfs_attrs(struct usb_serial_port *port);
+static int edge_remove_sysfs_attrs(struct usb_serial_port *port);
+
 /* circular buffer */
 static struct edge_buf *edge_buf_alloc(unsigned int size);
 static void edge_buf_free(struct edge_buf *eb);
@@ -1706,13 +1719,14 @@
 	int length = urb->actual_length;
 	int port_number;
 	int function;
-	int status;
+	int retval;
 	__u8 lsr;
 	__u8 msr;
+	int status = urb->status;
 
 	dbg("%s", __FUNCTION__);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -1720,10 +1734,12 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
-		dev_err(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __FUNCTION__, urb->status);
+		dev_err(&urb->dev->dev, "%s - nonzero urb status received: "
+			"%d\n", __FUNCTION__, status);
 		goto exit;
 	}
 
@@ -1781,10 +1797,10 @@
 	}
 
 exit:
-	status = usb_submit_urb (urb, GFP_ATOMIC);
-	if (status)
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
 		dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
-			 __FUNCTION__, status);
+			 __FUNCTION__, retval);
 }
 
 static void edge_bulk_in_callback (struct urb *urb)
@@ -1792,12 +1808,13 @@
 	struct edgeport_port *edge_port = (struct edgeport_port *)urb->context;
 	unsigned char *data = urb->transfer_buffer;
 	struct tty_struct *tty;
-	int status = 0;
+	int retval = 0;
 	int port_number;
+	int status = urb->status;
 
 	dbg("%s", __FUNCTION__);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -1805,17 +1822,18 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
 		dev_err (&urb->dev->dev,"%s - nonzero read bulk status received: %d\n",
-		     __FUNCTION__, urb->status );
+		     __FUNCTION__, status);
 	}
 
-	if (urb->status == -EPIPE)
+	if (status == -EPIPE)
 		goto exit;
 
-	if (urb->status) {
+	if (status) {
 		dev_err(&urb->dev->dev,"%s - stopping read!\n", __FUNCTION__);
 		return;
 	}
@@ -1849,14 +1867,14 @@
 	spin_lock(&edge_port->ep_lock);
 	if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING) {
 		urb->dev = edge_port->port->serial->dev;
-		status = usb_submit_urb(urb, GFP_ATOMIC);
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
 	} else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING) {
 		edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED;
 	}
 	spin_unlock(&edge_port->ep_lock);
-	if (status)
+	if (retval)
 		dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
-			 __FUNCTION__, status);
+			 __FUNCTION__, retval);
 }
 
 static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length)
@@ -1883,12 +1901,13 @@
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	int status = urb->status;
 
 	dbg ("%s - port %d", __FUNCTION__, port->number);
 
 	edge_port->ep_write_urb_in_use = 0;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -1896,11 +1915,12 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
-		dev_err (&urb->dev->dev,"%s - nonzero write bulk status received: %d\n",
-		     __FUNCTION__, urb->status);
+		dev_err(&urb->dev->dev, "%s - nonzero write bulk status "
+			"received: %d\n", __FUNCTION__, status);
 	}
 
 	/* send any buffered data */
@@ -2351,7 +2371,7 @@
 		urb->complete = edge_bulk_in_callback;
 		urb->context = edge_port;
 		urb->dev = edge_port->port->serial->dev;
-		status = usb_submit_urb(urb, GFP_KERNEL);
+		status = usb_submit_urb(urb, GFP_ATOMIC);
 	}
 	edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING;
 	edge_port->shadow_mcr |= MCR_RTS;
@@ -2524,14 +2544,6 @@
 	}
 
 	cflag = tty->termios->c_cflag;
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if (cflag == old_termios->c_cflag &&
-		    tty->termios->c_iflag == old_termios->c_iflag) {
-			dbg ("%s - nothing to change", __FUNCTION__);
-			return;
-		}
-	}
 
 	dbg("%s - clfag %08x iflag %08x", __FUNCTION__, 
 	    tty->termios->c_cflag, tty->termios->c_iflag);
@@ -2758,7 +2770,7 @@
 		edge_port->port = serial->port[i];
 		edge_port->edge_serial = edge_serial;
 		usb_set_serial_port_data(serial->port[i], edge_port);
-		edge_port->bUartMode = 0;	/* Default is RS232 */
+		edge_port->bUartMode = default_uart_mode;
 	}
 	
 	return 0;
@@ -2784,6 +2796,7 @@
 
 	for (i=0; i < serial->num_ports; ++i) {
 		edge_port = usb_get_serial_port_data(serial->port[i]);
+		edge_remove_sysfs_attrs(edge_port->port);
 		if (edge_port) {
 			edge_buf_free(edge_port->ep_out_buf);
 			kfree(edge_port);
@@ -2795,6 +2808,48 @@
 }
 
 
+/* Sysfs Attributes */
+
+static ssize_t show_uart_mode(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+
+	return sprintf(buf, "%d\n", edge_port->bUartMode);
+}
+
+static ssize_t store_uart_mode(struct device *dev,
+	struct device_attribute *attr, const char *valbuf, size_t count)
+{
+	struct usb_serial_port *port = to_usb_serial_port(dev);
+	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+	unsigned int v = simple_strtoul(valbuf, NULL, 0);
+
+	dbg("%s: setting uart_mode = %d", __FUNCTION__, v);
+
+	if (v < 256)
+		edge_port->bUartMode = v;
+	else
+		dev_err(dev, "%s - uart_mode %d is invalid\n", __FUNCTION__, v);
+
+	return count;
+}
+
+static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode, store_uart_mode);
+
+static int edge_create_sysfs_attrs(struct usb_serial_port *port)
+{
+	return device_create_file(&port->dev, &dev_attr_uart_mode);
+}
+
+static int edge_remove_sysfs_attrs(struct usb_serial_port *port)
+{
+	device_remove_file(&port->dev, &dev_attr_uart_mode);
+	return 0;
+}
+
+
 /* Circular Buffer */
 
 /*
@@ -2991,6 +3046,7 @@
 	.unthrottle		= edge_unthrottle,
 	.attach			= edge_startup,
 	.shutdown		= edge_shutdown,
+	.port_probe		= edge_create_sysfs_attrs,
 	.ioctl			= edge_ioctl,
 	.set_termios		= edge_set_termios,
 	.tiocmget		= edge_tiocmget,
@@ -3022,6 +3078,7 @@
 	.unthrottle		= edge_unthrottle,
 	.attach			= edge_startup,
 	.shutdown		= edge_shutdown,
+	.port_probe		= edge_create_sysfs_attrs,
 	.ioctl			= edge_ioctl,
 	.set_termios		= edge_set_termios,
 	.tiocmget		= edge_tiocmget,
@@ -3085,3 +3142,6 @@
 module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a device");
 
+module_param(default_uart_mode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(default_uart_mode, "Default uart_mode, 0=RS232, ...");
+
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h
index e57fa11..8e1a491 100644
--- a/drivers/usb/serial/io_usbvend.h
+++ b/drivers/usb/serial/io_usbvend.h
@@ -131,7 +131,7 @@
 #define ION_DEVICE_ID_TI_EDGEPORT_2I		0x0207	// Edgeport/2i RS422/RS485
 #define ION_DEVICE_ID_TI_EDGEPORT_421		0x020C	// Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port)
 #define ION_DEVICE_ID_TI_EDGEPORT_21		0x020D	// Edgeport/21 2 RS232 + Parallel (lucent on a different hub port)
-#define ION_DEVICE_ID_TI_EDGEPORT_8		0x020F	// Edgeport/8 (single-CPU)
+#define ION_DEVICE_ID_TI_EDGEPORT_416		0x0212  // Edgeport/416
 #define ION_DEVICE_ID_TI_EDGEPORT_1		0x0215	// Edgeport/1 RS232
 #define ION_DEVICE_ID_TI_EDGEPORT_42		0x0217	// Edgeport/42 4 hub 2 RS232
 #define ION_DEVICE_ID_TI_EDGEPORT_22I  		0x021A	// Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232
@@ -143,12 +143,14 @@
 #define ION_DEVICE_ID_TI_EDGEPORT_21C		0x021E	// Edgeport/21c is a TI based Edgeport/2 with lucent chip
 
 // Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) 
-#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1	0x240	// Edgeport/1 RS232
-#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I	0x241	// Edgeport/1i- RS422 model
+#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1	0x0240	// Edgeport/1 RS232
+#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I	0x0241	// Edgeport/1i- RS422 model
 
 // Ti based software switchable RS232/RS422/RS485 devices
-#define ION_DEVICE_ID_TI_EDGEPORT_4S		0x242	// Edgeport/4s - software switchable model
-#define ION_DEVICE_ID_IT_EDGEPORT_8S		0x243	// Edgeport/8s - software switchable model
+#define ION_DEVICE_ID_TI_EDGEPORT_4S		0x0242	// Edgeport/4s - software switchable model
+#define ION_DEVICE_ID_TI_EDGEPORT_8S		0x0243	// Edgeport/8s - software switchable model
+#define ION_DEVICE_ID_TI_EDGEPORT_8		0x0244	// Edgeport/8 (single-CPU)
+#define ION_DEVICE_ID_TI_EDGEPORT_416B		0x0247	// Edgeport/416
 
 
 /************************************************************************
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index 4df0ec7..0455c15 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -732,11 +732,13 @@
 	struct tty_struct	*tty;
 	unsigned char		*data = urb->transfer_buffer;
 	int			result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -870,11 +872,13 @@
 	struct ipaq_private	*priv = usb_get_serial_port_data(port);
 	unsigned long		flags;
 	int			result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
-	
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 1bc5860..1b94daa 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -167,11 +167,13 @@
 	unsigned char *data = urb->transfer_buffer;
 	struct tty_struct *tty;
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -369,13 +371,15 @@
 static void ipw_write_bulk_callback(struct urb *urb)
 {
 	struct usb_serial_port *port = urb->context;
+	int status = urb->status;
 
 	dbg("%s", __FUNCTION__);
 
 	port->write_urb_busy = 0;
 
-	if (urb->status)
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status)
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 
 	usb_serial_port_softint(port);
 }
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 9d847f6..5ab6a0c 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -21,6 +21,10 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
+ * 2007_Jun_21  Alan Cox <alan@redhat.com>
+ *	Minimal cleanups for some of the driver problens and tty layer abuse.
+ *	Still needs fixing to allow multiple dongles.
+ *
  * 2002_Mar_07	greg kh
  *	moved some needed structures and #define values from the
  *	net/irda/irda-usb.h file into our file, as we don't want to depend on
@@ -109,6 +113,7 @@
 static void ir_read_bulk_callback (struct urb *urb);
 static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios);
 
+/* Not that this lot means you can only have one per system */
 static u8 ir_baud = 0;
 static u8 ir_xbof = 0;
 static u8 ir_add_bof = 0;
@@ -392,12 +397,14 @@
 static void ir_write_bulk_callback (struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	port->write_urb_busy = 0;
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -417,6 +424,7 @@
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -425,8 +433,7 @@
 		return;
 	}
 
-	switch (urb->status) {
-
+	switch (status) {
 		case 0: /* Successful */
 
 			/*
@@ -444,22 +451,12 @@
 				urb->actual_length,
 				data);
 
-			/*
-			 * Bypass flip-buffers, and feed the ldisc directly
-			 * due to our potentially large buffer size.  Since we
-			 * used to set low_latency, this is exactly what the
-			 * tty layer did anyway :)
-			 */
 			tty = port->tty;
 
-			/*
-			 *	FIXME: must not do this in IRQ context
-			 */
-			tty->ldisc.receive_buf(
-				tty,
-				data+1,
-				NULL,
-				urb->actual_length-1);
+			if (tty_buffer_request_room(tty, urb->actual_length - 1)) {
+				tty_insert_flip_string(tty, data+1, urb->actual_length - 1);
+				tty_flip_buffer_push(tty);
+			}
 
 			/*
 			 * No break here.
@@ -490,7 +487,7 @@
 		default:
 			dbg("%s - nonzero read bulk status received: %d",
 				__FUNCTION__, 
-				urb->status);
+				status);
 			break ;
 
 	}
@@ -501,8 +498,9 @@
 static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios)
 {
 	unsigned char *transfer_buffer;
-	unsigned int cflag;
 	int result;
+	speed_t baud;
+	int ir_baud;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -511,77 +509,59 @@
 		return;
 	}
 
-	cflag = port->tty->termios->c_cflag;
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("%s - nothing to change...", __FUNCTION__);
-			return;
-		}
+	baud = tty_get_baud_rate(port->tty);
+
+	/*
+	 * FIXME, we should compare the baud request against the
+	 * capability stated in the IR header that we got in the
+	 * startup function.
+	 */
+
+	switch (baud) {
+		case 2400:	ir_baud = SPEED_2400; break;
+		case 9600:	ir_baud = SPEED_9600; break;
+		case 19200:	ir_baud = SPEED_19200; break;
+		case 38400:	ir_baud = SPEED_38400; break;
+		case 57600:	ir_baud = SPEED_57600; break;
+		case 115200:	ir_baud = SPEED_115200; break;
+		case 576000:	ir_baud = SPEED_576000; break;
+		case 1152000:	ir_baud = SPEED_1152000; break;
+		case 4000000:	ir_baud = SPEED_4000000; break;
+			break;
+		default:
+			ir_baud = SPEED_9600;
+			baud = 9600;
+			/* And once the new tty stuff is all done we need to
+			   call back to correct the baud bits */
 	}
 
-	/* All we can change is the baud rate */
-	if (cflag & CBAUD) {
+	if (xbof == -1)
+		ir_xbof = ir_xbof_change(ir_add_bof);
+	else
+		ir_xbof = ir_xbof_change(xbof) ;
 
-		dbg ("%s - asking for baud %d",
-			__FUNCTION__,
-			tty_get_baud_rate(port->tty));
+	/* FIXME need to check to see if our write urb is busy right
+	 * now, or use a urb pool.
+	 *
+	 * send the baud change out on an "empty" data packet
+	 */
+	transfer_buffer = port->write_urb->transfer_buffer;
+	*transfer_buffer = ir_xbof | ir_baud;
 
-		/* 
-		 * FIXME, we should compare the baud request against the
-		 * capability stated in the IR header that we got in the
-		 * startup function.
-		 */
-		switch (cflag & CBAUD) {
-			case B2400:    ir_baud = SPEED_2400;    break;
-			default:
-			case B9600:    ir_baud = SPEED_9600;    break;
-			case B19200:   ir_baud = SPEED_19200;   break;
-			case B38400:   ir_baud = SPEED_38400;   break;
-			case B57600:   ir_baud = SPEED_57600;   break;
-			case B115200:  ir_baud = SPEED_115200;  break;
-			case B576000:  ir_baud = SPEED_576000;  break;
-			case B1152000: ir_baud = SPEED_1152000; break;
-#ifdef B4000000
-			case B4000000: ir_baud = SPEED_4000000; break;
-#endif
-		}
+	usb_fill_bulk_urb (
+		port->write_urb,
+		port->serial->dev,
+		usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
+		port->write_urb->transfer_buffer,
+		1,
+		ir_write_bulk_callback,
+		port);
 
-		if (xbof == -1) {
-			ir_xbof = ir_xbof_change(ir_add_bof);
-		} else {
-			ir_xbof = ir_xbof_change(xbof) ;
-		}
+	port->write_urb->transfer_flags = URB_ZERO_PACKET;
 
-		/* Notify the tty driver that the termios have changed. */
-		port->tty->ldisc.set_termios(port->tty, NULL);
-
-		/* FIXME need to check to see if our write urb is busy right
-		 * now, or use a urb pool.
-		 *
-		 * send the baud change out on an "empty" data packet
-		 */
-		transfer_buffer = port->write_urb->transfer_buffer;
-		*transfer_buffer = ir_xbof | ir_baud;
-
-		usb_fill_bulk_urb (
-			port->write_urb,
-			port->serial->dev,
-			usb_sndbulkpipe(port->serial->dev,
-				port->bulk_out_endpointAddress),
-			port->write_urb->transfer_buffer,
-			1,
-			ir_write_bulk_callback,
-			port);
-
-		port->write_urb->transfer_flags = URB_ZERO_PACKET;
-
-		result = usb_submit_urb (port->write_urb, GFP_KERNEL);
-		if (result)
-			dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
-	}
-	return;
+	result = usb_submit_urb (port->write_urb, GFP_KERNEL);
+	if (result)
+		dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
 }
 
 
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index e6966f1..f2a6fce 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -115,12 +115,13 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.1.4"
+#define DRIVER_VERSION "v1.1.5"
 #define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu"
 #define DRIVER_DESC "Keyspan USB to Serial Converter Driver"
 
 #define INSTAT_BUFLEN	32
 #define GLOCONT_BUFLEN	64
+#define INDAT49W_BUFLEN	512
 
 	/* Per device and per port private data */
 struct keyspan_serial_private {
@@ -129,9 +130,15 @@
 	struct urb	*instat_urb;
 	char		instat_buf[INSTAT_BUFLEN];
 
+	/* added to support 49wg, where data from all 4 ports comes in on 1 EP */
+	/* and high-speed supported */
+	struct urb	*indat_urb;
+	char		indat_buf[INDAT49W_BUFLEN];
+
 	/* XXX this one probably will need a lock */
 	struct urb	*glocont_urb;
 	char		glocont_buf[GLOCONT_BUFLEN];
+	char		ctrl_buf[8];			// for EP0 control message
 };
 
 struct keyspan_port_private {
@@ -179,12 +186,13 @@
 
 	
 /* Include Keyspan message headers.  All current Keyspan Adapters
-   make use of one of four message formats which are referred
-   to as USA-26, USA-28 and USA-49, USA-90 by Keyspan and within this driver. */
+   make use of one of five message formats which are referred
+   to as USA-26, USA-28, USA-49, USA-90, USA-67 by Keyspan and within this driver. */
 #include "keyspan_usa26msg.h"
 #include "keyspan_usa28msg.h"
 #include "keyspan_usa49msg.h"
 #include "keyspan_usa90msg.h"
+#include "keyspan_usa67msg.h"
 	
 
 /* Functions used by new usb-serial code. */
@@ -419,14 +427,15 @@
 	struct usb_serial_port	*port;
 	struct tty_struct	*tty;
 	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
 
 	dbg ("%s", __FUNCTION__); 
 
 	endpoint = usb_pipeendpoint(urb->pipe);
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s - nonzero status: %x on endpoint %d.",
-		    __FUNCTION__, urb->status, endpoint);
+		    __FUNCTION__, status, endpoint);
 		return;
 	}
 
@@ -511,11 +520,12 @@
 	struct usb_serial_port			*port;
 	struct keyspan_port_private	 	*p_priv;
 	int old_dcd_state, err;
+	int status = urb->status;
 
 	serial = (struct usb_serial *) urb->context;
 
-	if (urb->status) {
-		dbg("%s - nonzero status: %x", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, status);
 		return;
 	}
 	if (urb->actual_length != 9) {
@@ -579,6 +589,7 @@
 	struct tty_struct       *tty;
 	unsigned char           *data;
 	struct keyspan_port_private             *p_priv;
+	int status = urb->status;
 
 	dbg ("%s", __FUNCTION__);
 
@@ -590,9 +601,9 @@
 		return;
 
 	do {
-		if (urb->status) {
+		if (status) {
 			dbg("%s - nonzero status: %x on endpoint %d.",
-			    __FUNCTION__, urb->status, usb_pipeendpoint(urb->pipe));
+			    __FUNCTION__, status, usb_pipeendpoint(urb->pipe));
 			return;
 		}
 
@@ -648,11 +659,12 @@
 	struct usb_serial_port			*port;
 	struct keyspan_port_private	 	*p_priv;
 	int old_dcd_state;
+	int status = urb->status;
 
 	serial = (struct usb_serial *) urb->context;
 
-	if (urb->status) {
-		dbg("%s - nonzero status: %x", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, status);
 		return;
 	}
 
@@ -739,13 +751,14 @@
 	struct usb_serial_port			*port;
 	struct keyspan_port_private	 	*p_priv;
 	int old_dcd_state;
+	int status = urb->status;
 
 	dbg ("%s", __FUNCTION__);
 
 	serial = (struct usb_serial *) urb->context;
 
-	if (urb->status) {
-		dbg("%s - nonzero status: %x", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, status);
 		return;
 	}
 
@@ -805,14 +818,15 @@
 	struct usb_serial_port	*port;
 	struct tty_struct	*tty;
 	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
 
 	dbg ("%s", __FUNCTION__);
 
 	endpoint = usb_pipeendpoint(urb->pipe);
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s - nonzero status: %x on endpoint %d.", __FUNCTION__,
-		    urb->status, endpoint);
+		    status, endpoint);
 		return;
 	}
 
@@ -850,13 +864,90 @@
 		}
 }
 
+static void usa49wg_indat_callback(struct urb *urb)
+{
+	int			i, len, x, err;
+	struct usb_serial	*serial;
+	struct usb_serial_port	*port;
+	struct tty_struct	*tty;
+	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
+
+	dbg ("%s", __FUNCTION__);
+
+	serial = urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, status);
+		return;
+	}
+
+	/* inbound data is in the form P#, len, status, data */
+	i = 0;
+	len = 0;
+
+	if (urb->actual_length) {
+		while (i < urb->actual_length) {
+
+			/* Check port number from message*/
+			if (data[i] >= serial->num_ports) {
+				dbg ("%s - Unexpected port number %d",
+					__FUNCTION__, data[i]);
+				return;
+			}
+			port = serial->port[data[i++]];
+			tty = port->tty;
+			len = data[i++];
+
+			/* 0x80 bit is error flag */
+			if ((data[i] & 0x80) == 0) {
+				/* no error on any byte */
+				i++;
+				for (x = 1; x < len ; ++x)
+					if (port->open_count)
+						tty_insert_flip_char(tty,
+								data[i++], 0);
+					else
+						i++;
+			} else {
+				/*
+				 * some bytes had errors, every byte has status
+				 */
+				for (x = 0; x + 1 < len; x += 2) {
+					int stat = data[i], flag = 0;
+					if (stat & RXERROR_OVERRUN)
+						flag |= TTY_OVERRUN;
+					if (stat & RXERROR_FRAMING)
+						flag |= TTY_FRAME;
+					if (stat & RXERROR_PARITY)
+						flag |= TTY_PARITY;
+					/* XXX should handle break (0x10) */
+					if (port->open_count)
+						tty_insert_flip_char(tty,
+							data[i+1], flag);
+					i += 2;
+				}
+			}
+			if (port->open_count)
+				tty_flip_buffer_push(tty);
+		}
+	}
+
+	/* Resubmit urb so we continue receiving */
+	urb->dev = serial->dev;
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+}
+
 /* not used, usa-49 doesn't have per-port control endpoints */
-static void	usa49_outcont_callback(struct urb *urb)
+static void usa49_outcont_callback(struct urb *urb)
 {
 	dbg ("%s", __FUNCTION__);
 }
 
-static void	usa90_indat_callback(struct urb *urb)
+static void usa90_indat_callback(struct urb *urb)
 {
 	int			i, err;
 	int			endpoint;
@@ -864,15 +955,15 @@
 	struct keyspan_port_private	 	*p_priv;
 	struct tty_struct	*tty;
 	unsigned char 		*data = urb->transfer_buffer;
+	int status = urb->status;
 
 	dbg ("%s", __FUNCTION__); 
 
 	endpoint = usb_pipeendpoint(urb->pipe);
 
-
-	if (urb->status) {
+	if (status) {
 		dbg("%s - nonzero status: %x on endpoint %d.",
-		    __FUNCTION__, urb->status, endpoint);
+		    __FUNCTION__, status, endpoint);
 		return;
 	}
 
@@ -938,11 +1029,12 @@
 	struct usb_serial_port			*port;
 	struct keyspan_port_private	 	*p_priv;
 	int old_dcd_state, err;
+	int status = urb->status;
 
 	serial = (struct usb_serial *) urb->context;
 
-	if (urb->status) {
-		dbg("%s - nonzero status: %x", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, status);
 		return;
 	}
 	if (urb->actual_length < 14) {
@@ -995,6 +1087,88 @@
 	}
 }
 
+/* Status messages from the 28xg */
+static void	usa67_instat_callback(struct urb *urb)
+{
+	int					err;
+	unsigned char 				*data = urb->transfer_buffer;
+	struct keyspan_usa67_portStatusMessage	*msg;
+	struct usb_serial			*serial;
+	struct usb_serial_port			*port;
+	struct keyspan_port_private	 	*p_priv;
+	int old_dcd_state;
+	int status = urb->status;
+
+	dbg ("%s", __FUNCTION__);
+
+	serial = urb->context;
+
+	if (status) {
+		dbg("%s - nonzero status: %x", __FUNCTION__, status);
+		return;
+	}
+
+	if (urb->actual_length != sizeof(struct keyspan_usa67_portStatusMessage)) {
+		dbg("%s - bad length %d", __FUNCTION__, urb->actual_length);
+		return;
+	}
+
+
+	/* Now do something useful with the data */
+	msg = (struct keyspan_usa67_portStatusMessage *)data;
+
+	/* Check port number from message and retrieve private data */
+	if (msg->port >= serial->num_ports) {
+		dbg ("%s - Unexpected port number %d", __FUNCTION__, msg->port);
+		return;
+	}
+
+	port = serial->port[msg->port];
+	p_priv = usb_get_serial_port_data(port);
+
+	/* Update handshaking pin state information */
+	old_dcd_state = p_priv->dcd_state;
+	p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0);
+	p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);
+
+	if (port->tty && !C_CLOCAL(port->tty)
+	    && old_dcd_state != p_priv->dcd_state) {
+		if (old_dcd_state)
+			tty_hangup(port->tty);
+		/*  else */
+		/*	wake_up_interruptible(&p_priv->open_wait); */
+	}
+
+	/* Resubmit urb so we continue receiving */
+	urb->dev = serial->dev;
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err);
+}
+
+static void usa67_glocont_callback(struct urb *urb)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	struct keyspan_port_private *p_priv;
+	int i;
+
+	dbg ("%s", __FUNCTION__);
+
+	serial = urb->context;
+	for (i = 0; i < serial->num_ports; ++i) {
+		port = serial->port[i];
+		p_priv = usb_get_serial_port_data(port);
+
+		if (p_priv->resend_cont) {
+			dbg ("%s - sending setup", __FUNCTION__);
+			keyspan_usa67_send_setup(serial, port,
+						p_priv->resend_cont - 1);
+			break;
+		}
+	}
+}
+
 static int keyspan_write_room (struct usb_serial_port *port)
 {
 	struct keyspan_port_private	*p_priv;
@@ -1311,6 +1485,11 @@
 		return NULL;
 	}
 
+	if (endpoint == 0) {
+		/* control EP filled in when used */
+		return urb;
+	}
+
 	ep_desc = find_ep(serial, endpoint);
 	if (!ep_desc) {
 		/* leak the urb, something's wrong and the callers don't care */
@@ -1380,6 +1559,14 @@
 		.outdat_callback =	usa2x_outdat_callback,
 		.inack_callback =	usa28_inack_callback,
 		.outcont_callback =	usa90_outcont_callback,
+	}, {
+		/* msg_usa67 callbacks */
+		.instat_callback =	usa67_instat_callback,
+		.glocont_callback =	usa67_glocont_callback,
+		.indat_callback =	usa26_indat_callback,
+		.outdat_callback =	usa2x_outdat_callback,
+		.inack_callback =	usa26_inack_callback,
+		.outcont_callback =	usa26_outcont_callback,
 	}
 };
 
@@ -1410,6 +1597,11 @@
 		 serial, s_priv->instat_buf, INSTAT_BUFLEN,
 		 cback->instat_callback);
 
+	s_priv->indat_urb = keyspan_setup_urb
+		(serial, d_details->indat_endpoint, USB_DIR_IN,
+		 serial, s_priv->indat_buf, INDAT49W_BUFLEN,
+		 usa49wg_indat_callback);
+
 	s_priv->glocont_urb = keyspan_setup_urb
 		(serial, d_details->glocont_endpoint, USB_DIR_OUT,
 		 serial, s_priv->glocont_buf, GLOCONT_BUFLEN,
@@ -1685,8 +1877,8 @@
 	}
 
 	/* Save reset port val for resend.
-	Don't overwrite resend for close condition. */
-	if (p_priv->resend_cont != 3)
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
 		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		/*  dbg ("%s - already writing", __FUNCTION__); */
@@ -1836,8 +2028,8 @@
 	}
 
 	/* Save reset port val for resend.
-	   Don't overwrite resend for close condition. */
-	if (p_priv->resend_cont != 3)
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
 		p_priv->resend_cont = reset_port + 1;
 	if (this_urb->status == -EINPROGRESS) {
 		dbg ("%s already writing", __FUNCTION__);
@@ -1940,11 +2132,11 @@
 				    struct usb_serial_port *port,
 				    int reset_port)
 {
-	struct keyspan_usa49_portControlMessage	msg;		
+	struct keyspan_usa49_portControlMessage	msg;
+	struct usb_ctrlrequest 			*dr = NULL;
 	struct keyspan_serial_private 		*s_priv;
 	struct keyspan_port_private 		*p_priv;
 	const struct keyspan_device_details	*d_details;
-	int 					glocont_urb;
 	struct urb				*this_urb;
 	int 					err, device_port;
 
@@ -1954,10 +2146,9 @@
 	p_priv = usb_get_serial_port_data(port);
 	d_details = s_priv->device_details;
 
-	glocont_urb = d_details->glocont_endpoint;
 	this_urb = s_priv->glocont_urb;
 
-		/* Work out which port within the device is being setup */
+	/* Work out which port within the device is being setup */
 	device_port = port->number - port->serial->minor;
 
 	dbg("%s - endpoint %d port %d (%d)",__FUNCTION__, usb_pipeendpoint(this_urb->pipe), port->number, device_port);
@@ -1969,9 +2160,10 @@
 	}
 
 	/* Save reset port val for resend.
-	   Don't overwrite resend for close condition. */
-	if (p_priv->resend_cont != 3)
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
 		p_priv->resend_cont = reset_port + 1;
+
 	if (this_urb->status == -EINPROGRESS) {
 		/*  dbg ("%s - already writing", __FUNCTION__); */
 		mdelay(5);
@@ -2083,20 +2275,39 @@
 	msg.dtr = p_priv->dtr_state;
 		
 	p_priv->resend_cont = 0;
-	memcpy (this_urb->transfer_buffer, &msg, sizeof(msg));
-	
-	/* send the data out the device on control endpoint */
-	this_urb->transfer_buffer_length = sizeof(msg);
 
-	this_urb->dev = serial->dev;
+	/* if the device is a 49wg, we send control message on usb control EP 0 */
+
+	if (d_details->product_id == keyspan_usa49wg_product_id) {
+		dr = (void *)(s_priv->ctrl_buf);
+		dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_OUT;
+		dr->bRequest = 0xB0;	/* 49wg control message */;
+		dr->wValue = 0;
+		dr->wIndex = 0;
+		dr->wLength = cpu_to_le16(sizeof(msg));
+
+		memcpy (s_priv->glocont_buf, &msg, sizeof(msg));
+
+		usb_fill_control_urb(this_urb, serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     (unsigned char *)dr, s_priv->glocont_buf, sizeof(msg),
+			     usa49_glocont_callback, serial);
+
+	} else {
+		memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+	
+		/* send the data out the device on control endpoint */
+		this_urb->transfer_buffer_length = sizeof(msg);
+
+		this_urb->dev = serial->dev;
+	}
 	if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) {
 		dbg("%s - usb_submit_urb(setup) failed (%d)", __FUNCTION__, err);
 	}
 #if 0
 	else {
 		dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __FUNCTION__,
-		    outcont_urb, this_urb->transfer_buffer_length,
-		    usb_pipeendpoint(this_urb->pipe));
+			   outcont_urb, this_urb->transfer_buffer_length,
+			   usb_pipeendpoint(this_urb->pipe));
 	}
 #endif
 
@@ -2241,6 +2452,154 @@
 	return (0);
 }
 
+static int keyspan_usa67_send_setup(struct usb_serial *serial,
+				    struct usb_serial_port *port,
+				    int reset_port)
+{
+	struct keyspan_usa67_portControlMessage	msg;
+	struct keyspan_serial_private 		*s_priv;
+	struct keyspan_port_private 		*p_priv;
+	const struct keyspan_device_details	*d_details;
+	struct urb				*this_urb;
+	int 					err, device_port;
+
+	dbg ("%s", __FUNCTION__);
+
+	s_priv = usb_get_serial_data(serial);
+	p_priv = usb_get_serial_port_data(port);
+	d_details = s_priv->device_details;
+
+	this_urb = s_priv->glocont_urb;
+
+	/* Work out which port within the device is being setup */
+	device_port = port->number - port->serial->minor;
+
+	/* Make sure we have an urb then send the message */
+	if (this_urb == NULL) {
+		dbg("%s - oops no urb for port %d.", __FUNCTION__,
+			port->number);
+		return -1;
+	}
+
+	/* Save reset port val for resend.
+	   Don't overwrite resend for open/close condition. */
+	if ((reset_port + 1) > p_priv->resend_cont)
+		p_priv->resend_cont = reset_port + 1;
+	if (this_urb->status == -EINPROGRESS) {
+		/*  dbg ("%s - already writing", __FUNCTION__); */
+		mdelay(5);
+		return(-1);
+	}
+
+	memset(&msg, 0, sizeof(struct keyspan_usa67_portControlMessage));
+
+	msg.port = device_port;
+
+	/* Only set baud rate if it's changed */
+	if (p_priv->old_baud != p_priv->baud) {
+		p_priv->old_baud = p_priv->baud;
+		msg.setClocking = 0xff;
+		if (d_details->calculate_baud_rate
+		    (p_priv->baud, d_details->baudclk, &msg.baudHi,
+		     &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE ) {
+			dbg("%s - Invalid baud rate %d requested, using 9600.", __FUNCTION__,
+			    p_priv->baud);
+			msg.baudLo = 0;
+			msg.baudHi = 125;	/* Values for 9600 baud */
+			msg.prescaler = 10;
+		}
+		msg.setPrescaler = 0xff;
+	}
+
+	msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1;
+	switch (p_priv->cflag & CSIZE) {
+	case CS5:
+		msg.lcr |= USA_DATABITS_5;
+		break;
+	case CS6:
+		msg.lcr |= USA_DATABITS_6;
+		break;
+	case CS7:
+		msg.lcr |= USA_DATABITS_7;
+		break;
+	case CS8:
+		msg.lcr |= USA_DATABITS_8;
+		break;
+	}
+	if (p_priv->cflag & PARENB) {
+		/* note USA_PARITY_NONE == 0 */
+		msg.lcr |= (p_priv->cflag & PARODD)?
+			USA_PARITY_ODD: USA_PARITY_EVEN;
+	}
+	msg.setLcr = 0xff;
+
+	msg.ctsFlowControl = (p_priv->flow_control == flow_cts);
+	msg.xonFlowControl = 0;
+	msg.setFlowControl = 0xff;
+	msg.forwardingLength = 16;
+	msg.xonChar = 17;
+	msg.xoffChar = 19;
+
+	if (reset_port == 1) {
+		/* Opening port */
+		msg._txOn = 1;
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 1;
+		msg.rxOff = 0;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0xff;
+	} else if (reset_port == 2) {
+		/* Closing port */
+		msg._txOn = 0;
+		msg._txOff = 1;
+		msg.txFlush = 0;
+		msg.txBreak = 0;
+		msg.rxOn = 0;
+		msg.rxOff = 1;
+		msg.rxFlush = 1;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0;
+	} else {
+		/* Sending intermediate configs */
+		msg._txOn = (! p_priv->break_on);
+		msg._txOff = 0;
+		msg.txFlush = 0;
+		msg.txBreak = (p_priv->break_on);
+		msg.rxOn = 0;
+		msg.rxOff = 0;
+		msg.rxFlush = 0;
+		msg.rxForward = 0;
+		msg.returnStatus = 0;
+		msg.resetDataToggle = 0x0;
+	}
+
+	/* Do handshaking outputs */
+	msg.setTxTriState_setRts = 0xff;
+	msg.txTriState_rts = p_priv->rts_state;
+
+	msg.setHskoa_setDtr = 0xff;
+	msg.hskoa_dtr = p_priv->dtr_state;
+
+	p_priv->resend_cont = 0;
+
+	memcpy(this_urb->transfer_buffer, &msg, sizeof(msg));
+
+	/* send the data out the device on control endpoint */
+	this_urb->transfer_buffer_length = sizeof(msg);
+	this_urb->dev = serial->dev;
+
+	err = usb_submit_urb(this_urb, GFP_ATOMIC);
+	if (err != 0)
+		dbg("%s - usb_submit_urb(setup) failed (%d)", __FUNCTION__,
+				err);
+	return (0);
+}
+
 static void keyspan_send_setup(struct usb_serial_port *port, int reset_port)
 {
 	struct usb_serial *serial = port->serial;
@@ -2265,6 +2624,9 @@
 	case msg_usa90:
 		keyspan_usa90_send_setup(serial, port, reset_port);
 		break;
+	case msg_usa67:
+		keyspan_usa67_send_setup(serial, port, reset_port);
+		break;
 	}
 }
 
@@ -2313,9 +2675,19 @@
 
 	keyspan_setup_urbs(serial);
 
-	s_priv->instat_urb->dev = serial->dev;
-	if ((err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL)) != 0) {
-		dbg("%s - submit instat urb failed %d", __FUNCTION__, err);
+	if (s_priv->instat_urb != NULL) {
+		s_priv->instat_urb->dev = serial->dev;
+		err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL);
+		if (err != 0)
+			dbg("%s - submit instat urb failed %d", __FUNCTION__,
+				err);
+	}
+	if (s_priv->indat_urb != NULL) {
+		s_priv->indat_urb->dev = serial->dev;
+		err = usb_submit_urb(s_priv->indat_urb, GFP_KERNEL);
+		if (err != 0)
+			dbg("%s - submit indat urb failed %d", __FUNCTION__,
+				err);
 	}
 			
 	return (0);
@@ -2335,6 +2707,7 @@
 	/* Stop reading/writing urbs */
 	stop_urb(s_priv->instat_urb);
 	stop_urb(s_priv->glocont_urb);
+	stop_urb(s_priv->indat_urb);
 	for (i = 0; i < serial->num_ports; ++i) {
 		port = serial->port[i];
 		p_priv = usb_get_serial_port_data(port);
@@ -2348,6 +2721,7 @@
 
 	/* Now free them */
 	usb_free_urb(s_priv->instat_urb);
+	usb_free_urb(s_priv->indat_urb);
 	usb_free_urb(s_priv->glocont_urb);
 	for (i = 0; i < serial->num_ports; ++i) {
 		port = serial->port[i];
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h
index c6830cb..8a0d174 100644
--- a/drivers/usb/serial/keyspan.h
+++ b/drivers/usb/serial/keyspan.h
@@ -99,6 +99,10 @@
 					 struct usb_serial_port *port,
 					 int reset_port);
 
+static int  keyspan_usa67_send_setup	(struct usb_serial *serial,
+					 struct usb_serial_port *port,
+					 int reset_port);
+
 /* Struct used for firmware - increased size of data section
    to allow Keyspan's 'C' firmware struct to be used unmodified */
 struct ezusb_hex_record {
@@ -229,15 +233,17 @@
 #define	keyspan_usa28_product_id		0x010f
 #define	keyspan_usa28x_product_id		0x0110
 #define	keyspan_usa28xa_product_id		0x0115
+#define	keyspan_usa28xb_product_id		0x0110
+#define	keyspan_usa28xg_product_id		0x0135
 #define	keyspan_usa49w_product_id		0x010a
 #define	keyspan_usa49wlc_product_id		0x012a
-
+#define	keyspan_usa49wg_product_id		0x0131
 
 struct keyspan_device_details {
 	/* product ID value */
 	int	product_id;
 
-	enum	{msg_usa26, msg_usa28, msg_usa49, msg_usa90} msg_format;
+	enum	{msg_usa26, msg_usa28, msg_usa49, msg_usa90, msg_usa67} msg_format;
 
 		/* Number of physical ports */
 	int	num_ports;
@@ -264,6 +270,9 @@
 		/* Endpoint used for input status */
 	int	instat_endpoint;
 
+		/* Endpoint used for input data 49WG only */
+	int	indat_endpoint;
+
 		/* Endpoint used for global control functions */
 	int	glocont_endpoint;
 
@@ -287,6 +296,7 @@
 	.inack_endpoints	= {0x85},
 	.outcont_endpoints	= {0x05},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA18X_BAUDCLK,
@@ -303,6 +313,7 @@
 	.inack_endpoints	= {0x83},
 	.outcont_endpoints	= {0x03},
 	.instat_endpoint	= 0x84,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= -1,
 	.calculate_baud_rate	= keyspan_usa19_calc_baud,
 	.baudclk		= KEYSPAN_USA19_BAUDCLK,
@@ -319,6 +330,7 @@
 	.inack_endpoints	= {0x83},
 	.outcont_endpoints	= {0x03},
 	.instat_endpoint	= 0x84,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= -1,
 	.calculate_baud_rate	= keyspan_usa28_calc_baud,
 	.baudclk		= KEYSPAN_USA19_BAUDCLK,
@@ -335,6 +347,7 @@
 	.inack_endpoints	= {0x83},
 	.outcont_endpoints	= {0x03},
 	.instat_endpoint	= 0x84,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= -1,
 	.calculate_baud_rate	= keyspan_usa28_calc_baud,
 	.baudclk		= KEYSPAN_USA19_BAUDCLK,
@@ -351,6 +364,7 @@
 	.inack_endpoints	= {0x85},
 	.outcont_endpoints	= {0x05},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
@@ -367,6 +381,7 @@
 	.inack_endpoints	= {0x85},
 	.outcont_endpoints	= {0x05},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
@@ -383,6 +398,7 @@
 	.inack_endpoints	= {-1},
 	.outcont_endpoints	= {0x02},
 	.instat_endpoint	= 0x82,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= -1,
 	.calculate_baud_rate	= keyspan_usa19hs_calc_baud,
 	.baudclk		= KEYSPAN_USA19HS_BAUDCLK,
@@ -399,6 +415,7 @@
 	.inack_endpoints	= {0x85, 0x86},
 	.outcont_endpoints	= {0x05, 0x06},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa28_calc_baud,
 	.baudclk		= KEYSPAN_USA28_BAUDCLK,		
@@ -415,6 +432,7 @@
 	.inack_endpoints	= {0x85, 0x86},
 	.outcont_endpoints	= {0x05, 0x06},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA28X_BAUDCLK,
@@ -431,11 +449,28 @@
 	.inack_endpoints	= {0x85, 0x86},
 	.outcont_endpoints	= {0x05, 0x06},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA28X_BAUDCLK,
 };
 
+static const struct keyspan_device_details usa28xg_device_details = {
+	.product_id		= keyspan_usa28xg_product_id,
+	.msg_format		= msg_usa67,
+	.num_ports		= 2,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {0x84, 0x88},
+	.outdat_endpoints	= {0x02, 0x06},
+	.inack_endpoints	= {-1, -1},
+	.outcont_endpoints	= {-1, -1},
+	.instat_endpoint	= 0x81,
+	.indat_endpoint		= -1,
+	.glocont_endpoint	= 0x01,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA28X_BAUDCLK,
+};
 /* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */
 
 static const struct keyspan_device_details usa49w_device_details = {
@@ -449,6 +484,7 @@
 	.inack_endpoints	= {-1, -1, -1, -1},
 	.outcont_endpoints	= {-1, -1, -1, -1},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA49W_BAUDCLK,
@@ -465,11 +501,29 @@
 	.inack_endpoints	= {-1, -1, -1, -1},
 	.outcont_endpoints	= {-1, -1, -1, -1},
 	.instat_endpoint	= 0x87,
+	.indat_endpoint		= -1,
 	.glocont_endpoint	= 0x07,
 	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
 	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
 };
 
+static const struct keyspan_device_details usa49wg_device_details = {
+	.product_id		= keyspan_usa49wg_product_id,
+	.msg_format		= msg_usa49,
+	.num_ports		= 4,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {-1, -1, -1, -1},		/* single 'global' data in EP */
+	.outdat_endpoints	= {0x01, 0x02, 0x04, 0x06},
+	.inack_endpoints	= {-1, -1, -1, -1},
+	.outcont_endpoints	= {-1, -1, -1, -1},
+	.instat_endpoint	= 0x81,
+	.indat_endpoint		= 0x88,
+	.glocont_endpoint	= 0x00,				/* uses control EP */
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
+};
+
 static const struct keyspan_device_details *keyspan_devices[] = {
 	&usa18x_device_details,
 	&usa19_device_details,
@@ -481,9 +535,11 @@
 	&usa28_device_details,
 	&usa28x_device_details,
 	&usa28xa_device_details,
+	&usa28xg_device_details,
 	/* 28xb not required as it renumerates as a 28x */
 	&usa49w_device_details,
 	&usa49wlc_device_details,
+	&usa49wg_device_details,
 	NULL,
 };
 
@@ -510,8 +566,11 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)},
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
 	{ } /* Terminating entry */
 };
 
@@ -557,12 +616,15 @@
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) },
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) },
 	{ } /* Terminating entry */
 };
 
 static struct usb_device_id keyspan_4port_ids[] = {
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) },
 	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)},
+	{ USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)},
 	{ } /* Terminating entry */
 };
 
@@ -573,7 +635,6 @@
 		.name		= "keyspan_no_firm",
 	},
 	.description		= "Keyspan - (without firmware)",
-	.usb_driver		= &keyspan_driver,
 	.id_table		= keyspan_pre_ids,
 	.num_interrupt_in	= NUM_DONT_CARE,
 	.num_bulk_in		= NUM_DONT_CARE,
@@ -588,7 +649,6 @@
 		.name		= "keyspan_1",
 	},
 	.description		= "Keyspan 1 port adapter",
-	.usb_driver		= &keyspan_driver,
 	.id_table		= keyspan_1port_ids,
 	.num_interrupt_in	= NUM_DONT_CARE,
 	.num_bulk_in		= NUM_DONT_CARE,
@@ -616,7 +676,6 @@
 		.name		= "keyspan_2",
 	},
 	.description		= "Keyspan 2 port adapter",
-	.usb_driver		= &keyspan_driver,
 	.id_table		= keyspan_2port_ids,
 	.num_interrupt_in	= NUM_DONT_CARE,
 	.num_bulk_in		= NUM_DONT_CARE,
@@ -644,11 +703,10 @@
 		.name		= "keyspan_4",
 	},
 	.description		= "Keyspan 4 port adapter",
-	.usb_driver		= &keyspan_driver,
 	.id_table		= keyspan_4port_ids,
 	.num_interrupt_in	= NUM_DONT_CARE,
-	.num_bulk_in		= 5,
-	.num_bulk_out		= 5,
+	.num_bulk_in		= NUM_DONT_CARE,
+	.num_bulk_out		= NUM_DONT_CARE,
 	.num_ports		= 4,
 	.open			= keyspan_open,
 	.close			= keyspan_close,
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index dd0b66a..be9ac20 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -218,11 +218,12 @@
        	struct tty_struct *tty = port->tty;
 	unsigned char *data = urb->transfer_buffer;
 	int i;
-	int status;
+	int retval;
+	int status = urb->status;
 	struct keyspan_pda_private *priv;
 	priv = usb_get_serial_port_data(port);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -230,10 +231,12 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
-		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, status);
 		goto exit;
 	}
 
@@ -268,10 +271,10 @@
 	}
 
 exit:
-	status = usb_submit_urb (urb, GFP_ATOMIC);
-	if (status)
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
 		err ("%s - usb_submit_urb failed with result %d",
-		     __FUNCTION__, status);
+		     __FUNCTION__, retval);
 }
 
 
diff --git a/drivers/usb/serial/keyspan_usa67msg.h b/drivers/usb/serial/keyspan_usa67msg.h
new file mode 100644
index 0000000..20fa3e2
--- /dev/null
+++ b/drivers/usb/serial/keyspan_usa67msg.h
@@ -0,0 +1,254 @@
+/*
+	usa67msg.h
+
+	Copyright (c) 1998-2007 InnoSys Incorporated.  All Rights Reserved
+	This file is available under a BSD-style copyright
+
+	Keyspan USB Async Firmware to run on Anchor FX1
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions are
+	met:
+
+	1. Redistributions of source code must retain this licence text
+   	without modification, this list of conditions, and the following
+   	disclaimer.  The following copyright notice must appear immediately at
+   	the beginning of all source files:
+
+        	Copyright (c) 1998-2007 InnoSys Incorporated.  All Rights Reserved
+
+        	This file is available under a BSD-style copyright
+
+	2. Redistributions in binary form must reproduce the above copyright
+   	notice, this list of conditions and the following disclaimer in the
+   	documentation and/or other materials provided with the distribution.
+
+	3. The name of InnoSys Incorprated may not be used to endorse or promote
+   	products derived from this software without specific prior written
+   	permission.
+
+	THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+	NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+	INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+	SUCH DAMAGE.
+
+	Fourth revision: This message format supports the USA28XG
+
+	Buffer formats for RX/TX data messages are not defined by
+	a structure, but are described here:
+
+	USB OUT (host -> USAxx, transmit) messages contain a
+	REQUEST_ACK indicator (set to 0xff to request an ACK at the
+	completion of transmit; 0x00 otherwise), followed by data:
+
+		RQSTACK DAT DAT DAT ...
+
+	with a total data length of up to 63.
+
+	USB IN (USAxx -> host, receive) messages begin with a status
+	byte in which the 0x80 bit is either:
+
+		(a)	0x80 bit clear
+			indicates that the bytes following it are all data
+			bytes:
+
+				STAT DATA DATA DATA DATA DATA ...
+
+			for a total of up to 63 DATA bytes,
+
+	or:
+
+		(b)	0x80 bit set
+			indiates that the bytes following alternate data and
+			status bytes:
+
+				STAT DATA STAT DATA STAT DATA STAT DATA ...
+
+			for a total of up to 32 DATA bytes.
+
+	The valid bits in the STAT bytes are:
+
+		OVERRUN	0x02
+		PARITY	0x04
+		FRAMING	0x08
+		BREAK	0x10
+
+	Notes:
+
+	(1) The OVERRUN bit can appear in either (a) or (b) format
+		messages, but the but the PARITY/FRAMING/BREAK bits
+		only appear in (b) format messages.
+	(2) For the host to determine the exact point at which the
+		overrun occurred (to identify the point in the data
+		stream at which the data was lost), it needs to count
+		128 characters, starting at the first character of the
+		message in which OVERRUN was reported; the lost character(s)
+		would have been received between the 128th and 129th
+		characters.
+	(3)	An RX data message in which the first byte has 0x80 clear
+		serves as a "break off" indicator.
+
+	revision history:
+
+	1999feb10	add reportHskiaChanges to allow us to ignore them
+	1999feb10	add txAckThreshold for fast+loose throughput enhancement
+	1999mar30	beef up support for RX error reporting
+	1999apr14	add resetDataToggle to control message
+	2000jan04	merge with usa17msg.h
+	2000jun01	add extended BSD-style copyright text
+	2001jul05	change message format to improve OVERRUN case
+	2002jun05	update copyright date, improve comments
+	2006feb06	modify for FX1 chip
+
+*/
+
+#ifndef	__USA67MSG__
+#define	__USA67MSG__
+
+
+// all things called "ControlMessage" are sent on the 'control' endpoint
+
+typedef struct keyspan_usa67_portControlMessage
+{
+	u8	port;		// 0 or 1 (selects port)
+	/*
+		there are three types of "commands" sent in the control message:
+
+		1.	configuration changes which must be requested by setting
+			the corresponding "set" flag (and should only be requested
+			when necessary, to reduce overhead on the device):
+	*/
+	u8	setClocking,	// host requests baud rate be set
+		baudLo,			// host does baud divisor calculation
+		baudHi,			// baudHi is only used for first port (gives lower rates)
+		externalClock_txClocking,
+						// 0=internal, other=external
+
+		setLcr,			// host requests lcr be set
+		lcr,			// use PARITY, STOPBITS, DATABITS below
+
+		setFlowControl,	// host requests flow control be set
+		ctsFlowControl,	// 1=use CTS flow control, 0=don't
+		xonFlowControl,	// 1=use XON/XOFF flow control, 0=don't
+		xonChar,		// specified in current character format
+		xoffChar,		// specified in current character format
+
+		setTxTriState_setRts,
+						// host requests TX tri-state be set
+		txTriState_rts,	// 1=active (normal), 0=tristate (off)
+
+		setHskoa_setDtr,
+						// host requests HSKOA output be set
+		hskoa_dtr,		// 1=on, 0=off
+
+		setPrescaler,	// host requests prescalar be set (default: 13)
+		prescaler;		// specified as N/8; values 8-ff are valid
+						// must be set any time internal baud rate is set;
+						// must not be set when external clocking is used
+
+	/*
+		3.	configuration data which is simply used as is (no overhead,
+			but must be specified correctly in every host message).
+	*/
+	u8	forwardingLength,  // forward when this number of chars available
+		reportHskiaChanges_dsrFlowControl,
+						// 1=normal; 0=ignore external clock
+						// 1=use DSR flow control, 0=don't
+		txAckThreshold,	// 0=not allowed, 1=normal, 2-255 deliver ACK faster
+		loopbackMode;	// 0=no loopback, 1=loopback enabled
+
+	/*
+		4.	commands which are flags only; these are processed in order
+			(so that, e.g., if both _txOn and _txOff flags are set, the
+			port ends in a TX_OFF state); any non-zero value is respected
+	*/
+	u8	_txOn,			// enable transmitting (and continue if there's data)
+		_txOff,			// stop transmitting
+		txFlush,		// toss outbound data
+		txBreak,		// turn on break (cleared by _txOn)
+		rxOn,			// turn on receiver
+		rxOff,			// turn off receiver
+		rxFlush,		// toss inbound data
+		rxForward,		// forward all inbound data, NOW (as if fwdLen==1)
+		returnStatus,	// return current status (even if it hasn't changed)
+		resetDataToggle;// reset data toggle state to DATA0
+
+} keyspan_usa67_portControlMessage;
+
+// defines for bits in lcr
+#define	USA_DATABITS_5		0x00
+#define	USA_DATABITS_6		0x01
+#define	USA_DATABITS_7		0x02
+#define	USA_DATABITS_8		0x03
+#define	STOPBITS_5678_1		0x00	// 1 stop bit for all byte sizes
+#define	STOPBITS_5_1p5		0x04	// 1.5 stop bits for 5-bit byte
+#define	STOPBITS_678_2		0x04	// 2 stop bits for 6/7/8-bit byte
+#define	USA_PARITY_NONE		0x00
+#define	USA_PARITY_ODD		0x08
+#define	USA_PARITY_EVEN		0x18
+#define	PARITY_1			0x28
+#define	PARITY_0			0x38
+
+// all things called "StatusMessage" are sent on the status endpoint
+
+typedef struct keyspan_usa67_portStatusMessage	// one for each port
+{
+	u8	port,			// 0=first, 1=second, other=see below
+		hskia_cts,		// reports HSKIA pin
+		gpia_dcd,		// reports GPIA pin
+		_txOff,			// port has been disabled (by host)
+		_txXoff,		// port is in XOFF state (either host or RX XOFF)
+		txAck,			// indicates a TX message acknowledgement
+		rxEnabled,		// as configured by rxOn/rxOff 1=on, 0=off
+		controlResponse;// 1=a control message has been processed
+} keyspan_usa67_portStatusMessage;
+
+// bits in RX data message when STAT byte is included
+#define	RXERROR_OVERRUN	0x02
+#define	RXERROR_PARITY	0x04
+#define	RXERROR_FRAMING	0x08
+#define	RXERROR_BREAK	0x10
+
+typedef struct keyspan_usa67_globalControlMessage
+{
+	u8	port,	 			// 3
+		sendGlobalStatus,	// 2=request for two status responses
+		resetStatusToggle,	// 1=reset global status toggle
+		resetStatusCount;	// a cycling value
+} keyspan_usa67_globalControlMessage;
+
+typedef struct keyspan_usa67_globalStatusMessage
+{
+	u8	port,				// 3
+		sendGlobalStatus,	// from request, decremented
+		resetStatusCount;	// as in request
+} keyspan_usa67_globalStatusMessage;
+
+typedef struct keyspan_usa67_globalDebugMessage
+{
+	u8	port,				// 2
+		a,
+		b,
+		c,
+		d;
+} keyspan_usa67_globalDebugMessage;
+
+// ie: the maximum length of an FX1 endpoint buffer
+#define	MAX_DATA_LEN			64
+
+// update status approx. 60 times a second (16.6666 ms)
+#define	STATUS_UPDATE_INTERVAL	16
+
+// status rationing tuning value (each port gets checked each n ms)
+#define	STATUS_RATION	10
+
+#endif
+
+
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index 7b085f3..5a4127e 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -567,12 +567,13 @@
 static void klsi_105_write_bulk_callback ( struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
-	
-	if (urb->status) {
+
+	if (status) {
 		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		return;
 	}
 
@@ -631,16 +632,17 @@
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
 	int rc;
+	int status = urb->status;
 
-        dbg("%s - port %d", __FUNCTION__, port->number);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	/* The urb might have been killed. */
-        if (urb->status) {
-                dbg("%s - nonzero read bulk status received: %d", __FUNCTION__,
-		    urb->status);
-                return;
-        }
-	
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__,
+		    status);
+		return;
+	}
+
 	/* The data received is again preceded by a length double-byte in LSB-
 	 * first order (see klsi_105_write() )
 	 */
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 0683b51..02a86db 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -358,24 +358,26 @@
 }
 
 
-static void kobil_read_int_callback( struct urb *purb)
+static void kobil_read_int_callback(struct urb *urb)
 {
 	int result;
-	struct usb_serial_port *port = (struct usb_serial_port *) purb->context;
+	struct usb_serial_port *port = urb->context;
 	struct tty_struct *tty;
-	unsigned char *data = purb->transfer_buffer;
+	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
 //	char *dbg_data;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (purb->status) {
-		dbg("%s - port %d Read int status not zero: %d", __FUNCTION__, port->number, purb->status);
+	if (status) {
+		dbg("%s - port %d Read int status not zero: %d",
+		    __FUNCTION__, port->number, status);
 		return;
 	}
-	
-	tty = port->tty; 
-	if (purb->actual_length) {
-		
+
+	tty = port->tty;
+	if (urb->actual_length) {
+
 		// BEGIN DEBUG
 		/*
 		  dbg_data = kzalloc((3 *  purb->actual_length + 10) * sizeof(char), GFP_KERNEL);
@@ -390,15 +392,15 @@
 		*/
 		// END DEBUG
 
-		tty_buffer_request_room(tty, purb->actual_length);
-		tty_insert_flip_string(tty, data, purb->actual_length);
+		tty_buffer_request_room(tty, urb->actual_length);
+		tty_insert_flip_string(tty, data, urb->actual_length);
 		tty_flip_buffer_push(tty);
 	}
 
 	// someone sets the dev to 0 if the close method has been called
 	port->interrupt_in_urb->dev = port->serial->dev;
 
-	result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); 
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
 	dbg("%s - port %d Send read URB returns: %i", __FUNCTION__, port->number, result);
 }
 
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 3db1adc..2a3fabc 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -81,7 +81,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "z2.0"		/* Linux in-kernel version */
+#define DRIVER_VERSION "z2.1"		/* Linux in-kernel version */
 #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
 #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
 
@@ -110,6 +110,10 @@
 static int  mct_u232_tiocmset		 (struct usb_serial_port *port,
 					  struct file *file, unsigned int set,
 					  unsigned int clear);
+static void mct_u232_throttle		 (struct usb_serial_port *port);
+static void mct_u232_unthrottle		 (struct usb_serial_port *port);
+
+
 /*
  * All of the device info needed for the MCT USB-RS232 converter.
  */
@@ -145,6 +149,8 @@
 	.num_ports =	     1,
 	.open =		     mct_u232_open,
 	.close =	     mct_u232_close,
+	.throttle =	     mct_u232_throttle,
+	.unthrottle =	     mct_u232_unthrottle,
 	.read_int_callback = mct_u232_read_int_callback,
 	.ioctl =	     mct_u232_ioctl,
 	.set_termios =	     mct_u232_set_termios,
@@ -162,8 +168,11 @@
 	unsigned char        last_lcr;      /* Line Control Register */
 	unsigned char	     last_lsr;      /* Line Status Register */
 	unsigned char	     last_msr;      /* Modem Status Register */
+	unsigned int	     rx_flags;      /* Throttling flags */
 };
 
+#define THROTTLED		0x01
+
 /*
  * Handle vendor specific USB requests
  */
@@ -216,11 +225,13 @@
 	}
 }
 
-static int mct_u232_set_baud_rate(struct usb_serial *serial, int value)
+static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port,
+				  int value)
 {
 	__le32 divisor;
         int rc;
         unsigned char zero_byte = 0;
+        unsigned char cts_enable_byte = 0;
 
 	divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value));
 
@@ -238,10 +249,17 @@
 	   'baud rate change' message.  The actual functionality of the
 	   request codes in these messages is not fully understood but these
 	   particular codes are never seen in any operation besides a baud
-	   rate change.  Both of these messages send a single byte of data
-	   whose value is always zero.  The second of these two extra messages
-	   is required in order for data to be properly written to an RS-232
-	   device which does not assert the 'CTS' signal. */
+	   rate change.  Both of these messages send a single byte of data.
+	   In the first message, the value of this byte is always zero.
+
+	   The second message has been determined experimentally to control
+	   whether data will be transmitted to a device which is not asserting
+	   the 'CTS' signal.  If the second message's data byte is zero, data
+	   will be transmitted even if 'CTS' is not asserted (i.e. no hardware
+	   flow control).  if the second message's data byte is nonzero (a value
+	   of 1 is used by this driver), data will not be transmitted to a device
+	   which is not asserting 'CTS'.
+	*/
 
 	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
 			     MCT_U232_SET_UNKNOWN1_REQUEST, 
@@ -252,14 +270,19 @@
 		err("Sending USB device request code %d failed (error = %d)", 
 		    MCT_U232_SET_UNKNOWN1_REQUEST, rc);
 
+	if (port && C_CRTSCTS(port->tty)) {
+	   cts_enable_byte = 1;
+	}
+
+        dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte);
 	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
-			     MCT_U232_SET_UNKNOWN2_REQUEST, 
+			     MCT_U232_SET_CTS_REQUEST,
 			     MCT_U232_SET_REQUEST_TYPE,
-			     0, 0, &zero_byte, MCT_U232_SET_UNKNOWN2_SIZE, 
+			     0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE,
 			     WDR_TIMEOUT);
 	if (rc < 0)
-		err("Sending USB device request code %d failed (error = %d)", 
-		    MCT_U232_SET_UNKNOWN2_REQUEST, rc);
+	  err("Sending USB device request code %d failed (error = %d)",
+	      MCT_U232_SET_CTS_REQUEST, rc);
 
         return rc;
 } /* mct_u232_set_baud_rate */
@@ -458,8 +481,25 @@
 
 static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
 {
+	unsigned int c_cflag;
+	unsigned long flags;
+	unsigned int control_state;
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
 	dbg("%s port %d", __FUNCTION__, port->number);
 
+   	if (port->tty) {
+		c_cflag = port->tty->termios->c_cflag;
+		if (c_cflag & HUPCL) {
+		   /* drop DTR and RTS */
+		   spin_lock_irqsave(&priv->lock, flags);
+		   priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		   control_state = priv->control_state;
+		   spin_unlock_irqrestore(&priv->lock, flags);
+		   mct_u232_set_modem_ctrl(port->serial, control_state);
+		}
+	}
+
+
 	if (port->serial->dev) {
 		/* shutdown our urbs */
 		usb_kill_urb(port->write_urb);
@@ -476,10 +516,11 @@
 	struct usb_serial *serial = port->serial;
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
-	int status;
+	int retval;
+	int status = urb->status;
 	unsigned long flags;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -487,10 +528,12 @@
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, status);
 		return;
 	default:
-		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, status);
 		goto exit;
 	}
 
@@ -554,10 +597,10 @@
 #endif
 	spin_unlock_irqrestore(&priv->lock, flags);
 exit:
-	status = usb_submit_urb (urb, GFP_ATOMIC);
-	if (status)
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
 		err ("%s - usb_submit_urb failed with result %d",
-		     __FUNCTION__, status);
+		     __FUNCTION__, retval);
 } /* mct_u232_read_int_callback */
 
 static void mct_u232_set_termios (struct usb_serial_port *port,
@@ -565,11 +608,10 @@
 {
 	struct usb_serial *serial = port->serial;
 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
-	unsigned int iflag = port->tty->termios->c_iflag;
 	unsigned int cflag = port->tty->termios->c_cflag;
 	unsigned int old_cflag = old_termios->c_cflag;
 	unsigned long flags;
-	unsigned int control_state, new_state;
+	unsigned int control_state;
 	unsigned char last_lcr;
 
 	/* get a local copy of the current port settings */
@@ -585,18 +627,14 @@
 	 * Premature optimization is the root of all evil.
 	 */
 
-        /* reassert DTR and (maybe) RTS on transition from B0 */
+        /* reassert DTR and RTS on transition from B0 */
 	if ((old_cflag & CBAUD) == B0) {
 		dbg("%s: baud was B0", __FUNCTION__);
-		control_state |= TIOCM_DTR;
-		/* don't set RTS if using hardware flow control */
-		if (!(old_cflag & CRTSCTS)) {
-			control_state |= TIOCM_RTS;
-		}
+		control_state |= TIOCM_DTR | TIOCM_RTS;
 		mct_u232_set_modem_ctrl(serial, control_state);
 	}
 
-	mct_u232_set_baud_rate(serial, cflag & CBAUD);
+	mct_u232_set_baud_rate(serial, port, cflag & CBAUD);
 
 	if ((cflag & CBAUD) == B0 ) {
 		dbg("%s: baud is B0", __FUNCTION__);
@@ -638,21 +676,6 @@
 
 	mct_u232_set_line_ctrl(serial, last_lcr);
 
-	/*
-	 * Set flow control: well, I do not really now how to handle DTR/RTS.
-	 * Just do what we have seen with SniffUSB on Win98.
-	 */
-	/* Drop DTR/RTS if no flow control otherwise assert */
-	new_state = control_state;
-	if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
-		new_state |= TIOCM_DTR | TIOCM_RTS;
-	else
-		new_state &= ~(TIOCM_DTR | TIOCM_RTS);
-	if (new_state != control_state) {
-		mct_u232_set_modem_ctrl(serial, new_state);
-		control_state = new_state;
-	}
-
 	/* save off the modified port settings */
 	spin_lock_irqsave(&priv->lock, flags);
 	priv->control_state = control_state;
@@ -747,6 +770,50 @@
 	return 0;
 } /* mct_u232_ioctl */
 
+static void mct_u232_throttle (struct usb_serial_port *port)
+{
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int control_state;
+	struct tty_struct *tty;
+
+	tty = port->tty;
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->rx_flags |= THROTTLED;
+	if (C_CRTSCTS(tty)) {
+	  priv->control_state &= ~TIOCM_RTS;
+	  control_state = priv->control_state;
+	  spin_unlock_irqrestore(&priv->lock, flags);
+	  (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+	} else {
+	  spin_unlock_irqrestore(&priv->lock, flags);
+	}
+}
+
+
+static void mct_u232_unthrottle (struct usb_serial_port *port)
+{
+	struct mct_u232_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int control_state;
+	struct tty_struct *tty;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	tty = port->tty;
+	spin_lock_irqsave(&priv->lock, flags);
+	if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
+	  priv->rx_flags &= ~THROTTLED;
+	  priv->control_state |= TIOCM_RTS;
+	  control_state = priv->control_state;
+	  spin_unlock_irqrestore(&priv->lock, flags);
+	  (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+	} else {
+	  spin_unlock_irqrestore(&priv->lock, flags);
+	}
+}
 
 static int __init mct_u232_init (void)
 {
diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h
index 73dd0d9..a61bac8 100644
--- a/drivers/usb/serial/mct_u232.h
+++ b/drivers/usb/serial/mct_u232.h
@@ -63,14 +63,15 @@
 #define MCT_U232_SET_UNKNOWN1_REQUEST   11  /* Unknown functionality */
 #define MCT_U232_SET_UNKNOWN1_SIZE       1
 
-/* This USB device request code is not well understood.  It is transmitted by
-   the MCT-supplied Windows driver whenever the baud rate changes. 
+/* This USB device request code appears to control whether CTS is required
+   during transmission.
    
-   Without this USB device request, the USB/RS-232 adapter will not write to
-   RS-232 devices which do not assert the 'CTS' signal.
+   Sending a zero byte allows data transmission to a device which is not
+   asserting CTS.  Sending a '1' byte will cause transmission to be deferred
+   until the device asserts CTS.
 */
-#define MCT_U232_SET_UNKNOWN2_REQUEST   12  /* Unknown functionality */
-#define MCT_U232_SET_UNKNOWN2_SIZE       1
+#define MCT_U232_SET_CTS_REQUEST   12
+#define MCT_U232_SET_CTS_SIZE       1
 
 /*
  * Baud rate (divisor)
@@ -439,7 +440,7 @@
  * which says "U232-P9" ;-)
  * 
  * The circuit board inside the adaptor contains a Philips PDIUSBD12
- * USB endpoint chip and a Phillips P87C52UBAA microcontroller with
+ * USB endpoint chip and a Philips P87C52UBAA microcontroller with
  * embedded UART.  Exhaustive documentation for these is available at:
  *
  *   http://www.semiconductors.philips.com/pip/p87c52ubaa
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index b563e2a..231b584 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -9,9 +9,9 @@
  * the Free Software Foundation, version 2 of the License.
  *
  * Developed by:
- * 	VijayaKumar.G.N. <vijaykumar@aspirecom.net>
- *	AjayKumar <ajay@aspirecom.net>
- *	Gurudeva.N. <gurudev@aspirecom.net>
+ * 	Vijaya Kumar <vijaykumar.gn@gmail.com>
+ *	Ajay Kumar <naanuajay@yahoo.com>
+ *	Gurudeva <ngurudeva@yahoo.com>
  *
  * Cleaned up from the original by:
  *	Greg Kroah-Hartman <gregkh@suse.de>
@@ -103,6 +103,7 @@
 {
 	int result;
 	int length;
+	int status = urb->status;
 	__u8 *data;
 	__u8 sp1;
 	__u8 sp2;
@@ -114,7 +115,7 @@
 		return;
 	}
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -123,11 +124,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		goto exit;
 	}
 
@@ -198,14 +199,15 @@
  */
 static void mos7720_bulk_in_callback(struct urb *urb)
 {
-	int status;
+	int retval;
 	unsigned char *data ;
 	struct usb_serial_port *port;
 	struct moschip_port *mos7720_port;
 	struct tty_struct *tty;
+	int status = urb->status;
 
-	if (urb->status) {
-		dbg("nonzero read bulk status received: %d",urb->status);
+	if (status) {
+		dbg("nonzero read bulk status received: %d", status);
 		return;
 	}
 
@@ -236,10 +238,10 @@
 	if (port->read_urb->status != -EINPROGRESS) {
 		port->read_urb->dev = port->serial->dev;
 
-		status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-		if (status)
-			dbg("usb_submit_urb(read bulk) failed, status = %d",
-			    status);
+		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (retval)
+			dbg("usb_submit_urb(read bulk) failed, retval = %d",
+			    retval);
 	}
 }
 
@@ -252,9 +254,10 @@
 {
 	struct moschip_port *mos7720_port;
 	struct tty_struct *tty;
+	int status = urb->status;
 
-	if (urb->status) {
-		dbg("nonzero write bulk status received:%d", urb->status);
+	if (status) {
+		dbg("nonzero write bulk status received:%d", status);
 		return;
 	}
 
@@ -1235,16 +1238,6 @@
 		return;
 	}
 
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(tty->termios->c_iflag) ==
-		     RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("Nothing to change");
-			return;
-		}
-	}
-
 	dbg("%s - clfag %08x iflag %08x", __FUNCTION__,
 	    tty->termios->c_cflag,
 	    RELEVANT_IFLAG(tty->termios->c_iflag));
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 36620c6..37f41f5 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -434,6 +434,7 @@
 	struct moschip_port *mos7840_port;
 	__u8 regval = 0x0;
 	int result = 0;
+	int status = urb->status;
 
 	if (!urb) {
 		dbg("%s", "Invalid Pointer !!!!:\n");
@@ -442,7 +443,7 @@
 
 	mos7840_port = (struct moschip_port *)urb->context;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -451,11 +452,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		goto exit;
 	}
 
@@ -521,6 +522,7 @@
 	__u8 sp[5], st;
 	int i, rv = 0;
 	__u16 wval, wreg = 0;
+	int status = urb->status;
 
 	dbg("%s", " : Entering\n");
 	if (!urb) {
@@ -528,7 +530,7 @@
 		return;
 	}
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -537,11 +539,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		goto exit;
 	}
 
@@ -666,20 +668,21 @@
 
 static void mos7840_bulk_in_callback(struct urb *urb)
 {
-	int status;
+	int retval;
 	unsigned char *data;
 	struct usb_serial *serial;
 	struct usb_serial_port *port;
 	struct moschip_port *mos7840_port;
 	struct tty_struct *tty;
+	int status = urb->status;
 
 	if (!urb) {
 		dbg("%s", "Invalid Pointer !!!!:\n");
 		return;
 	}
 
-	if (urb->status) {
-		dbg("nonzero read bulk status received: %d", urb->status);
+	if (status) {
+		dbg("nonzero read bulk status received: %d", status);
 		return;
 	}
 
@@ -729,11 +732,11 @@
 
 	mos7840_port->read_urb->dev = serial->dev;
 
-	status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+	retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
 
-	if (status) {
-		dbg(" usb_submit_urb(read bulk) failed, status = %d",
-		 status);
+	if (retval) {
+		dbg(" usb_submit_urb(read bulk) failed, retval = %d",
+		 retval);
 	}
 }
 
@@ -747,6 +750,7 @@
 {
 	struct moschip_port *mos7840_port;
 	struct tty_struct *tty;
+	int status = urb->status;
 	int i;
 
 	if (!urb) {
@@ -764,8 +768,8 @@
 	}
 	spin_unlock(&mos7840_port->pool_lock);
 
-	if (urb->status) {
-		dbg("nonzero write bulk status received:%d\n", urb->status);
+	if (status) {
+		dbg("nonzero write bulk status received:%d\n", status);
 		return;
 	}
 
@@ -2185,16 +2189,6 @@
 		return;
 	}
 
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(tty->termios->c_iflag) ==
-		     RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("%s\n", "Nothing to change");
-			return;
-		}
-	}
-
 	dbg("%s - clfag %08x iflag %08x", __FUNCTION__,
 	    tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag));
 
@@ -2254,30 +2248,6 @@
 }
 
 /*****************************************************************************
- * mos7840_get_bytes_avail - get number of bytes available
- *
- * Purpose: Let user call ioctl to get the count of number of bytes available.
- *****************************************************************************/
-
-static int mos7840_get_bytes_avail(struct moschip_port *mos7840_port,
-				   unsigned int __user *value)
-{
-	unsigned int result = 0;
-	struct tty_struct *tty = mos7840_port->port->tty;
-
-	if (!tty)
-		return -ENOIOCTLCMD;
-
-	result = tty->read_cnt;
-
-	dbg("%s(%d) = %d", __FUNCTION__, mos7840_port->port->number, result);
-	if (copy_to_user(value, &result, sizeof(int)))
-		return -EFAULT;
-
-	return -ENOIOCTLCMD;
-}
-
-/*****************************************************************************
  * mos7840_set_modem_info
  *      function to set modem info
  *****************************************************************************/
@@ -2425,8 +2395,6 @@
 	struct async_icount cprev;
 	struct serial_icounter_struct icount;
 	int mosret = 0;
-	int retval;
-	struct tty_ldisc *ld;
 
 	if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
 		dbg("%s", "Invalid port \n");
@@ -2445,42 +2413,6 @@
 	switch (cmd) {
 		/* return number of bytes available */
 
-	case TIOCINQ:
-		dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number);
-		return mos7840_get_bytes_avail(mos7840_port, argp);
-
-	case TIOCOUTQ:
-		dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number);
-		return put_user(tty->driver->chars_in_buffer ?
-				tty->driver->chars_in_buffer(tty) : 0,
-				(int __user *)arg);
-
-	case TCFLSH:
-		retval = tty_check_change(tty);
-		if (retval)
-			return retval;
-
-		ld = tty_ldisc_ref(tty);
-		switch (arg) {
-		case TCIFLUSH:
-			if (ld && ld->flush_buffer)
-				ld->flush_buffer(tty);
-			break;
-		case TCIOFLUSH:
-			if (ld && ld->flush_buffer)
-				ld->flush_buffer(tty);
-			/* fall through */
-		case TCOFLUSH:
-			if (tty->driver->flush_buffer)
-				tty->driver->flush_buffer(tty);
-			break;
-		default:
-			tty_ldisc_deref(ld);
-			return -EINVAL;
-		}
-		tty_ldisc_deref(ld);
-		return 0;
-
 	case TIOCSERGETLSR:
 		dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number);
 		return mos7840_get_lsr_info(mos7840_port, argp);
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
index 9070111..7f337c9 100644
--- a/drivers/usb/serial/navman.c
+++ b/drivers/usb/serial/navman.c
@@ -37,9 +37,10 @@
 	struct usb_serial_port *port = urb->context;
 	unsigned char *data = urb->transfer_buffer;
 	struct tty_struct *tty;
+	int status = urb->status;
 	int result;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -48,11 +49,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 		goto exit;
 	}
 
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 00afc17..ee94d96 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -1,10 +1,9 @@
 /*
  * USB ZyXEL omni.net LCD PLUS driver
  *
- *	This program is free software; you can redistribute it and/or modify
- *	it under the terms of the GNU General Public License as published by
- *	the Free Software Foundation; either version 2 of the License, or
- *	(at your option) any later version.
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
@@ -201,14 +200,15 @@
 	struct usb_serial_port 	*port 	= (struct usb_serial_port *)urb->context;
 	unsigned char 		*data 	= urb->transfer_buffer;
 	struct omninet_header 	*header = (struct omninet_header *) &data[0];
-
+	int status = urb->status;
 	int i;
 	int result;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -312,12 +312,14 @@
 {
 /*	struct omninet_header	*header = (struct omninet_header  *) urb->transfer_buffer; */
 	struct usb_serial_port 	*port   = (struct usb_serial_port *) urb->context;
+	int status = urb->status;
 
 	dbg("%s - port %0x\n", __FUNCTION__, port->number);
 
 	port->write_urb_busy = 0;
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 5d3999e..84c12b5 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -38,6 +38,7 @@
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/module.h>
+#include <linux/bitops.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
 
@@ -240,6 +241,7 @@
 	/* Output endpoints and buffer for this port */
 	struct urb *out_urbs[N_OUT_URB];
 	char out_buffer[N_OUT_URB][OUT_BUFLEN];
+	unsigned long out_busy;		/* Bit vector of URBs in use */
 
 	/* Settings for the port */
 	int rts_state;	/* Handshaking pins (outputs) */
@@ -370,7 +372,7 @@
 			todo = OUT_BUFLEN;
 
 		this_urb = portdata->out_urbs[i];
-		if (this_urb->status == -EINPROGRESS) {
+		if (test_and_set_bit(i, &portdata->out_busy)) {
 			if (time_before(jiffies,
 					portdata->tx_start_time[i] + 10 * HZ))
 				continue;
@@ -394,6 +396,7 @@
 			dbg("usb_submit_urb %p (write bulk) failed "
 				"(%d, has %d)", this_urb,
 				err, this_urb->status);
+			clear_bit(i, &portdata->out_busy);
 			continue;
 		}
 		portdata->tx_start_time[i] = jiffies;
@@ -413,15 +416,16 @@
 	struct usb_serial_port *port;
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
 
 	dbg("%s: %p", __FUNCTION__, urb);
 
 	endpoint = usb_pipeendpoint(urb->pipe);
 	port = (struct usb_serial_port *) urb->context;
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s: nonzero status: %d on endpoint %02x.",
-		    __FUNCTION__, urb->status, endpoint);
+		    __FUNCTION__, status, endpoint);
 	} else {
 		tty = port->tty;
 		if (urb->actual_length) {
@@ -433,7 +437,7 @@
 		}
 
 		/* Resubmit urb so we continue receiving */
-		if (port->open_count && urb->status != -ESHUTDOWN) {
+		if (port->open_count && status != -ESHUTDOWN) {
 			err = usb_submit_urb(urb, GFP_ATOMIC);
 			if (err)
 				printk(KERN_ERR "%s: resubmit read urb failed. "
@@ -446,17 +450,29 @@
 static void option_outdat_callback(struct urb *urb)
 {
 	struct usb_serial_port *port;
+	struct option_port_private *portdata;
+	int i;
 
 	dbg("%s", __FUNCTION__);
 
 	port = (struct usb_serial_port *) urb->context;
 
 	usb_serial_port_softint(port);
+
+	portdata = usb_get_serial_port_data(port);
+	for (i = 0; i < N_OUT_URB; ++i) {
+		if (portdata->out_urbs[i] == urb) {
+			smp_mb__before_clear_bit();
+			clear_bit(i, &portdata->out_busy);
+			break;
+		}
+	}
 }
 
 static void option_instat_callback(struct urb *urb)
 {
 	int err;
+	int status = urb->status;
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
 	struct option_port_private *portdata = usb_get_serial_port_data(port);
 	struct usb_serial *serial = port->serial;
@@ -464,7 +480,7 @@
 	dbg("%s", __FUNCTION__);
 	dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
 
-	if (urb->status == 0) {
+	if (status == 0) {
 		struct usb_ctrlrequest *req_pkt =
 				(struct usb_ctrlrequest *)urb->transfer_buffer;
 
@@ -495,10 +511,10 @@
 				req_pkt->bRequestType,req_pkt->bRequest);
 		}
 	} else
-		dbg("%s: error %d", __FUNCTION__, urb->status);
+		dbg("%s: error %d", __FUNCTION__, status);
 
 	/* Resubmit urb so we continue receiving IRQ data */
-	if (urb->status != -ESHUTDOWN) {
+	if (status != -ESHUTDOWN) {
 		urb->dev = serial->dev;
 		err = usb_submit_urb(urb, GFP_ATOMIC);
 		if (err)
@@ -518,7 +534,7 @@
 
 	for (i=0; i < N_OUT_URB; i++) {
 		this_urb = portdata->out_urbs[i];
-		if (this_urb && this_urb->status != -EINPROGRESS)
+		if (this_urb && !test_bit(i, &portdata->out_busy))
 			data_len += OUT_BUFLEN;
 	}
 
@@ -537,7 +553,7 @@
 
 	for (i=0; i < N_OUT_URB; i++) {
 		this_urb = portdata->out_urbs[i];
-		if (this_urb && this_urb->status == -EINPROGRESS)
+		if (this_urb && test_bit(i, &portdata->out_busy))
 			data_len += this_urb->transfer_buffer_length;
 	}
 	dbg("%s: %d", __FUNCTION__, data_len);
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
new file mode 100644
index 0000000..d7db71e
--- /dev/null
+++ b/drivers/usb/serial/oti6858.c
@@ -0,0 +1,1342 @@
+/*
+ * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
+ *
+ * Copyleft  (C) 2007 Kees Lemmens (adapted for kernel 2.6.20)
+ * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail)
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2003 IBM Corp.
+ *
+ * Many thanks to the authors of pl2303 driver: all functions in this file
+ * are heavily based on pl2303 code, buffering code is a 1-to-1 copy.
+ *
+ * Warning! You use this driver on your own risk! The only official
+ * description of this device I have is datasheet from manufacturer,
+ * and it doesn't contain almost any information needed to write a driver.
+ * Almost all knowlegde used while writing this driver was gathered by:
+ *  - analyzing traffic between device and the M$ Windows 2000 driver,
+ *  - trying different bit combinations and checking pin states
+ *    with a voltmeter,
+ *  - receiving malformed frames and producing buffer overflows
+ *    to learn how errors are reported,
+ * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE
+ * CONNECTED TO IT!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * TODO:
+ *  - implement correct flushing for ioctls and oti6858_close()
+ *  - check how errors (rx overflow, parity error, framing error) are reported
+ *  - implement oti6858_break_ctl()
+ *  - implement more ioctls
+ *  - test/implement flow control
+ *  - allow setting custom baud rates
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/uaccess.h>
+#include "oti6858.h"
+
+#define OTI6858_DESCRIPTION \
+	"Ours Technology Inc. OTi-6858 USB to serial adapter driver"
+#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>"
+#define OTI6858_VERSION "0.1"
+
+static struct usb_device_id id_table [] = {
+	{ USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver oti6858_driver = {
+	.name =		"oti6858",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+	.id_table =	id_table,
+	.no_dynamic_id = 	1,
+};
+
+static int debug;
+
+
+/* buffering code, copied from pl2303 driver */
+#define PL2303_BUF_SIZE		1024
+#define PL2303_TMP_BUF_SIZE	1024
+
+struct pl2303_buf {
+	unsigned int	buf_size;
+	char		*buf_buf;
+	char		*buf_get;
+	char		*buf_put;
+};
+
+/* requests */
+#define	OTI6858_REQ_GET_STATUS		(USB_DIR_IN | USB_TYPE_VENDOR | 0x00)
+#define	OTI6858_REQ_T_GET_STATUS	0x01
+
+#define	OTI6858_REQ_SET_LINE		(USB_DIR_OUT | USB_TYPE_VENDOR | 0x00)
+#define	OTI6858_REQ_T_SET_LINE		0x00
+
+#define	OTI6858_REQ_CHECK_TXBUFF	(USB_DIR_IN | USB_TYPE_VENDOR | 0x01)
+#define	OTI6858_REQ_T_CHECK_TXBUFF	0x00
+
+/* format of the control packet */
+struct oti6858_control_pkt {
+	u16	divisor;	/* baud rate = 96000000 / (16 * divisor), LE */
+#define OTI6858_MAX_BAUD_RATE	3000000
+	u8	frame_fmt;
+#define FMT_STOP_BITS_MASK	0xc0
+#define FMT_STOP_BITS_1		0x00
+#define FMT_STOP_BITS_2		0x40	/* 1.5 stop bits if FMT_DATA_BITS_5 */
+#define FMT_PARITY_MASK		0x38
+#define FMT_PARITY_NONE		0x00
+#define FMT_PARITY_ODD		0x08
+#define FMT_PARITY_EVEN		0x18
+#define FMT_PARITY_MARK		0x28
+#define FMT_PARITY_SPACE	0x38
+#define FMT_DATA_BITS_MASK	0x03
+#define FMT_DATA_BITS_5		0x00
+#define FMT_DATA_BITS_6		0x01
+#define FMT_DATA_BITS_7		0x02
+#define FMT_DATA_BITS_8		0x03
+	u8	something;	/* always equals 0x43 */
+	u8	control;	/* settings of flow control lines */
+#define CONTROL_MASK		0x0c
+#define CONTROL_DTR_HIGH	0x08
+#define CONTROL_RTS_HIGH	0x04
+	u8	tx_status;
+#define	TX_BUFFER_EMPTIED	0x09
+	u8	pin_state;
+#define PIN_MASK		0x3f
+#define PIN_RTS			0x20	/* output pin */
+#define PIN_CTS			0x10	/* input pin, active low */
+#define PIN_DSR			0x08	/* input pin, active low */
+#define PIN_DTR			0x04	/* output pin */
+#define PIN_RI			0x02	/* input pin, active low */
+#define PIN_DCD			0x01	/* input pin, active low */
+	u8	rx_bytes_avail;		/* number of bytes in rx buffer */;
+};
+
+#define OTI6858_CTRL_PKT_SIZE	sizeof(struct oti6858_control_pkt)
+#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \
+	(    ((a)->divisor == (priv)->pending_setup.divisor) \
+	  && ((a)->control == (priv)->pending_setup.control) \
+	  && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) )
+
+/* function prototypes */
+static int oti6858_open(struct usb_serial_port *port, struct file *filp);
+static void oti6858_close(struct usb_serial_port *port, struct file *filp);
+static void oti6858_set_termios(struct usb_serial_port *port,
+				struct ktermios *old);
+static int oti6858_ioctl(struct usb_serial_port *port, struct file *file,
+			unsigned int cmd, unsigned long arg);
+static void oti6858_read_int_callback(struct urb *urb);
+static void oti6858_read_bulk_callback(struct urb *urb);
+static void oti6858_write_bulk_callback(struct urb *urb);
+static int oti6858_write(struct usb_serial_port *port,
+			const unsigned char *buf, int count);
+static int oti6858_write_room(struct usb_serial_port *port);
+static void oti6858_break_ctl(struct usb_serial_port *port, int break_state);
+static int oti6858_chars_in_buffer(struct usb_serial_port *port);
+static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file);
+static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file,
+				unsigned int set, unsigned int clear);
+static int oti6858_startup(struct usb_serial *serial);
+static void oti6858_shutdown(struct usb_serial *serial);
+
+/* functions operating on buffers */
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
+static void pl2303_buf_free(struct pl2303_buf *pb);
+static void pl2303_buf_clear(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+					unsigned int count);
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+					unsigned int count);
+
+
+/* device info */
+static struct usb_serial_driver oti6858_device = {
+	.driver = {
+		.owner =	THIS_MODULE,
+		.name =		"oti6858",
+	},
+	.id_table =		id_table,
+	.num_interrupt_in =	1,
+	.num_bulk_in =		1,
+	.num_bulk_out =		1,
+	.num_ports =		1,
+	.open =			oti6858_open,
+	.close =		oti6858_close,
+	.write =		oti6858_write,
+	.ioctl =		oti6858_ioctl,
+	.break_ctl =		oti6858_break_ctl,
+	.set_termios =		oti6858_set_termios,
+	.tiocmget =		oti6858_tiocmget,
+	.tiocmset =		oti6858_tiocmset,
+	.read_bulk_callback =	oti6858_read_bulk_callback,
+	.read_int_callback =	oti6858_read_int_callback,
+	.write_bulk_callback =	oti6858_write_bulk_callback,
+	.write_room =		oti6858_write_room,
+	.chars_in_buffer =	oti6858_chars_in_buffer,
+	.attach =		oti6858_startup,
+	.shutdown =		oti6858_shutdown,
+};
+
+struct oti6858_private {
+	spinlock_t lock;
+
+	struct pl2303_buf *buf;
+	struct oti6858_control_pkt status;
+
+	struct {
+		u8 read_urb_in_use;
+		u8 write_urb_in_use;
+		u8 termios_initialized;
+	} flags;
+	struct delayed_work delayed_write_work;
+
+	struct {
+		u16 divisor;
+		u8 frame_fmt;
+		u8 control;
+	} pending_setup;
+	u8 transient;
+	u8 setup_done;
+	struct delayed_work delayed_setup_work;
+
+	wait_queue_head_t intr_wait;
+        struct usb_serial_port *port;   /* USB port with which associated */
+};
+
+#undef dbg
+/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */
+#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg)
+
+static void setup_line(struct work_struct *work)
+{
+	struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_setup_work.work);
+	struct usb_serial_port *port = priv->port;
+	struct oti6858_control_pkt *new_setup;
+	unsigned long flags;
+	int result;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	if ((new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) {
+		dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__);
+		/* we will try again */
+		schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2));
+		return;
+	}
+
+	result = usb_control_msg(port->serial->dev,
+				usb_rcvctrlpipe(port->serial->dev, 0),
+				OTI6858_REQ_T_GET_STATUS,
+				OTI6858_REQ_GET_STATUS,
+				0, 0,
+				new_setup, OTI6858_CTRL_PKT_SIZE,
+				100);
+
+	if (result != OTI6858_CTRL_PKT_SIZE) {
+		dev_err(&port->dev, "%s(): error reading status", __FUNCTION__);
+		kfree(new_setup);
+		/* we will try again */
+		schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2));
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) {
+		new_setup->divisor = priv->pending_setup.divisor;
+		new_setup->control = priv->pending_setup.control;
+		new_setup->frame_fmt = priv->pending_setup.frame_fmt;
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+		result = usb_control_msg(port->serial->dev,
+					usb_sndctrlpipe(port->serial->dev, 0),
+					OTI6858_REQ_T_SET_LINE,
+					OTI6858_REQ_SET_LINE,
+					0, 0,
+					new_setup, OTI6858_CTRL_PKT_SIZE,
+					100);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		result = 0;
+	}
+	kfree(new_setup);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (result != OTI6858_CTRL_PKT_SIZE)
+		priv->transient = 0;
+	priv->setup_done = 1;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	dbg("%s(): submitting interrupt urb", __FUNCTION__);
+	port->interrupt_in_urb->dev = port->serial->dev;
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
+				" with error %d\n", __FUNCTION__, result);
+	}
+}
+
+void send_data(struct work_struct *work)
+{
+	struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_write_work.work);
+	struct usb_serial_port *port = priv->port;
+	int count = 0, result;
+	unsigned long flags;
+	unsigned char allow;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (priv->flags.write_urb_in_use) {
+		spin_unlock_irqrestore(&priv->lock, flags);
+		schedule_delayed_work(&priv->delayed_write_work, msecs_to_jiffies(2));
+		return;
+	}
+	priv->flags.write_urb_in_use = 1;
+
+	count = pl2303_buf_data_avail(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
+	if (count > port->bulk_out_size)
+		count = port->bulk_out_size;
+
+	if (count != 0) {
+		result = usb_control_msg(port->serial->dev,
+				usb_rcvctrlpipe(port->serial->dev, 0),
+				OTI6858_REQ_T_CHECK_TXBUFF,
+				OTI6858_REQ_CHECK_TXBUFF,
+				count, 0, &allow, 1, 100);
+		if (result != 1 || allow != 0)
+			count = 0;
+	}
+
+	if (count == 0) {
+		priv->flags.write_urb_in_use = 0;
+
+		dbg("%s(): submitting interrupt urb", __FUNCTION__);
+		port->interrupt_in_urb->dev = port->serial->dev;
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+		if (result != 0) {
+			dev_err(&port->dev, "%s(): usb_submit_urb() failed"
+				" with error %d\n", __FUNCTION__, result);
+		}
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, count);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	port->write_urb->transfer_buffer_length = count;
+	port->write_urb->dev = port->serial->dev;
+	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
+			       " with error %d\n", __FUNCTION__, result);
+		priv->flags.write_urb_in_use = 0;
+	}
+
+	usb_serial_port_softint(port);
+}
+
+static int oti6858_startup(struct usb_serial *serial)
+{
+        struct usb_serial_port *port = serial->port[0];
+        struct oti6858_private *priv;
+	int i;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL);
+		if (!priv)
+			break;
+		priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
+		if (priv->buf == NULL) {
+			kfree(priv);
+			break;
+		}
+
+		spin_lock_init(&priv->lock);
+		init_waitqueue_head(&priv->intr_wait);
+//		INIT_WORK(&priv->setup_work, setup_line, serial->port[i]);
+//		INIT_WORK(&priv->write_work, send_data, serial->port[i]);
+		priv->port = port;
+		INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line);
+		INIT_DELAYED_WORK(&priv->delayed_write_work, send_data);
+
+		usb_set_serial_port_data(serial->port[i], priv);
+	}
+	if (i == serial->num_ports)
+		return 0;
+
+	for (--i; i >= 0; --i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		pl2303_buf_free(priv->buf);
+		kfree(priv);
+		usb_set_serial_port_data(serial->port[i], NULL);
+	}
+	return -ENOMEM;
+}
+
+static int oti6858_write(struct usb_serial_port *port,
+			const unsigned char *buf, int count)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+
+	dbg("%s(port = %d, count = %d)", __FUNCTION__, port->number, count);
+
+	if (!count)
+		return count;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	count = pl2303_buf_put(priv->buf, buf, count);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return count;
+}
+
+static int oti6858_write_room(struct usb_serial_port *port)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	int room = 0;
+	unsigned long flags;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	room = pl2303_buf_space_avail(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return room;
+}
+
+static int oti6858_chars_in_buffer(struct usb_serial_port *port)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	int chars = 0;
+	unsigned long flags;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	chars = pl2303_buf_data_avail(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return chars;
+}
+
+static void oti6858_set_termios(struct usb_serial_port *port,
+				struct ktermios *old_termios)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int cflag;
+	u8 frame_fmt, control;
+	u16 divisor;
+	int br;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	if ((!port->tty) || (!port->tty->termios)) {
+		dbg("%s(): no tty structures", __FUNCTION__);
+		return;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (!priv->flags.termios_initialized) {
+		*(port->tty->termios) = tty_std_termios;
+		port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+		priv->flags.termios_initialized = 1;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	cflag = port->tty->termios->c_cflag;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	divisor = priv->pending_setup.divisor;
+	frame_fmt = priv->pending_setup.frame_fmt;
+	control = priv->pending_setup.control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	frame_fmt &= ~FMT_DATA_BITS_MASK;
+	switch (cflag & CSIZE) {
+		case CS5:
+			frame_fmt |= FMT_DATA_BITS_5;
+			break;
+		case CS6:
+			frame_fmt |= FMT_DATA_BITS_6;
+			break;
+		case CS7:
+			frame_fmt |= FMT_DATA_BITS_7;
+			break;
+		default:
+		case CS8:
+			frame_fmt |= FMT_DATA_BITS_8;
+			break;
+	}
+
+	/* manufacturer claims that this device can work with baud rates
+	 * up to 3 Mbps; I've tested it only on 115200 bps, so I can't
+	 * guarantee that any other baud rate will work (especially
+	 * the higher ones)
+	 */
+	br = tty_get_baud_rate(port->tty);
+	if (br == 0) {
+		divisor = 0;
+	} else if (br <= OTI6858_MAX_BAUD_RATE) {
+		int real_br;
+
+		divisor = (96000000 + 8 * br) / (16 * br);
+		real_br = 96000000 / (16 * divisor);
+		if ((((real_br - br) * 100 + br - 1) / br) > 2) {
+			dbg("%s(): baud rate %d is invalid", __FUNCTION__, br);
+			return;
+		}
+		divisor = cpu_to_le16(divisor);
+	} else {
+		dbg("%s(): baud rate %d is too high", __FUNCTION__, br);
+		return;
+	}
+
+	frame_fmt &= ~FMT_STOP_BITS_MASK;
+	if ((cflag & CSTOPB) != 0) {
+		frame_fmt |= FMT_STOP_BITS_2;
+	} else {
+		frame_fmt |= FMT_STOP_BITS_1;
+	}
+
+	frame_fmt &= ~FMT_PARITY_MASK;
+	if ((cflag & PARENB) != 0) {
+		if ((cflag & PARODD) != 0) {
+			frame_fmt |= FMT_PARITY_ODD;
+		} else {
+			frame_fmt |= FMT_PARITY_EVEN;
+		}
+	} else {
+		frame_fmt |= FMT_PARITY_NONE;
+	}
+
+	control &= ~CONTROL_MASK;
+	if ((cflag & CRTSCTS) != 0)
+		control |= (CONTROL_DTR_HIGH | CONTROL_RTS_HIGH);
+
+	/* change control lines if we are switching to or from B0 */
+	/* FIXME:
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->line_control;
+	if ((cflag & CBAUD) == B0)
+		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+	else
+		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
+	if (control != priv->line_control) {
+		control = priv->line_control;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		set_control_lines(serial->dev, control);
+	} else {
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+	*/
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (divisor != priv->pending_setup.divisor
+			|| control != priv->pending_setup.control
+			|| frame_fmt != priv->pending_setup.frame_fmt) {
+		priv->pending_setup.divisor = divisor;
+		priv->pending_setup.control = control;
+		priv->pending_setup.frame_fmt = frame_fmt;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int oti6858_open(struct usb_serial_port *port, struct file *filp)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	struct ktermios tmp_termios;
+	struct usb_serial *serial = port->serial;
+	struct oti6858_control_pkt *buf;
+	unsigned long flags;
+	int result;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	if (port->open_count != 1)
+		return 0;
+
+	if ((buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) {
+		dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				OTI6858_REQ_T_GET_STATUS,
+				OTI6858_REQ_GET_STATUS,
+				0, 0,
+				buf, OTI6858_CTRL_PKT_SIZE,
+				100);
+	if (result != OTI6858_CTRL_PKT_SIZE) {
+		/* assume default (after power-on reset) values */
+		buf->divisor = cpu_to_le16(0x009c);	/* 38400 bps */
+		buf->frame_fmt = 0x03;	/* 8N1 */
+		buf->something = 0x43;
+		buf->control = 0x4c;	/* DTR, RTS */
+		buf->tx_status = 0x00;
+		buf->pin_state = 0x5b;	/* RTS, CTS, DSR, DTR, RI, DCD */
+		buf->rx_bytes_avail = 0x00;
+	}
+
+	spin_lock_irqsave(&priv->lock, flags);
+	memcpy(&priv->status, buf, OTI6858_CTRL_PKT_SIZE);
+	priv->pending_setup.divisor = buf->divisor;
+	priv->pending_setup.frame_fmt = buf->frame_fmt;
+	priv->pending_setup.control = buf->control;
+	spin_unlock_irqrestore(&priv->lock, flags);
+	kfree(buf);
+
+	dbg("%s(): submitting interrupt urb", __FUNCTION__);
+	port->interrupt_in_urb->dev = serial->dev;
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
+			       " with error %d\n", __FUNCTION__, result);
+		oti6858_close(port, NULL);
+		return -EPROTO;
+	}
+
+	/* setup termios */
+	if (port->tty)
+		oti6858_set_termios(port, &tmp_termios);
+
+	return 0;
+}
+
+static void oti6858_close(struct usb_serial_port *port, struct file *filp)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	long timeout;
+	wait_queue_t wait;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	/* wait for data to drain from the buffer */
+	spin_lock_irqsave(&priv->lock, flags);
+	timeout = 30 * HZ;	/* PL2303_CLOSING_WAIT */
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(&port->tty->write_wait, &wait);
+	dbg("%s(): entering wait loop", __FUNCTION__);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (pl2303_buf_data_avail(priv->buf) == 0
+		|| timeout == 0 || signal_pending(current)
+		|| !usb_get_intfdata(port->serial->interface))	/* disconnect */
+			break;
+		spin_unlock_irqrestore(&priv->lock, flags);
+		timeout = schedule_timeout(timeout);
+		spin_lock_irqsave(&priv->lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&port->tty->write_wait, &wait);
+	dbg("%s(): after wait loop", __FUNCTION__);
+
+	/* clear out any remaining data in the buffer */
+	pl2303_buf_clear(priv->buf);
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* wait for characters to drain from the device */
+	/* (this is long enough for the entire 256 byte */
+	/* pl2303 hardware buffer to drain with no flow */
+	/* control for data rates of 1200 bps or more, */
+	/* for lower rates we should really know how much */
+	/* data is in the buffer to compute a delay */
+	/* that is not unnecessarily long) */
+	/* FIXME
+	bps = tty_get_baud_rate(port->tty);
+	if (bps > 1200)
+		timeout = max((HZ*2560)/bps,HZ/10);
+	else
+	*/
+		timeout = 2*HZ;
+	schedule_timeout_interruptible(timeout);
+	dbg("%s(): after schedule_timeout_interruptible()", __FUNCTION__);
+
+	/* cancel scheduled setup */
+	cancel_delayed_work(&priv->delayed_setup_work);
+	cancel_delayed_work(&priv->delayed_write_work);
+	flush_scheduled_work();
+
+	/* shutdown our urbs */
+	dbg("%s(): shutting down urbs", __FUNCTION__);
+	usb_kill_urb(port->write_urb);
+	usb_kill_urb(port->read_urb);
+	usb_kill_urb(port->interrupt_in_urb);
+
+	/*
+	if (port->tty && (port->tty->termios->c_cflag) & HUPCL) {
+		// drop DTR and RTS
+		spin_lock_irqsave(&priv->lock, flags);
+		priv->pending_setup.control &= ~CONTROL_MASK;
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+	*/
+}
+
+static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file,
+				unsigned int set, unsigned int clear)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	u8 control;
+
+	dbg("%s(port = %d, set = 0x%08x, clear = 0x%08x)",
+				__FUNCTION__, port->number, set, clear);
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	/* FIXME: check if this is correct (active high/low) */
+	spin_lock_irqsave(&priv->lock, flags);
+	control = priv->pending_setup.control;
+	if ((set & TIOCM_RTS) != 0)
+		control |= CONTROL_RTS_HIGH;
+	if ((set & TIOCM_DTR) != 0)
+		control |= CONTROL_DTR_HIGH;
+	if ((clear & TIOCM_RTS) != 0)
+		control &= ~CONTROL_RTS_HIGH;
+	if ((clear & TIOCM_DTR) != 0)
+		control &= ~CONTROL_DTR_HIGH;
+
+	if (control != priv->pending_setup.control) {
+		priv->pending_setup.control = control;
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned pin_state;
+	unsigned result = 0;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	if (!usb_get_intfdata(port->serial->interface))
+		return -ENODEV;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	pin_state = priv->status.pin_state & PIN_MASK;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* FIXME: check if this is correct (active high/low) */
+	if ((pin_state & PIN_RTS) != 0)
+		result |= TIOCM_RTS;
+	if ((pin_state & PIN_CTS) != 0)
+		result |= TIOCM_CTS;
+	if ((pin_state & PIN_DSR) != 0)
+		result |= TIOCM_DSR;
+	if ((pin_state & PIN_DTR) != 0)
+		result |= TIOCM_DTR;
+	if ((pin_state & PIN_RI) != 0)
+		result |= TIOCM_RI;
+	if ((pin_state & PIN_DCD) != 0)
+		result |= TIOCM_CD;
+
+	dbg("%s() = 0x%08x", __FUNCTION__, result);
+
+	return result;
+}
+
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	unsigned long flags;
+	unsigned int prev, status;
+	unsigned int changed;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	prev = priv->status.pin_state;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	while (1) {
+		wait_event_interruptible(priv->intr_wait, priv->status.pin_state != prev);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		status = priv->status.pin_state & PIN_MASK;
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		changed = prev ^ status;
+		/* FIXME: check if this is correct (active high/low) */
+		if (	((arg & TIOCM_RNG) && (changed & PIN_RI)) ||
+			((arg & TIOCM_DSR) && (changed & PIN_DSR)) ||
+			((arg & TIOCM_CD)  && (changed & PIN_DCD)) ||
+			((arg & TIOCM_CTS) && (changed & PIN_CTS))) {
+				return 0;
+		}
+		prev = status;
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+
+static int oti6858_ioctl(struct usb_serial_port *port, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	void __user *user_arg = (void __user *) arg;
+	unsigned int x;
+
+	dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)",
+				__FUNCTION__, port->number, cmd, arg);
+
+	switch (cmd) {
+		case TCGETS:
+			if (copy_to_user(user_arg, port->tty->termios,
+						sizeof(struct ktermios))) {
+				return -EFAULT;
+			}
+			return 0;
+
+		case TCSETS:
+		case TCSETSW:	/* FIXME: this is not the same! */
+		case TCSETSF:	/* FIXME: this is not the same! */
+			if (copy_from_user(port->tty->termios, user_arg,
+						sizeof(struct ktermios))) {
+				return -EFAULT;
+			}
+			oti6858_set_termios(port, NULL);
+			return 0;
+
+		case TCFLSH:
+			/* FIXME */
+			return 0;
+
+		case TIOCMBIS:
+			if (copy_from_user(&x, user_arg, sizeof(x)))
+				return -EFAULT;
+			return oti6858_tiocmset(port, NULL, x, 0);
+
+		case TIOCMBIC:
+			if (copy_from_user(&x, user_arg, sizeof(x)))
+				return -EFAULT;
+			return oti6858_tiocmset(port, NULL, 0, x);
+
+		case TIOCGSERIAL:
+			if (copy_to_user(user_arg, port->tty->termios,
+						sizeof(struct ktermios))) {
+				return -EFAULT;
+			}
+                        return 0;
+
+		case TIOCSSERIAL:
+			if (copy_from_user(port->tty->termios, user_arg,
+						sizeof(struct ktermios))) {
+				return -EFAULT;
+			}
+			oti6858_set_termios(port, NULL);
+			return 0;
+
+		case TIOCMIWAIT:
+			dbg("%s(): TIOCMIWAIT", __FUNCTION__);
+			return wait_modem_info(port, arg);
+
+		default:
+			dbg("%s(): 0x%04x not supported", __FUNCTION__, cmd);
+			break;
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+static void oti6858_break_ctl(struct usb_serial_port *port, int break_state)
+{
+	int state;
+
+	dbg("%s(port = %d)", __FUNCTION__, port->number);
+
+	state = (break_state == 0) ? 0 : 1;
+	dbg("%s(): turning break %s", __FUNCTION__, state ? "on" : "off");
+
+	/* FIXME */
+/*
+	result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
+				  BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
+				  0, NULL, 0, 100);
+	if (result != 0)
+		dbg("%s(): error sending break", __FUNCTION__);
+ */
+}
+
+static void oti6858_shutdown(struct usb_serial *serial)
+{
+	struct oti6858_private *priv;
+	int i;
+
+	dbg("%s()", __FUNCTION__);
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = usb_get_serial_port_data(serial->port[i]);
+		if (priv) {
+			pl2303_buf_free(priv->buf);
+			kfree(priv);
+			usb_set_serial_port_data(serial->port[i], NULL);
+		}
+	}
+}
+
+static void oti6858_read_int_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	int transient = 0, can_recv = 0, resubmit = 1;
+	int status = urb->status;
+
+	dbg("%s(port = %d, status = %d)",
+				__FUNCTION__, port->number, status);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s(): urb shutting down with status: %d",
+					__FUNCTION__, status);
+		return;
+	default:
+		dbg("%s(): nonzero urb status received: %d",
+					__FUNCTION__, status);
+		break;
+	}
+
+	if (status == 0 && urb->actual_length == OTI6858_CTRL_PKT_SIZE) {
+		struct oti6858_control_pkt *xs = urb->transfer_buffer;
+		unsigned long flags;
+
+		spin_lock_irqsave(&priv->lock, flags);
+
+		if (!priv->transient) {
+			if (!OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
+				if (xs->rx_bytes_avail == 0) {
+					priv->transient = 4;
+					priv->setup_done = 0;
+					resubmit = 0;
+					dbg("%s(): scheduling setup_line()",
+					    __FUNCTION__);
+					schedule_delayed_work(&priv->delayed_setup_work, 0);
+				}
+			}
+		} else {
+			if (OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
+				priv->transient = 0;
+			} else if (!priv->setup_done) {
+				resubmit = 0;
+			} else if (--priv->transient == 0) {
+				if (xs->rx_bytes_avail == 0) {
+					priv->transient = 4;
+					priv->setup_done = 0;
+					resubmit = 0;
+					dbg("%s(): scheduling setup_line()",
+					    __FUNCTION__);
+					schedule_delayed_work(&priv->delayed_setup_work, 0);
+				}
+			}
+		}
+
+		if (!priv->transient) {
+			if (xs->pin_state != priv->status.pin_state)
+				wake_up_interruptible(&priv->intr_wait);
+			memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE);
+		}
+
+		if (!priv->transient && xs->rx_bytes_avail != 0) {
+			can_recv = xs->rx_bytes_avail;
+			priv->flags.read_urb_in_use = 1;
+		}
+
+		transient = priv->transient;
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	if (can_recv) {
+		int result;
+
+		port->read_urb->dev = port->serial->dev;
+		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+		if (result != 0) {
+			priv->flags.read_urb_in_use = 0;
+			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
+					" error %d\n", __FUNCTION__, result);
+		} else {
+			resubmit = 0;
+		}
+	} else if (!transient) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&priv->lock, flags);
+		if (priv->flags.write_urb_in_use == 0
+				&& pl2303_buf_data_avail(priv->buf) != 0) {
+			schedule_delayed_work(&priv->delayed_write_work,0);
+			resubmit = 0;
+		}
+		spin_unlock_irqrestore(&priv->lock, flags);
+	}
+
+	if (resubmit) {
+		int result;
+
+//		dbg("%s(): submitting interrupt urb", __FUNCTION__);
+		urb->dev = port->serial->dev;
+		result = usb_submit_urb(urb, GFP_ATOMIC);
+		if (result != 0) {
+			dev_err(&urb->dev->dev,
+					"%s(): usb_submit_urb() failed with"
+					" error %d\n", __FUNCTION__, result);
+		}
+	}
+}
+
+static void oti6858_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	unsigned long flags;
+	int i, result;
+	int status = urb->status;
+	char tty_flag;
+
+	dbg("%s(port = %d, status = %d)",
+				__FUNCTION__, port->number, status);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->flags.read_urb_in_use = 0;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (status != 0) {
+		if (!port->open_count) {
+			dbg("%s(): port is closed, exiting", __FUNCTION__);
+			return;
+		}
+		/*
+		if (status == -EPROTO) {
+			// PL2303 mysteriously fails with -EPROTO reschedule the read
+			dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
+			result = usb_submit_urb(urb, GFP_ATOMIC);
+			if (result)
+				dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+			return;
+		}
+		*/
+		dbg("%s(): unable to handle the error, exiting", __FUNCTION__);
+		return;
+	}
+
+	// get tty_flag from status
+	tty_flag = TTY_NORMAL;
+
+/* FIXME: probably, errors will be signalled using interrupt pipe! */
+/*
+	// break takes precedence over parity,
+	// which takes precedence over framing errors
+	if (status & UART_BREAK_ERROR )
+		tty_flag = TTY_BREAK;
+	else if (status & UART_PARITY_ERROR)
+		tty_flag = TTY_PARITY;
+	else if (status & UART_FRAME_ERROR)
+		tty_flag = TTY_FRAME;
+	dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);
+*/
+
+	tty = port->tty;
+	if (tty != NULL && urb->actual_length > 0) {
+		tty_buffer_request_room(tty, urb->actual_length);
+		for (i = 0; i < urb->actual_length; ++i)
+			tty_insert_flip_char(tty, data[i], tty_flag);
+		tty_flip_buffer_push(tty);
+	}
+
+	// schedule the interrupt urb if we are still open */
+	if (port->open_count != 0) {
+		port->interrupt_in_urb->dev = port->serial->dev;
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+		if (result != 0) {
+			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
+					" error %d\n", __FUNCTION__, result);
+		}
+	}
+}
+
+static void oti6858_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+	struct oti6858_private *priv = usb_get_serial_port_data(port);
+	int status = urb->status;
+	int result;
+
+	dbg("%s(port = %d, status = %d)",
+				__FUNCTION__, port->number, status);
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s(): urb shutting down with status: %d",
+					__FUNCTION__, status);
+		priv->flags.write_urb_in_use = 0;
+		return;
+	default:
+		/* error in the urb, so we have to resubmit it */
+		dbg("%s(): nonzero write bulk status received: %d",
+					__FUNCTION__, status);
+		dbg("%s(): overflow in write", __FUNCTION__);
+
+		port->write_urb->transfer_buffer_length = 1;
+		port->write_urb->dev = port->serial->dev;
+		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (result) {
+			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
+					" error %d\n", __FUNCTION__, result);
+		} else {
+			return;
+		}
+	}
+
+	priv->flags.write_urb_in_use = 0;
+
+	// schedule the interrupt urb if we are still open */
+	port->interrupt_in_urb->dev = port->serial->dev;
+	dbg("%s(): submitting interrupt urb", __FUNCTION__);
+	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+	if (result != 0) {
+		dev_err(&port->dev, "%s(): failed submitting int urb,"
+					" error %d\n", __FUNCTION__, result);
+	}
+}
+
+
+/*
+ * pl2303_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+{
+	struct pl2303_buf *pb;
+
+	if (size == 0)
+		return NULL;
+
+	pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+	if (pb == NULL)
+		return NULL;
+
+	pb->buf_buf = kmalloc(size, GFP_KERNEL);
+	if (pb->buf_buf == NULL) {
+		kfree(pb);
+		return NULL;
+	}
+
+	pb->buf_size = size;
+	pb->buf_get = pb->buf_put = pb->buf_buf;
+
+	return pb;
+}
+
+/*
+ * pl2303_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void pl2303_buf_free(struct pl2303_buf *pb)
+{
+	if (pb) {
+		kfree(pb->buf_buf);
+		kfree(pb);
+	}
+}
+
+/*
+ * pl2303_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void pl2303_buf_clear(struct pl2303_buf *pb)
+{
+	if (pb != NULL) {
+		/* equivalent to a get of all data available */
+		pb->buf_get = pb->buf_put;
+	}
+}
+
+/*
+ * pl2303_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+{
+	if (pb == NULL)
+		return 0;
+	return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+{
+	if (pb == NULL)
+		return 0;
+	return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+					unsigned int count)
+{
+	unsigned int len;
+
+	if (pb == NULL)
+		return 0;
+
+	len  = pl2303_buf_space_avail(pb);
+	if (count > len)
+		count = len;
+
+	if (count == 0)
+		return 0;
+
+	len = pb->buf_buf + pb->buf_size - pb->buf_put;
+	if (count > len) {
+		memcpy(pb->buf_put, buf, len);
+		memcpy(pb->buf_buf, buf+len, count - len);
+		pb->buf_put = pb->buf_buf + count - len;
+	} else {
+		memcpy(pb->buf_put, buf, count);
+		if (count < len)
+			pb->buf_put += count;
+		else /* count == len */
+			pb->buf_put = pb->buf_buf;
+	}
+
+	return count;
+}
+
+/*
+ * pl2303_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+					unsigned int count)
+{
+	unsigned int len;
+
+	if (pb == NULL)
+		return 0;
+
+	len = pl2303_buf_data_avail(pb);
+	if (count > len)
+		count = len;
+
+	if (count == 0)
+		return 0;
+
+	len = pb->buf_buf + pb->buf_size - pb->buf_get;
+	if (count > len) {
+		memcpy(buf, pb->buf_get, len);
+		memcpy(buf+len, pb->buf_buf, count - len);
+		pb->buf_get = pb->buf_buf + count - len;
+	} else {
+		memcpy(buf, pb->buf_get, count);
+		if (count < len)
+			pb->buf_get += count;
+		else /* count == len */
+			pb->buf_get = pb->buf_buf;
+	}
+
+	return count;
+}
+
+/* module description and (de)initialization */
+
+static int __init oti6858_init(void)
+{
+	int retval;
+
+	if ((retval = usb_serial_register(&oti6858_device)) == 0) {
+		if ((retval = usb_register(&oti6858_driver)) != 0)
+			usb_serial_deregister(&oti6858_device);
+		else
+			return 0;
+	}
+
+	return retval;
+}
+
+static void __exit oti6858_exit(void)
+{
+	usb_deregister(&oti6858_driver);
+	usb_serial_deregister(&oti6858_device);
+}
+
+module_init(oti6858_init);
+module_exit(oti6858_exit);
+
+MODULE_DESCRIPTION(OTI6858_DESCRIPTION);
+MODULE_AUTHOR(OTI6858_AUTHOR);
+MODULE_VERSION(OTI6858_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "enable debug output");
+
diff --git a/drivers/usb/serial/oti6858.h b/drivers/usb/serial/oti6858.h
new file mode 100644
index 0000000..704ac3a
--- /dev/null
+++ b/drivers/usb/serial/oti6858.h
@@ -0,0 +1,15 @@
+/*
+ * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __LINUX_USB_SERIAL_OTI6858_H
+#define __LINUX_USB_SERIAL_OTI6858_H
+
+#define OTI6858_VENDOR_ID	0x0ea0
+#define OTI6858_PRODUCT_ID	0x6858
+
+#endif
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 83dfae9..f9f85f5 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -1,14 +1,14 @@
 /*
  * Prolific PL2303 USB to serial adaptor driver
  *
- * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
  * Copyright (C) 2003 IBM Corp.
  *
  * Original driver for 2.2.x by anonymous
  *
- *	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.
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
@@ -484,15 +484,6 @@
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	cflag = port->tty->termios->c_cflag;
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) ==
-		     RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("%s - nothing to change...", __FUNCTION__);
-			return;
-		}
-	}
 
 	buf = kzalloc(7, GFP_KERNEL);
 	if (!buf) {
@@ -517,29 +508,7 @@
 		dbg("%s - data bits = %d", __FUNCTION__, buf[6]);
 	}
 
-	baud = 0;
-	switch (cflag & CBAUD) {
-		case B0:	baud = 0;	break;
-		case B75:	baud = 75;	break;
-		case B150:	baud = 150;	break;
-		case B300:	baud = 300;	break;
-		case B600:	baud = 600;	break;
-		case B1200:	baud = 1200;	break;
-		case B1800:	baud = 1800;	break;
-		case B2400:	baud = 2400;	break;
-		case B4800:	baud = 4800;	break;
-		case B9600:	baud = 9600;	break;
-		case B19200:	baud = 19200;	break;
-		case B38400:	baud = 38400;	break;
-		case B57600:	baud = 57600;	break;
-		case B115200:	baud = 115200;	break;
-		case B230400:	baud = 230400;	break;
-		case B460800:	baud = 460800;	break;
-		default:
-			dev_err(&port->dev, "pl2303 driver does not support"
-				" the baudrate requested (fix it)\n");
-			break;
-	}
+	baud = tty_get_baud_rate(port->tty);;
 	dbg("%s - baud = %d", __FUNCTION__, baud);
 	if (baud) {
 		buf[0] = baud & 0xff;
@@ -617,6 +586,13 @@
 				    VENDOR_WRITE_REQUEST_TYPE,
 				    0x0, index, NULL, 0, 100);
 		dbg("0x40:0x1:0x0:0x%x  %d", index, i);
+	} else {
+		i = usb_control_msg(serial->dev,
+				    usb_sndctrlpipe(serial->dev, 0),
+				    VENDOR_WRITE_REQUEST,
+				    VENDOR_WRITE_REQUEST_TYPE,
+				    0x0, 0x0, NULL, 0, 100);
+		dbg ("0x40:0x1:0x0:0x0  %d", i);
 	}
 
 	kfree(buf);
@@ -954,11 +930,12 @@
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
 	unsigned char *data = urb->transfer_buffer;
 	unsigned int actual_length = urb->actual_length;
-	int status;
+	int status = urb->status;
+	int retval;
 
 	dbg("%s (%d)", __FUNCTION__, port->number);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -967,11 +944,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		goto exit;
 	}
 
@@ -981,11 +958,11 @@
 	pl2303_update_line_status(port, data, actual_length);
 
 exit:
-	status = usb_submit_urb(urb, GFP_ATOMIC);
-	if (status)
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
 		dev_err(&urb->dev->dev,
 			"%s - usb_submit_urb failed with result %d\n",
-			__FUNCTION__, status);
+			__FUNCTION__, retval);
 }
 
 static void pl2303_read_bulk_callback(struct urb *urb)
@@ -997,23 +974,23 @@
 	unsigned long flags;
 	int i;
 	int result;
-	u8 status;
+	int status = urb->status;
+	u8 line_status;
 	char tty_flag;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - urb status = %d", __FUNCTION__, status);
 		if (!port->open_count) {
 			dbg("%s - port is closed, exiting.", __FUNCTION__);
 			return;
 		}
-		if (urb->status == -EPROTO) {
+		if (status == -EPROTO) {
 			/* PL2303 mysteriously fails with -EPROTO reschedule
 			 * the read */
 			dbg("%s - caught -EPROTO, resubmitting the urb",
 			    __FUNCTION__);
-			urb->status = 0;
 			urb->dev = port->serial->dev;
 			result = usb_submit_urb(urb, GFP_ATOMIC);
 			if (result)
@@ -1033,18 +1010,18 @@
 	tty_flag = TTY_NORMAL;
 
 	spin_lock_irqsave(&priv->lock, flags);
-	status = priv->line_status;
+	line_status = priv->line_status;
 	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
 	spin_unlock_irqrestore(&priv->lock, flags);
 	wake_up_interruptible(&priv->delta_msr_wait);
 
 	/* break takes precedence over parity, */
 	/* which takes precedence over framing errors */
-	if (status & UART_BREAK_ERROR )
+	if (line_status & UART_BREAK_ERROR )
 		tty_flag = TTY_BREAK;
-	else if (status & UART_PARITY_ERROR)
+	else if (line_status & UART_PARITY_ERROR)
 		tty_flag = TTY_PARITY;
-	else if (status & UART_FRAME_ERROR)
+	else if (line_status & UART_FRAME_ERROR)
 		tty_flag = TTY_FRAME;
 	dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);
 
@@ -1052,7 +1029,7 @@
 	if (tty && urb->actual_length) {
 		tty_buffer_request_room(tty, urb->actual_length + 1);
 		/* overrun is special, not associated with a char */
-		if (status & UART_OVERRUN_ERROR)
+		if (line_status & UART_OVERRUN_ERROR)
 			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
 		for (i = 0; i < urb->actual_length; ++i)
 			tty_insert_flip_char(tty, data[i], tty_flag);
@@ -1076,10 +1053,11 @@
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
 	struct pl2303_private *priv = usb_get_serial_port_data(port);
 	int result;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -1088,14 +1066,14 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		priv->write_urb_in_use = 0;
 		return;
 	default:
 		/* error in the urb, so we have to resubmit it */
 		dbg("%s - Overflow in write", __FUNCTION__);
 		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
-		    urb->status);
+		    status);
 		port->write_urb->transfer_buffer_length = 1;
 		port->write_urb->dev = port->serial->dev;
 		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 5a03a3f..86899d5 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -211,11 +211,13 @@
 	unsigned char length = urb->actual_length;
 	int i;
 	int result;
+	int status = urb->status;
 
 	dbg ("%s", __FUNCTION__);
 
-	if (urb->status) {
-		dbg ("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ac1829c..e7db203 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -86,15 +86,14 @@
 #define N_IN_URB	4
 #define N_OUT_URB	4
 #define IN_BUFLEN	4096
-#define OUT_BUFLEN	128
 
 struct sierra_port_private {
+	spinlock_t lock;	/* lock the structure */
+	int outstanding_urbs;	/* number of out urbs in flight */
+
 	/* Input endpoints and buffer for this port */
 	struct urb *in_urbs[N_IN_URB];
 	char in_buffer[N_IN_URB][IN_BUFLEN];
-	/* Output endpoints and buffer for this port */
-	struct urb *out_urbs[N_OUT_URB];
-	char out_buffer[N_OUT_URB][OUT_BUFLEN];
 
 	/* Settings for the port */
 	int rts_state;	/* Handshaking pins (outputs) */
@@ -103,8 +102,6 @@
 	int dsr_state;
 	int dcd_state;
 	int ri_state;
-
-	unsigned long tx_start_time[N_OUT_URB];
 };
 
 static int sierra_send_setup(struct usb_serial_port *port)
@@ -197,61 +194,98 @@
 	return -ENOIOCTLCMD;
 }
 
+static void sierra_outdat_callback(struct urb *urb)
+{
+	struct usb_serial_port *port = urb->context;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	int status = urb->status;
+	unsigned long flags;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	/* free up the transfer buffer, as usb_free_urb() does not do this */
+	kfree(urb->transfer_buffer);
+
+	if (status)
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
+
+	spin_lock_irqsave(&portdata->lock, flags);
+	--portdata->outstanding_urbs;
+	spin_unlock_irqrestore(&portdata->lock, flags);
+
+	usb_serial_port_softint(port);
+}
+
 /* Write */
 static int sierra_write(struct usb_serial_port *port,
 			const unsigned char *buf, int count)
 {
-	struct sierra_port_private *portdata;
-	int i;
-	int left, todo;
-	struct urb *this_urb = NULL; /* spurious */
-	int err;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+	unsigned char *buffer;
+	struct urb *urb;
+	int status;
 
 	portdata = usb_get_serial_port_data(port);
 
 	dbg("%s: write (%d chars)", __FUNCTION__, count);
 
-	i = 0;
-	left = count;
-	for (i=0; left > 0 && i < N_OUT_URB; i++) {
-		todo = left;
-		if (todo > OUT_BUFLEN)
-			todo = OUT_BUFLEN;
+	spin_lock_irqsave(&portdata->lock, flags);
+	if (portdata->outstanding_urbs > N_OUT_URB) {
+		spin_unlock_irqrestore(&portdata->lock, flags);
+		dbg("%s - write limit hit\n", __FUNCTION__);
+		return 0;
+	}
+	portdata->outstanding_urbs++;
+	spin_unlock_irqrestore(&portdata->lock, flags);
 
-		this_urb = portdata->out_urbs[i];
-		if (this_urb->status == -EINPROGRESS) {
-			if (time_before(jiffies,
-					portdata->tx_start_time[i] + 10 * HZ))
-				continue;
-			usb_unlink_urb(this_urb);
-			continue;
-		}
-		if (this_urb->status != 0)
-			dbg("usb_write %p failed (err=%d)",
-				this_urb, this_urb->status);
-
-		dbg("%s: endpoint %d buf %d", __FUNCTION__,
-			usb_pipeendpoint(this_urb->pipe), i);
-
-		/* send the data */
-		memcpy (this_urb->transfer_buffer, buf, todo);
-		this_urb->transfer_buffer_length = todo;
-
-		this_urb->dev = port->serial->dev;
-		err = usb_submit_urb(this_urb, GFP_ATOMIC);
-		if (err) {
-			dbg("usb_submit_urb %p (write bulk) failed "
-				"(%d, has %d)", this_urb,
-				err, this_urb->status);
-			continue;
-		}
-		portdata->tx_start_time[i] = jiffies;
-		buf += todo;
-		left -= todo;
+	buffer = kmalloc(count, GFP_ATOMIC);
+	if (!buffer) {
+		dev_err(&port->dev, "out of memory\n");
+		count = -ENOMEM;
+		goto error_no_buffer;
 	}
 
-	count -= left;
-	dbg("%s: wrote (did %d)", __FUNCTION__, count);
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		dev_err(&port->dev, "no more free urbs\n");
+		count = -ENOMEM;
+		goto error_no_urb;
+	}
+
+	memcpy(buffer, buf, count);
+
+	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
+
+	usb_fill_bulk_urb(urb, serial->dev,
+			  usb_sndbulkpipe(serial->dev,
+					  port->bulk_out_endpointAddress),
+			  buffer, count, sierra_outdat_callback, port);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed "
+			"with status = %d\n", __FUNCTION__, status);
+		count = status;
+		goto error;
+	}
+
+	/* we are done with this urb, so let the host driver
+	 * really free it when it is finished with it */
+	usb_free_urb(urb);
+
+	return count;
+error:
+	usb_free_urb(urb);
+error_no_urb:
+	kfree(buffer);
+error_no_buffer:
+	spin_lock_irqsave(&portdata->lock, flags);
+	--portdata->outstanding_urbs;
+	spin_unlock_irqrestore(&portdata->lock, flags);
 	return count;
 }
 
@@ -262,15 +296,16 @@
 	struct usb_serial_port *port;
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
 
 	dbg("%s: %p", __FUNCTION__, urb);
 
 	endpoint = usb_pipeendpoint(urb->pipe);
 	port = (struct usb_serial_port *) urb->context;
 
-	if (urb->status) {
+	if (status) {
 		dbg("%s: nonzero status: %d on endpoint %02x.",
-		    __FUNCTION__, urb->status, endpoint);
+		    __FUNCTION__, status, endpoint);
 	} else {
 		tty = port->tty;
 		if (urb->actual_length) {
@@ -282,30 +317,20 @@
 		}
 
 		/* Resubmit urb so we continue receiving */
-		if (port->open_count && urb->status != -ESHUTDOWN) {
+		if (port->open_count && status != -ESHUTDOWN) {
 			err = usb_submit_urb(urb, GFP_ATOMIC);
 			if (err)
-				printk(KERN_ERR "%s: resubmit read urb failed. "
-					"(%d)", __FUNCTION__, err);
+				dev_err(&port->dev, "resubmit read urb failed."
+					"(%d)", err);
 		}
 	}
 	return;
 }
 
-static void sierra_outdat_callback(struct urb *urb)
-{
-	struct usb_serial_port *port;
-
-	dbg("%s", __FUNCTION__);
-
-	port = (struct usb_serial_port *) urb->context;
-
-	usb_serial_port_softint(port);
-}
-
 static void sierra_instat_callback(struct urb *urb)
 {
 	int err;
+	int status = urb->status;
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
 	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
 	struct usb_serial *serial = port->serial;
@@ -313,7 +338,7 @@
 	dbg("%s", __FUNCTION__);
 	dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
 
-	if (urb->status == 0) {
+	if (status == 0) {
 		struct usb_ctrlrequest *req_pkt =
 				(struct usb_ctrlrequest *)urb->transfer_buffer;
 
@@ -344,10 +369,10 @@
 				req_pkt->bRequestType,req_pkt->bRequest);
 		}
 	} else
-		dbg("%s: error %d", __FUNCTION__, urb->status);
+		dbg("%s: error %d", __FUNCTION__, status);
 
 	/* Resubmit urb so we continue receiving IRQ data */
-	if (urb->status != -ESHUTDOWN) {
+	if (status != -ESHUTDOWN) {
 		urb->dev = serial->dev;
 		err = usb_submit_urb(urb, GFP_ATOMIC);
 		if (err)
@@ -358,46 +383,42 @@
 
 static int sierra_write_room(struct usb_serial_port *port)
 {
-	struct sierra_port_private *portdata;
-	int i;
-	int data_len = 0;
-	struct urb *this_urb;
+	struct sierra_port_private *portdata = usb_get_serial_port_data(port);
+	unsigned long flags;
 
-	portdata = usb_get_serial_port_data(port);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	for (i=0; i < N_OUT_URB; i++) {
-		this_urb = portdata->out_urbs[i];
-		if (this_urb && this_urb->status != -EINPROGRESS)
-			data_len += OUT_BUFLEN;
+	/* try to give a good number back based on if we have any free urbs at
+	 * this point in time */
+	spin_lock_irqsave(&portdata->lock, flags);
+	if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) {
+		spin_unlock_irqrestore(&portdata->lock, flags);
+		dbg("%s - write limit hit\n", __FUNCTION__);
+		return 0;
 	}
+	spin_unlock_irqrestore(&portdata->lock, flags);
 
-	dbg("%s: %d", __FUNCTION__, data_len);
-	return data_len;
+	return 2048;
 }
 
 static int sierra_chars_in_buffer(struct usb_serial_port *port)
 {
-	struct sierra_port_private *portdata;
-	int i;
-	int data_len = 0;
-	struct urb *this_urb;
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	portdata = usb_get_serial_port_data(port);
-
-	for (i=0; i < N_OUT_URB; i++) {
-		this_urb = portdata->out_urbs[i];
-		if (this_urb && this_urb->status == -EINPROGRESS)
-			data_len += this_urb->transfer_buffer_length;
-	}
-	dbg("%s: %d", __FUNCTION__, data_len);
-	return data_len;
+	/*
+	 * We can't really account for how much data we
+	 * have sent out, but hasn't made it through to the
+	 * device as we can't see the backend here, so just
+	 * tell the tty layer that everything is flushed.
+	 */
+	return 0;
 }
 
 static int sierra_open(struct usb_serial_port *port, struct file *filp)
 {
 	struct sierra_port_private *portdata;
 	struct usb_serial *serial = port->serial;
-	int i, err;
+	int i;
 	struct urb *urb;
 	int result;
 	__u16 set_mode_dzero = 0x0000;
@@ -413,7 +434,7 @@
 	/* Reset low level data toggle and start reading from endpoints */
 	for (i = 0; i < N_IN_URB; i++) {
 		urb = portdata->in_urbs[i];
-		if (! urb)
+		if (!urb)
 			continue;
 		if (urb->dev != serial->dev) {
 			dbg("%s: dev %p != %p", __FUNCTION__,
@@ -427,24 +448,13 @@
 		 */
 		usb_clear_halt(urb->dev, urb->pipe);
 
-		err = usb_submit_urb(urb, GFP_KERNEL);
-		if (err) {
-			dbg("%s: submit urb %d failed (%d) %d",
-				__FUNCTION__, i, err,
-				urb->transfer_buffer_length);
+		result = usb_submit_urb(urb, GFP_KERNEL);
+		if (result) {
+			dev_err(&port->dev, "submit urb %d failed (%d) %d",
+				i, result, urb->transfer_buffer_length);
 		}
 	}
 
-	/* Reset low level data toggle on out endpoints */
-	for (i = 0; i < N_OUT_URB; i++) {
-		urb = portdata->out_urbs[i];
-		if (! urb)
-			continue;
-		urb->dev = serial->dev;
-		/* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
-				usb_pipeout(urb->pipe), 0); */
-	}
-
 	port->tty->low_latency = 1;
 
 	/* set mode to D0 */
@@ -455,7 +465,14 @@
 
 	sierra_send_setup(port);
 
-	return (0);
+	/* start up the interrupt endpoint if we have one */
+	if (port->interrupt_in_urb) {
+		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+		if (result)
+			dev_err(&port->dev, "submit irq_in urb failed %d",
+				result);
+	}
+	return 0;
 }
 
 static void sierra_close(struct usb_serial_port *port, struct file *filp)
@@ -475,71 +492,21 @@
 
 		/* Stop reading/writing urbs */
 		for (i = 0; i < N_IN_URB; i++)
-			usb_unlink_urb(portdata->in_urbs[i]);
-		for (i = 0; i < N_OUT_URB; i++)
-			usb_unlink_urb(portdata->out_urbs[i]);
+			usb_kill_urb(portdata->in_urbs[i]);
 	}
+
+	usb_kill_urb(port->interrupt_in_urb);
+
 	port->tty = NULL;
 }
 
-/* Helper functions used by sierra_setup_urbs */
-static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
-				    int dir, void *ctx, char *buf, int len,
-				    usb_complete_t callback)
-{
-	struct urb *urb;
-
-	if (endpoint == -1)
-		return NULL;		/* endpoint not needed */
-
-	urb = usb_alloc_urb(0, GFP_KERNEL);		/* No ISO */
-	if (urb == NULL) {
-		dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
-		return NULL;
-	}
-
-		/* Fill URB using supplied data. */
-	usb_fill_bulk_urb(urb, serial->dev,
-		      usb_sndbulkpipe(serial->dev, endpoint) | dir,
-		      buf, len, callback, ctx);
-
-	return urb;
-}
-
-/* Setup urbs */
-static void sierra_setup_urbs(struct usb_serial *serial)
-{
-	int i,j;
-	struct usb_serial_port *port;
-	struct sierra_port_private *portdata;
-
-	dbg("%s", __FUNCTION__);
-
-	for (i = 0; i < serial->num_ports; i++) {
-		port = serial->port[i];
-		portdata = usb_get_serial_port_data(port);
-
-	/* Do indat endpoints first */
-		for (j = 0; j < N_IN_URB; ++j) {
-			portdata->in_urbs[j] = sierra_setup_urb (serial,
-                  	port->bulk_in_endpointAddress, USB_DIR_IN, port,
-                  	portdata->in_buffer[j], IN_BUFLEN, sierra_indat_callback);
-		}
-
-		/* outdat endpoints */
-		for (j = 0; j < N_OUT_URB; ++j) {
-			portdata->out_urbs[j] = sierra_setup_urb (serial,
-                  	port->bulk_out_endpointAddress, USB_DIR_OUT, port,
-                  	portdata->out_buffer[j], OUT_BUFLEN, sierra_outdat_callback);
-		}
-	}
-}
-
 static int sierra_startup(struct usb_serial *serial)
 {
-	int i, err;
 	struct usb_serial_port *port;
 	struct sierra_port_private *portdata;
+	struct urb *urb;
+	int i;
+	int j;
 
 	dbg("%s", __FUNCTION__);
 
@@ -550,22 +517,31 @@
 		if (!portdata) {
 			dbg("%s: kmalloc for sierra_port_private (%d) failed!.",
 					__FUNCTION__, i);
-			return (1);
+			return -ENOMEM;
 		}
+		spin_lock_init(&portdata->lock);
 
 		usb_set_serial_port_data(port, portdata);
 
-		if (! port->interrupt_in_urb)
-			continue;
-		err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
-		if (err)
-			dbg("%s: submit irq_in urb failed %d",
-				__FUNCTION__, err);
+		/* initialize the in urbs */
+		for (j = 0; j < N_IN_URB; ++j) {
+			urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (urb == NULL) {
+				dbg("%s: alloc for in port failed.",
+				    __FUNCTION__);
+				continue;
+			}
+			/* Fill URB using supplied data. */
+			usb_fill_bulk_urb(urb, serial->dev,
+					  usb_rcvbulkpipe(serial->dev,
+						port->bulk_in_endpointAddress),
+					  portdata->in_buffer[j], IN_BUFLEN,
+					  sierra_indat_callback, port);
+			portdata->in_urbs[j] = urb;
+		}
 	}
 
-	sierra_setup_urbs(serial);
-
-	return (0);
+	return 0;
 }
 
 static void sierra_shutdown(struct usb_serial *serial)
@@ -576,22 +552,6 @@
 
 	dbg("%s", __FUNCTION__);
 
-	/* Stop reading/writing urbs */
-	for (i = 0; i < serial->num_ports; ++i) {
-		port = serial->port[i];
-		if (!port)
-			continue;
-		portdata = usb_get_serial_port_data(port);
-		if (!portdata)
-			continue;
-
-		for (j = 0; j < N_IN_URB; j++)
-			usb_unlink_urb(portdata->in_urbs[j]);
-		for (j = 0; j < N_OUT_URB; j++)
-			usb_unlink_urb(portdata->out_urbs[j]);
-	}
-
-	/* Now free them */
 	for (i = 0; i < serial->num_ports; ++i) {
 		port = serial->port[i];
 		if (!port)
@@ -601,25 +561,12 @@
 			continue;
 
 		for (j = 0; j < N_IN_URB; j++) {
-			if (portdata->in_urbs[j]) {
-				usb_free_urb(portdata->in_urbs[j]);
-				portdata->in_urbs[j] = NULL;
-			}
+			usb_kill_urb(portdata->in_urbs[j]);
+			usb_free_urb(portdata->in_urbs[j]);
+			portdata->in_urbs[j] = NULL;
 		}
-		for (j = 0; j < N_OUT_URB; j++) {
-			if (portdata->out_urbs[j]) {
-				usb_free_urb(portdata->out_urbs[j]);
-				portdata->out_urbs[j] = NULL;
-			}
-		}
-	}
-
-	/* Now free per port private data */
-	for (i = 0; i < serial->num_ports; i++) {
-		port = serial->port[i];
-		if (!port)
-			continue;
-		kfree(usb_get_serial_port_data(port));
+		kfree(portdata);
+		usb_set_serial_port_data(port, NULL);
 	}
 }
 
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 3d505fd..f98626a 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -1112,22 +1112,24 @@
 	int length = urb->actual_length;
 	int port_number;
 	int function;
-	int status;
+	int status = urb->status;
+	int retval;
 	__u8 msr;
 
 	dbg("%s", __FUNCTION__);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		break;
 	case -ECONNRESET:
 	case -ENOENT:
 	case -ESHUTDOWN:
-		dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down, %d", __FUNCTION__, status);
 		tdev->td_urb_error = 1;
 		return;
 	default:
-		dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status);
+		dev_err(dev, "%s - nonzero urb status, %d\n",
+			__FUNCTION__, status);
 		tdev->td_urb_error = 1;
 		goto exit;
 	}
@@ -1175,9 +1177,10 @@
 	}
 
 exit:
-	status = usb_submit_urb(urb, GFP_ATOMIC);
-	if (status)
-		dev_err(dev, "%s - resubmit interrupt urb failed, %d\n", __FUNCTION__, status);
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(dev, "%s - resubmit interrupt urb failed, %d\n",
+			__FUNCTION__, retval);
 }
 
 
@@ -1186,30 +1189,32 @@
 	struct ti_port *tport = (struct ti_port *)urb->context;
 	struct usb_serial_port *port = tport->tp_port;
 	struct device *dev = &urb->dev->dev;
-	int status = 0;
+	int status = urb->status;
+	int retval = 0;
 
 	dbg("%s", __FUNCTION__);
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		break;
 	case -ECONNRESET:
 	case -ENOENT:
 	case -ESHUTDOWN:
-		dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down, %d", __FUNCTION__, status);
 		tport->tp_tdev->td_urb_error = 1;
 		wake_up_interruptible(&tport->tp_write_wait);
 		return;
 	default:
-		dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status );
+		dev_err(dev, "%s - nonzero urb status, %d\n",
+			__FUNCTION__, status );
 		tport->tp_tdev->td_urb_error = 1;
 		wake_up_interruptible(&tport->tp_write_wait);
 	}
 
-	if (urb->status == -EPIPE)
+	if (status == -EPIPE)
 		goto exit;
 
-	if (urb->status) {
+	if (status) {
 		dev_err(dev, "%s - stopping read!\n", __FUNCTION__);
 		return;
 	}
@@ -1234,13 +1239,14 @@
 	spin_lock(&tport->tp_lock);
 	if (tport->tp_read_urb_state == TI_READ_URB_RUNNING) {
 		urb->dev = port->serial->dev;
-		status = usb_submit_urb(urb, GFP_ATOMIC);
+		retval = usb_submit_urb(urb, GFP_ATOMIC);
 	} else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING) {
 		tport->tp_read_urb_state = TI_READ_URB_STOPPED;
 	}
 	spin_unlock(&tport->tp_lock);
-	if (status)
-		dev_err(dev, "%s - resubmit read urb failed, %d\n", __FUNCTION__, status);
+	if (retval)
+		dev_err(dev, "%s - resubmit read urb failed, %d\n",
+			__FUNCTION__, retval);
 }
 
 
@@ -1249,23 +1255,25 @@
 	struct ti_port *tport = (struct ti_port *)urb->context;
 	struct usb_serial_port *port = tport->tp_port;
 	struct device *dev = &urb->dev->dev;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	tport->tp_write_urb_in_use = 0;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		break;
 	case -ECONNRESET:
 	case -ENOENT:
 	case -ESHUTDOWN:
-		dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status);
+		dbg("%s - urb shutting down, %d", __FUNCTION__, status);
 		tport->tp_tdev->td_urb_error = 1;
 		wake_up_interruptible(&tport->tp_write_wait);
 		return;
 	default:
-		dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status);
+		dev_err(dev, "%s - nonzero urb status, %d\n",
+			__FUNCTION__, status);
 		tport->tp_tdev->td_urb_error = 1;
 		wake_up_interruptible(&tport->tp_write_wait);
 	}
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 87f3788..a366565 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -46,6 +46,8 @@
 	.name =		"usbserial",
 	.probe =	usb_serial_probe,
 	.disconnect =	usb_serial_disconnect,
+	.suspend =	usb_serial_suspend,
+	.resume =	usb_serial_resume,
 	.no_dynamic_id = 	1,
 };
 
@@ -120,11 +122,9 @@
 	if (serial == NULL)
 		return;
 
-	spin_lock(&table_lock);
 	for (i = 0; i < serial->num_ports; ++i) {
 		serial_table[serial->minor + i] = NULL;
 	}
-	spin_unlock(&table_lock);
 }
 
 static void destroy_serial(struct kref *kref)
@@ -172,7 +172,9 @@
 
 void usb_serial_put(struct usb_serial *serial)
 {
+	spin_lock(&table_lock);
 	kref_put(&serial->kref, destroy_serial);
+	spin_unlock(&table_lock);
 }
 
 /*****************************************************************************
@@ -1069,6 +1071,35 @@
 	dev_info(dev, "device disconnected\n");
 }
 
+int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+	struct usb_serial_port *port;
+	int i, r = 0;
+
+	if (serial) {
+		for (i = 0; i < serial->num_ports; ++i) {
+			port = serial->port[i];
+			if (port)
+				kill_traffic(port);
+		}
+	}
+
+	if (serial->type->suspend)
+		serial->type->suspend(serial, message);
+
+	return r;
+}
+EXPORT_SYMBOL(usb_serial_suspend);
+
+int usb_serial_resume(struct usb_interface *intf)
+{
+	struct usb_serial *serial = usb_get_intfdata(intf);
+
+	return serial->type->resume(serial);
+}
+EXPORT_SYMBOL(usb_serial_resume);
+
 static const struct tty_operations serial_ops = {
 	.open =			serial_open,
 	.close =		serial_close,
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index ffbe601..7d84a76 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -5,9 +5,9 @@
  *	Copyright (C) 1999 - 2004
  *	    Greg Kroah-Hartman (greg@kroah.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; either version 2 of the License.
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License version
+ *	2 as published by the Free Software Foundation.
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
@@ -273,7 +273,8 @@
 	int bytes_in;
 	int bytes_out;
 	int outstanding_urbs;
-	int throttled;
+	unsigned char throttled;
+	unsigned char actually_throttled;
 };
 
 /* number of outstanding urbs to prevent userspace DoS from happening */
@@ -484,16 +485,17 @@
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct visor_private *priv = usb_get_serial_port_data(port);
+	int status = urb->status;
 	unsigned long flags;
 
 	/* free up the transfer buffer, as usb_free_urb() does not do this */
 	kfree (urb->transfer_buffer);
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
-	
-	if (urb->status)
+
+	if (status)
 		dbg("%s - nonzero write bulk status received: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 
 	spin_lock_irqsave(&priv->lock, flags);
 	--priv->outstanding_urbs;
@@ -508,15 +510,16 @@
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct visor_private *priv = usb_get_serial_port_data(port);
 	unsigned char *data = urb->transfer_buffer;
+	int status = urb->status;
 	struct tty_struct *tty;
-	unsigned long flags;
-	int throttled;
 	int result;
+	int available_room;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -524,17 +527,20 @@
 
 	tty = port->tty;
 	if (tty && urb->actual_length) {
-		tty_buffer_request_room(tty, urb->actual_length);
-		tty_insert_flip_string(tty, data, urb->actual_length);
-		tty_flip_buffer_push(tty);
+		available_room = tty_buffer_request_room(tty, urb->actual_length);
+		if (available_room) {
+			tty_insert_flip_string(tty, data, available_room);
+			tty_flip_buffer_push(tty);
+		}
+		spin_lock(&priv->lock);
+		priv->bytes_in += available_room;
+
+	} else {
+		spin_lock(&priv->lock);
 	}
-	spin_lock_irqsave(&priv->lock, flags);
-	priv->bytes_in += urb->actual_length;
-	throttled = priv->throttled;
-	spin_unlock_irqrestore(&priv->lock, flags);
 
 	/* Continue trying to always read if we should */
-	if (!throttled) {
+	if (!priv->throttled) {
 		usb_fill_bulk_urb (port->read_urb, port->serial->dev,
 				   usb_rcvbulkpipe(port->serial->dev,
 						   port->bulk_in_endpointAddress),
@@ -544,16 +550,19 @@
 		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
 		if (result)
 			dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+	} else {
+		priv->actually_throttled = 1;
 	}
-	return;
+	spin_unlock(&priv->lock);
 }
 
 static void visor_read_int_callback (struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	int status = urb->status;
 	int result;
 
-	switch (urb->status) {
+	switch (status) {
 	case 0:
 		/* success */
 		break;
@@ -562,11 +571,11 @@
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
 		dbg("%s - urb shutting down with status: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 		return;
 	default:
 		dbg("%s - nonzero urb status received: %d",
-		    __FUNCTION__, urb->status);
+		    __FUNCTION__, status);
 		goto exit;
 	}
 
@@ -608,6 +617,7 @@
 	dbg("%s - port %d", __FUNCTION__, port->number);
 	spin_lock_irqsave(&priv->lock, flags);
 	priv->throttled = 0;
+	priv->actually_throttled = 0;
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	port->read_urb->dev = port->serial->dev;
@@ -938,14 +948,6 @@
 	}
 
 	cflag = port->tty->termios->c_cflag;
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((cflag == old_termios->c_cflag) &&
-		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg("%s - nothing to change...", __FUNCTION__);
-			return;
-		}
-	}
 
 	/* get the byte size */
 	switch (cflag & CSIZE) {
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 27c5f8f..cc8b44c 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -74,6 +74,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <asm/uaccess.h>
 #include <asm/termbits.h>
 #include <linux/usb.h>
@@ -203,7 +204,7 @@
 
 
 struct whiteheat_command_private {
-	spinlock_t		lock;
+	struct mutex		mutex;
 	__u8			port_running;
 	__u8			command_finished;
 	wait_queue_head_t	wait_command;	/* for handling sleeping while waiting for a command to finish */
@@ -232,6 +233,7 @@
 	struct usb_serial_port	*port;
 	struct list_head	tx_urbs_free;
 	struct list_head	tx_urbs_submitted;
+	struct mutex		deathwarrant;
 };
 
 
@@ -425,6 +427,7 @@
 		}
 
 		spin_lock_init(&info->lock);
+		mutex_init(&info->deathwarrant);
 		info->flags = 0;
 		info->mcr = 0;
 		INIT_WORK(&info->rx_work, rx_data_softint);
@@ -495,7 +498,7 @@
 		goto no_command_private;
 	}
 
-	spin_lock_init(&command_info->lock);
+	mutex_init(&command_info->mutex);
 	command_info->port_running = 0;
 	init_waitqueue_head(&command_info->wait_command);
 	usb_set_serial_port_data(command_port, command_info);
@@ -654,7 +657,6 @@
 	struct urb *urb;
 	struct list_head *tmp;
 	struct list_head *tmp2;
-	unsigned long flags;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 	
@@ -683,24 +685,32 @@
 
 	firm_close(port);
 
+printk(KERN_ERR"Before processing rx_urbs_submitted.\n");
 	/* shutdown our bulk reads and writes */
-	spin_lock_irqsave(&info->lock, flags);
+	mutex_lock(&info->deathwarrant);
+	spin_lock_irq(&info->lock);
 	list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
+		list_del(tmp);
+		spin_unlock_irq(&info->lock);
 		usb_kill_urb(urb);
-		list_move(tmp, &info->rx_urbs_free);
+		spin_lock_irq(&info->lock);
+		list_add(tmp, &info->rx_urbs_free);
 	}
 	list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
 		list_move(tmp, &info->rx_urbs_free);
-
 	list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
+		list_del(tmp);
+		spin_unlock_irq(&info->lock);
 		usb_kill_urb(urb);
-		list_move(tmp, &info->tx_urbs_free);
+		spin_lock_irq(&info->lock);
+		list_add(tmp, &info->tx_urbs_free);
 	}
-	spin_unlock_irqrestore(&info->lock, flags);
+	spin_unlock_irq(&info->lock);
+	mutex_unlock(&info->deathwarrant);
 
 	stop_command_port(port->serial);
 
@@ -872,7 +882,7 @@
 }
 
 
-static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios *old_termios)
+static void whiteheat_set_termios(struct usb_serial_port *port, struct ktermios *old_termios)
 {
 	dbg("%s -port %d", __FUNCTION__, port->number);
 
@@ -881,15 +891,6 @@
 		goto exit;
 	}
 	
-	/* check that they really want us to change something */
-	if (old_termios) {
-		if ((port->tty->termios->c_cflag == old_termios->c_cflag) &&
-		    (port->tty->termios->c_iflag == old_termios->c_iflag)) {
-			dbg("%s - nothing to change...", __FUNCTION__);
-			goto exit;
-		}
-	}
-
 	firm_setup_port(port);
 
 exit:
@@ -920,7 +921,7 @@
 	spin_unlock_irqrestore(&info->lock, flags);
 
 	dbg ("%s - returns %d", __FUNCTION__, chars);
-	return (chars);
+	return chars;
 }
 
 
@@ -962,54 +963,57 @@
 /*****************************************************************************
  * Connect Tech's White Heat callback routines
  *****************************************************************************/
-static void command_port_write_callback (struct urb *urb)
+static void command_port_write_callback(struct urb *urb)
 {
+	int status = urb->status;
+
 	dbg("%s", __FUNCTION__);
 
-	if (urb->status) {
-		dbg ("nonzero urb status: %d", urb->status);
+	if (status) {
+		dbg("nonzero urb status: %d", status);
 		return;
 	}
 }
 
 
-static void command_port_read_callback (struct urb *urb)
+static void command_port_read_callback(struct urb *urb)
 {
 	struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context;
 	struct whiteheat_command_private *command_info;
+	int status = urb->status;
 	unsigned char *data = urb->transfer_buffer;
 	int result;
-	unsigned long flags;
 
 	dbg("%s", __FUNCTION__);
 
-	if (urb->status) {
-		dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status);
-		return;
-	}
-
-	usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data);
-
 	command_info = usb_get_serial_port_data(command_port);
 	if (!command_info) {
 		dbg ("%s - command_info is NULL, exiting.", __FUNCTION__);
 		return;
 	}
-	spin_lock_irqsave(&command_info->lock, flags);
+	if (status) {
+		dbg("%s - nonzero urb status: %d", __FUNCTION__, status);
+		if (status != -ENOENT)
+			command_info->command_finished = WHITEHEAT_CMD_FAILURE;
+		wake_up(&command_info->wait_command);
+		return;
+	}
+
+	usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data);
 
 	if (data[0] == WHITEHEAT_CMD_COMPLETE) {
 		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
-		wake_up_interruptible(&command_info->wait_command);
+		wake_up(&command_info->wait_command);
 	} else if (data[0] == WHITEHEAT_CMD_FAILURE) {
 		command_info->command_finished = WHITEHEAT_CMD_FAILURE;
-		wake_up_interruptible(&command_info->wait_command);
+		wake_up(&command_info->wait_command);
 	} else if (data[0] == WHITEHEAT_EVENT) {
 		/* These are unsolicited reports from the firmware, hence no waiting command to wakeup */
 		dbg("%s - event received", __FUNCTION__);
 	} else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
 		memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1);
 		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
-		wake_up_interruptible(&command_info->wait_command);
+		wake_up(&command_info->wait_command);
 	} else {
 		dbg("%s - bad reply from firmware", __FUNCTION__);
 	}
@@ -1017,7 +1021,6 @@
 	/* Continue trying to always read */
 	command_port->read_urb->dev = command_port->serial->dev;
 	result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
-	spin_unlock_irqrestore(&command_info->lock, flags);
 	if (result)
 		dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
 }
@@ -1029,6 +1032,7 @@
 	struct whiteheat_urb_wrap *wrap;
 	unsigned char *data = urb->transfer_buffer;
 	struct whiteheat_private *info = usb_get_serial_port_data(port);
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -1042,8 +1046,9 @@
 	list_del(&wrap->list);
 	spin_unlock(&info->lock);
 
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero read bulk status received: %d",
+		    __FUNCTION__, status);
 		spin_lock(&info->lock);
 		list_add(&wrap->list, &info->rx_urbs_free);
 		spin_unlock(&info->lock);
@@ -1070,6 +1075,7 @@
 	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
 	struct whiteheat_private *info = usb_get_serial_port_data(port);
 	struct whiteheat_urb_wrap *wrap;
+	int status = urb->status;
 
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -1083,8 +1089,9 @@
 	list_move(&wrap->list, &info->tx_urbs_free);
 	spin_unlock(&info->lock);
 
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+	if (status) {
+		dbg("%s - nonzero write bulk status received: %d",
+		    __FUNCTION__, status);
 		return;
 	}
 
@@ -1095,20 +1102,20 @@
 /*****************************************************************************
  * Connect Tech's White Heat firmware interface
  *****************************************************************************/
-static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize)
+static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize)
 {
 	struct usb_serial_port *command_port;
 	struct whiteheat_command_private *command_info;
 	struct whiteheat_private *info;
 	__u8 *transfer_buffer;
 	int retval = 0;
-	unsigned long flags;
+	int t;
 
 	dbg("%s - command %d", __FUNCTION__, command);
 
 	command_port = port->serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
-	spin_lock_irqsave(&command_info->lock, flags);
+	mutex_lock(&command_info->mutex);
 	command_info->command_finished = false;
 	
 	transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
@@ -1116,18 +1123,17 @@
 	memcpy (&transfer_buffer[1], data, datasize);
 	command_port->write_urb->transfer_buffer_length = datasize + 1;
 	command_port->write_urb->dev = port->serial->dev;
-	retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL);
+	retval = usb_submit_urb (command_port->write_urb, GFP_NOIO);
 	if (retval) {
 		dbg("%s - submit urb failed", __FUNCTION__);
 		goto exit;
 	}
-	spin_unlock_irqrestore(&command_info->lock, flags);
 
 	/* wait for the command to complete */
-	wait_event_interruptible_timeout(command_info->wait_command,
+	t = wait_event_timeout(command_info->wait_command,
 		(bool)command_info->command_finished, COMMAND_TIMEOUT);
-
-	spin_lock_irqsave(&command_info->lock, flags);
+	if (!t)
+		usb_kill_urb(command_port->write_urb);
 
 	if (command_info->command_finished == false) {
 		dbg("%s - command timed out.", __FUNCTION__);
@@ -1152,7 +1158,7 @@
 	}
 
 exit:
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	mutex_unlock(&command_info->mutex);
 	return retval;
 }
 
@@ -1305,12 +1311,11 @@
 {
 	struct usb_serial_port *command_port;
 	struct whiteheat_command_private *command_info;
-	unsigned long flags;
 	int retval = 0;
 	
 	command_port = serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
-	spin_lock_irqsave(&command_info->lock, flags);
+	mutex_lock(&command_info->mutex);
 	if (!command_info->port_running) {
 		/* Work around HCD bugs */
 		usb_clear_halt(serial->dev, command_port->read_urb->pipe);
@@ -1325,7 +1330,7 @@
 	command_info->port_running++;
 
 exit:
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	mutex_unlock(&command_info->mutex);
 	return retval;
 }
 
@@ -1334,15 +1339,14 @@
 {
 	struct usb_serial_port *command_port;
 	struct whiteheat_command_private *command_info;
-	unsigned long flags;
 
 	command_port = serial->port[COMMAND_PORT];
 	command_info = usb_get_serial_port_data(command_port);
-	spin_lock_irqsave(&command_info->lock, flags);
+	mutex_lock(&command_info->mutex);
 	command_info->port_running--;
 	if (!command_info->port_running)
 		usb_kill_urb(command_port->read_urb);
-	spin_unlock_irqrestore(&command_info->lock, flags);
+	mutex_unlock(&command_info->mutex);
 }
 
 
@@ -1363,17 +1367,23 @@
 		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 		urb = wrap->urb;
 		urb->dev = port->serial->dev;
+		spin_unlock_irqrestore(&info->lock, flags);
 		retval = usb_submit_urb(urb, GFP_KERNEL);
 		if (retval) {
+			spin_lock_irqsave(&info->lock, flags);
 			list_add(tmp, &info->rx_urbs_free);
 			list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
 				wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
 				urb = wrap->urb;
+				list_del(tmp);
+				spin_unlock_irqrestore(&info->lock, flags);
 				usb_kill_urb(urb);
-				list_move(tmp, &info->rx_urbs_free);
+				spin_lock_irqsave(&info->lock, flags);
+				list_add(tmp, &info->rx_urbs_free);
 			}
 			break;
 		}
+		spin_lock_irqsave(&info->lock, flags);
 		list_add(tmp, &info->rx_urbs_submitted);
 	}
 
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index e227f64..47e5607 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -285,10 +285,15 @@
 
 	US_DEBUGP("%s called\n", __FUNCTION__);
 
-	/* lock the device pointers and do the reset */
-	mutex_lock(&(us->dev_mutex));
-	result = us->transport_reset(us);
-	mutex_unlock(&us->dev_mutex);
+	result = usb_autopm_get_interface(us->pusb_intf);
+	if (result == 0) {
+
+		/* lock the device pointers and do the reset */
+		mutex_lock(&(us->dev_mutex));
+		result = us->transport_reset(us);
+		mutex_unlock(&us->dev_mutex);
+		usb_autopm_put_interface(us->pusb_intf);
+	}
 
 	return result < 0 ? FAILED : SUCCESS;
 }
@@ -321,10 +326,14 @@
 
 /* Report a driver-initiated bus reset to the SCSI layer.
  * Calling this for a SCSI-initiated reset is unnecessary but harmless.
- * The caller must own the SCSI host lock. */
+ * The caller must not own the SCSI host lock. */
 void usb_stor_report_bus_reset(struct us_data *us)
 {
-	scsi_report_bus_reset(us_to_host(us), 0);
+	struct Scsi_Host *host = us_to_host(us);
+
+	scsi_lock(host);
+	scsi_report_bus_reset(host, 0);
+	scsi_unlock(host);
 }
 
 /***********************************************************************
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 54979c2..b6bf31a 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -50,10 +50,10 @@
 /* patch submitted by Vivian Bregier <Vivian.Bregier@imag.fr>
  */
 UNUSUAL_DEV(  0x03eb, 0x2002, 0x0100, 0x0100,
-                "ATMEL",
-                "SND1 Storage",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_IGNORE_RESIDUE),
+		"ATMEL",
+		"SND1 Storage",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_IGNORE_RESIDUE),
 
 /* modified by Tobias Lorenz <tobias.lorenz@gmx.net> */
 UNUSUAL_DEV(  0x03ee, 0x6901, 0x0000, 0x0200,
@@ -69,18 +69,18 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE ),
 
-UNUSUAL_DEV(  0x03f0, 0x0107, 0x0200, 0x0200, 
+UNUSUAL_DEV(  0x03f0, 0x0107, 0x0200, 0x0200,
 		"HP",
 		"CD-Writer+",
-		US_SC_8070, US_PR_CB, NULL, 0), 
+		US_SC_8070, US_PR_CB, NULL, 0),
 
 #ifdef CONFIG_USB_STORAGE_USBAT
-UNUSUAL_DEV(  0x03f0, 0x0207, 0x0001, 0x0001, 
+UNUSUAL_DEV(  0x03f0, 0x0207, 0x0001, 0x0001,
 		"HP",
 		"CD-Writer+ 8200e",
 		US_SC_8070, US_PR_USBAT, init_usbat_cd, 0),
 
-UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001, 
+UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001,
 		"HP",
 		"CD-Writer+ CD-4e",
 		US_SC_8070, US_PR_USBAT, init_usbat_cd, 0),
@@ -115,10 +115,10 @@
 
 /* Submitted by Ernestas Vaiciukevicius <ernisv@gmail.com> */
 UNUSUAL_DEV(  0x0419, 0x0100, 0x0100, 0x0100,
-                "Samsung Info. Systems America, Inc.",
-                "MP3 Player",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_IGNORE_RESIDUE ),
+		"Samsung Info. Systems America, Inc.",
+		"MP3 Player",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_IGNORE_RESIDUE ),
 
 /* Reported by Orgad Shaneh <orgads@gmail.com> */
 UNUSUAL_DEV(  0x0419, 0xaace, 0x0100, 0x0100,
@@ -256,10 +256,10 @@
 * the revision to my model only
 */
 UNUSUAL_DEV(  0x0457, 0x0151, 0x0100, 0x0100,
-                "USB 2.0",
-                "Flash Disk",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_NOT_LOCKABLE ),
+		"USB 2.0",
+		"Flash Disk",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_NOT_LOCKABLE ),
 
 #ifdef CONFIG_USB_STORAGE_KARMA
 UNUSUAL_DEV(  0x045a, 0x5210, 0x0101, 0x0101,
@@ -408,19 +408,19 @@
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
  */
-UNUSUAL_DEV(  0x04e6, 0x0001, 0x0200, 0x0200, 
+UNUSUAL_DEV(  0x04e6, 0x0001, 0x0200, 0x0200,
 		"Matshita",
 		"LS-120",
 		US_SC_8020, US_PR_CB, NULL, 0),
 
-UNUSUAL_DEV(  0x04e6, 0x0002, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x04e6, 0x0002, 0x0100, 0x0100,
 		"Shuttle",
 		"eUSCSI Bridge",
 		US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init, 
-		US_FL_SCM_MULT_TARG ), 
+		US_FL_SCM_MULT_TARG ),
 
 #ifdef CONFIG_USB_STORAGE_SDDR09
-UNUSUAL_DEV(  0x04e6, 0x0003, 0x0000, 0x9999, 
+UNUSUAL_DEV(  0x04e6, 0x0003, 0x0000, 0x9999,
 		"Sandisk",
 		"ImageMate SDDR09",
 		US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,
@@ -431,52 +431,52 @@
 		"SCM Microsystems",
 		"eUSB SmartMedia / CompactFlash Adapter",
 		US_SC_SCSI, US_PR_DPCM_USB, usb_stor_sddr09_dpcm_init,
-		0), 
+		0),
 #endif
 
 /* Reported by Markus Demleitner <msdemlei@cl.uni-heidelberg.de> */
-UNUSUAL_DEV(  0x04e6, 0x0006, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x04e6, 0x0006, 0x0100, 0x0100,
 		"SCM Microsystems Inc.",
 		"eUSB MMC Adapter",
-		US_SC_SCSI, US_PR_CB, NULL, 
-		US_FL_SINGLE_LUN), 
+		US_SC_SCSI, US_PR_CB, NULL,
+		US_FL_SINGLE_LUN),
 
 /* Reported by Daniel Nouri <dpunktnpunkt@web.de> */
-UNUSUAL_DEV(  0x04e6, 0x0006, 0x0205, 0x0205, 
+UNUSUAL_DEV(  0x04e6, 0x0006, 0x0205, 0x0205,
 		"Shuttle",
 		"eUSB MMC Adapter",
-		US_SC_SCSI, US_PR_DEVICE, NULL, 
-		US_FL_SINGLE_LUN), 
+		US_SC_SCSI, US_PR_DEVICE, NULL,
+		US_FL_SINGLE_LUN),
 
-UNUSUAL_DEV(  0x04e6, 0x0007, 0x0100, 0x0200, 
+UNUSUAL_DEV(  0x04e6, 0x0007, 0x0100, 0x0200,
 		"Sony",
 		"Hifd",
-		US_SC_SCSI, US_PR_CB, NULL, 
-		US_FL_SINGLE_LUN), 
+		US_SC_SCSI, US_PR_CB, NULL,
+		US_FL_SINGLE_LUN),
 
-UNUSUAL_DEV(  0x04e6, 0x0009, 0x0200, 0x0200, 
+UNUSUAL_DEV(  0x04e6, 0x0009, 0x0200, 0x0200,
 		"Shuttle",
 		"eUSB ATA/ATAPI Adapter",
 		US_SC_8020, US_PR_CB, NULL, 0),
 
-UNUSUAL_DEV(  0x04e6, 0x000a, 0x0200, 0x0200, 
+UNUSUAL_DEV(  0x04e6, 0x000a, 0x0200, 0x0200,
 		"Shuttle",
 		"eUSB CompactFlash Adapter",
 		US_SC_8020, US_PR_CB, NULL, 0),
 
-UNUSUAL_DEV(  0x04e6, 0x000B, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x04e6, 0x000B, 0x0100, 0x0100,
 		"Shuttle",
 		"eUSCSI Bridge",
-		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, 
+		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ), 
 
-UNUSUAL_DEV(  0x04e6, 0x000C, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x04e6, 0x000C, 0x0100, 0x0100,
 		"Shuttle",
 		"eUSCSI Bridge",
-		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, 
-		US_FL_SCM_MULT_TARG ), 
+		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+		US_FL_SCM_MULT_TARG ),
 
-UNUSUAL_DEV(  0x04e6, 0x0101, 0x0200, 0x0200, 
+UNUSUAL_DEV(  0x04e6, 0x0101, 0x0200, 0x0200,
 		"Shuttle",
 		"CD-RW Device",
 		US_SC_8020, US_PR_CB, NULL, 0),
@@ -556,9 +556,9 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE ),
 
-UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0450, 
+UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0450,
 		"Sony",
-		"DSC-S30/S70/S75/505V/F505/F707/F717/P8", 
+		"DSC-S30/S70/S75/505V/F505/F707/F717/P8",
 		US_SC_SCSI, US_PR_DEVICE, NULL,
 		US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE | US_FL_NO_WP_DETECT ),
 
@@ -572,7 +572,7 @@
 
 
 /* Reported by wim@geeks.nl */
-UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100,
 		"Sony",
 		"Memorystick NW-MS7",
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
@@ -593,21 +593,21 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_SINGLE_LUN ),
 
-UNUSUAL_DEV(  0x054c, 0x002d, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x054c, 0x002d, 0x0100, 0x0100,
 		"Sony",
 		"Memorystick MSAC-US1",
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_SINGLE_LUN ),
 
 /* Submitted by Klaus Mueller <k.mueller@intershop.de> */
-UNUSUAL_DEV(  0x054c, 0x002e, 0x0106, 0x0310, 
+UNUSUAL_DEV(  0x054c, 0x002e, 0x0106, 0x0310,
 		"Sony",
 		"Handycam",
 		US_SC_SCSI, US_PR_DEVICE, NULL,
 		US_FL_SINGLE_LUN ),
 
 /* Submitted by Rajesh Kumble Nayak <nayak@obs-nice.fr> */
-UNUSUAL_DEV(  0x054c, 0x002e, 0x0500, 0x0500, 
+UNUSUAL_DEV(  0x054c, 0x002e, 0x0500, 0x0500,
 		"Sony",
 		"Handycam HC-85",
 		US_SC_UFI, US_PR_DEVICE, NULL,
@@ -648,26 +648,26 @@
 		
 /* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */
 UNUSUAL_DEV(  0x054c, 0x0099, 0x0000, 0x9999,
-                "Sony",
-                "PEG Mass Storage",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_FIX_INQUIRY ),
+		"Sony",
+		"PEG Mass Storage",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_INQUIRY ),
 
 /* floppy reports multiple luns */
 UNUSUAL_DEV(  0x055d, 0x2020, 0x0000, 0x0210,
-               "SAMSUNG",
-               "SFD-321U [FW 0C]",
-               US_SC_DEVICE, US_PR_DEVICE, NULL,
-               US_FL_SINGLE_LUN ),
+		"SAMSUNG",
+		"SFD-321U [FW 0C]",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_SINGLE_LUN ),
 
 		
-UNUSUAL_DEV(  0x057b, 0x0000, 0x0000, 0x0299, 
+UNUSUAL_DEV(  0x057b, 0x0000, 0x0000, 0x0299,
 		"Y-E Data",
 		"Flashbuster-U",
 		US_SC_DEVICE,  US_PR_CB, NULL,
 		US_FL_SINGLE_LUN),
 
-UNUSUAL_DEV(  0x057b, 0x0000, 0x0300, 0x9999, 
+UNUSUAL_DEV(  0x057b, 0x0000, 0x0300, 0x9999,
 		"Y-E Data",
 		"Flashbuster-U",
 		US_SC_DEVICE,  US_PR_DEVICE, NULL,
@@ -677,7 +677,7 @@
  * This entry is needed only because the device reports
  * bInterfaceClass = 0xff (vendor-specific)
  */
-UNUSUAL_DEV(  0x057b, 0x0022, 0x0000, 0x9999, 
+UNUSUAL_DEV(  0x057b, 0x0022, 0x0000, 0x9999,
 		"Y-E Data",
 		"Silicon Media R/W",
 		US_SC_DEVICE, US_PR_DEVICE, NULL, 0),
@@ -825,13 +825,13 @@
 		US_SC_SCSI, US_PR_BULK, NULL,
 		US_FL_FIX_INQUIRY ),
 
-UNUSUAL_DEV(  0x0644, 0x0000, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x0644, 0x0000, 0x0100, 0x0100,
 		"TEAC",
 		"Floppy Drive",
-		US_SC_UFI, US_PR_CB, NULL, 0 ), 
+		US_SC_UFI, US_PR_CB, NULL, 0 ),
 
 #ifdef CONFIG_USB_STORAGE_SDDR09
-UNUSUAL_DEV(  0x066b, 0x0105, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x066b, 0x0105, 0x0100, 0x0100,
 		"Olympus",
 		"Camedia MAUSB-2",
 		US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,
@@ -867,14 +867,14 @@
 
 /* Reported by Miguel A. Fosas <amn3s1a@ono.com> */
 UNUSUAL_DEV(  0x0686, 0x4017, 0x0001, 0x0001,
-                "Minolta",
-                "DIMAGE E223",
-                US_SC_SCSI, US_PR_DEVICE, NULL, 0 ),
+		"Minolta",
+		"DIMAGE E223",
+		US_SC_SCSI, US_PR_DEVICE, NULL, 0 ),
 
 UNUSUAL_DEV(  0x0693, 0x0005, 0x0100, 0x0100,
 		"Hagiwara",
 		"Flashgate",
-		US_SC_SCSI, US_PR_BULK, NULL, 0 ), 
+		US_SC_SCSI, US_PR_BULK, NULL, 0 ),
 
 /* Reported by David Hamilton <niftimusmaximus@lycos.com> */
 UNUSUAL_DEV(  0x069b, 0x3004, 0x0001, 0x0001,
@@ -918,7 +918,7 @@
 		US_FL_SINGLE_LUN ),
 
 #ifdef CONFIG_USB_STORAGE_SDDR09
-UNUSUAL_DEV(  0x0781, 0x0200, 0x0000, 0x9999, 
+UNUSUAL_DEV(  0x0781, 0x0200, 0x0000, 0x9999,
 		"Sandisk",
 		"ImageMate SDDR-09",
 		US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init,
@@ -939,17 +939,17 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_CAPACITY),
 
-UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133, 
+UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133,
 		"Microtech",
 		"USB-SCSI-DB25",
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ), 
 
-UNUSUAL_DEV(  0x07af, 0x0005, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x07af, 0x0005, 0x0100, 0x0100,
 		"Microtech",
 		"USB-SCSI-HD50",
 		US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,
-		US_FL_SCM_MULT_TARG ), 
+		US_FL_SCM_MULT_TARG ),
 
 #ifdef CONFIG_USB_STORAGE_DPCM
 UNUSUAL_DEV(  0x07af, 0x0006, 0x0100, 0x0100,
@@ -1053,10 +1053,10 @@
  * as "DualSlot CompactFlash(TM) & MStick Drive USB"
  */
 UNUSUAL_DEV(  0x07c4, 0xa10b, 0x0000, 0xffff,
-                "DataFab Systems Inc.",
-                "USB CF+MS",
-                US_SC_SCSI, US_PR_DATAFAB, NULL,
-                0 ),
+		"DataFab Systems Inc.",
+		"USB CF+MS",
+		US_SC_SCSI, US_PR_DATAFAB, NULL,
+		0 ),
 
 #endif
 
@@ -1119,10 +1119,10 @@
  * US_FL_IGNORE_RESIDUE Needed
  */
 UNUSUAL_DEV(  0x08ca, 0x3103, 0x0100, 0x0100,
-                "AIPTEK",
-                "Aiptek USB Keychain MP3 Player",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_IGNORE_RESIDUE),
+		"AIPTEK",
+		"Aiptek USB Keychain MP3 Player",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_IGNORE_RESIDUE),
 
 /* Entry needed for flags. Moreover, all devices with this ID use
  * bulk-only transport, but _some_ falsely report Control/Bulk instead.
@@ -1166,26 +1166,26 @@
  * Submitted by James Courtier-Dutton <James@superbug.demon.co.uk>
  */
 UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000,
-                "Pentax",
-                "Optio 2/3/400",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_FIX_INQUIRY ),
+		"Pentax",
+		"Optio 2/3/400",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_INQUIRY ),
 
 
 /* Submitted by Per Winkvist <per.winkvist@uk.com> */
 UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff,
-                "Pentax",
-                "Optio S/S4",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_FIX_INQUIRY ),
+		"Pentax",
+		"Optio S/S4",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_INQUIRY ),
 
 /* These are virtual windows driver CDs, which the zd1211rw driver
  * automatically converts into WLAN devices. */
 UNUSUAL_DEV( 0x0ace, 0x2011, 0x0101, 0x0101,
-                "ZyXEL",
-                "G-220F USB-WLAN Install",
-                US_SC_DEVICE, US_PR_DEVICE, NULL,
-                US_FL_IGNORE_DEVICE ),
+		"ZyXEL",
+		"G-220F USB-WLAN Install",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_IGNORE_DEVICE ),
 
 UNUSUAL_DEV( 0x0ace, 0x20ff, 0x0101, 0x0101,
 		"SiteCom",
@@ -1211,17 +1211,17 @@
 
 #ifdef CONFIG_USB_STORAGE_DATAFAB
 UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff,
-	       "Acomdata",
-	       "CF",
-	       US_SC_SCSI, US_PR_DATAFAB, NULL,
-	       US_FL_SINGLE_LUN ),
+		"Acomdata",
+		"CF",
+		US_SC_SCSI, US_PR_DATAFAB, NULL,
+		US_FL_SINGLE_LUN ),
 #endif
 #ifdef CONFIG_USB_STORAGE_SDDR55
 UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff,
-	       "Acomdata",
-	       "SM",
-	       US_SC_SCSI, US_PR_SDDR55, NULL,
-	       US_FL_SINGLE_LUN ),
+		"Acomdata",
+		"SM",
+		US_SC_SCSI, US_PR_SDDR55, NULL,
+		US_FL_SINGLE_LUN ),
 #endif
 
 /* Submitted by: Nick Sillik <n.sillik@temple.edu>
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 8e898e3..bef8bcd 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -191,16 +191,13 @@
 {
 	struct us_data *us = usb_get_intfdata(iface);
 
+	US_DEBUGP("%s\n", __FUNCTION__);
+
 	/* Wait until no command is running */
 	mutex_lock(&us->dev_mutex);
 
-	US_DEBUGP("%s\n", __FUNCTION__);
 	if (us->suspend_resume_hook)
 		(us->suspend_resume_hook)(us, US_SUSPEND);
-	iface->dev.power.power_state.event = message.event;
-
-	/* When runtime PM is working, we'll set a flag to indicate
-	 * whether we should autoresume when a SCSI request arrives. */
 
 	mutex_unlock(&us->dev_mutex);
 	return 0;
@@ -210,14 +207,25 @@
 {
 	struct us_data *us = usb_get_intfdata(iface);
 
-	mutex_lock(&us->dev_mutex);
-
 	US_DEBUGP("%s\n", __FUNCTION__);
+
 	if (us->suspend_resume_hook)
 		(us->suspend_resume_hook)(us, US_RESUME);
-	iface->dev.power.power_state.event = PM_EVENT_ON;
 
-	mutex_unlock(&us->dev_mutex);
+	return 0;
+}
+
+static int storage_reset_resume(struct usb_interface *iface)
+{
+	struct us_data *us = usb_get_intfdata(iface);
+
+	US_DEBUGP("%s\n", __FUNCTION__);
+
+	/* Report the reset to the SCSI core */
+	usb_stor_report_bus_reset(us);
+
+	/* FIXME: Notify the subdrivers that they need to reinitialize
+	 * the device */
 	return 0;
 }
 
@@ -228,7 +236,7 @@
  * a USB port reset, whether from this driver or a different one.
  */
 
-static void storage_pre_reset(struct usb_interface *iface)
+static int storage_pre_reset(struct usb_interface *iface)
 {
 	struct us_data *us = usb_get_intfdata(iface);
 
@@ -236,22 +244,23 @@
 
 	/* Make sure no command runs during the reset */
 	mutex_lock(&us->dev_mutex);
+	return 0;
 }
 
-static void storage_post_reset(struct usb_interface *iface)
+static int storage_post_reset(struct usb_interface *iface)
 {
 	struct us_data *us = usb_get_intfdata(iface);
 
 	US_DEBUGP("%s\n", __FUNCTION__);
 
 	/* Report the reset to the SCSI core */
-	scsi_lock(us_to_host(us));
 	usb_stor_report_bus_reset(us);
-	scsi_unlock(us_to_host(us));
 
 	/* FIXME: Notify the subdrivers that they need to reinitialize
 	 * the device */
+
 	mutex_unlock(&us->dev_mutex);
+	return 0;
 }
 
 /*
@@ -300,6 +309,7 @@
 {
 	struct us_data *us = (struct us_data *)__us;
 	struct Scsi_Host *host = us_to_host(us);
+	int autopm_rc;
 
 	current->flags |= PF_NOFREEZE;
 
@@ -310,6 +320,9 @@
 			
 		US_DEBUGP("*** thread awakened.\n");
 
+		/* Autoresume the device */
+		autopm_rc = usb_autopm_get_interface(us->pusb_intf);
+
 		/* lock the device pointers */
 		mutex_lock(&(us->dev_mutex));
 
@@ -368,6 +381,12 @@
 			us->srb->result = SAM_STAT_GOOD;
 		}
 
+		/* Did the autoresume fail? */
+		else if (autopm_rc < 0) {
+			US_DEBUGP("Could not wake device\n");
+			us->srb->result = DID_ERROR << 16;
+		}
+
 		/* we've got a command, let's do it! */
 		else {
 			US_DEBUG(usb_stor_show_command(us->srb));
@@ -410,25 +429,21 @@
 
 		/* unlock the device pointers */
 		mutex_unlock(&us->dev_mutex);
+
+		/* Start an autosuspend */
+		if (autopm_rc == 0)
+			usb_autopm_put_interface(us->pusb_intf);
 	} /* for (;;) */
 
-	scsi_host_put(host);
-
-	/* notify the exit routine that we're actually exiting now 
-	 *
-	 * complete()/wait_for_completion() is similar to up()/down(),
-	 * except that complete() is safe in the case where the structure
-	 * is getting deleted in a parallel mode of execution (i.e. just
-	 * after the down() -- that's necessary for the thread-shutdown
-	 * case.
-	 *
-	 * complete_and_exit() goes even further than this -- it is safe in
-	 * the case that the thread of the caller is going away (not just
-	 * the structure) -- this is necessary for the module-remove case.
-	 * This is important in preemption kernels, which transfer the flow
-	 * of execution immediately upon a complete().
-	 */
-	complete_and_exit(&threads_gone, 0);
+	/* Wait until we are told to stop */
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (kthread_should_stop())
+			break;
+		schedule();
+	}
+	__set_current_state(TASK_RUNNING);
+	return 0;
 }	
 
 /***********************************************************************
@@ -796,19 +811,13 @@
 	}
 
 	/* Start up our control thread */
-	th = kthread_create(usb_stor_control_thread, us, "usb-storage");
+	th = kthread_run(usb_stor_control_thread, us, "usb-storage");
 	if (IS_ERR(th)) {
 		printk(KERN_WARNING USB_STORAGE 
 		       "Unable to start control thread\n");
 		return PTR_ERR(th);
 	}
-
-	/* Take a reference to the host for the control thread and
-	 * count it among all the threads we have launched.  Then
-	 * start it up. */
-	scsi_host_get(us_to_host(us));
-	atomic_inc(&total_threads);
-	wake_up_process(th);
+	us->ctl_thread = th;
 
 	return 0;
 }
@@ -825,6 +834,8 @@
 	US_DEBUGP("-- sending exit command to thread\n");
 	set_bit(US_FLIDX_DISCONNECTING, &us->flags);
 	up(&us->sema);
+	if (us->ctl_thread)
+		kthread_stop(us->ctl_thread);
 
 	/* Call the destructor routine, if it exists */
 	if (us->extra_destructor) {
@@ -938,6 +949,7 @@
 	}
 
 	scsi_host_put(us_to_host(us));
+	usb_autopm_put_interface(us->pusb_intf);
 	complete_and_exit(&threads_gone, 0);
 }
 
@@ -1027,6 +1039,7 @@
 	 * start it up. */
 	scsi_host_get(us_to_host(us));
 	atomic_inc(&total_threads);
+	usb_autopm_get_interface(intf); /* dropped in the scanning thread */
 	wake_up_process(th);
 
 	return 0;
@@ -1059,10 +1072,12 @@
 #ifdef CONFIG_PM
 	.suspend =	storage_suspend,
 	.resume =	storage_resume,
+	.reset_resume =	storage_reset_resume,
 #endif
 	.pre_reset =	storage_pre_reset,
 	.post_reset =	storage_post_reset,
 	.id_table =	storage_usb_ids,
+	.supports_autosuspend = 1,
 };
 
 static int __init usb_stor_init(void)
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 6dac1ff..6445665 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -144,6 +144,7 @@
 	unsigned char		*sensebuf;	 /* sense data buffer	 */
 	dma_addr_t		cr_dma;		 /* buffer DMA addresses */
 	dma_addr_t		iobuf_dma;
+	struct task_struct	*ctl_thread;	 /* the control thread   */
 
 	/* mutual exclusion and synchronization structures */
 	struct semaphore	sema;		 /* to sleep thread on	    */
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 8432bf1..8de11de 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -34,9 +34,6 @@
 };
 MODULE_DEVICE_TABLE(usb, skel_table);
 
-/* to prevent a race between open and disconnect */
-static DEFINE_MUTEX(skel_open_lock);
-
 
 /* Get a minor range for your devices from the usb maintainer */
 #define USB_SKEL_MINOR_BASE	192
@@ -54,16 +51,21 @@
 	struct usb_device	*udev;			/* the usb device for this device */
 	struct usb_interface	*interface;		/* the interface for this device */
 	struct semaphore	limit_sem;		/* limiting the number of writes in progress */
+	struct usb_anchor	submitted;		/* in case we need to retract our submissions */
 	unsigned char           *bulk_in_buffer;	/* the buffer to receive data */
 	size_t			bulk_in_size;		/* the size of the receive buffer */
 	__u8			bulk_in_endpointAddr;	/* the address of the bulk in endpoint */
 	__u8			bulk_out_endpointAddr;	/* the address of the bulk out endpoint */
+	int			errors;			/* the last request tanked */
+	int			open_count;		/* count the number of openers */
+	spinlock_t		err_lock;		/* lock for errors */
 	struct kref		kref;
 	struct mutex		io_mutex;		/* synchronize I/O with disconnect */
 };
 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
 
 static struct usb_driver skel_driver;
+static void skel_draw_down(struct usb_skel *dev);
 
 static void skel_delete(struct kref *kref)
 {
@@ -83,10 +85,8 @@
 
 	subminor = iminor(inode);
 
-	mutex_lock(&skel_open_lock);
 	interface = usb_find_interface(&skel_driver, subminor);
 	if (!interface) {
-		mutex_unlock(&skel_open_lock);
 		err ("%s - error, can't find device for minor %d",
 		     __FUNCTION__, subminor);
 		retval = -ENODEV;
@@ -95,22 +95,33 @@
 
 	dev = usb_get_intfdata(interface);
 	if (!dev) {
-		mutex_unlock(&skel_open_lock);
 		retval = -ENODEV;
 		goto exit;
 	}
 
 	/* increment our usage count for the device */
 	kref_get(&dev->kref);
-	/* now we can drop the lock */
-	mutex_unlock(&skel_open_lock);
 
-	/* prevent the device from being autosuspended */
-	retval = usb_autopm_get_interface(interface);
-	if (retval) {
+	/* lock the device to allow correctly handling errors
+	 * in resumption */
+	mutex_lock(&dev->io_mutex);
+
+	if (!dev->open_count++) {
+		retval = usb_autopm_get_interface(interface);
+			if (retval) {
+				dev->open_count--;
+				mutex_unlock(&dev->io_mutex);
+				kref_put(&dev->kref, skel_delete);
+				goto exit;
+			}
+	} /* else { //uncomment this block if you want exclusive open
+		retval = -EBUSY;
+		dev->open_count--;
+		mutex_unlock(&dev->io_mutex);
 		kref_put(&dev->kref, skel_delete);
 		goto exit;
-	}
+	} */
+	/* prevent the device from being autosuspended */
 
 	/* save our object in the file's private structure */
 	file->private_data = dev;
@@ -129,7 +140,7 @@
 
 	/* allow the device to be autosuspended */
 	mutex_lock(&dev->io_mutex);
-	if (dev->interface)
+	if (!--dev->open_count && dev->interface)
 		usb_autopm_put_interface(dev->interface);
 	mutex_unlock(&dev->io_mutex);
 
@@ -138,6 +149,30 @@
 	return 0;
 }
 
+static int skel_flush(struct file *file, fl_owner_t id)
+{
+	struct usb_skel *dev;
+	int res;
+
+	dev = (struct usb_skel *)file->private_data;
+	if (dev == NULL)
+		return -ENODEV;
+
+	/* wait for io to stop */
+	mutex_lock(&dev->io_mutex);
+	skel_draw_down(dev);
+
+	/* read out errors, leave subsequent opens a clean slate */
+	spin_lock_irq(&dev->err_lock);
+	res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
+	dev->errors = 0;
+	spin_unlock_irq(&dev->err_lock);
+
+	mutex_unlock(&dev->io_mutex);
+
+	return res;
+}
+
 static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct usb_skel *dev;
@@ -179,12 +214,16 @@
 	dev = (struct usb_skel *)urb->context;
 
 	/* sync/async unlink faults aren't errors */
-	if (urb->status &&
-	    !(urb->status == -ENOENT ||
-	      urb->status == -ECONNRESET ||
-	      urb->status == -ESHUTDOWN)) {
-		err("%s - nonzero write bulk status received: %d",
-		    __FUNCTION__, urb->status);
+	if (urb->status) {
+		if(!(urb->status == -ENOENT ||
+		    urb->status == -ECONNRESET ||
+		    urb->status == -ESHUTDOWN))
+			err("%s - nonzero write bulk status received: %d",
+			    __FUNCTION__, urb->status);
+
+		spin_lock(&dev->err_lock);
+		dev->errors = urb->status;
+		spin_unlock(&dev->err_lock);
 	}
 
 	/* free up our allocated buffer */
@@ -213,6 +252,17 @@
 		goto exit;
 	}
 
+	spin_lock_irq(&dev->err_lock);
+	if ((retval = dev->errors) < 0) {
+		/* any error is reported once */
+		dev->errors = 0;
+		/* to preserve notifications about reset */
+		retval = (retval == -EPIPE) ? retval : -EIO;
+	}
+	spin_unlock_irq(&dev->err_lock);
+	if (retval < 0)
+		goto error;
+
 	/* create a urb, and a buffer for it, and copy the data to the urb */
 	urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!urb) {
@@ -244,13 +294,14 @@
 			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
 			  buf, writesize, skel_write_bulk_callback, dev);
 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	usb_anchor_urb(urb, &dev->submitted);
 
 	/* send the data out the bulk port */
 	retval = usb_submit_urb(urb, GFP_KERNEL);
 	mutex_unlock(&dev->io_mutex);
 	if (retval) {
 		err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
-		goto error;
+		goto error_unanchor;
 	}
 
 	/* release our reference to this urb, the USB core will eventually free it entirely */
@@ -259,6 +310,8 @@
 
 	return writesize;
 
+error_unanchor:
+	usb_unanchor_urb(urb);
 error:
 	if (urb) {
 		usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
@@ -276,6 +329,7 @@
 	.write =	skel_write,
 	.open =		skel_open,
 	.release =	skel_release,
+	.flush =	skel_flush,
 };
 
 /*
@@ -306,6 +360,8 @@
 	kref_init(&dev->kref);
 	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
 	mutex_init(&dev->io_mutex);
+	spin_lock_init(&dev->err_lock);
+	init_usb_anchor(&dev->submitted);
 
 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
 	dev->interface = interface;
@@ -368,22 +424,18 @@
 	struct usb_skel *dev;
 	int minor = interface->minor;
 
-	/* prevent skel_open() from racing skel_disconnect() */
-	mutex_lock(&skel_open_lock);
-
 	dev = usb_get_intfdata(interface);
 	usb_set_intfdata(interface, NULL);
 
 	/* give back our minor */
 	usb_deregister_dev(interface, &skel_class);
-	mutex_unlock(&skel_open_lock);
 
 	/* prevent more I/O from starting */
 	mutex_lock(&dev->io_mutex);
 	dev->interface = NULL;
 	mutex_unlock(&dev->io_mutex);
 
-
+	usb_kill_anchored_urbs(&dev->submitted);
 
 	/* decrement our usage count */
 	kref_put(&dev->kref, skel_delete);
@@ -391,10 +443,59 @@
 	info("USB Skeleton #%d now disconnected", minor);
 }
 
+static void skel_draw_down(struct usb_skel *dev)
+{
+	int time;
+
+	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
+	if (!time)
+		usb_kill_anchored_urbs(&dev->submitted);
+}
+
+static int skel_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_skel *dev = usb_get_intfdata(intf);
+
+	if (!dev)
+		return 0;
+	skel_draw_down(dev);
+	return 0;
+}
+
+static int skel_resume (struct usb_interface *intf)
+{
+	return 0;
+}
+
+static int skel_pre_reset(struct usb_interface *intf)
+{
+	struct usb_skel *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&dev->io_mutex);
+	skel_draw_down(dev);
+
+	return 0;
+}
+
+static int skel_post_reset(struct usb_interface *intf)
+{
+	struct usb_skel *dev = usb_get_intfdata(intf);
+
+	/* we are sure no URBs are active - no locking needed */
+	dev->errors = -EPIPE;
+	mutex_unlock(&dev->io_mutex);
+
+	return 0;
+}
+
 static struct usb_driver skel_driver = {
 	.name =		"skeleton",
 	.probe =	skel_probe,
 	.disconnect =	skel_disconnect,
+	.suspend =	skel_suspend,
+	.resume =	skel_resume,
+	.pre_reset =	skel_pre_reset,
+	.post_reset =	skel_post_reset,
 	.id_table =	skel_table,
 	.supports_autosuspend = 1,
 };
diff --git a/include/asm-arm/arch-ixp4xx/udc.h b/include/asm-arm/arch-ixp4xx/udc.h
index 79b850a..dbdec36f 100644
--- a/include/asm-arm/arch-ixp4xx/udc.h
+++ b/include/asm-arm/arch-ixp4xx/udc.h
@@ -6,25 +6,3 @@
 
 extern void ixp4xx_set_udc_info(struct pxa2xx_udc_mach_info *info);
 
-static inline int udc_gpio_to_irq(unsigned gpio)
-{
-	return 0;
-}
-
-static inline void udc_gpio_init_vbus(unsigned gpio)
-{
-}
-
-static inline void udc_gpio_init_pullup(unsigned gpio)
-{
-}
-
-static inline int udc_gpio_get(unsigned gpio)
-{
-	return 0;
-}
-
-static inline void udc_gpio_set(unsigned gpio, int is_on)
-{
-}
-
diff --git a/include/asm-arm/arch-pxa/udc.h b/include/asm-arm/arch-pxa/udc.h
index 8bc6f9c..27aa3a9 100644
--- a/include/asm-arm/arch-pxa/udc.h
+++ b/include/asm-arm/arch-pxa/udc.h
@@ -1,41 +1,8 @@
 /*
  * linux/include/asm-arm/arch-pxa/udc.h
  *
- * This supports machine-specific differences in how the PXA2xx
- * USB Device Controller (UDC) is wired.
- *
  */
 #include <asm/mach/udc_pxa2xx.h>
 
 extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
 
-static inline int udc_gpio_to_irq(unsigned gpio)
-{
-	return IRQ_GPIO(gpio & GPIO_MD_MASK_NR);
-}
-
-static inline void udc_gpio_init_vbus(unsigned gpio)
-{
-	pxa_gpio_mode((gpio & GPIO_MD_MASK_NR) | GPIO_IN);
-}
-
-static inline void udc_gpio_init_pullup(unsigned gpio)
-{
-	pxa_gpio_mode((gpio & GPIO_MD_MASK_NR) | GPIO_OUT | GPIO_DFLT_LOW);
-}
-
-static inline int udc_gpio_get(unsigned gpio)
-{
-	return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
-}
-
-static inline void udc_gpio_set(unsigned gpio, int is_on)
-{
-	int mask = GPIO_bit(gpio);
-
-	if (is_on)
-		GPSR(gpio) = mask;
-	else
-		GPCR(gpio) = mask;
-}
-
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 56aa2ee..7a60946 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -146,6 +146,10 @@
 					 * active alternate setting */
 	unsigned num_altsetting;	/* number of alternate settings */
 
+	/* If there is an interface association descriptor then it will list
+	 * the associated interfaces */
+	struct usb_interface_assoc_descriptor *intf_assoc;
+
 	int minor;			/* minor number this interface is
 					 * bound to */
 	enum usb_interface_condition condition;		/* state of binding */
@@ -175,6 +179,7 @@
 
 /* this maximum is arbitrary */
 #define USB_MAXINTERFACES	32
+#define USB_MAXIADS		USB_MAXINTERFACES/2
 
 /**
  * struct usb_interface_cache - long-term representation of a device interface
@@ -245,6 +250,11 @@
 	struct usb_config_descriptor	desc;
 
 	char *string;		/* iConfiguration string, if present */
+
+	/* List of any Interface Association Descriptors in this
+	 * configuration. */
+	struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
+
 	/* the interfaces associated with this configuration,
 	 * stored in no particular order */
 	struct usb_interface *interface[USB_MAXINTERFACES];
@@ -403,6 +413,8 @@
 
 	unsigned auto_pm:1;		/* autosuspend/resume in progress */
 	unsigned do_remote_wakeup:1;	/* remote wakeup should be enabled */
+	unsigned reset_resume:1;	/* needs reset instead of resume */
+	unsigned persist_enabled:1;	/* USB_PERSIST enabled for this dev */
 	unsigned autosuspend_disabled:1; /* autosuspend and autoresume */
 	unsigned autoresume_disabled:1;  /*  disabled by the user */
 #endif
@@ -770,6 +782,28 @@
 	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \
 	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
 
+/**
+ * USB_DEVICE_AND_INTERFACE_INFO - macro used to describe a specific usb device
+ * 		with a class of usb interfaces
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ * @cl: bInterfaceClass value
+ * @sc: bInterfaceSubClass value
+ * @pr: bInterfaceProtocol value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific device with a specific class of interfaces.
+ *
+ * This is especially useful when explicitly matching devices that have
+ * vendor specific bDeviceClass values, but standards-compliant interfaces.
+ */
+#define USB_DEVICE_AND_INTERFACE_INFO(vend,prod,cl,sc,pr) \
+	.match_flags = USB_DEVICE_ID_MATCH_INT_INFO \
+		| USB_DEVICE_ID_MATCH_DEVICE, \
+	.idVendor = (vend), .idProduct = (prod), \
+	.bInterfaceClass = (cl), \
+	.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
+
 /* ----------------------------------------------------------------------- */
 
 /* Stuff for dynamic usb ids */
@@ -816,10 +850,15 @@
  *	do (or don't) show up otherwise in the filesystem.
  * @suspend: Called when the device is going to be suspended by the system.
  * @resume: Called when the device is being resumed by the system.
+ * @reset_resume: Called when the suspended device has been reset instead
+ *	of being resumed.
  * @pre_reset: Called by usb_reset_composite_device() when the device
  *	is about to be reset.
  * @post_reset: Called by usb_reset_composite_device() after the device
- *	has been reset.
+ *	has been reset, or in lieu of @resume following a reset-resume
+ *	(i.e., the device is reset instead of being resumed, as might
+ *	happen if power was lost).  The second argument tells which is
+ *	the reason.
  * @id_table: USB drivers use ID table to support hotplugging.
  *	Export this with MODULE_DEVICE_TABLE(usb,...).  This must be set
  *	or your driver's probe function will never get called.
@@ -859,9 +898,10 @@
 
 	int (*suspend) (struct usb_interface *intf, pm_message_t message);
 	int (*resume) (struct usb_interface *intf);
+	int (*reset_resume)(struct usb_interface *intf);
 
-	void (*pre_reset) (struct usb_interface *intf);
-	void (*post_reset) (struct usb_interface *intf);
+	int (*pre_reset)(struct usb_interface *intf);
+	int (*post_reset)(struct usb_interface *intf);
 
 	const struct usb_device_id *id_table;
 
@@ -964,6 +1004,7 @@
 #define URB_ZERO_PACKET		0x0040	/* Finish bulk OUT with short packet */
 #define URB_NO_INTERRUPT	0x0080	/* HINT: no non-error interrupt
 					 * needed */
+#define URB_FREE_BUFFER		0x0100	/* Free transfer buffer with the URB */
 
 struct usb_iso_packet_descriptor {
 	unsigned int offset;
@@ -974,11 +1015,26 @@
 
 struct urb;
 
+struct usb_anchor {
+	struct list_head urb_list;
+	wait_queue_head_t wait;
+	spinlock_t lock;
+};
+
+static inline void init_usb_anchor(struct usb_anchor *anchor)
+{
+	INIT_LIST_HEAD(&anchor->urb_list);
+	init_waitqueue_head(&anchor->wait);
+	spin_lock_init(&anchor->lock);
+}
+
 typedef void (*usb_complete_t)(struct urb *);
 
 /**
  * struct urb - USB Request Block
  * @urb_list: For use by current owner of the URB.
+ * @anchor_list: membership in the list of an anchor
+ * @anchor: to anchor URBs to a common mooring
  * @pipe: Holds endpoint number, direction, type, and more.
  *	Create these values with the eight macros available;
  *	usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl"
@@ -1151,6 +1207,8 @@
 	/* public: documented fields in the urb that can be used by drivers */
 	struct list_head urb_list;	/* list head for use by the urb's
 					 * current owner */
+	struct list_head anchor_list;	/* the URB may be anchored by the driver */
+	struct usb_anchor *anchor;
 	struct usb_device *dev; 	/* (in) pointer to associated device */
 	unsigned int pipe;		/* (in) pipe information */
 	int status;			/* (return) non-ISO status */
@@ -1286,6 +1344,11 @@
 extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
 extern int usb_unlink_urb(struct urb *urb);
 extern void usb_kill_urb(struct urb *urb);
+extern void usb_kill_anchored_urbs(struct usb_anchor *anchor);
+extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);
+extern void usb_unanchor_urb(struct urb *urb);
+extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
+					 unsigned int timeout);
 
 void *usb_buffer_alloc (struct usb_device *dev, size_t size,
 	gfp_t mem_flags, dma_addr_t *dma);
diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild
index 43f160c..6ce42bf 100644
--- a/include/linux/usb/Kbuild
+++ b/include/linux/usb/Kbuild
@@ -1,5 +1,6 @@
 unifdef-y += audio.h
 unifdef-y += cdc.h
 unifdef-y += ch9.h
+unifdef-y += gadgetfs.h
 unifdef-y += midi.h
 
diff --git a/include/linux/usb_gadgetfs.h b/include/linux/usb/gadgetfs.h
similarity index 74%
rename from include/linux/usb_gadgetfs.h
rename to include/linux/usb/gadgetfs.h
index 8086d5a..e8654c3 100644
--- a/include/linux/usb_gadgetfs.h
+++ b/include/linux/usb/gadgetfs.h
@@ -1,3 +1,5 @@
+#ifndef __LINUX_USB_GADGETFS_H
+#define __LINUX_USB_GADGETFS_H
 
 #include <asm/types.h>
 #include <asm/ioctl.h>
@@ -7,11 +9,12 @@
 /*
  * Filesystem based user-mode API to USB Gadget controller hardware
  *
- * Almost everything can be done with only read and write operations,
+ * Other than ep0 operations, most things are done by read() and write()
  * on endpoint files found in one directory.  They are configured by
  * writing descriptors, and then may be used for normal stream style
  * i/o requests.  When ep0 is configured, the device can enumerate;
- * when it's closed, the device disconnects from usb.
+ * when it's closed, the device disconnects from usb.  Operations on
+ * ep0 require ioctl() operations.
  *
  * Configuration and device descriptors get written to /dev/gadget/$CHIP,
  * which may then be used to read usb_gadgetfs_event structs.  The driver
@@ -21,9 +24,9 @@
  */
 
 /*
- * Events are delivered on the ep0 file descriptor, if the user mode driver
+ * Events are delivered on the ep0 file descriptor, when the user mode driver
  * reads from this file descriptor after writing the descriptors.  Don't
- * stop polling this descriptor, if you write that kind of driver.
+ * stop polling this descriptor.
  */
 
 enum usb_gadgetfs_event_type {
@@ -36,8 +39,10 @@
 	// and likely more !
 };
 
+/* NOTE:  this structure must stay the same size and layout on
+ * both 32-bit and 64-bit kernels.
+ */
 struct usb_gadgetfs_event {
-	enum usb_gadgetfs_event_type	type;
 	union {
 		// NOP, DISCONNECT, SUSPEND: nothing
 		// ... some hardware can't report disconnection
@@ -46,19 +51,20 @@
 		enum usb_device_speed	speed;
 
 		// SETUP: packet; DATA phase i/o precedes next event
-		// (setup.bmRequestType & USB_DIR_IN) flags direction 
+		// (setup.bmRequestType & USB_DIR_IN) flags direction
 		// ... includes SET_CONFIGURATION, SET_INTERFACE
 		struct usb_ctrlrequest	setup;
 	} u;
+	enum usb_gadgetfs_event_type	type;
 };
 
 
 /* endpoint ioctls */
 
 /* IN transfers may be reported to the gadget driver as complete
- * 	when the fifo is loaded, before the host reads the data;
+ *	when the fifo is loaded, before the host reads the data;
  * OUT transfers may be reported to the host's "client" driver as
- * 	complete when they're sitting in the FIFO unread.
+ *	complete when they're sitting in the FIFO unread.
  * THIS returns how many bytes are "unclaimed" in the endpoint fifo
  * (needed for precise fault handling, when the hardware allows it)
  */
@@ -72,4 +78,4 @@
  */
 #define	GADGETFS_CLEAR_HALT	_IO('g',3)
 
-
+#endif /* __LINUX_USB_GADGETFS_H */
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 6bac8fa..8da374c 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -9,3 +9,6 @@
 
 /* string descriptors must not be fetched using a 255-byte read */
 #define USB_QUIRK_STRING_FETCH_255	0x00000002
+
+/* device can't resume correctly so reset it instead */
+#define USB_QUIRK_RESET_RESUME		0x00000004
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index 32acbae..e8b8928 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -221,6 +221,9 @@
 	int (*port_probe) (struct usb_serial_port *port);
 	int (*port_remove) (struct usb_serial_port *port);
 
+	int (*suspend) (struct usb_serial *serial, pm_message_t message);
+	int (*resume) (struct usb_serial *serial);
+
 	/* serial function calls */
 	int  (*open)		(struct usb_serial_port *port, struct file * filp);
 	void (*close)		(struct usb_serial_port *port, struct file * filp);
@@ -249,6 +252,9 @@
 extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id);
 extern void usb_serial_disconnect(struct usb_interface *iface);
 
+extern int usb_serial_suspend(struct usb_interface *intf, pm_message_t message);
+extern int usb_serial_resume(struct usb_interface *intf);
+
 extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
 extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
 
@@ -269,6 +275,7 @@
 extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp);
 extern int usb_serial_generic_write (struct usb_serial_port *port, const unsigned char *buf, int count);
 extern void usb_serial_generic_close (struct usb_serial_port *port, struct file *filp);
+extern int usb_serial_generic_resume (struct usb_serial *serial);
 extern int usb_serial_generic_write_room (struct usb_serial_port *port);
 extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port);
 extern void usb_serial_generic_read_bulk_callback (struct urb *urb);
diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
index e17186d..4f59b2a 100644
--- a/include/linux/usb_gadget.h
+++ b/include/linux/usb_gadget.h
@@ -110,13 +110,6 @@
 		gfp_t gfp_flags);
 	void (*free_request) (struct usb_ep *ep, struct usb_request *req);
 
-	void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes,
-		dma_addr_t *dma, gfp_t gfp_flags);
-	void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma,
-		unsigned bytes);
-	// NOTE:  on 2.6, drivers may also use dma_map() and
-	// dma_sync_single_*() to directly manage dma overhead. 
-
 	int (*queue) (struct usb_ep *ep, struct usb_request *req,
 		gfp_t gfp_flags);
 	int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
@@ -235,47 +228,6 @@
 }
 
 /**
- * usb_ep_alloc_buffer - allocate an I/O buffer
- * @ep:the endpoint associated with the buffer
- * @len:length of the desired buffer
- * @dma:pointer to the buffer's DMA address; must be valid
- * @gfp_flags:GFP_* flags to use
- *
- * Returns a new buffer, or null if one could not be allocated.
- * The buffer is suitably aligned for dma, if that endpoint uses DMA,
- * and the caller won't have to care about dma-inconsistency
- * or any hidden "bounce buffer" mechanism.  No additional per-request
- * DMA mapping will be required for such buffers.
- * Free it later with usb_ep_free_buffer().
- *
- * You don't need to use this call to allocate I/O buffers unless you
- * want to make sure drivers don't incur costs for such "bounce buffer"
- * copies or per-request DMA mappings.
- */
-static inline void *
-usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len, dma_addr_t *dma,
-	gfp_t gfp_flags)
-{
-	return ep->ops->alloc_buffer (ep, len, dma, gfp_flags);
-}
-
-/**
- * usb_ep_free_buffer - frees an i/o buffer
- * @ep:the endpoint associated with the buffer
- * @buf:CPU view address of the buffer
- * @dma:the buffer's DMA address
- * @len:length of the buffer
- *
- * reverses the effect of usb_ep_alloc_buffer().
- * caller guarantees the buffer will no longer be accessed
- */
-static inline void
-usb_ep_free_buffer (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned len)
-{
-	ep->ops->free_buffer (ep, buf, dma, len);
-}
-
-/**
  * usb_ep_queue - queues (submits) an I/O request to an endpoint.
  * @ep:the endpoint associated with the request
  * @req:the request being submitted