File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
new file mode 100644
index 0000000..f7a7fc2
--- /dev/null
+++ b/drivers/usb/misc/Kconfig
@@ -0,0 +1,270 @@
+#
+# USB Miscellaneous driver configuration
+#
+comment "USB Miscellaneous drivers"
+
+config USB_EMI62
+	tristate "EMI 6|2m USB Audio interface support"
+	---help---
+	  This driver loads firmware to Emagic EMI 6|2m low latency USB
+	  Audio and Midi interface.
+
+	  After firmware load the device is handled with standard linux
+	  USB Audio driver.
+
+	  This code is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called audio. If you want to compile it as a
+	  module, say M here and read <file:Documentation/kbuild/modules.txt>.
+
+config USB_EMI26
+	tristate "EMI 2|6 USB Audio interface support"
+	---help---
+	  This driver loads firmware to Emagic EMI 2|6 low latency USB
+	  Audio interface.
+
+	  After firmware load the device is handled with standard linux
+	  USB Audio driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called emi26.
+
+config USB_ADUTUX
+	tristate "ADU devices from Ontrak Control Systems"
+	help
+	  Say Y if you want to use an ADU device from Ontrak Control
+	  Systems.
+
+	  To compile this driver as a module, choose M here.  The module
+	  will be called adutux.
+
+config USB_SEVSEG
+	tristate "USB 7-Segment LED Display"
+	help
+	  Say Y here if you have a USB 7-Segment Display by Delcom
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbsevseg.
+
+config USB_RIO500
+	tristate "USB Diamond Rio500 support"
+	help
+	  Say Y here if you want to connect a USB Rio500 mp3 player to your
+	  computer's USB port. Please read <file:Documentation/usb/rio.txt>
+	  for more information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rio500.
+
+config USB_LEGOTOWER
+	tristate "USB Lego Infrared Tower support"
+	help
+	  Say Y here if you want to connect a USB Lego Infrared Tower to your
+	  computer's USB port.
+
+	  This code is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  The module will be called legousbtower. If you want to compile it as
+	  a module, say M here and read
+	  <file:Documentation/kbuild/modules.txt>.
+
+config USB_LCD
+	tristate "USB LCD driver support"
+	help
+	  Say Y here if you want to connect an USBLCD to your computer's
+	  USB port. The USBLCD is a small USB interface board for
+	  alphanumeric LCD modules. See <http://www.usblcd.de/> for more
+	  information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usblcd.
+
+config USB_LED
+	tristate "USB LED driver support"
+	help
+	  Say Y here if you want to connect an USBLED device to your 
+	  computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbled.
+
+config USB_CYPRESS_CY7C63
+	tristate "Cypress CY7C63xxx USB driver support"
+	help
+	  Say Y here if you want to connect a Cypress CY7C63xxx
+	  micro controller to your computer's USB port. Currently this
+	  driver supports the pre-programmed devices (incl. firmware)
+	  by AK Modul-Bus Computer GmbH.
+
+	  Please see: http://www.ak-modul-bus.de/stat/mikrocontroller.html
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cypress_cy7c63.
+
+config USB_CYTHERM
+	tristate "Cypress USB thermometer driver support"
+	help
+	  Say Y here if you want to connect a Cypress USB thermometer
+	  device to your computer's USB port. This device is also known
+	  as the Cypress USB Starter kit or demo board. The Elektor
+	  magazine published a modified version of this device in issue 
+	  #291.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cytherm.
+
+config USB_IDMOUSE
+	tristate "Siemens ID USB Mouse Fingerprint sensor support"
+	help
+	  Say Y here if you want to use the fingerprint sensor on
+	  the Siemens ID Mouse. There is also a Siemens ID Mouse
+	  _Professional_, which has not been tested with this driver,
+	  but uses the same sensor and may therefore work.
+
+	  This driver creates an entry "/dev/idmouseX" or "/dev/usb/idmouseX",
+	  which can be used by, e.g.,"cat /dev/idmouse0 > fingerprint.pnm".
+
+	  See also <http://www.fs.tum.de/~echtler/idmouse/>.
+
+config USB_FTDI_ELAN
+	tristate "Elan PCMCIA CardBus Adapter USB Client"
+	help
+	  ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
+	  Currently only the U132 adapter is available.
+
+	  The U132 is specifically designed for CardBus PC cards that contain
+	  an OHCI host controller. Typical PC cards are the Orange Mobile 3G
+	  Option GlobeTrotter Fusion card. The U132 adapter will *NOT* work
+	  with PC cards that do not contain an OHCI controller. To use a U132
+	  adapter you will need this "ftdi-elan" module as well as the "u132-hcd"
+	  module which is a USB host controller driver that talks to the OHCI
+	  controller within CardBus card that are inserted in the U132 adapter.
+
+	  This driver has been tested with a CardBus OHCI USB adapter, and
+	  worked with a USB PEN Drive inserted into the first USB port of
+	  the PCCARD. A rather pointless thing to do, but useful for testing.
+
+	  See also the USB_U132_HCD entry "Elan U132 Adapter Host Controller"
+
+	  It is safe to say M here.
+
+config USB_APPLEDISPLAY
+	tristate "Apple Cinema Display support"
+	select BACKLIGHT_LCD_SUPPORT
+	select BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y here if you want to control the backlight of Apple Cinema
+	  Displays over USB. This driver provides a sysfs interface.
+
+source "drivers/usb/misc/sisusbvga/Kconfig"
+
+config USB_LD
+	tristate "USB LD driver"
+	help
+	  This driver is for generic USB devices that use interrupt transfers,
+	  like LD Didactic's USB devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ldusb.
+
+config USB_TRANCEVIBRATOR
+	tristate "PlayStation 2 Trance Vibrator driver support"
+	help
+	  Say Y here if you want to connect a PlayStation 2 Trance Vibrator
+	  device to your computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called trancevibrator.
+
+config USB_IOWARRIOR
+	tristate "IO Warrior driver support"
+	help
+	  Say Y here if you want to support the IO Warrior devices from Code
+	  Mercenaries.  This includes support for the following devices:
+	  	IO Warrior 40
+		IO Warrior 24
+		IO Warrior 56
+		IO Warrior 24 Power Vampire
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called iowarrior.
+
+config USB_TEST
+	tristate "USB testing driver"
+	help
+	  This driver is for testing host controller software.  It is used
+	  with specialized device firmware for regression and stress testing,
+	  to help prevent problems from cropping up with "real" drivers.
+
+	  See <http://www.linux-usb.org/usbtest/> for more information,
+	  including sample test device firmware and "how to use it".
+
+config USB_EHSET_TEST_FIXTURE
+        tristate "USB EHSET Test Fixture driver"
+        help
+	  Say Y here if you want to support the special test fixture device
+	  used for the USB-IF Embedded Host High-Speed Electrical Test procedure.
+
+	  When the test fixture is connected, it can enumerate as one of several
+	  VID/PID pairs. This driver then initiates a corresponding test mode on
+	  the downstream port to which the test fixture is attached.
+
+	  See <http://www.usb.org/developers/onthego/EHSET_v1.01.pdf> for more
+	  information.
+
+config USB_ISIGHTFW
+	tristate "iSight firmware loading support"
+	select FW_LOADER
+	help
+	  This driver loads firmware for USB Apple iSight cameras, allowing
+	  them to be driven by the USB video class driver available at
+	  http://linux-uvc.berlios.de
+
+	  The firmware for this driver must be extracted from the MacOS
+	  driver beforehand. Tools for doing so are available at
+	  http://bersace03.free.fr
+
+config USB_YUREX
+	tristate "USB YUREX driver support"
+	help
+	  Say Y here if you want to connect a YUREX to your computer's
+	  USB port. The YUREX is a leg-shakes sensor. See
+	  <http://bbu.kayac.com/en/> for further information.
+	  This driver supports read/write of leg-shakes counter and
+	  fasync for the counter update via a device file /dev/yurex*.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called yurex.
+
+config USB_EZUSB_FX2
+	tristate "Functions for loading firmware on EZUSB chips"
+	help
+	  Say Y here if you need EZUSB device support.
+	  (Cypress FX/FX2/FX2LP microcontrollers)
+
+config USB_HSIC_USB3503
+       tristate "USB3503 HSIC to USB20 Driver"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
+
+config USB_LINK_LAYER_TEST
+	tristate "USB Link Layer Test driver"
+	help
+	  This driver is for generating specific traffic for Super Speed Link
+	  Layer Test Device. Say Y only when you want to conduct USB Super Speed
+	  Link Layer Test for host controllers.
+
+config USB_CHAOSKEY
+	tristate "ChaosKey random number generator driver support"
+	depends on HW_RANDOM
+	help
+	  Say Y here if you want to connect an AltusMetrum ChaosKey to
+	  your computer's USB port. The ChaosKey is a hardware random
+	  number generator which hooks into the kernel entropy pool to
+	  ensure a large supply of entropy for /dev/random and
+	  /dev/urandom and also provides direct access via /dev/chaoskeyX
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called chaoskey.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
new file mode 100644
index 0000000..45fd4ac
--- /dev/null
+++ b/drivers/usb/misc/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the rest of the USB drivers
+# (the ones that don't fit into any other categories)
+#
+obj-$(CONFIG_USB_ADUTUX)		+= adutux.o
+obj-$(CONFIG_USB_APPLEDISPLAY)		+= appledisplay.o
+obj-$(CONFIG_USB_CYPRESS_CY7C63)	+= cypress_cy7c63.o
+obj-$(CONFIG_USB_CYTHERM)		+= cytherm.o
+obj-$(CONFIG_USB_EMI26)			+= emi26.o
+obj-$(CONFIG_USB_EMI62)			+= emi62.o
+obj-$(CONFIG_USB_EZUSB_FX2)		+= ezusb.o
+obj-$(CONFIG_USB_FTDI_ELAN)		+= ftdi-elan.o
+obj-$(CONFIG_USB_IDMOUSE)		+= idmouse.o
+obj-$(CONFIG_USB_IOWARRIOR)		+= iowarrior.o
+obj-$(CONFIG_USB_ISIGHTFW)		+= isight_firmware.o
+obj-$(CONFIG_USB_LCD)			+= usblcd.o
+obj-$(CONFIG_USB_LD)			+= ldusb.o
+obj-$(CONFIG_USB_LED)			+= usbled.o
+obj-$(CONFIG_USB_LEGOTOWER)		+= legousbtower.o
+obj-$(CONFIG_USB_RIO500)		+= rio500.o
+obj-$(CONFIG_USB_TEST)			+= usbtest.o
+obj-$(CONFIG_USB_EHSET_TEST_FIXTURE)    += ehset.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o
+obj-$(CONFIG_USB_USS720)		+= uss720.o
+obj-$(CONFIG_USB_SEVSEG)		+= usbsevseg.o
+obj-$(CONFIG_USB_YUREX)			+= yurex.o
+obj-$(CONFIG_USB_HSIC_USB3503)		+= usb3503.o
+obj-$(CONFIG_USB_CHAOSKEY)		+= chaoskey.o
+
+obj-$(CONFIG_USB_SISUSBVGA)		+= sisusbvga/
+obj-$(CONFIG_USB_LINK_LAYER_TEST)	+= lvstest.o
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
new file mode 100644
index 0000000..3071c0e
--- /dev/null
+++ b/drivers/usb/misc/adutux.c
@@ -0,0 +1,835 @@
+/*
+ * adutux - driver for ADU devices from Ontrak Control Systems
+ * This is an experimental driver. Use at your own risk.
+ * This driver is not supported by Ontrak Control Systems.
+ *
+ * Copyright (c) 2003 John Homppi (SCO, leave this notice here)
+ *
+ * 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.
+ *
+ * derived from the Lego USB Tower driver 0.56:
+ * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net>
+ *               2001 Juergen Stuber <stuber@loria.fr>
+ * that was derived from USB Skeleton driver - 0.5
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v0.0.13"
+#define DRIVER_AUTHOR "John Homppi"
+#define DRIVER_DESC "adutux (see www.ontrak.net)"
+
+/* Define these values to match your device */
+#define ADU_VENDOR_ID 0x0a07
+#define ADU_PRODUCT_ID 0x0064
+
+/* table of devices that work with this driver */
+static const struct usb_device_id device_table[] = {
+	{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) },		/* ADU100 */
+	{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) },	/* ADU120 */
+	{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) },	/* ADU130 */
+	{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) },	/* ADU200 */
+	{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) },	/* ADU208 */
+	{ USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) },	/* ADU218 */
+	{ } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define ADU_MINOR_BASE	0
+#else
+#define ADU_MINOR_BASE	67
+#endif
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES	16
+
+#define COMMAND_TIMEOUT	(2*HZ)	/* 60 second timeout for a command */
+
+/*
+ * The locking scheme is a vanilla 3-lock:
+ *   adu_device.buflock: A spinlock, covers what IRQs touch.
+ *   adutux_mutex:       A Static lock to cover open_count. It would also cover
+ *                       any globals, but we don't have them in 2.6.
+ *   adu_device.mtx:     A mutex to hold across sleepers like copy_from_user.
+ *                       It covers all of adu_device, except the open_count
+ *                       and what .buflock covers.
+ */
+
+/* Structure to hold all of our device specific stuff */
+struct adu_device {
+	struct mutex		mtx;
+	struct usb_device *udev; /* save off the usb device pointer */
+	struct usb_interface *interface;
+	unsigned int		minor; /* the starting minor number for this device */
+	char			serial_number[8];
+
+	int			open_count; /* number of times this port has been opened */
+
+	char		*read_buffer_primary;
+	int			read_buffer_length;
+	char		*read_buffer_secondary;
+	int			secondary_head;
+	int			secondary_tail;
+	spinlock_t		buflock;
+
+	wait_queue_head_t	read_wait;
+	wait_queue_head_t	write_wait;
+
+	char		*interrupt_in_buffer;
+	struct usb_endpoint_descriptor *interrupt_in_endpoint;
+	struct urb	*interrupt_in_urb;
+	int			read_urb_finished;
+
+	char		*interrupt_out_buffer;
+	struct usb_endpoint_descriptor *interrupt_out_endpoint;
+	struct urb	*interrupt_out_urb;
+	int			out_urb_finished;
+};
+
+static DEFINE_MUTEX(adutux_mutex);
+
+static struct usb_driver adu_driver;
+
+static inline void adu_debug_data(struct device *dev, const char *function,
+				  int size, const unsigned char *data)
+{
+	dev_dbg(dev, "%s - length = %d, data = %*ph\n",
+		function, size, size, data);
+}
+
+/**
+ * adu_abort_transfers
+ *      aborts transfers and frees associated data structures
+ */
+static void adu_abort_transfers(struct adu_device *dev)
+{
+	unsigned long flags;
+
+	if (dev->udev == NULL)
+		return;
+
+	/* shutdown transfer */
+
+	/* XXX Anchor these instead */
+	spin_lock_irqsave(&dev->buflock, flags);
+	if (!dev->read_urb_finished) {
+		spin_unlock_irqrestore(&dev->buflock, flags);
+		usb_kill_urb(dev->interrupt_in_urb);
+	} else
+		spin_unlock_irqrestore(&dev->buflock, flags);
+
+	spin_lock_irqsave(&dev->buflock, flags);
+	if (!dev->out_urb_finished) {
+		spin_unlock_irqrestore(&dev->buflock, flags);
+		usb_kill_urb(dev->interrupt_out_urb);
+	} else
+		spin_unlock_irqrestore(&dev->buflock, flags);
+}
+
+static void adu_delete(struct adu_device *dev)
+{
+	/* free data structures */
+	usb_free_urb(dev->interrupt_in_urb);
+	usb_free_urb(dev->interrupt_out_urb);
+	kfree(dev->read_buffer_primary);
+	kfree(dev->read_buffer_secondary);
+	kfree(dev->interrupt_in_buffer);
+	kfree(dev->interrupt_out_buffer);
+	kfree(dev);
+}
+
+static void adu_interrupt_in_callback(struct urb *urb)
+{
+	struct adu_device *dev = urb->context;
+	int status = urb->status;
+
+	adu_debug_data(&dev->udev->dev, __func__,
+		       urb->actual_length, urb->transfer_buffer);
+
+	spin_lock(&dev->buflock);
+
+	if (status != 0) {
+		if ((status != -ENOENT) && (status != -ECONNRESET) &&
+			(status != -ESHUTDOWN)) {
+			dev_dbg(&dev->udev->dev,
+				"%s : nonzero status received: %d\n",
+				__func__, status);
+		}
+		goto exit;
+	}
+
+	if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) {
+		if (dev->read_buffer_length <
+		    (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) -
+		     (urb->actual_length)) {
+			memcpy (dev->read_buffer_primary +
+				dev->read_buffer_length,
+				dev->interrupt_in_buffer, urb->actual_length);
+
+			dev->read_buffer_length += urb->actual_length;
+			dev_dbg(&dev->udev->dev,"%s reading  %d\n", __func__,
+				urb->actual_length);
+		} else {
+			dev_dbg(&dev->udev->dev,"%s : read_buffer overflow\n",
+				__func__);
+		}
+	}
+
+exit:
+	dev->read_urb_finished = 1;
+	spin_unlock(&dev->buflock);
+	/* always wake up so we recover from errors */
+	wake_up_interruptible(&dev->read_wait);
+}
+
+static void adu_interrupt_out_callback(struct urb *urb)
+{
+	struct adu_device *dev = urb->context;
+	int status = urb->status;
+
+	adu_debug_data(&dev->udev->dev, __func__,
+		       urb->actual_length, urb->transfer_buffer);
+
+	if (status != 0) {
+		if ((status != -ENOENT) &&
+		    (status != -ECONNRESET)) {
+			dev_dbg(&dev->udev->dev,
+				"%s :nonzero status received: %d\n", __func__,
+				status);
+		}
+		return;
+	}
+
+	spin_lock(&dev->buflock);
+	dev->out_urb_finished = 1;
+	wake_up(&dev->write_wait);
+	spin_unlock(&dev->buflock);
+}
+
+static int adu_open(struct inode *inode, struct file *file)
+{
+	struct adu_device *dev = NULL;
+	struct usb_interface *interface;
+	int subminor;
+	int retval;
+
+	subminor = iminor(inode);
+
+	retval = mutex_lock_interruptible(&adutux_mutex);
+	if (retval)
+		goto exit_no_lock;
+
+	interface = usb_find_interface(&adu_driver, subminor);
+	if (!interface) {
+		pr_err("%s - error, can't find device for minor %d\n",
+		       __func__, subminor);
+		retval = -ENODEV;
+		goto exit_no_device;
+	}
+
+	dev = usb_get_intfdata(interface);
+	if (!dev || !dev->udev) {
+		retval = -ENODEV;
+		goto exit_no_device;
+	}
+
+	/* check that nobody else is using the device */
+	if (dev->open_count) {
+		retval = -EBUSY;
+		goto exit_no_device;
+	}
+
+	++dev->open_count;
+	dev_dbg(&dev->udev->dev, "%s: open count %d\n", __func__,
+		dev->open_count);
+
+	/* save device in the file's private structure */
+	file->private_data = dev;
+
+	/* initialize in direction */
+	dev->read_buffer_length = 0;
+
+	/* fixup first read by having urb waiting for it */
+	usb_fill_int_urb(dev->interrupt_in_urb, dev->udev,
+			 usb_rcvintpipe(dev->udev,
+					dev->interrupt_in_endpoint->bEndpointAddress),
+			 dev->interrupt_in_buffer,
+			 usb_endpoint_maxp(dev->interrupt_in_endpoint),
+			 adu_interrupt_in_callback, dev,
+			 dev->interrupt_in_endpoint->bInterval);
+	dev->read_urb_finished = 0;
+	if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL))
+		dev->read_urb_finished = 1;
+	/* we ignore failure */
+	/* end of fixup for first read */
+
+	/* initialize out direction */
+	dev->out_urb_finished = 1;
+
+	retval = 0;
+
+exit_no_device:
+	mutex_unlock(&adutux_mutex);
+exit_no_lock:
+	return retval;
+}
+
+static void adu_release_internal(struct adu_device *dev)
+{
+	/* decrement our usage count for the device */
+	--dev->open_count;
+	dev_dbg(&dev->udev->dev, "%s : open count %d\n", __func__,
+		dev->open_count);
+	if (dev->open_count <= 0) {
+		adu_abort_transfers(dev);
+		dev->open_count = 0;
+	}
+}
+
+static int adu_release(struct inode *inode, struct file *file)
+{
+	struct adu_device *dev;
+	int retval = 0;
+
+	if (file == NULL) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	dev = file->private_data;
+	if (dev == NULL) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	mutex_lock(&adutux_mutex); /* not interruptible */
+
+	if (dev->open_count <= 0) {
+		dev_dbg(&dev->udev->dev, "%s : device not opened\n", __func__);
+		retval = -ENODEV;
+		goto unlock;
+	}
+
+	adu_release_internal(dev);
+	if (dev->udev == NULL) {
+		/* the device was unplugged before the file was released */
+		if (!dev->open_count)	/* ... and we're the last user */
+			adu_delete(dev);
+	}
+unlock:
+	mutex_unlock(&adutux_mutex);
+exit:
+	return retval;
+}
+
+static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
+			loff_t *ppos)
+{
+	struct adu_device *dev;
+	size_t bytes_read = 0;
+	size_t bytes_to_read = count;
+	int i;
+	int retval = 0;
+	int timeout = 0;
+	int should_submit = 0;
+	unsigned long flags;
+	DECLARE_WAITQUEUE(wait, current);
+
+	dev = file->private_data;
+	if (mutex_lock_interruptible(&dev->mtx))
+		return -ERESTARTSYS;
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+		pr_err("No device or device unplugged %d\n", retval);
+		goto exit;
+	}
+
+	/* verify that some data was requested */
+	if (count == 0) {
+		dev_dbg(&dev->udev->dev, "%s : read request of 0 bytes\n",
+			__func__);
+		goto exit;
+	}
+
+	timeout = COMMAND_TIMEOUT;
+	dev_dbg(&dev->udev->dev, "%s : about to start looping\n", __func__);
+	while (bytes_to_read) {
+		int data_in_secondary = dev->secondary_tail - dev->secondary_head;
+		dev_dbg(&dev->udev->dev,
+			"%s : while, data_in_secondary=%d, status=%d\n",
+			__func__, data_in_secondary,
+			dev->interrupt_in_urb->status);
+
+		if (data_in_secondary) {
+			/* drain secondary buffer */
+			int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary;
+			i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount);
+			if (i) {
+				retval = -EFAULT;
+				goto exit;
+			}
+			dev->secondary_head += (amount - i);
+			bytes_read += (amount - i);
+			bytes_to_read -= (amount - i);
+			if (i) {
+				retval = bytes_read ? bytes_read : -EFAULT;
+				goto exit;
+			}
+		} else {
+			/* we check the primary buffer */
+			spin_lock_irqsave (&dev->buflock, flags);
+			if (dev->read_buffer_length) {
+				/* we secure access to the primary */
+				char *tmp;
+				dev_dbg(&dev->udev->dev,
+					"%s : swap, read_buffer_length = %d\n",
+					__func__, dev->read_buffer_length);
+				tmp = dev->read_buffer_secondary;
+				dev->read_buffer_secondary = dev->read_buffer_primary;
+				dev->read_buffer_primary = tmp;
+				dev->secondary_head = 0;
+				dev->secondary_tail = dev->read_buffer_length;
+				dev->read_buffer_length = 0;
+				spin_unlock_irqrestore(&dev->buflock, flags);
+				/* we have a free buffer so use it */
+				should_submit = 1;
+			} else {
+				/* even the primary was empty - we may need to do IO */
+				if (!dev->read_urb_finished) {
+					/* somebody is doing IO */
+					spin_unlock_irqrestore(&dev->buflock, flags);
+					dev_dbg(&dev->udev->dev,
+						"%s : submitted already\n",
+						__func__);
+				} else {
+					/* we must initiate input */
+					dev_dbg(&dev->udev->dev,
+						"%s : initiate input\n",
+						__func__);
+					dev->read_urb_finished = 0;
+					spin_unlock_irqrestore(&dev->buflock, flags);
+
+					usb_fill_int_urb(dev->interrupt_in_urb, dev->udev,
+							usb_rcvintpipe(dev->udev,
+								dev->interrupt_in_endpoint->bEndpointAddress),
+							 dev->interrupt_in_buffer,
+							 usb_endpoint_maxp(dev->interrupt_in_endpoint),
+							 adu_interrupt_in_callback,
+							 dev,
+							 dev->interrupt_in_endpoint->bInterval);
+					retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+					if (retval) {
+						dev->read_urb_finished = 1;
+						if (retval == -ENOMEM) {
+							retval = bytes_read ? bytes_read : -ENOMEM;
+						}
+						dev_dbg(&dev->udev->dev,
+							"%s : submit failed\n",
+							__func__);
+						goto exit;
+					}
+				}
+
+				/* we wait for I/O to complete */
+				set_current_state(TASK_INTERRUPTIBLE);
+				add_wait_queue(&dev->read_wait, &wait);
+				spin_lock_irqsave(&dev->buflock, flags);
+				if (!dev->read_urb_finished) {
+					spin_unlock_irqrestore(&dev->buflock, flags);
+					timeout = schedule_timeout(COMMAND_TIMEOUT);
+				} else {
+					spin_unlock_irqrestore(&dev->buflock, flags);
+					set_current_state(TASK_RUNNING);
+				}
+				remove_wait_queue(&dev->read_wait, &wait);
+
+				if (timeout <= 0) {
+					dev_dbg(&dev->udev->dev,
+						"%s : timeout\n", __func__);
+					retval = bytes_read ? bytes_read : -ETIMEDOUT;
+					goto exit;
+				}
+
+				if (signal_pending(current)) {
+					dev_dbg(&dev->udev->dev,
+						"%s : signal pending\n",
+						__func__);
+					retval = bytes_read ? bytes_read : -EINTR;
+					goto exit;
+				}
+			}
+		}
+	}
+
+	retval = bytes_read;
+	/* if the primary buffer is empty then use it */
+	spin_lock_irqsave(&dev->buflock, flags);
+	if (should_submit && dev->read_urb_finished) {
+		dev->read_urb_finished = 0;
+		spin_unlock_irqrestore(&dev->buflock, flags);
+		usb_fill_int_urb(dev->interrupt_in_urb, dev->udev,
+				 usb_rcvintpipe(dev->udev,
+					dev->interrupt_in_endpoint->bEndpointAddress),
+				dev->interrupt_in_buffer,
+				usb_endpoint_maxp(dev->interrupt_in_endpoint),
+				adu_interrupt_in_callback,
+				dev,
+				dev->interrupt_in_endpoint->bInterval);
+		if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL) != 0)
+			dev->read_urb_finished = 1;
+		/* we ignore failure */
+	} else {
+		spin_unlock_irqrestore(&dev->buflock, flags);
+	}
+
+exit:
+	/* unlock the device */
+	mutex_unlock(&dev->mtx);
+
+	return retval;
+}
+
+static ssize_t adu_write(struct file *file, const __user char *buffer,
+			 size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(waita, current);
+	struct adu_device *dev;
+	size_t bytes_written = 0;
+	size_t bytes_to_write;
+	size_t buffer_size;
+	unsigned long flags;
+	int retval;
+
+	dev = file->private_data;
+
+	retval = mutex_lock_interruptible(&dev->mtx);
+	if (retval)
+		goto exit_nolock;
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+		pr_err("No device or device unplugged %d\n", retval);
+		goto exit;
+	}
+
+	/* verify that we actually have some data to write */
+	if (count == 0) {
+		dev_dbg(&dev->udev->dev, "%s : write request of 0 bytes\n",
+			__func__);
+		goto exit;
+	}
+
+	while (count > 0) {
+		add_wait_queue(&dev->write_wait, &waita);
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&dev->buflock, flags);
+		if (!dev->out_urb_finished) {
+			spin_unlock_irqrestore(&dev->buflock, flags);
+
+			mutex_unlock(&dev->mtx);
+			if (signal_pending(current)) {
+				dev_dbg(&dev->udev->dev, "%s : interrupted\n",
+					__func__);
+				set_current_state(TASK_RUNNING);
+				retval = -EINTR;
+				goto exit_onqueue;
+			}
+			if (schedule_timeout(COMMAND_TIMEOUT) == 0) {
+				dev_dbg(&dev->udev->dev,
+					"%s - command timed out.\n", __func__);
+				retval = -ETIMEDOUT;
+				goto exit_onqueue;
+			}
+			remove_wait_queue(&dev->write_wait, &waita);
+			retval = mutex_lock_interruptible(&dev->mtx);
+			if (retval) {
+				retval = bytes_written ? bytes_written : retval;
+				goto exit_nolock;
+			}
+
+			dev_dbg(&dev->udev->dev,
+				"%s : in progress, count = %Zd\n",
+				__func__, count);
+		} else {
+			spin_unlock_irqrestore(&dev->buflock, flags);
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&dev->write_wait, &waita);
+			dev_dbg(&dev->udev->dev, "%s : sending, count = %Zd\n",
+				__func__, count);
+
+			/* write the data into interrupt_out_buffer from userspace */
+			buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint);
+			bytes_to_write = count > buffer_size ? buffer_size : count;
+			dev_dbg(&dev->udev->dev,
+				"%s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd\n",
+				__func__, buffer_size, count, bytes_to_write);
+
+			if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+				retval = -EFAULT;
+				goto exit;
+			}
+
+			/* send off the urb */
+			usb_fill_int_urb(
+				dev->interrupt_out_urb,
+				dev->udev,
+				usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+				dev->interrupt_out_buffer,
+				bytes_to_write,
+				adu_interrupt_out_callback,
+				dev,
+				dev->interrupt_out_endpoint->bInterval);
+			dev->interrupt_out_urb->actual_length = bytes_to_write;
+			dev->out_urb_finished = 0;
+			retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
+			if (retval < 0) {
+				dev->out_urb_finished = 1;
+				dev_err(&dev->udev->dev, "Couldn't submit "
+					"interrupt_out_urb %d\n", retval);
+				goto exit;
+			}
+
+			buffer += bytes_to_write;
+			count -= bytes_to_write;
+
+			bytes_written += bytes_to_write;
+		}
+	}
+	mutex_unlock(&dev->mtx);
+	return bytes_written;
+
+exit:
+	mutex_unlock(&dev->mtx);
+exit_nolock:
+	return retval;
+
+exit_onqueue:
+	remove_wait_queue(&dev->write_wait, &waita);
+	return retval;
+}
+
+/* file operations needed when we register this driver */
+static const struct file_operations adu_fops = {
+	.owner = THIS_MODULE,
+	.read  = adu_read,
+	.write = adu_write,
+	.open = adu_open,
+	.release = adu_release,
+	.llseek = noop_llseek,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver adu_class = {
+	.name = "usb/adutux%d",
+	.fops = &adu_fops,
+	.minor_base = ADU_MINOR_BASE,
+};
+
+/**
+ * adu_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int adu_probe(struct usb_interface *interface,
+		     const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct adu_device *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int retval = -ENODEV;
+	int in_end_size;
+	int out_end_size;
+	int i;
+
+	if (udev == NULL) {
+		dev_err(&interface->dev, "udev is NULL.\n");
+		goto exit;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&interface->dev, "Out of memory\n");
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	mutex_init(&dev->mtx);
+	spin_lock_init(&dev->buflock);
+	dev->udev = udev;
+	init_waitqueue_head(&dev->read_wait);
+	init_waitqueue_head(&dev->write_wait);
+
+	iface_desc = &interface->altsetting[0];
+
+	/* set up the endpoint information */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(endpoint))
+			dev->interrupt_in_endpoint = endpoint;
+
+		if (usb_endpoint_is_int_out(endpoint))
+			dev->interrupt_out_endpoint = endpoint;
+	}
+	if (dev->interrupt_in_endpoint == NULL) {
+		dev_err(&interface->dev, "interrupt in endpoint not found\n");
+		goto error;
+	}
+	if (dev->interrupt_out_endpoint == NULL) {
+		dev_err(&interface->dev, "interrupt out endpoint not found\n");
+		goto error;
+	}
+
+	in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
+	out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint);
+
+	dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
+	if (!dev->read_buffer_primary) {
+		dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n");
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	/* debug code prime the buffer */
+	memset(dev->read_buffer_primary, 'a', in_end_size);
+	memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size);
+	memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size);
+	memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size);
+
+	dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL);
+	if (!dev->read_buffer_secondary) {
+		dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n");
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	/* debug code prime the buffer */
+	memset(dev->read_buffer_secondary, 'e', in_end_size);
+	memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size);
+	memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size);
+	memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size);
+
+	dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL);
+	if (!dev->interrupt_in_buffer) {
+		dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
+		goto error;
+	}
+
+	/* debug code prime the buffer */
+	memset(dev->interrupt_in_buffer, 'i', in_end_size);
+
+	dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->interrupt_in_urb) {
+		dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+		goto error;
+	}
+	dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL);
+	if (!dev->interrupt_out_buffer) {
+		dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n");
+		goto error;
+	}
+	dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->interrupt_out_urb) {
+		dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n");
+		goto error;
+	}
+
+	if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number,
+			sizeof(dev->serial_number))) {
+		dev_err(&interface->dev, "Could not retrieve serial number\n");
+		goto error;
+	}
+	dev_dbg(&interface->dev,"serial_number=%s", dev->serial_number);
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, dev);
+
+	retval = usb_register_dev(interface, &adu_class);
+
+	if (retval) {
+		/* something prevented us from registering this driver */
+		dev_err(&interface->dev, "Not able to get a minor for this device.\n");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	dev->minor = interface->minor;
+
+	/* let the user know what node this device is now attached to */
+	dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d\n",
+		 le16_to_cpu(udev->descriptor.idProduct), dev->serial_number,
+		 (dev->minor - ADU_MINOR_BASE));
+exit:
+	return retval;
+
+error:
+	adu_delete(dev);
+	return retval;
+}
+
+/**
+ * adu_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void adu_disconnect(struct usb_interface *interface)
+{
+	struct adu_device *dev;
+	int minor;
+
+	dev = usb_get_intfdata(interface);
+
+	mutex_lock(&dev->mtx);	/* not interruptible */
+	dev->udev = NULL;	/* poison */
+	minor = dev->minor;
+	usb_deregister_dev(interface, &adu_class);
+	mutex_unlock(&dev->mtx);
+
+	mutex_lock(&adutux_mutex);
+	usb_set_intfdata(interface, NULL);
+
+	/* if the device is not opened, then we clean up right now */
+	if (!dev->open_count)
+		adu_delete(dev);
+
+	mutex_unlock(&adutux_mutex);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver adu_driver = {
+	.name = "adutux",
+	.probe = adu_probe,
+	.disconnect = adu_disconnect,
+	.id_table = device_table,
+};
+
+module_usb_driver(adu_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
new file mode 100644
index 0000000..a0a3827
--- /dev/null
+++ b/drivers/usb/misc/appledisplay.c
@@ -0,0 +1,389 @@
+/*
+ * Apple Cinema Display driver
+ *
+ * Copyright (C) 2006  Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * Thanks to Caskey L. Dickson for his work with acdctl.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/backlight.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/atomic.h>
+
+#define APPLE_VENDOR_ID		0x05AC
+
+#define USB_REQ_GET_REPORT	0x01
+#define USB_REQ_SET_REPORT	0x09
+
+#define ACD_USB_TIMEOUT		250
+
+#define ACD_USB_EDID		0x0302
+#define ACD_USB_BRIGHTNESS	0x0310
+
+#define ACD_BTN_NONE		0
+#define ACD_BTN_BRIGHT_UP	3
+#define ACD_BTN_BRIGHT_DOWN	4
+
+#define ACD_URB_BUFFER_LEN	2
+#define ACD_MSG_BUFFER_LEN	2
+
+#define APPLEDISPLAY_DEVICE(prod)				\
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |		\
+		       USB_DEVICE_ID_MATCH_INT_CLASS |		\
+		       USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
+	.idVendor = APPLE_VENDOR_ID,				\
+	.idProduct = (prod),					\
+	.bInterfaceClass = USB_CLASS_HID,			\
+	.bInterfaceProtocol = 0x00
+
+/* table of devices that work with this driver */
+static const struct usb_device_id appledisplay_table[] = {
+	{ APPLEDISPLAY_DEVICE(0x9218) },
+	{ APPLEDISPLAY_DEVICE(0x9219) },
+	{ APPLEDISPLAY_DEVICE(0x921c) },
+	{ APPLEDISPLAY_DEVICE(0x921d) },
+	{ APPLEDISPLAY_DEVICE(0x9236) },
+
+	/* Terminating entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, appledisplay_table);
+
+/* Structure to hold all of our device specific stuff */
+struct appledisplay {
+	struct usb_device *udev;	/* usb device */
+	struct urb *urb;		/* usb request block */
+	struct backlight_device *bd;	/* backlight device */
+	u8 *urbdata;			/* interrupt URB data buffer */
+	u8 *msgdata;			/* control message data buffer */
+
+	struct delayed_work work;
+	int button_pressed;
+	spinlock_t lock;
+	struct mutex sysfslock;		/* concurrent read and write */
+};
+
+static atomic_t count_displays = ATOMIC_INIT(0);
+static struct workqueue_struct *wq;
+
+static void appledisplay_complete(struct urb *urb)
+{
+	struct appledisplay *pdata = urb->context;
+	struct device *dev = &pdata->udev->dev;
+	unsigned long flags;
+	int status = urb->status;
+	int retval;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -EOVERFLOW:
+		dev_err(dev,
+			"OVERFLOW with data length %d, actual length is %d\n",
+			ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* This urb is terminated, clean up */
+		dev_dbg(dev, "%s - urb shuttingdown with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
+	}
+
+	spin_lock_irqsave(&pdata->lock, flags);
+
+	switch(pdata->urbdata[1]) {
+	case ACD_BTN_BRIGHT_UP:
+	case ACD_BTN_BRIGHT_DOWN:
+		pdata->button_pressed = 1;
+		queue_delayed_work(wq, &pdata->work, 0);
+		break;
+	case ACD_BTN_NONE:
+	default:
+		pdata->button_pressed = 0;
+		break;
+	}
+
+	spin_unlock_irqrestore(&pdata->lock, flags);
+
+exit:
+	retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);
+	if (retval) {
+		dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+	}
+}
+
+static int appledisplay_bl_update_status(struct backlight_device *bd)
+{
+	struct appledisplay *pdata = bl_get_data(bd);
+	int retval;
+
+	mutex_lock(&pdata->sysfslock);
+	pdata->msgdata[0] = 0x10;
+	pdata->msgdata[1] = bd->props.brightness;
+
+	retval = usb_control_msg(
+		pdata->udev,
+		usb_sndctrlpipe(pdata->udev, 0),
+		USB_REQ_SET_REPORT,
+		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		ACD_USB_BRIGHTNESS,
+		0,
+		pdata->msgdata, 2,
+		ACD_USB_TIMEOUT);
+	mutex_unlock(&pdata->sysfslock);
+	
+	return retval;
+}
+
+static int appledisplay_bl_get_brightness(struct backlight_device *bd)
+{
+	struct appledisplay *pdata = bl_get_data(bd);
+	int retval, brightness;
+
+	mutex_lock(&pdata->sysfslock);
+	retval = usb_control_msg(
+		pdata->udev,
+		usb_rcvctrlpipe(pdata->udev, 0),
+		USB_REQ_GET_REPORT,
+		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		ACD_USB_BRIGHTNESS,
+		0,
+		pdata->msgdata, 2,
+		ACD_USB_TIMEOUT);
+	brightness = pdata->msgdata[1];
+	mutex_unlock(&pdata->sysfslock);
+
+	if (retval < 0)
+		return retval;
+	else
+		return brightness;
+}
+
+static const struct backlight_ops appledisplay_bl_data = {
+	.get_brightness	= appledisplay_bl_get_brightness,
+	.update_status	= appledisplay_bl_update_status,
+};
+
+static void appledisplay_work(struct work_struct *work)
+{
+	struct appledisplay *pdata =
+		container_of(work, struct appledisplay, work.work);
+	int retval;
+
+	retval = appledisplay_bl_get_brightness(pdata->bd);
+	if (retval >= 0)
+		pdata->bd->props.brightness = retval;
+
+	/* Poll again in about 125ms if there's still a button pressed */
+	if (pdata->button_pressed)
+		schedule_delayed_work(&pdata->work, HZ / 8);
+}
+
+static int appledisplay_probe(struct usb_interface *iface,
+	const struct usb_device_id *id)
+{
+	struct backlight_properties props;
+	struct appledisplay *pdata;
+	struct usb_device *udev = interface_to_usbdev(iface);
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int int_in_endpointAddr = 0;
+	int i, retval = -ENOMEM, brightness;
+	char bl_name[20];
+
+	/* set up the endpoint information */
+	/* use only the first interrupt-in endpoint */
+	iface_desc = iface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
+			/* we found an interrupt in endpoint */
+			int_in_endpointAddr = endpoint->bEndpointAddress;
+			break;
+		}
+	}
+	if (!int_in_endpointAddr) {
+		dev_err(&iface->dev, "Could not find int-in endpoint\n");
+		return -EIO;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
+	if (!pdata) {
+		retval = -ENOMEM;
+		dev_err(&iface->dev, "Out of memory\n");
+		goto error;
+	}
+
+	pdata->udev = udev;
+
+	spin_lock_init(&pdata->lock);
+	INIT_DELAYED_WORK(&pdata->work, appledisplay_work);
+	mutex_init(&pdata->sysfslock);
+
+	/* Allocate buffer for control messages */
+	pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
+	if (!pdata->msgdata) {
+		retval = -ENOMEM;
+		dev_err(&iface->dev,
+			"Allocating buffer for control messages failed\n");
+		goto error;
+	}
+
+	/* Allocate interrupt URB */
+	pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pdata->urb) {
+		retval = -ENOMEM;
+		dev_err(&iface->dev, "Allocating URB failed\n");
+		goto error;
+	}
+
+	/* Allocate buffer for interrupt data */
+	pdata->urbdata = usb_alloc_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
+		GFP_KERNEL, &pdata->urb->transfer_dma);
+	if (!pdata->urbdata) {
+		retval = -ENOMEM;
+		dev_err(&iface->dev, "Allocating URB buffer failed\n");
+		goto error;
+	}
+
+	/* Configure interrupt URB */
+	usb_fill_int_urb(pdata->urb, udev,
+		usb_rcvintpipe(udev, int_in_endpointAddr),
+		pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
+		pdata, 1);
+	if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
+		retval = -EIO;
+		dev_err(&iface->dev, "Submitting URB failed\n");
+		goto error;
+	}
+
+	/* Register backlight device */
+	snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
+		atomic_inc_return(&count_displays) - 1);
+	memset(&props, 0, sizeof(struct backlight_properties));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = 0xff;
+	pdata->bd = backlight_device_register(bl_name, NULL, pdata,
+					      &appledisplay_bl_data, &props);
+	if (IS_ERR(pdata->bd)) {
+		dev_err(&iface->dev, "Backlight registration failed\n");
+		retval = PTR_ERR(pdata->bd);
+		goto error;
+	}
+
+	/* Try to get brightness */
+	brightness = appledisplay_bl_get_brightness(pdata->bd);
+
+	if (brightness < 0) {
+		retval = brightness;
+		dev_err(&iface->dev,
+			"Error while getting initial brightness: %d\n", retval);
+		goto error;
+	}
+
+	/* Set brightness in backlight device */
+	pdata->bd->props.brightness = brightness;
+
+	/* save our data pointer in the interface device */
+	usb_set_intfdata(iface, pdata);
+
+	printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n");
+
+	return 0;
+
+error:
+	if (pdata) {
+		if (pdata->urb) {
+			usb_kill_urb(pdata->urb);
+			if (pdata->urbdata)
+				usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
+					pdata->urbdata, pdata->urb->transfer_dma);
+			usb_free_urb(pdata->urb);
+		}
+		if (!IS_ERR(pdata->bd))
+			backlight_device_unregister(pdata->bd);
+		kfree(pdata->msgdata);
+	}
+	usb_set_intfdata(iface, NULL);
+	kfree(pdata);
+	return retval;
+}
+
+static void appledisplay_disconnect(struct usb_interface *iface)
+{
+	struct appledisplay *pdata = usb_get_intfdata(iface);
+
+	if (pdata) {
+		usb_kill_urb(pdata->urb);
+		cancel_delayed_work(&pdata->work);
+		backlight_device_unregister(pdata->bd);
+		usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
+			pdata->urbdata, pdata->urb->transfer_dma);
+		usb_free_urb(pdata->urb);
+		kfree(pdata->msgdata);
+		kfree(pdata);
+	}
+
+	printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n");
+}
+
+static struct usb_driver appledisplay_driver = {
+	.name		= "appledisplay",
+	.probe		= appledisplay_probe,
+	.disconnect	= appledisplay_disconnect,
+	.id_table	= appledisplay_table,
+};
+
+static int __init appledisplay_init(void)
+{
+	wq = create_singlethread_workqueue("appledisplay");
+	if (!wq) {
+		printk(KERN_ERR "appledisplay: Could not create work queue\n");
+		return -ENOMEM;
+	}
+
+	return usb_register(&appledisplay_driver);
+}
+
+static void __exit appledisplay_exit(void)
+{
+	flush_workqueue(wq);
+	destroy_workqueue(wq);
+	usb_deregister(&appledisplay_driver);
+}
+
+MODULE_AUTHOR("Michael Hanselmann");
+MODULE_DESCRIPTION("Apple Cinema Display driver");
+MODULE_LICENSE("GPL");
+
+module_init(appledisplay_init);
+module_exit(appledisplay_exit);
diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c
new file mode 100644
index 0000000..23c7948
--- /dev/null
+++ b/drivers/usb/misc/chaoskey.c
@@ -0,0 +1,532 @@
+/*
+ * chaoskey - driver for ChaosKey device from Altus Metrum.
+ *
+ * This device provides true random numbers using a noise source based
+ * on a reverse-biased p-n junction in avalanche breakdown. More
+ * details can be found at http://chaoskey.org
+ *
+ * The driver connects to the kernel hardware RNG interface to provide
+ * entropy for /dev/random and other kernel activities. It also offers
+ * a separate /dev/ entry to allow for direct access to the random
+ * bit stream.
+ *
+ * Copyright © 2015 Keith Packard <keithp@keithp.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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/hw_random.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+static struct usb_driver chaoskey_driver;
+static struct usb_class_driver chaoskey_class;
+static int chaoskey_rng_read(struct hwrng *rng, void *data,
+			     size_t max, bool wait);
+
+#define usb_dbg(usb_if, format, arg...) \
+	dev_dbg(&(usb_if)->dev, format, ## arg)
+
+#define usb_err(usb_if, format, arg...) \
+	dev_err(&(usb_if)->dev, format, ## arg)
+
+/* Version Information */
+#define DRIVER_VERSION	"v0.1"
+#define DRIVER_AUTHOR	"Keith Packard, keithp@keithp.com"
+#define DRIVER_DESC	"Altus Metrum ChaosKey driver"
+#define DRIVER_SHORT	"chaoskey"
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define CHAOSKEY_VENDOR_ID	0x1d50	/* OpenMoko */
+#define CHAOSKEY_PRODUCT_ID	0x60c6	/* ChaosKey */
+
+#define CHAOSKEY_BUF_LEN	64	/* max size of USB full speed packet */
+
+#define NAK_TIMEOUT (HZ)		/* stall/wait timeout for device */
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define USB_CHAOSKEY_MINOR_BASE 0
+#else
+
+/* IOWARRIOR_MINOR_BASE + 16, not official yet */
+#define USB_CHAOSKEY_MINOR_BASE 224
+#endif
+
+static const struct usb_device_id chaoskey_table[] = {
+	{ USB_DEVICE(CHAOSKEY_VENDOR_ID, CHAOSKEY_PRODUCT_ID) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, chaoskey_table);
+
+/* Driver-local specific stuff */
+struct chaoskey {
+	struct usb_interface *interface;
+	char in_ep;
+	struct mutex lock;
+	struct mutex rng_lock;
+	int open;			/* open count */
+	int present;			/* device not disconnected */
+	int size;			/* size of buf */
+	int valid;			/* bytes of buf read */
+	int used;			/* bytes of buf consumed */
+	char *name;			/* product + serial */
+	struct hwrng hwrng;		/* Embedded struct for hwrng */
+	int hwrng_registered;		/* registered with hwrng API */
+	wait_queue_head_t wait_q;	/* for timeouts */
+	char *buf;
+};
+
+static void chaoskey_free(struct chaoskey *dev)
+{
+	usb_dbg(dev->interface, "free");
+	kfree(dev->name);
+	kfree(dev->buf);
+	kfree(dev);
+}
+
+static int chaoskey_probe(struct usb_interface *interface,
+			  const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *altsetting = interface->cur_altsetting;
+	int i;
+	int in_ep = -1;
+	struct chaoskey *dev;
+	int result;
+	int size;
+
+	usb_dbg(interface, "probe %s-%s", udev->product, udev->serial);
+
+	/* Find the first bulk IN endpoint and its packet size */
+	for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
+		if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) {
+			in_ep = usb_endpoint_num(&altsetting->endpoint[i].desc);
+			size = usb_endpoint_maxp(&altsetting->endpoint[i].desc);
+			break;
+		}
+	}
+
+	/* Validate endpoint and size */
+	if (in_ep == -1) {
+		usb_dbg(interface, "no IN endpoint found");
+		return -ENODEV;
+	}
+	if (size <= 0) {
+		usb_dbg(interface, "invalid size (%d)", size);
+		return -ENODEV;
+	}
+
+	if (size > CHAOSKEY_BUF_LEN) {
+		usb_dbg(interface, "size reduced from %d to %d\n",
+			size, CHAOSKEY_BUF_LEN);
+		size = CHAOSKEY_BUF_LEN;
+	}
+
+	/* Looks good, allocate and initialize */
+
+	dev = kzalloc(sizeof(struct chaoskey), GFP_KERNEL);
+
+	if (dev == NULL)
+		return -ENOMEM;
+
+	dev->buf = kmalloc(size, GFP_KERNEL);
+
+	if (dev->buf == NULL) {
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	/* Construct a name using the product and serial values. Each
+	 * device needs a unique name for the hwrng code
+	 */
+
+	if (udev->product && udev->serial) {
+		dev->name = kmalloc(strlen(udev->product) + 1 +
+				    strlen(udev->serial) + 1, GFP_KERNEL);
+		if (dev->name == NULL) {
+			kfree(dev->buf);
+			kfree(dev);
+			return -ENOMEM;
+		}
+
+		strcpy(dev->name, udev->product);
+		strcat(dev->name, "-");
+		strcat(dev->name, udev->serial);
+	}
+
+	dev->interface = interface;
+
+	dev->in_ep = in_ep;
+
+	dev->size = size;
+	dev->present = 1;
+
+	init_waitqueue_head(&dev->wait_q);
+
+	mutex_init(&dev->lock);
+	mutex_init(&dev->rng_lock);
+
+	usb_set_intfdata(interface, dev);
+
+	result = usb_register_dev(interface, &chaoskey_class);
+	if (result) {
+		usb_err(interface, "Unable to allocate minor number.");
+		usb_set_intfdata(interface, NULL);
+		chaoskey_free(dev);
+		return result;
+	}
+
+	dev->hwrng.name = dev->name ? dev->name : chaoskey_driver.name;
+	dev->hwrng.read = chaoskey_rng_read;
+
+	/* Set the 'quality' metric.  Quality is measured in units of
+	 * 1/1024's of a bit ("mills"). This should be set to 1024,
+	 * but there is a bug in the hwrng core which masks it with
+	 * 1023.
+	 *
+	 * The patch that has been merged to the crypto development
+	 * tree for that bug limits the value to 1024 at most, so by
+	 * setting this to 1024 + 1023, we get 1023 before the fix is
+	 * merged and 1024 afterwards. We'll patch this driver once
+	 * both bits of code are in the same tree.
+	 */
+	dev->hwrng.quality = 1024 + 1023;
+
+	dev->hwrng_registered = (hwrng_register(&dev->hwrng) == 0);
+	if (!dev->hwrng_registered)
+		usb_err(interface, "Unable to register with hwrng");
+
+	usb_enable_autosuspend(udev);
+
+	usb_dbg(interface, "chaoskey probe success, size %d", dev->size);
+	return 0;
+}
+
+static void chaoskey_disconnect(struct usb_interface *interface)
+{
+	struct chaoskey	*dev;
+
+	usb_dbg(interface, "disconnect");
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		usb_dbg(interface, "disconnect failed - no dev");
+		return;
+	}
+
+	if (dev->hwrng_registered)
+		hwrng_unregister(&dev->hwrng);
+
+	usb_deregister_dev(interface, &chaoskey_class);
+
+	usb_set_intfdata(interface, NULL);
+	mutex_lock(&dev->lock);
+
+	dev->present = 0;
+
+	if (!dev->open) {
+		mutex_unlock(&dev->lock);
+		chaoskey_free(dev);
+	} else
+		mutex_unlock(&dev->lock);
+
+	usb_dbg(interface, "disconnect done");
+}
+
+static int chaoskey_open(struct inode *inode, struct file *file)
+{
+	struct chaoskey *dev;
+	struct usb_interface *interface;
+
+	/* get the interface from minor number and driver information */
+	interface = usb_find_interface(&chaoskey_driver, iminor(inode));
+	if (!interface)
+		return -ENODEV;
+
+	usb_dbg(interface, "open");
+
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		usb_dbg(interface, "open (dev)");
+		return -ENODEV;
+	}
+
+	file->private_data = dev;
+	mutex_lock(&dev->lock);
+	++dev->open;
+	mutex_unlock(&dev->lock);
+
+	usb_dbg(interface, "open success");
+	return 0;
+}
+
+static int chaoskey_release(struct inode *inode, struct file *file)
+{
+	struct chaoskey *dev = file->private_data;
+	struct usb_interface *interface;
+
+	if (dev == NULL)
+		return -ENODEV;
+
+	interface = dev->interface;
+
+	usb_dbg(interface, "release");
+
+	mutex_lock(&dev->lock);
+
+	usb_dbg(interface, "open count at release is %d", dev->open);
+
+	if (dev->open <= 0) {
+		usb_dbg(interface, "invalid open count (%d)", dev->open);
+		mutex_unlock(&dev->lock);
+		return -ENODEV;
+	}
+
+	--dev->open;
+
+	if (!dev->present) {
+		if (dev->open == 0) {
+			mutex_unlock(&dev->lock);
+			chaoskey_free(dev);
+		} else
+			mutex_unlock(&dev->lock);
+	} else
+		mutex_unlock(&dev->lock);
+
+	usb_dbg(interface, "release success");
+	return 0;
+}
+
+/* Fill the buffer. Called with dev->lock held
+ */
+static int _chaoskey_fill(struct chaoskey *dev)
+{
+	DEFINE_WAIT(wait);
+	int result;
+	int this_read;
+	struct usb_device *udev = interface_to_usbdev(dev->interface);
+
+	usb_dbg(dev->interface, "fill");
+
+	/* Return immediately if someone called before the buffer was
+	 * empty */
+	if (dev->valid != dev->used) {
+		usb_dbg(dev->interface, "not empty yet (valid %d used %d)",
+			dev->valid, dev->used);
+		return 0;
+	}
+
+	/* Bail if the device has been removed */
+	if (!dev->present) {
+		usb_dbg(dev->interface, "device not present");
+		return -ENODEV;
+	}
+
+	/* Make sure the device is awake */
+	result = usb_autopm_get_interface(dev->interface);
+	if (result) {
+		usb_dbg(dev->interface, "wakeup failed (result %d)", result);
+		return result;
+	}
+
+	result = usb_bulk_msg(udev,
+			      usb_rcvbulkpipe(udev, dev->in_ep),
+			      dev->buf, dev->size, &this_read,
+			      NAK_TIMEOUT);
+
+	/* Let the device go back to sleep eventually */
+	usb_autopm_put_interface(dev->interface);
+
+	if (result == 0) {
+		dev->valid = this_read;
+		dev->used = 0;
+	}
+
+	usb_dbg(dev->interface, "bulk_msg result %d this_read %d",
+		result, this_read);
+
+	return result;
+}
+
+static ssize_t chaoskey_read(struct file *file,
+			     char __user *buffer,
+			     size_t count,
+			     loff_t *ppos)
+{
+	struct chaoskey *dev;
+	ssize_t read_count = 0;
+	int this_time;
+	int result = 0;
+	unsigned long remain;
+
+	dev = file->private_data;
+
+	if (dev == NULL || !dev->present)
+		return -ENODEV;
+
+	usb_dbg(dev->interface, "read %zu", count);
+
+	while (count > 0) {
+
+		/* Grab the rng_lock briefly to ensure that the hwrng interface
+		 * gets priority over other user access
+		 */
+		result = mutex_lock_interruptible(&dev->rng_lock);
+		if (result)
+			goto bail;
+		mutex_unlock(&dev->rng_lock);
+
+		result = mutex_lock_interruptible(&dev->lock);
+		if (result)
+			goto bail;
+		if (dev->valid == dev->used) {
+			result = _chaoskey_fill(dev);
+			if (result) {
+				mutex_unlock(&dev->lock);
+				goto bail;
+			}
+
+			/* Read returned zero bytes */
+			if (dev->used == dev->valid) {
+				mutex_unlock(&dev->lock);
+				goto bail;
+			}
+		}
+
+		this_time = dev->valid - dev->used;
+		if (this_time > count)
+			this_time = count;
+
+		remain = copy_to_user(buffer, dev->buf + dev->used, this_time);
+		if (remain) {
+			result = -EFAULT;
+
+			/* Consume the bytes that were copied so we don't leak
+			 * data to user space
+			 */
+			dev->used += this_time - remain;
+			mutex_unlock(&dev->lock);
+			goto bail;
+		}
+
+		count -= this_time;
+		read_count += this_time;
+		buffer += this_time;
+		dev->used += this_time;
+		mutex_unlock(&dev->lock);
+	}
+bail:
+	if (read_count) {
+		usb_dbg(dev->interface, "read %zu bytes", read_count);
+		return read_count;
+	}
+	usb_dbg(dev->interface, "empty read, result %d", result);
+	return result;
+}
+
+static int chaoskey_rng_read(struct hwrng *rng, void *data,
+			     size_t max, bool wait)
+{
+	struct chaoskey *dev = container_of(rng, struct chaoskey, hwrng);
+	int this_time;
+
+	usb_dbg(dev->interface, "rng_read max %zu wait %d", max, wait);
+
+	if (!dev->present) {
+		usb_dbg(dev->interface, "device not present");
+		return 0;
+	}
+
+	/* Hold the rng_lock until we acquire the device lock so that
+	 * this operation gets priority over other user access to the
+	 * device
+	 */
+	mutex_lock(&dev->rng_lock);
+
+	mutex_lock(&dev->lock);
+
+	mutex_unlock(&dev->rng_lock);
+
+	/* Try to fill the buffer if empty. It doesn't actually matter
+	 * if _chaoskey_fill works; we'll just return zero bytes as
+	 * the buffer will still be empty
+	 */
+	if (dev->valid == dev->used)
+		(void) _chaoskey_fill(dev);
+
+	this_time = dev->valid - dev->used;
+	if (this_time > max)
+		this_time = max;
+
+	memcpy(data, dev->buf + dev->used, this_time);
+
+	dev->used += this_time;
+
+	mutex_unlock(&dev->lock);
+
+	usb_dbg(dev->interface, "rng_read this_time %d\n", this_time);
+	return this_time;
+}
+
+#ifdef CONFIG_PM
+static int chaoskey_suspend(struct usb_interface *interface,
+			    pm_message_t message)
+{
+	usb_dbg(interface, "suspend");
+	return 0;
+}
+
+static int chaoskey_resume(struct usb_interface *interface)
+{
+	usb_dbg(interface, "resume");
+	return 0;
+}
+#else
+#define chaoskey_suspend NULL
+#define chaoskey_resume NULL
+#endif
+
+/* file operation pointers */
+static const struct file_operations chaoskey_fops = {
+	.owner = THIS_MODULE,
+	.read = chaoskey_read,
+	.open = chaoskey_open,
+	.release = chaoskey_release,
+	.llseek = default_llseek,
+};
+
+/* class driver information */
+static struct usb_class_driver chaoskey_class = {
+	.name = "chaoskey%d",
+	.fops = &chaoskey_fops,
+	.minor_base = USB_CHAOSKEY_MINOR_BASE,
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver chaoskey_driver = {
+	.name = DRIVER_SHORT,
+	.probe = chaoskey_probe,
+	.disconnect = chaoskey_disconnect,
+	.suspend = chaoskey_suspend,
+	.resume = chaoskey_resume,
+	.reset_resume = chaoskey_resume,
+	.id_table = chaoskey_table,
+	.supports_autosuspend = 1,
+};
+
+module_usb_driver(chaoskey_driver);
+
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
new file mode 100644
index 0000000..402b94d
--- /dev/null
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -0,0 +1,278 @@
+/*
+* cypress_cy7c63.c
+*
+* Copyright (c) 2006-2007 Oliver Bock (bock@tfh-berlin.de)
+*
+*	This driver is based on the Cypress USB Driver by Marcus Maul
+*	(cyport) and the 2.0 version of Greg Kroah-Hartman's
+*	USB Skeleton driver.
+*
+*	This is a generic driver for the Cypress CY7C63xxx family.
+*	For the time being it enables you to read from and write to
+*	the single I/O ports of the device.
+*
+*	Supported vendors:	AK Modul-Bus Computer GmbH
+*				(Firmware "Port-Chip")
+*
+*	Supported devices:	CY7C63001A-PC
+*				CY7C63001C-PXC
+*				CY7C63001C-SXC
+*
+*	Supported functions:	Read/Write Ports
+*
+*
+*	For up-to-date information please visit:
+*	http://www.obock.de/kernel/cypress
+*
+*	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.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#define DRIVER_AUTHOR		"Oliver Bock (bock@tfh-berlin.de)"
+#define DRIVER_DESC		"Cypress CY7C63xxx USB driver"
+
+#define CYPRESS_VENDOR_ID	0xa2c
+#define CYPRESS_PRODUCT_ID	0x8
+
+#define CYPRESS_READ_PORT	0x4
+#define CYPRESS_WRITE_PORT	0x5
+
+#define CYPRESS_READ_RAM	0x2
+#define CYPRESS_WRITE_RAM	0x3
+#define CYPRESS_READ_ROM	0x1
+
+#define CYPRESS_READ_PORT_ID0	0
+#define CYPRESS_WRITE_PORT_ID0	0
+#define CYPRESS_READ_PORT_ID1	0x2
+#define CYPRESS_WRITE_PORT_ID1	1
+
+#define CYPRESS_MAX_REQSIZE	8
+
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cypress_table[] = {
+	{ USB_DEVICE(CYPRESS_VENDOR_ID, CYPRESS_PRODUCT_ID) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, cypress_table);
+
+/* structure to hold all of our device specific stuff */
+struct cypress {
+	struct usb_device *	udev;
+	unsigned char		port[2];
+};
+
+/* used to send usb control messages to device */
+static int vendor_command(struct cypress *dev, unsigned char request,
+			  unsigned char address, unsigned char data)
+{
+	int retval = 0;
+	unsigned int pipe;
+	unsigned char *iobuf;
+
+	/* allocate some memory for the i/o buffer*/
+	iobuf = kzalloc(CYPRESS_MAX_REQSIZE, GFP_KERNEL);
+	if (!iobuf) {
+		dev_err(&dev->udev->dev, "Out of memory!\n");
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	dev_dbg(&dev->udev->dev, "Sending usb_control_msg (data: %d)\n", data);
+
+	/* prepare usb control message and send it upstream */
+	pipe = usb_rcvctrlpipe(dev->udev, 0);
+	retval = usb_control_msg(dev->udev, pipe, request,
+				 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+				 address, data, iobuf, CYPRESS_MAX_REQSIZE,
+				 USB_CTRL_GET_TIMEOUT);
+
+	/* store returned data (more READs to be added) */
+	switch (request) {
+		case CYPRESS_READ_PORT:
+			if (address == CYPRESS_READ_PORT_ID0) {
+				dev->port[0] = iobuf[1];
+				dev_dbg(&dev->udev->dev,
+					"READ_PORT0 returned: %d\n",
+					dev->port[0]);
+			}
+			else if (address == CYPRESS_READ_PORT_ID1) {
+				dev->port[1] = iobuf[1];
+				dev_dbg(&dev->udev->dev,
+					"READ_PORT1 returned: %d\n",
+					dev->port[1]);
+			}
+			break;
+	}
+
+	kfree(iobuf);
+error:
+	return retval;
+}
+
+/* write port value */
+static ssize_t write_port(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count,
+			  int port_num, int write_id)
+{
+	int value = -1;
+	int result = 0;
+
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct cypress *cyp = usb_get_intfdata(intf);
+
+	dev_dbg(&cyp->udev->dev, "WRITE_PORT%d called\n", port_num);
+
+	/* validate input data */
+	if (sscanf(buf, "%d", &value) < 1) {
+		result = -EINVAL;
+		goto error;
+	}
+	if (value < 0 || value > 255) {
+		result = -EINVAL;
+		goto error;
+	}
+
+	result = vendor_command(cyp, CYPRESS_WRITE_PORT, write_id,
+				(unsigned char)value);
+
+	dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result);
+error:
+	return result < 0 ? result : count;
+}
+
+/* attribute callback handler (write) */
+static ssize_t set_port0_handler(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	return write_port(dev, attr, buf, count, 0, CYPRESS_WRITE_PORT_ID0);
+}
+
+/* attribute callback handler (write) */
+static ssize_t set_port1_handler(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	return write_port(dev, attr, buf, count, 1, CYPRESS_WRITE_PORT_ID1);
+}
+
+/* read port value */
+static ssize_t read_port(struct device *dev, struct device_attribute *attr,
+			 char *buf, int port_num, int read_id)
+{
+	int result = 0;
+
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct cypress *cyp = usb_get_intfdata(intf);
+
+	dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num);
+
+	result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0);
+
+	dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result);
+
+	return sprintf(buf, "%d", cyp->port[port_num]);
+}
+
+/* attribute callback handler (read) */
+static ssize_t get_port0_handler(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0);
+}
+
+/* attribute callback handler (read) */
+static ssize_t get_port1_handler(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1);
+}
+
+static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR, get_port0_handler, set_port0_handler);
+
+static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR, get_port1_handler, set_port1_handler);
+
+
+static int cypress_probe(struct usb_interface *interface,
+			 const struct usb_device_id *id)
+{
+	struct cypress *dev = NULL;
+	int retval = -ENOMEM;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&interface->dev, "Out of memory!\n");
+		goto error_mem;
+	}
+
+	dev->udev = usb_get_dev(interface_to_usbdev(interface));
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, dev);
+
+	/* create device attribute files */
+	retval = device_create_file(&interface->dev, &dev_attr_port0);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_port1);
+	if (retval)
+		goto error;
+
+	/* let the user know that the device is now attached */
+	dev_info(&interface->dev,
+		 "Cypress CY7C63xxx device now attached\n");
+	return 0;
+
+error:
+	device_remove_file(&interface->dev, &dev_attr_port0);
+	device_remove_file(&interface->dev, &dev_attr_port1);
+	usb_set_intfdata(interface, NULL);
+	usb_put_dev(dev->udev);
+	kfree(dev);
+
+error_mem:
+	return retval;
+}
+
+static void cypress_disconnect(struct usb_interface *interface)
+{
+	struct cypress *dev;
+
+	dev = usb_get_intfdata(interface);
+
+	/* remove device attribute files */
+	device_remove_file(&interface->dev, &dev_attr_port0);
+	device_remove_file(&interface->dev, &dev_attr_port1);
+	/* the intfdata can be set to NULL only after the
+	 * device files have been removed */
+	usb_set_intfdata(interface, NULL);
+
+	usb_put_dev(dev->udev);
+
+	dev_info(&interface->dev,
+		 "Cypress CY7C63xxx device now disconnected\n");
+
+	kfree(dev);
+}
+
+static struct usb_driver cypress_driver = {
+	.name = "cypress_cy7c63",
+	.probe = cypress_probe,
+	.disconnect = cypress_disconnect,
+	.id_table = cypress_table,
+};
+
+module_usb_driver(cypress_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
new file mode 100644
index 0000000..9bab1a3
--- /dev/null
+++ b/drivers/usb/misc/cytherm.c
@@ -0,0 +1,423 @@
+/* -*- linux-c -*-
+ * Cypress USB Thermometer driver 
+ * 
+ * Copyright (c) 2004 Erik Rigtorp <erkki@linux.nu> <erik@rigtorp.com>
+ * 
+ * This driver works with Elektor magazine USB Interface as published in 
+ * issue #291. It should also work with the original starter kit/demo board
+ * from Cypress.
+ *
+ * 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.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "Erik Rigtorp"
+#define DRIVER_DESC "Cypress USB Thermometer driver"
+
+#define USB_SKEL_VENDOR_ID	0x04b4
+#define USB_SKEL_PRODUCT_ID	0x0002
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
+	{ }
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+/* Structure to hold all of our device specific stuff */
+struct usb_cytherm {
+	struct usb_device    *udev;	 /* save off the usb device pointer */
+	struct usb_interface *interface; /* the interface for this device */
+	int brightness;
+};
+
+
+/* local function prototypes */
+static int cytherm_probe(struct usb_interface *interface, 
+			 const struct usb_device_id *id);
+static void cytherm_disconnect(struct usb_interface *interface);
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver cytherm_driver = {
+	.name =		"cytherm",
+	.probe =	cytherm_probe,
+	.disconnect =	cytherm_disconnect,
+	.id_table =	id_table,
+};
+
+/* Vendor requests */
+/* They all operate on one byte at a time */
+#define PING       0x00
+#define READ_ROM   0x01 /* Reads form ROM, value = address */
+#define READ_RAM   0x02 /* Reads form RAM, value = address */
+#define WRITE_RAM  0x03 /* Write to RAM, value = address, index = data */
+#define READ_PORT  0x04 /* Reads from port, value = address */
+#define WRITE_PORT 0x05 /* Write to port, value = address, index = data */ 
+
+
+/* Send a vendor command to device */
+static int vendor_command(struct usb_device *dev, unsigned char request, 
+			  unsigned char value, unsigned char index,
+			  void *buf, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			       request, 
+			       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+			       value, 
+			       index, buf, size,
+			       USB_CTRL_GET_TIMEOUT);
+}
+
+
+
+#define BRIGHTNESS 0x2c     /* RAM location for brightness value */
+#define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */
+
+static ssize_t show_brightness(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);    
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);     
+
+	return sprintf(buf, "%i", cytherm->brightness);
+}
+
+static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, const char *buf,
+			      size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	unsigned char *buffer;
+	int retval;
+   
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	cytherm->brightness = simple_strtoul(buf, NULL, 10);
+   
+	if (cytherm->brightness > 0xFF)
+		cytherm->brightness = 0xFF;
+	else if (cytherm->brightness < 0)
+		cytherm->brightness = 0;
+   
+	/* Set brightness */
+	retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS, 
+				cytherm->brightness, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+	/* Inform µC that we have changed the brightness setting */
+	retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS_SEM,
+				0x01, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+   
+	kfree(buffer);
+   
+	return count;
+}
+
+static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP, 
+		   show_brightness, set_brightness);
+
+
+#define TEMP 0x33 /* RAM location for temperature */
+#define SIGN 0x34 /* RAM location for temperature sign */
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	int retval;
+	unsigned char *buffer;
+
+	int temp, sign;
+   
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	/* read temperature */
+	retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+	temp = buffer[1];
+   
+	/* read sign */
+	retval = vendor_command(cytherm->udev, READ_RAM, SIGN, 0, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+	sign = buffer[1];
+
+	kfree(buffer);
+   
+	return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1,
+		       5*(temp - ((temp >> 1) << 1)));
+}
+
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	return count;
+}
+
+static DEVICE_ATTR(temp, S_IRUGO, show_temp, set_temp);
+
+
+#define BUTTON 0x7a
+
+static ssize_t show_button(struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	int retval;
+	unsigned char *buffer;
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	/* check button */
+	retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+   
+	retval = buffer[1];
+
+	kfree(buffer);
+
+	if (retval)
+		return sprintf(buf, "1");
+	else
+		return sprintf(buf, "0");
+}
+
+
+static ssize_t set_button(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	return count;
+}
+
+static DEVICE_ATTR(button, S_IRUGO, show_button, set_button);
+
+
+static ssize_t show_port0(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	int retval;
+	unsigned char *buffer;
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+
+	retval = buffer[1];
+
+	kfree(buffer);
+
+	return sprintf(buf, "%d", retval);
+}
+
+
+static ssize_t set_port0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	unsigned char *buffer;
+	int retval;
+	int tmp;
+   
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	tmp = simple_strtoul(buf, NULL, 10);
+   
+	if (tmp > 0xFF)
+		tmp = 0xFF;
+	else if (tmp < 0)
+		tmp = 0;
+   
+	retval = vendor_command(cytherm->udev, WRITE_PORT, 0,
+				tmp, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+
+	kfree(buffer);
+
+	return count;
+}
+
+static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR | S_IWGRP, show_port0, set_port0);
+
+static ssize_t show_port1(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	int retval;
+	unsigned char *buffer;
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+   
+	retval = buffer[1];
+
+	kfree(buffer);
+
+	return sprintf(buf, "%d", retval);
+}
+
+
+static ssize_t set_port1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_cytherm *cytherm = usb_get_intfdata(intf);
+
+	unsigned char *buffer;
+	int retval;
+	int tmp;
+   
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&cytherm->udev->dev, "out of memory\n");
+		return 0;
+	}
+
+	tmp = simple_strtoul(buf, NULL, 10);
+   
+	if (tmp > 0xFF)
+		tmp = 0xFF;
+	else if (tmp < 0)
+		tmp = 0;
+   
+	retval = vendor_command(cytherm->udev, WRITE_PORT, 1,
+				tmp, buffer, 8);
+	if (retval)
+		dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
+
+	kfree(buffer);
+
+	return count;
+}
+
+static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR | S_IWGRP, show_port1, set_port1);
+
+
+
+static int cytherm_probe(struct usb_interface *interface, 
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_cytherm *dev = NULL;
+	int retval = -ENOMEM;
+
+	dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err (&interface->dev, "Out of memory\n");
+		goto error_mem;
+	}
+
+	dev->udev = usb_get_dev(udev);
+
+	usb_set_intfdata (interface, dev);
+
+	dev->brightness = 0xFF;
+
+	retval = device_create_file(&interface->dev, &dev_attr_brightness);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_temp);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_button);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_port0);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_port1);
+	if (retval)
+		goto error;
+
+	dev_info (&interface->dev,
+		  "Cypress thermometer device now attached\n");
+	return 0;
+error:
+	device_remove_file(&interface->dev, &dev_attr_brightness);
+	device_remove_file(&interface->dev, &dev_attr_temp);
+	device_remove_file(&interface->dev, &dev_attr_button);
+	device_remove_file(&interface->dev, &dev_attr_port0);
+	device_remove_file(&interface->dev, &dev_attr_port1);
+	usb_set_intfdata (interface, NULL);
+	usb_put_dev(dev->udev);
+	kfree(dev);
+error_mem:
+	return retval;
+}
+
+static void cytherm_disconnect(struct usb_interface *interface)
+{
+	struct usb_cytherm *dev;
+
+	dev = usb_get_intfdata (interface);
+
+	device_remove_file(&interface->dev, &dev_attr_brightness);
+	device_remove_file(&interface->dev, &dev_attr_temp);
+	device_remove_file(&interface->dev, &dev_attr_button);
+	device_remove_file(&interface->dev, &dev_attr_port0);
+	device_remove_file(&interface->dev, &dev_attr_port1);
+
+	/* first remove the files, then NULL the pointer */
+	usb_set_intfdata (interface, NULL);
+
+	usb_put_dev(dev->udev);
+
+	kfree(dev);
+
+	dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
+}
+
+module_usb_driver(cytherm_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c
new file mode 100644
index 0000000..c31b4a3
--- /dev/null
+++ b/drivers/usb/misc/ehset.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+
+#define TEST_SE0_NAK_PID			0x0101
+#define TEST_J_PID				0x0102
+#define TEST_K_PID				0x0103
+#define TEST_PACKET_PID				0x0104
+#define TEST_HS_HOST_PORT_SUSPEND_RESUME	0x0106
+#define TEST_SINGLE_STEP_GET_DEV_DESC		0x0107
+#define TEST_SINGLE_STEP_SET_FEATURE		0x0108
+
+static int ehset_probe(struct usb_interface *intf,
+		       const struct usb_device_id *id)
+{
+	int ret = -EINVAL;
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_device *hub_udev = dev->parent;
+	struct usb_device_descriptor *buf;
+	u8 portnum = dev->portnum;
+	u16 test_pid = le16_to_cpu(dev->descriptor.idProduct);
+
+	switch (test_pid) {
+	case TEST_SE0_NAK_PID:
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_SET_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_TEST,
+					(TEST_SE0_NAK << 8) | portnum,
+					NULL, 0, 1000);
+		break;
+	case TEST_J_PID:
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_SET_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_TEST,
+					(TEST_J << 8) | portnum,
+					NULL, 0, 1000);
+		break;
+	case TEST_K_PID:
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_SET_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_TEST,
+					(TEST_K << 8) | portnum,
+					NULL, 0, 1000);
+		break;
+	case TEST_PACKET_PID:
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_SET_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_TEST,
+					(TEST_PACKET << 8) | portnum,
+					NULL, 0, 1000);
+		break;
+	case TEST_HS_HOST_PORT_SUSPEND_RESUME:
+		/* Test: wait for 15secs -> suspend -> 15secs delay -> resume */
+		msleep(15 * 1000);
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_SET_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_SUSPEND, portnum,
+					NULL, 0, 1000);
+		if (ret < 0)
+			break;
+
+		msleep(15 * 1000);
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_CLEAR_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_SUSPEND, portnum,
+					NULL, 0, 1000);
+		break;
+	case TEST_SINGLE_STEP_GET_DEV_DESC:
+		/* Test: wait for 15secs -> GetDescriptor request */
+		msleep(15 * 1000);
+		buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+
+		ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+					USB_DT_DEVICE << 8, 0,
+					buf, USB_DT_DEVICE_SIZE,
+					USB_CTRL_GET_TIMEOUT);
+		kfree(buf);
+		break;
+	case TEST_SINGLE_STEP_SET_FEATURE:
+		/*
+		 * GetDescriptor SETUP request -> 15secs delay -> IN & STATUS
+		 *
+		 * Note, this test is only supported on root hubs since the
+		 * SetPortFeature handling can only be done inside the HCD's
+		 * hub_control callback function.
+		 */
+		if (hub_udev != dev->bus->root_hub) {
+			dev_err(&intf->dev, "SINGLE_STEP_SET_FEATURE test only supported on root hub\n");
+			break;
+		}
+
+		ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
+					USB_REQ_SET_FEATURE, USB_RT_PORT,
+					USB_PORT_FEAT_TEST,
+					(6 << 8) | portnum,
+					NULL, 0, 60 * 1000);
+
+		break;
+	default:
+		dev_err(&intf->dev, "%s: unsupported PID: 0x%x\n",
+			__func__, test_pid);
+	}
+
+	return (ret < 0) ? ret : 0;
+}
+
+static void ehset_disconnect(struct usb_interface *intf)
+{
+}
+
+static const struct usb_device_id ehset_id_table[] = {
+	{ USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_J_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_K_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_PACKET_PID) },
+	{ USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) },
+	{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) },
+	{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) },
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, ehset_id_table);
+
+static struct usb_driver ehset_driver = {
+	.name =		"usb_ehset_test",
+	.probe =	ehset_probe,
+	.disconnect =	ehset_disconnect,
+	.id_table =	ehset_id_table,
+};
+
+module_usb_driver(ehset_driver);
+
+MODULE_DESCRIPTION("USB Driver for EHSET Test Fixture");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c
new file mode 100644
index 0000000..8950fa5
--- /dev/null
+++ b/drivers/usb/misc/emi26.c
@@ -0,0 +1,262 @@
+/* 
+ * Emagic EMI 2|6 usb audio interface firmware loader.
+ * Copyright (C) 2002
+ * 	Tapio Laxström (tapio.laxstrom@iptime.fi)
+ *
+ * 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.
+ * 
+ * emi26.c,v 1.13 2002/03/08 13:10:26 tapio Exp
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+
+#define EMI26_VENDOR_ID 		0x086a  /* Emagic Soft-und Hardware GmBH */
+#define EMI26_PRODUCT_ID		0x0100	/* EMI 2|6 without firmware */
+#define EMI26B_PRODUCT_ID		0x0102	/* EMI 2|6 without firmware */
+
+#define ANCHOR_LOAD_INTERNAL	0xA0	/* Vendor specific request code for Anchor Upload/Download (This one is implemented in the core) */
+#define ANCHOR_LOAD_EXTERNAL	0xA3	/* This command is not implemented in the core. Requires firmware */
+#define ANCHOR_LOAD_FPGA	0xA5	/* This command is not implemented in the core. Requires firmware. Emagic extension */
+#define MAX_INTERNAL_ADDRESS	0x1B3F	/* This is the highest internal RAM address for the AN2131Q */
+#define CPUCS_REG		0x7F92  /* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */ 
+#define INTERNAL_RAM(address)   (address <= MAX_INTERNAL_ADDRESS)
+
+static int emi26_writememory( struct usb_device *dev, int address,
+			      const unsigned char *data, int length,
+			      __u8 bRequest);
+static int emi26_set_reset(struct usb_device *dev, unsigned char reset_bit);
+static int emi26_load_firmware (struct usb_device *dev);
+static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void emi26_disconnect(struct usb_interface *intf);
+
+/* thanks to drivers/usb/serial/keyspan_pda.c code */
+static int emi26_writememory (struct usb_device *dev, int address,
+			      const unsigned char *data, int length,
+			      __u8 request)
+{
+	int result;
+	unsigned char *buffer =  kmemdup(data, length, GFP_KERNEL);
+
+	if (!buffer) {
+		dev_err(&dev->dev, "kmalloc(%d) failed.\n", length);
+		return -ENOMEM;
+	}
+	/* Note: usb_control_msg returns negative value on error or length of the
+	 * 		 data that was written! */
+	result = usb_control_msg (dev, usb_sndctrlpipe(dev, 0), request, 0x40, address, 0, buffer, length, 300);
+	kfree (buffer);
+	return result;
+}
+
+/* thanks to drivers/usb/serial/keyspan_pda.c code */
+static int emi26_set_reset (struct usb_device *dev, unsigned char reset_bit)
+{
+	int response;
+	dev_info(&dev->dev, "%s - %d\n", __func__, reset_bit);
+	/* printk(KERN_DEBUG "%s - %d", __func__, reset_bit); */
+	response = emi26_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);
+	if (response < 0) {
+		dev_err(&dev->dev, "set_reset (%d) failed\n", reset_bit);
+	}
+	return response;
+}
+
+#define FW_LOAD_SIZE		1023
+
+static int emi26_load_firmware (struct usb_device *dev)
+{
+	const struct firmware *loader_fw = NULL;
+	const struct firmware *bitstream_fw = NULL;
+	const struct firmware *firmware_fw = NULL;
+	const struct ihex_binrec *rec;
+	int err = -ENOMEM;
+	int i;
+	__u32 addr;	/* Address to write */
+	__u8 *buf;
+
+	buf = kmalloc(FW_LOAD_SIZE, GFP_KERNEL);
+	if (!buf)
+		goto wraperr;
+
+	err = request_ihex_firmware(&loader_fw, "emi26/loader.fw", &dev->dev);
+	if (err)
+		goto nofw;
+
+	err = request_ihex_firmware(&bitstream_fw, "emi26/bitstream.fw",
+				    &dev->dev);
+	if (err)
+		goto nofw;
+
+	err = request_ihex_firmware(&firmware_fw, "emi26/firmware.fw",
+				    &dev->dev);
+	if (err) {
+	nofw:
+		dev_err(&dev->dev, "%s - request_firmware() failed\n",
+			__func__);
+		goto wraperr;
+	}
+
+	/* Assert reset (stop the CPU in the EMI) */
+	err = emi26_set_reset(dev,1);
+	if (err < 0)
+		goto wraperr;
+
+	rec = (const struct ihex_binrec *)loader_fw->data;
+	/* 1. We need to put the loader for the FPGA into the EZ-USB */
+	while (rec) {
+		err = emi26_writememory(dev, be32_to_cpu(rec->addr),
+					rec->data, be16_to_cpu(rec->len),
+					ANCHOR_LOAD_INTERNAL);
+		if (err < 0)
+			goto wraperr;
+		rec = ihex_next_binrec(rec);
+	}
+
+	/* De-assert reset (let the CPU run) */
+	err = emi26_set_reset(dev,0);
+	if (err < 0)
+		goto wraperr;
+	msleep(250);	/* let device settle */
+
+	/* 2. We upload the FPGA firmware into the EMI
+	 * Note: collect up to 1023 (yes!) bytes and send them with
+	 * a single request. This is _much_ faster! */
+	rec = (const struct ihex_binrec *)bitstream_fw->data;
+	do {
+		i = 0;
+		addr = be32_to_cpu(rec->addr);
+
+		/* intel hex records are terminated with type 0 element */
+		while (rec && (i + be16_to_cpu(rec->len) < FW_LOAD_SIZE)) {
+			memcpy(buf + i, rec->data, be16_to_cpu(rec->len));
+			i += be16_to_cpu(rec->len);
+			rec = ihex_next_binrec(rec);
+		}
+		err = emi26_writememory(dev, addr, buf, i, ANCHOR_LOAD_FPGA);
+		if (err < 0)
+			goto wraperr;
+	} while (rec);
+
+	/* Assert reset (stop the CPU in the EMI) */
+	err = emi26_set_reset(dev,1);
+	if (err < 0)
+		goto wraperr;
+
+	/* 3. We need to put the loader for the firmware into the EZ-USB (again...) */
+	for (rec = (const struct ihex_binrec *)loader_fw->data;
+	     rec; rec = ihex_next_binrec(rec)) {
+		err = emi26_writememory(dev, be32_to_cpu(rec->addr),
+					rec->data, be16_to_cpu(rec->len),
+					ANCHOR_LOAD_INTERNAL);
+		if (err < 0)
+			goto wraperr;
+	}
+	msleep(250);	/* let device settle */
+
+	/* De-assert reset (let the CPU run) */
+	err = emi26_set_reset(dev,0);
+	if (err < 0)
+		goto wraperr;
+
+	/* 4. We put the part of the firmware that lies in the external RAM into the EZ-USB */
+
+	for (rec = (const struct ihex_binrec *)firmware_fw->data;
+	     rec; rec = ihex_next_binrec(rec)) {
+		if (!INTERNAL_RAM(be32_to_cpu(rec->addr))) {
+			err = emi26_writememory(dev, be32_to_cpu(rec->addr),
+						rec->data, be16_to_cpu(rec->len),
+						ANCHOR_LOAD_EXTERNAL);
+			if (err < 0)
+				goto wraperr;
+		}
+	}
+
+	/* Assert reset (stop the CPU in the EMI) */
+	err = emi26_set_reset(dev,1);
+	if (err < 0)
+		goto wraperr;
+
+	for (rec = (const struct ihex_binrec *)firmware_fw->data;
+	     rec; rec = ihex_next_binrec(rec)) {
+		if (INTERNAL_RAM(be32_to_cpu(rec->addr))) {
+			err = emi26_writememory(dev, be32_to_cpu(rec->addr),
+						rec->data, be16_to_cpu(rec->len),
+						ANCHOR_LOAD_INTERNAL);
+			if (err < 0)
+				goto wraperr;
+		}
+	}
+
+	/* De-assert reset (let the CPU run) */
+	err = emi26_set_reset(dev,0);
+	if (err < 0)
+		goto wraperr;
+	msleep(250);	/* let device settle */
+
+	/* return 1 to fail the driver inialization
+	 * and give real driver change to load */
+	err = 1;
+
+wraperr:
+	if (err < 0)
+		dev_err(&dev->dev,"%s - error loading firmware: error = %d\n",
+			__func__, err);
+
+	release_firmware(loader_fw);
+	release_firmware(bitstream_fw);
+	release_firmware(firmware_fw);
+
+	kfree(buf);
+	return err;
+}
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(EMI26_VENDOR_ID, EMI26_PRODUCT_ID) },
+	{ USB_DEVICE(EMI26_VENDOR_ID, EMI26B_PRODUCT_ID) },
+	{ }                                             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+
+	dev_info(&intf->dev, "%s start\n", __func__);
+
+	emi26_load_firmware(dev);
+
+	/* do not return the driver context, let real audio driver do that */
+	return -EIO;
+}
+
+static void emi26_disconnect(struct usb_interface *intf)
+{
+}
+
+static struct usb_driver emi26_driver = {
+	.name		= "emi26 - firmware loader",
+	.probe		= emi26_probe,
+	.disconnect	= emi26_disconnect,
+	.id_table	= id_table,
+};
+
+module_usb_driver(emi26_driver);
+
+MODULE_AUTHOR("Tapio Laxström");
+MODULE_DESCRIPTION("Emagic EMI 2|6 firmware loader.");
+MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("emi26/loader.fw");
+MODULE_FIRMWARE("emi26/bitstream.fw");
+MODULE_FIRMWARE("emi26/firmware.fw");
+/* vi:ai:syntax=c:sw=8:ts=8:tw=80
+ */
diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c
new file mode 100644
index 0000000..1d9be44
--- /dev/null
+++ b/drivers/usb/misc/emi62.c
@@ -0,0 +1,275 @@
+/* 
+ * Emagic EMI 2|6 usb audio interface firmware loader.
+ * Copyright (C) 2002
+ * 	Tapio Laxström (tapio.laxstrom@iptime.fi)
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+
+/* include firmware (variables)*/
+
+/* FIXME: This is quick and dirty solution! */
+#define SPDIF	/* if you want SPDIF comment next line */
+//#undef SPDIF	/* if you want MIDI uncomment this line */ 
+
+#ifdef SPDIF
+#define FIRMWARE_FW "emi62/spdif.fw"
+#else
+#define FIRMWARE_FW "emi62/midi.fw"
+#endif
+
+#define EMI62_VENDOR_ID 		0x086a  /* Emagic Soft-und Hardware GmBH */
+#define EMI62_PRODUCT_ID		0x0110	/* EMI 6|2m without firmware */
+
+#define ANCHOR_LOAD_INTERNAL	0xA0	/* Vendor specific request code for Anchor Upload/Download (This one is implemented in the core) */
+#define ANCHOR_LOAD_EXTERNAL	0xA3	/* This command is not implemented in the core. Requires firmware */
+#define ANCHOR_LOAD_FPGA	0xA5	/* This command is not implemented in the core. Requires firmware. Emagic extension */
+#define MAX_INTERNAL_ADDRESS	0x1B3F	/* This is the highest internal RAM address for the AN2131Q */
+#define CPUCS_REG		0x7F92  /* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */ 
+#define INTERNAL_RAM(address)   (address <= MAX_INTERNAL_ADDRESS)
+
+static int emi62_writememory(struct usb_device *dev, int address,
+			     const unsigned char *data, int length,
+			     __u8 bRequest);
+static int emi62_set_reset(struct usb_device *dev, unsigned char reset_bit);
+static int emi62_load_firmware (struct usb_device *dev);
+static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void emi62_disconnect(struct usb_interface *intf);
+
+/* thanks to drivers/usb/serial/keyspan_pda.c code */
+static int emi62_writememory(struct usb_device *dev, int address,
+			     const unsigned char *data, int length,
+			     __u8 request)
+{
+	int result;
+	unsigned char *buffer =  kmemdup(data, length, GFP_KERNEL);
+
+	if (!buffer) {
+		dev_err(&dev->dev, "kmalloc(%d) failed.\n", length);
+		return -ENOMEM;
+	}
+	/* Note: usb_control_msg returns negative value on error or length of the
+	 * 		 data that was written! */
+	result = usb_control_msg (dev, usb_sndctrlpipe(dev, 0), request, 0x40, address, 0, buffer, length, 300);
+	kfree (buffer);
+	return result;
+}
+
+/* thanks to drivers/usb/serial/keyspan_pda.c code */
+static int emi62_set_reset (struct usb_device *dev, unsigned char reset_bit)
+{
+	int response;
+	dev_info(&dev->dev, "%s - %d\n", __func__, reset_bit);
+	
+	response = emi62_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);
+	if (response < 0)
+		dev_err(&dev->dev, "set_reset (%d) failed\n", reset_bit);
+	return response;
+}
+
+#define FW_LOAD_SIZE		1023
+
+static int emi62_load_firmware (struct usb_device *dev)
+{
+	const struct firmware *loader_fw = NULL;
+	const struct firmware *bitstream_fw = NULL;
+	const struct firmware *firmware_fw = NULL;
+	const struct ihex_binrec *rec;
+	int err = -ENOMEM;
+	int i;
+	__u32 addr;	/* Address to write */
+	__u8 *buf;
+
+	dev_dbg(&dev->dev, "load_firmware\n");
+	buf = kmalloc(FW_LOAD_SIZE, GFP_KERNEL);
+	if (!buf)
+		goto wraperr;
+
+	err = request_ihex_firmware(&loader_fw, "emi62/loader.fw", &dev->dev);
+	if (err)
+		goto nofw;
+
+	err = request_ihex_firmware(&bitstream_fw, "emi62/bitstream.fw",
+				    &dev->dev);
+	if (err)
+		goto nofw;
+
+	err = request_ihex_firmware(&firmware_fw, FIRMWARE_FW, &dev->dev);
+	if (err) {
+	nofw:
+		goto wraperr;
+	}
+
+	/* Assert reset (stop the CPU in the EMI) */
+	err = emi62_set_reset(dev,1);
+	if (err < 0)
+		goto wraperr;
+
+	rec = (const struct ihex_binrec *)loader_fw->data;
+
+	/* 1. We need to put the loader for the FPGA into the EZ-USB */
+	while (rec) {
+		err = emi62_writememory(dev, be32_to_cpu(rec->addr),
+					rec->data, be16_to_cpu(rec->len),
+					ANCHOR_LOAD_INTERNAL);
+		if (err < 0)
+			goto wraperr;
+		rec = ihex_next_binrec(rec);
+	}
+
+	/* De-assert reset (let the CPU run) */
+	err = emi62_set_reset(dev,0);
+	if (err < 0)
+		goto wraperr;
+	msleep(250);	/* let device settle */
+
+	/* 2. We upload the FPGA firmware into the EMI
+	 * Note: collect up to 1023 (yes!) bytes and send them with
+	 * a single request. This is _much_ faster! */
+	rec = (const struct ihex_binrec *)bitstream_fw->data;
+	do {
+		i = 0;
+		addr = be32_to_cpu(rec->addr);
+
+		/* intel hex records are terminated with type 0 element */
+		while (rec && (i + be16_to_cpu(rec->len) < FW_LOAD_SIZE)) {
+			memcpy(buf + i, rec->data, be16_to_cpu(rec->len));
+			i += be16_to_cpu(rec->len);
+			rec = ihex_next_binrec(rec);
+		}
+		err = emi62_writememory(dev, addr, buf, i, ANCHOR_LOAD_FPGA);
+		if (err < 0)
+			goto wraperr;
+	} while (rec);
+
+	/* Assert reset (stop the CPU in the EMI) */
+	err = emi62_set_reset(dev,1);
+	if (err < 0)
+		goto wraperr;
+
+	/* 3. We need to put the loader for the firmware into the EZ-USB (again...) */
+	for (rec = (const struct ihex_binrec *)loader_fw->data;
+	     rec; rec = ihex_next_binrec(rec)) {
+		err = emi62_writememory(dev, be32_to_cpu(rec->addr),
+					rec->data, be16_to_cpu(rec->len),
+					ANCHOR_LOAD_INTERNAL);
+		if (err < 0)
+			goto wraperr;
+	}
+
+	/* De-assert reset (let the CPU run) */
+	err = emi62_set_reset(dev,0);
+	if (err < 0)
+		goto wraperr;
+	msleep(250);	/* let device settle */
+
+	/* 4. We put the part of the firmware that lies in the external RAM into the EZ-USB */
+
+	for (rec = (const struct ihex_binrec *)firmware_fw->data;
+	     rec; rec = ihex_next_binrec(rec)) {
+		if (!INTERNAL_RAM(be32_to_cpu(rec->addr))) {
+			err = emi62_writememory(dev, be32_to_cpu(rec->addr),
+						rec->data, be16_to_cpu(rec->len),
+						ANCHOR_LOAD_EXTERNAL);
+			if (err < 0)
+				goto wraperr;
+		}
+	}
+
+	/* Assert reset (stop the CPU in the EMI) */
+	err = emi62_set_reset(dev,1);
+	if (err < 0)
+		goto wraperr;
+
+	for (rec = (const struct ihex_binrec *)firmware_fw->data;
+	     rec; rec = ihex_next_binrec(rec)) {
+		if (INTERNAL_RAM(be32_to_cpu(rec->addr))) {
+			err = emi62_writememory(dev, be32_to_cpu(rec->addr),
+						rec->data, be16_to_cpu(rec->len),
+						ANCHOR_LOAD_EXTERNAL);
+			if (err < 0)
+				goto wraperr;
+		}
+	}
+
+	/* De-assert reset (let the CPU run) */
+	err = emi62_set_reset(dev,0);
+	if (err < 0)
+		goto wraperr;
+	msleep(250);	/* let device settle */
+
+	release_firmware(loader_fw);
+	release_firmware(bitstream_fw);
+	release_firmware(firmware_fw);
+
+	kfree(buf);
+
+	/* return 1 to fail the driver inialization
+	 * and give real driver change to load */
+	return 1;
+
+wraperr:
+	if (err < 0)
+		dev_err(&dev->dev,"%s - error loading firmware: error = %d\n",
+			__func__, err);
+	release_firmware(loader_fw);
+	release_firmware(bitstream_fw);
+	release_firmware(firmware_fw);
+
+	kfree(buf);
+	dev_err(&dev->dev, "Error\n");
+	return err;
+}
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(EMI62_VENDOR_ID, EMI62_PRODUCT_ID) },
+	{ }                                             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	dev_dbg(&intf->dev, "emi62_probe\n");
+
+	dev_info(&intf->dev, "%s start\n", __func__);
+
+	emi62_load_firmware(dev);
+
+	/* do not return the driver context, let real audio driver do that */
+	return -EIO;
+}
+
+static void emi62_disconnect(struct usb_interface *intf)
+{
+}
+
+static struct usb_driver emi62_driver = {
+	.name		= "emi62 - firmware loader",
+	.probe		= emi62_probe,
+	.disconnect	= emi62_disconnect,
+	.id_table	= id_table,
+};
+
+module_usb_driver(emi62_driver);
+
+MODULE_AUTHOR("Tapio Laxström");
+MODULE_DESCRIPTION("Emagic EMI 6|2m firmware loader.");
+MODULE_LICENSE("GPL");
+
+MODULE_FIRMWARE("emi62/loader.fw");
+MODULE_FIRMWARE("emi62/bitstream.fw");
+MODULE_FIRMWARE(FIRMWARE_FW);
+/* vi:ai:syntax=c:sw=8:ts=8:tw=80
+ */
diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c
new file mode 100644
index 0000000..947811b
--- /dev/null
+++ b/drivers/usb/misc/ezusb.c
@@ -0,0 +1,166 @@
+/*
+ * EZ-USB specific functions used by some of the USB to Serial drivers.
+ *
+ * Copyright (C) 1999 - 2002 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 version
+ *	2 as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/usb/ezusb.h>
+
+struct ezusb_fx_type {
+	/* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
+	unsigned short cpucs_reg;
+	unsigned short max_internal_adress;
+};
+
+static struct ezusb_fx_type ezusb_fx1 = {
+	.cpucs_reg = 0x7F92,
+	.max_internal_adress = 0x1B3F,
+};
+
+/* Commands for writing to memory */
+#define WRITE_INT_RAM 0xA0
+#define WRITE_EXT_RAM 0xA3
+
+static int ezusb_writememory(struct usb_device *dev, int address,
+				unsigned char *data, int length, __u8 request)
+{
+	int result;
+	unsigned char *transfer_buffer;
+
+	if (!dev)
+		return -ENODEV;
+
+	transfer_buffer = kmemdup(data, length, GFP_KERNEL);
+	if (!transfer_buffer) {
+		dev_err(&dev->dev, "%s - kmalloc(%d) failed.\n",
+							__func__, length);
+		return -ENOMEM;
+	}
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+				 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				 address, 0, transfer_buffer, length, 3000);
+
+	kfree(transfer_buffer);
+	return result;
+}
+
+static int ezusb_set_reset(struct usb_device *dev, unsigned short cpucs_reg,
+			   unsigned char reset_bit)
+{
+	int response = ezusb_writememory(dev, cpucs_reg, &reset_bit, 1, WRITE_INT_RAM);
+	if (response < 0)
+		dev_err(&dev->dev, "%s-%d failed: %d\n",
+						__func__, reset_bit, response);
+	return response;
+}
+
+int ezusb_fx1_set_reset(struct usb_device *dev, unsigned char reset_bit)
+{
+	return ezusb_set_reset(dev, ezusb_fx1.cpucs_reg, reset_bit);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx1_set_reset);
+
+static int ezusb_ihex_firmware_download(struct usb_device *dev,
+					struct ezusb_fx_type fx,
+					const char *firmware_path)
+{
+	int ret = -ENOENT;
+	const struct firmware *firmware = NULL;
+	const struct ihex_binrec *record;
+
+	if (request_ihex_firmware(&firmware, firmware_path,
+				  &dev->dev)) {
+		dev_err(&dev->dev,
+			"%s - request \"%s\" failed\n",
+			__func__, firmware_path);
+		goto out;
+	}
+
+	ret = ezusb_set_reset(dev, fx.cpucs_reg, 0);
+	if (ret < 0)
+		goto out;
+
+	record = (const struct ihex_binrec *)firmware->data;
+	for (; record; record = ihex_next_binrec(record)) {
+		if (be32_to_cpu(record->addr) > fx.max_internal_adress) {
+			ret = ezusb_writememory(dev, be32_to_cpu(record->addr),
+						(unsigned char *)record->data,
+						be16_to_cpu(record->len), WRITE_EXT_RAM);
+			if (ret < 0) {
+				dev_err(&dev->dev, "%s - ezusb_writememory "
+					"failed writing internal memory "
+					"(%d %04X %p %d)\n", __func__, ret,
+					be32_to_cpu(record->addr), record->data,
+					be16_to_cpu(record->len));
+				goto out;
+			}
+		}
+	}
+
+	ret = ezusb_set_reset(dev, fx.cpucs_reg, 1);
+	if (ret < 0)
+		goto out;
+	record = (const struct ihex_binrec *)firmware->data;
+	for (; record; record = ihex_next_binrec(record)) {
+		if (be32_to_cpu(record->addr) <= fx.max_internal_adress) {
+			ret = ezusb_writememory(dev, be32_to_cpu(record->addr),
+						(unsigned char *)record->data,
+						be16_to_cpu(record->len), WRITE_INT_RAM);
+			if (ret < 0) {
+				dev_err(&dev->dev, "%s - ezusb_writememory "
+					"failed writing external memory "
+					"(%d %04X %p %d)\n", __func__, ret,
+					be32_to_cpu(record->addr), record->data,
+					be16_to_cpu(record->len));
+				goto out;
+			}
+		}
+	}
+	ret = ezusb_set_reset(dev, fx.cpucs_reg, 0);
+out:
+	release_firmware(firmware);
+	return ret;
+}
+
+int ezusb_fx1_ihex_firmware_download(struct usb_device *dev,
+				     const char *firmware_path)
+{
+	return ezusb_ihex_firmware_download(dev, ezusb_fx1, firmware_path);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx1_ihex_firmware_download);
+
+#if 0
+/*
+ * Once someone one needs these fx2 functions, uncomment them
+ * and add them to ezusb.h and all should be good.
+ */
+static struct ezusb_fx_type ezusb_fx2 = {
+	.cpucs_reg = 0xE600,
+	.max_internal_adress = 0x3FFF,
+};
+
+int ezusb_fx2_set_reset(struct usb_device *dev, unsigned char reset_bit)
+{
+	return ezusb_set_reset(dev, ezusb_fx2.cpucs_reg, reset_bit);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx2_set_reset);
+
+int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
+				     const char *firmware_path)
+{
+	return ezusb_ihex_firmware_download(dev, ezusb_fx2, firmware_path);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download);
+#endif
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
new file mode 100644
index 0000000..52c27ca
--- /dev/null
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -0,0 +1,2898 @@
+/*
+ * USB FTDI client driver for Elan Digital Systems's Uxxx adapters
+ *
+ * Copyright(C) 2006 Elan Digital Systems Limited
+ * http://www.elandigitalsystems.com
+ *
+ * Author and Maintainer - Tony Olech - Elan Digital Systems
+ * tony.olech@elandigitalsystems.com
+ *
+ * This program is free software;you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ *
+ * This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+ * based on various USB client drivers in the 2.6.15 linux kernel
+ * with constant reference to the 3rd Edition of Linux Device Drivers
+ * published by O'Reilly
+ *
+ * The U132 adapter is a USB to CardBus adapter specifically designed
+ * for PC cards that contain an OHCI host controller. Typical PC cards
+ * are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+ *
+ * The U132 adapter will *NOT *work with PC cards that do not contain
+ * an OHCI controller. A simple way to test whether a PC card has an
+ * OHCI controller as an interface is to insert the PC card directly
+ * into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+ * a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+ * then there is a good chance that the U132 adapter will support the
+ * PC card.(you also need the specific client driver for the PC card)
+ *
+ * Please inform the Author and Maintainer about any PC cards that
+ * contain OHCI Host Controller and work when directly connected to
+ * an embedded CardBus slot but do not work when they are connected
+ * via an ELAN U132 adapter.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+MODULE_AUTHOR("Tony Olech");
+MODULE_DESCRIPTION("FTDI ELAN driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+static bool distrust_firmware = 1;
+module_param(distrust_firmware, bool, 0);
+MODULE_PARM_DESC(distrust_firmware,
+		 "true to distrust firmware power/overcurrent setup");
+extern struct platform_driver u132_platform_driver;
+static struct workqueue_struct *status_queue;
+static struct workqueue_struct *command_queue;
+static struct workqueue_struct *respond_queue;
+/*
+ * ftdi_module_lock exists to protect access to global variables
+ *
+ */
+static struct mutex ftdi_module_lock;
+static int ftdi_instances = 0;
+static struct list_head ftdi_static_list;
+/*
+ * end of the global variables protected by ftdi_module_lock
+ */
+#include "usb_u132.h"
+#include <asm/io.h>
+#include <linux/usb/hcd.h>
+
+/* FIXME ohci.h is ONLY for internal use by the OHCI driver.
+ * If you're going to try stuff like this, you need to split
+ * out shareable stuff (register declarations?) into its own
+ * file, maybe name <linux/usb/ohci.h>
+ */
+
+#include "../host/ohci.h"
+/* Define these values to match your devices*/
+#define USB_FTDI_ELAN_VENDOR_ID 0x0403
+#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea
+/* table of devices that work with this driver*/
+static const struct usb_device_id ftdi_elan_table[] = {
+	{USB_DEVICE(USB_FTDI_ELAN_VENDOR_ID, USB_FTDI_ELAN_PRODUCT_ID)},
+	{ /* Terminating entry */ }
+};
+
+MODULE_DEVICE_TABLE(usb, ftdi_elan_table);
+/* only the jtag(firmware upgrade device) interface requires
+ * a device file and corresponding minor number, but the
+ * interface is created unconditionally - I suppose it could
+ * be configured or not according to a module parameter.
+ * But since we(now) require one interface per device,
+ * and since it unlikely that a normal installation would
+ * require more than a couple of elan-ftdi devices, 8 seems
+ * like a reasonable limit to have here, and if someone
+ * really requires more than 8 devices, then they can frig the
+ * code and recompile
+ */
+#define USB_FTDI_ELAN_MINOR_BASE 192
+#define COMMAND_BITS 5
+#define COMMAND_SIZE (1<<COMMAND_BITS)
+#define COMMAND_MASK (COMMAND_SIZE-1)
+struct u132_command {
+	u8 header;
+	u16 length;
+	u8 address;
+	u8 width;
+	u32 value;
+	int follows;
+	void *buffer;
+};
+#define RESPOND_BITS 5
+#define RESPOND_SIZE (1<<RESPOND_BITS)
+#define RESPOND_MASK (RESPOND_SIZE-1)
+struct u132_respond {
+	u8 header;
+	u8 address;
+	u32 *value;
+	int *result;
+	struct completion wait_completion;
+};
+struct u132_target {
+	void *endp;
+	struct urb *urb;
+	int toggle_bits;
+	int error_count;
+	int condition_code;
+	int repeat_number;
+	int halted;
+	int skipped;
+	int actual;
+	int non_null;
+	int active;
+	int abandoning;
+	void (*callback)(void *endp, struct urb *urb, u8 *buf, int len,
+			 int toggle_bits, int error_count, int condition_code,
+			 int repeat_number, int halted, int skipped, int actual,
+			 int non_null);
+};
+/* Structure to hold all of our device specific stuff*/
+struct usb_ftdi {
+	struct list_head ftdi_list;
+	struct mutex u132_lock;
+	int command_next;
+	int command_head;
+	struct u132_command command[COMMAND_SIZE];
+	int respond_next;
+	int respond_head;
+	struct u132_respond respond[RESPOND_SIZE];
+	struct u132_target target[4];
+	char device_name[16];
+	unsigned synchronized:1;
+	unsigned enumerated:1;
+	unsigned registered:1;
+	unsigned initialized:1;
+	unsigned card_ejected:1;
+	int function;
+	int sequence_num;
+	int disconnected;
+	int gone_away;
+	int stuck_status;
+	int status_queue_delay;
+	struct semaphore sw_lock;
+	struct usb_device *udev;
+	struct usb_interface *interface;
+	struct usb_class_driver *class;
+	struct delayed_work status_work;
+	struct delayed_work command_work;
+	struct delayed_work respond_work;
+	struct u132_platform_data platform_data;
+	struct resource resources[0];
+	struct platform_device platform_dev;
+	unsigned char *bulk_in_buffer;
+	size_t bulk_in_size;
+	size_t bulk_in_last;
+	size_t bulk_in_left;
+	__u8 bulk_in_endpointAddr;
+	__u8 bulk_out_endpointAddr;
+	struct kref kref;
+	u32 controlreg;
+	u8 response[4 + 1024];
+	int expected;
+	int received;
+	int ed_found;
+};
+#define kref_to_usb_ftdi(d) container_of(d, struct usb_ftdi, kref)
+#define platform_device_to_usb_ftdi(d) container_of(d, struct usb_ftdi, \
+						    platform_dev)
+static struct usb_driver ftdi_elan_driver;
+static void ftdi_elan_delete(struct kref *kref)
+{
+	struct usb_ftdi *ftdi = kref_to_usb_ftdi(kref);
+	dev_warn(&ftdi->udev->dev, "FREEING ftdi=%p\n", ftdi);
+	usb_put_dev(ftdi->udev);
+	ftdi->disconnected += 1;
+	mutex_lock(&ftdi_module_lock);
+	list_del_init(&ftdi->ftdi_list);
+	ftdi_instances -= 1;
+	mutex_unlock(&ftdi_module_lock);
+	kfree(ftdi->bulk_in_buffer);
+	ftdi->bulk_in_buffer = NULL;
+}
+
+static void ftdi_elan_put_kref(struct usb_ftdi *ftdi)
+{
+	kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_elan_get_kref(struct usb_ftdi *ftdi)
+{
+	kref_get(&ftdi->kref);
+}
+
+static void ftdi_elan_init_kref(struct usb_ftdi *ftdi)
+{
+	kref_init(&ftdi->kref);
+}
+
+static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+	if (!queue_delayed_work(status_queue, &ftdi->status_work, delta))
+		kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+	if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+		kref_get(&ftdi->kref);
+}
+
+static void ftdi_status_cancel_work(struct usb_ftdi *ftdi)
+{
+	if (cancel_delayed_work(&ftdi->status_work))
+		kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+	if (!queue_delayed_work(command_queue, &ftdi->command_work, delta))
+		kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+	if (queue_delayed_work(command_queue, &ftdi->command_work, delta))
+		kref_get(&ftdi->kref);
+}
+
+static void ftdi_command_cancel_work(struct usb_ftdi *ftdi)
+{
+	if (cancel_delayed_work(&ftdi->command_work))
+		kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_response_requeue_work(struct usb_ftdi *ftdi,
+				       unsigned int delta)
+{
+	if (!queue_delayed_work(respond_queue, &ftdi->respond_work, delta))
+		kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+	if (queue_delayed_work(respond_queue, &ftdi->respond_work, delta))
+		kref_get(&ftdi->kref);
+}
+
+static void ftdi_response_cancel_work(struct usb_ftdi *ftdi)
+{
+	if (cancel_delayed_work(&ftdi->respond_work))
+		kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+void ftdi_elan_gone_away(struct platform_device *pdev)
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	ftdi->gone_away += 1;
+	ftdi_elan_put_kref(ftdi);
+}
+
+
+EXPORT_SYMBOL_GPL(ftdi_elan_gone_away);
+static void ftdi_release_platform_dev(struct device *dev)
+{
+	dev->parent = NULL;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+				  struct u132_target *target, u8 *buffer, int length);
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi);
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi);
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi);
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi);
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_hcd_init(struct usb_ftdi *ftdi)
+{
+	int result;
+	if (ftdi->platform_dev.dev.parent)
+		return -EBUSY;
+	ftdi_elan_get_kref(ftdi);
+	ftdi->platform_data.potpg = 100;
+	ftdi->platform_data.reset = NULL;
+	ftdi->platform_dev.id = ftdi->sequence_num;
+	ftdi->platform_dev.resource = ftdi->resources;
+	ftdi->platform_dev.num_resources = ARRAY_SIZE(ftdi->resources);
+	ftdi->platform_dev.dev.platform_data = &ftdi->platform_data;
+	ftdi->platform_dev.dev.parent = NULL;
+	ftdi->platform_dev.dev.release = ftdi_release_platform_dev;
+	ftdi->platform_dev.dev.dma_mask = NULL;
+	snprintf(ftdi->device_name, sizeof(ftdi->device_name), "u132_hcd");
+	ftdi->platform_dev.name = ftdi->device_name;
+	dev_info(&ftdi->udev->dev, "requesting module '%s'\n", "u132_hcd");
+	request_module("u132_hcd");
+	dev_info(&ftdi->udev->dev, "registering '%s'\n",
+		 ftdi->platform_dev.name);
+	result = platform_device_register(&ftdi->platform_dev);
+	return result;
+}
+
+static void ftdi_elan_abandon_completions(struct usb_ftdi *ftdi)
+{
+	mutex_lock(&ftdi->u132_lock);
+	while (ftdi->respond_next > ftdi->respond_head) {
+		struct u132_respond *respond = &ftdi->respond[RESPOND_MASK &
+							      ftdi->respond_head++];
+		*respond->result = -ESHUTDOWN;
+		*respond->value = 0;
+		complete(&respond->wait_completion);
+	} mutex_unlock(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi)
+{
+	int ed_number = 4;
+	mutex_lock(&ftdi->u132_lock);
+	while (ed_number-- > 0) {
+		struct u132_target *target = &ftdi->target[ed_number];
+		if (target->active == 1) {
+			target->condition_code = TD_DEVNOTRESP;
+			mutex_unlock(&ftdi->u132_lock);
+			ftdi_elan_do_callback(ftdi, target, NULL, 0);
+			mutex_lock(&ftdi->u132_lock);
+		}
+	}
+	ftdi->received = 0;
+	ftdi->expected = 4;
+	ftdi->ed_found = 0;
+	mutex_unlock(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_flush_targets(struct usb_ftdi *ftdi)
+{
+	int ed_number = 4;
+	mutex_lock(&ftdi->u132_lock);
+	while (ed_number-- > 0) {
+		struct u132_target *target = &ftdi->target[ed_number];
+		target->abandoning = 1;
+	wait_1:if (target->active == 1) {
+			int command_size = ftdi->command_next -
+				ftdi->command_head;
+			if (command_size < COMMAND_SIZE) {
+				struct u132_command *command = &ftdi->command[
+					COMMAND_MASK & ftdi->command_next];
+				command->header = 0x80 | (ed_number << 5) | 0x4;
+				command->length = 0x00;
+				command->address = 0x00;
+				command->width = 0x00;
+				command->follows = 0;
+				command->value = 0;
+				command->buffer = &command->value;
+				ftdi->command_next += 1;
+				ftdi_elan_kick_command_queue(ftdi);
+			} else {
+				mutex_unlock(&ftdi->u132_lock);
+				msleep(100);
+				mutex_lock(&ftdi->u132_lock);
+				goto wait_1;
+			}
+		}
+	wait_2:if (target->active == 1) {
+			int command_size = ftdi->command_next -
+				ftdi->command_head;
+			if (command_size < COMMAND_SIZE) {
+				struct u132_command *command = &ftdi->command[
+					COMMAND_MASK & ftdi->command_next];
+				command->header = 0x90 | (ed_number << 5);
+				command->length = 0x00;
+				command->address = 0x00;
+				command->width = 0x00;
+				command->follows = 0;
+				command->value = 0;
+				command->buffer = &command->value;
+				ftdi->command_next += 1;
+				ftdi_elan_kick_command_queue(ftdi);
+			} else {
+				mutex_unlock(&ftdi->u132_lock);
+				msleep(100);
+				mutex_lock(&ftdi->u132_lock);
+				goto wait_2;
+			}
+		}
+	}
+	ftdi->received = 0;
+	ftdi->expected = 4;
+	ftdi->ed_found = 0;
+	mutex_unlock(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_cancel_targets(struct usb_ftdi *ftdi)
+{
+	int ed_number = 4;
+	mutex_lock(&ftdi->u132_lock);
+	while (ed_number-- > 0) {
+		struct u132_target *target = &ftdi->target[ed_number];
+		target->abandoning = 1;
+	wait:if (target->active == 1) {
+			int command_size = ftdi->command_next -
+				ftdi->command_head;
+			if (command_size < COMMAND_SIZE) {
+				struct u132_command *command = &ftdi->command[
+					COMMAND_MASK & ftdi->command_next];
+				command->header = 0x80 | (ed_number << 5) | 0x4;
+				command->length = 0x00;
+				command->address = 0x00;
+				command->width = 0x00;
+				command->follows = 0;
+				command->value = 0;
+				command->buffer = &command->value;
+				ftdi->command_next += 1;
+				ftdi_elan_kick_command_queue(ftdi);
+			} else {
+				mutex_unlock(&ftdi->u132_lock);
+				msleep(100);
+				mutex_lock(&ftdi->u132_lock);
+				goto wait;
+			}
+		}
+	}
+	ftdi->received = 0;
+	ftdi->expected = 4;
+	ftdi->ed_found = 0;
+	mutex_unlock(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi)
+{
+	ftdi_command_queue_work(ftdi, 0);
+}
+
+static void ftdi_elan_command_work(struct work_struct *work)
+{
+	struct usb_ftdi *ftdi =
+		container_of(work, struct usb_ftdi, command_work.work);
+
+	if (ftdi->disconnected > 0) {
+		ftdi_elan_put_kref(ftdi);
+		return;
+	} else {
+		int retval = ftdi_elan_command_engine(ftdi);
+		if (retval == -ESHUTDOWN) {
+			ftdi->disconnected += 1;
+		} else if (retval == -ENODEV) {
+			ftdi->disconnected += 1;
+		} else if (retval)
+			dev_err(&ftdi->udev->dev, "command error %d\n", retval);
+		ftdi_command_requeue_work(ftdi, msecs_to_jiffies(10));
+		return;
+	}
+}
+
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi)
+{
+	ftdi_respond_queue_work(ftdi, 0);
+}
+
+static void ftdi_elan_respond_work(struct work_struct *work)
+{
+	struct usb_ftdi *ftdi =
+		container_of(work, struct usb_ftdi, respond_work.work);
+	if (ftdi->disconnected > 0) {
+		ftdi_elan_put_kref(ftdi);
+		return;
+	} else {
+		int retval = ftdi_elan_respond_engine(ftdi);
+		if (retval == 0) {
+		} else if (retval == -ESHUTDOWN) {
+			ftdi->disconnected += 1;
+		} else if (retval == -ENODEV) {
+			ftdi->disconnected += 1;
+		} else if (retval == -EILSEQ) {
+			ftdi->disconnected += 1;
+		} else {
+			ftdi->disconnected += 1;
+			dev_err(&ftdi->udev->dev, "respond error %d\n", retval);
+		}
+		if (ftdi->disconnected > 0) {
+			ftdi_elan_abandon_completions(ftdi);
+			ftdi_elan_abandon_targets(ftdi);
+		}
+		ftdi_response_requeue_work(ftdi, msecs_to_jiffies(10));
+		return;
+	}
+}
+
+
+/*
+ * the sw_lock is initially held and will be freed
+ * after the FTDI has been synchronized
+ *
+ */
+static void ftdi_elan_status_work(struct work_struct *work)
+{
+	struct usb_ftdi *ftdi =
+		container_of(work, struct usb_ftdi, status_work.work);
+	int work_delay_in_msec = 0;
+	if (ftdi->disconnected > 0) {
+		ftdi_elan_put_kref(ftdi);
+		return;
+	} else if (ftdi->synchronized == 0) {
+		down(&ftdi->sw_lock);
+		if (ftdi_elan_synchronize(ftdi) == 0) {
+			ftdi->synchronized = 1;
+			ftdi_command_queue_work(ftdi, 1);
+			ftdi_respond_queue_work(ftdi, 1);
+			up(&ftdi->sw_lock);
+			work_delay_in_msec = 100;
+		} else {
+			dev_err(&ftdi->udev->dev, "synchronize failed\n");
+			up(&ftdi->sw_lock);
+			work_delay_in_msec = 10 *1000;
+		}
+	} else if (ftdi->stuck_status > 0) {
+		if (ftdi_elan_stuck_waiting(ftdi) == 0) {
+			ftdi->stuck_status = 0;
+			ftdi->synchronized = 0;
+		} else if ((ftdi->stuck_status++ % 60) == 1) {
+			dev_err(&ftdi->udev->dev, "WRONG type of card inserted - please remove\n");
+		} else
+			dev_err(&ftdi->udev->dev, "WRONG type of card inserted - checked %d times\n",
+				ftdi->stuck_status);
+		work_delay_in_msec = 100;
+	} else if (ftdi->enumerated == 0) {
+		if (ftdi_elan_enumeratePCI(ftdi) == 0) {
+			ftdi->enumerated = 1;
+			work_delay_in_msec = 250;
+		} else
+			work_delay_in_msec = 1000;
+	} else if (ftdi->initialized == 0) {
+		if (ftdi_elan_setupOHCI(ftdi) == 0) {
+			ftdi->initialized = 1;
+			work_delay_in_msec = 500;
+		} else {
+			dev_err(&ftdi->udev->dev, "initialized failed - trying again in 10 seconds\n");
+			work_delay_in_msec = 1 *1000;
+		}
+	} else if (ftdi->registered == 0) {
+		work_delay_in_msec = 10;
+		if (ftdi_elan_hcd_init(ftdi) == 0) {
+			ftdi->registered = 1;
+		} else
+			dev_err(&ftdi->udev->dev, "register failed\n");
+		work_delay_in_msec = 250;
+	} else {
+		if (ftdi_elan_checkingPCI(ftdi) == 0) {
+			work_delay_in_msec = 250;
+		} else if (ftdi->controlreg & 0x00400000) {
+			if (ftdi->gone_away > 0) {
+				dev_err(&ftdi->udev->dev, "PCI device eject confirmed platform_dev.dev.parent=%p platform_dev.dev=%p\n",
+					ftdi->platform_dev.dev.parent,
+					&ftdi->platform_dev.dev);
+				platform_device_unregister(&ftdi->platform_dev);
+				ftdi->platform_dev.dev.parent = NULL;
+				ftdi->registered = 0;
+				ftdi->enumerated = 0;
+				ftdi->card_ejected = 0;
+				ftdi->initialized = 0;
+				ftdi->gone_away = 0;
+			} else
+				ftdi_elan_flush_targets(ftdi);
+			work_delay_in_msec = 250;
+		} else {
+			dev_err(&ftdi->udev->dev, "PCI device has disappeared\n");
+			ftdi_elan_cancel_targets(ftdi);
+			work_delay_in_msec = 500;
+			ftdi->enumerated = 0;
+			ftdi->initialized = 0;
+		}
+	}
+	if (ftdi->disconnected > 0) {
+		ftdi_elan_put_kref(ftdi);
+		return;
+	} else {
+		ftdi_status_requeue_work(ftdi,
+					 msecs_to_jiffies(work_delay_in_msec));
+		return;
+	}
+}
+
+
+/*
+ * file_operations for the jtag interface
+ *
+ * the usage count for the device is incremented on open()
+ * and decremented on release()
+ */
+static int ftdi_elan_open(struct inode *inode, struct file *file)
+{
+	int subminor;
+	struct usb_interface *interface;
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&ftdi_elan_driver, subminor);
+
+	if (!interface) {
+		pr_err("can't find device for minor %d\n", subminor);
+		return -ENODEV;
+	} else {
+		struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+		if (!ftdi) {
+			return -ENODEV;
+		} else {
+			if (down_interruptible(&ftdi->sw_lock)) {
+				return -EINTR;
+			} else {
+				ftdi_elan_get_kref(ftdi);
+				file->private_data = ftdi;
+				return 0;
+			}
+		}
+	}
+}
+
+static int ftdi_elan_release(struct inode *inode, struct file *file)
+{
+	struct usb_ftdi *ftdi = file->private_data;
+	if (ftdi == NULL)
+		return -ENODEV;
+	up(&ftdi->sw_lock);        /* decrement the count on our device */
+	ftdi_elan_put_kref(ftdi);
+	return 0;
+}
+
+
+/*
+ *
+ * blocking bulk reads are used to get data from the device
+ *
+ */
+static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,
+			      size_t count, loff_t *ppos)
+{
+	char data[30 *3 + 4];
+	char *d = data;
+	int m = (sizeof(data) - 1) / 3;
+	int bytes_read = 0;
+	int retry_on_empty = 10;
+	int retry_on_timeout = 5;
+	struct usb_ftdi *ftdi = file->private_data;
+	if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	}
+	data[0] = 0;
+have:if (ftdi->bulk_in_left > 0) {
+		if (count-- > 0) {
+			char *p = ++ftdi->bulk_in_last + ftdi->bulk_in_buffer;
+			ftdi->bulk_in_left -= 1;
+			if (bytes_read < m) {
+				d += sprintf(d, " %02X", 0x000000FF & *p);
+			} else if (bytes_read > m) {
+			} else
+				d += sprintf(d, " ..");
+			if (copy_to_user(buffer++, p, 1)) {
+				return -EFAULT;
+			} else {
+				bytes_read += 1;
+				goto have;
+			}
+		} else
+			return bytes_read;
+	}
+more:if (count > 0) {
+		int packet_bytes = 0;
+		int retval = usb_bulk_msg(ftdi->udev,
+					  usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+					  ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+					  &packet_bytes, 50);
+		if (packet_bytes > 2) {
+			ftdi->bulk_in_left = packet_bytes - 2;
+			ftdi->bulk_in_last = 1;
+			goto have;
+		} else if (retval == -ETIMEDOUT) {
+			if (retry_on_timeout-- > 0) {
+				goto more;
+			} else if (bytes_read > 0) {
+				return bytes_read;
+			} else
+				return retval;
+		} else if (retval == 0) {
+			if (retry_on_empty-- > 0) {
+				goto more;
+			} else
+				return bytes_read;
+		} else
+			return retval;
+	} else
+		return bytes_read;
+}
+
+static void ftdi_elan_write_bulk_callback(struct urb *urb)
+{
+	struct usb_ftdi *ftdi = urb->context;
+	int status = urb->status;
+
+	if (status && !(status == -ENOENT || status == -ECONNRESET ||
+			status == -ESHUTDOWN)) {
+		dev_err(&ftdi->udev->dev,
+			"urb=%p write bulk status received: %d\n", urb, status);
+	}
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+}
+
+static int fill_buffer_with_all_queued_commands(struct usb_ftdi *ftdi,
+						char *buf, int command_size, int total_size)
+{
+	int ed_commands = 0;
+	int b = 0;
+	int I = command_size;
+	int i = ftdi->command_head;
+	while (I-- > 0) {
+		struct u132_command *command = &ftdi->command[COMMAND_MASK &
+							      i++];
+		int F = command->follows;
+		u8 *f = command->buffer;
+		if (command->header & 0x80) {
+			ed_commands |= 1 << (0x3 & (command->header >> 5));
+		}
+		buf[b++] = command->header;
+		buf[b++] = (command->length >> 0) & 0x00FF;
+		buf[b++] = (command->length >> 8) & 0x00FF;
+		buf[b++] = command->address;
+		buf[b++] = command->width;
+		while (F-- > 0) {
+			buf[b++] = *f++;
+		}
+	}
+	return ed_commands;
+}
+
+static int ftdi_elan_total_command_size(struct usb_ftdi *ftdi, int command_size)
+{
+	int total_size = 0;
+	int I = command_size;
+	int i = ftdi->command_head;
+	while (I-- > 0) {
+		struct u132_command *command = &ftdi->command[COMMAND_MASK &
+							      i++];
+		total_size += 5 + command->follows;
+	} return total_size;
+}
+
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
+{
+	int retval;
+	char *buf;
+	int ed_commands;
+	int total_size;
+	struct urb *urb;
+	int command_size = ftdi->command_next - ftdi->command_head;
+	if (command_size == 0)
+		return 0;
+	total_size = ftdi_elan_total_command_size(ftdi, command_size);
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&ftdi->udev->dev, "could not get a urb to write %d commands totaling %d bytes to the Uxxx\n",
+			command_size, total_size);
+		return -ENOMEM;
+	}
+	buf = usb_alloc_coherent(ftdi->udev, total_size, GFP_KERNEL,
+				 &urb->transfer_dma);
+	if (!buf) {
+		dev_err(&ftdi->udev->dev, "could not get a buffer to write %d commands totaling %d bytes to the Uxxx\n",
+			command_size, total_size);
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	ed_commands = fill_buffer_with_all_queued_commands(ftdi, buf,
+							   command_size, total_size);
+	usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+							   ftdi->bulk_out_endpointAddr), buf, total_size,
+			  ftdi_elan_write_bulk_callback, ftdi);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	if (ed_commands) {
+		char diag[40 *3 + 4];
+		char *d = diag;
+		int m = total_size;
+		u8 *c = buf;
+		int s = (sizeof(diag) - 1) / 3;
+		diag[0] = 0;
+		while (s-- > 0 && m-- > 0) {
+			if (s > 0 || m == 0) {
+				d += sprintf(d, " %02X", *c++);
+			} else
+				d += sprintf(d, " ..");
+		}
+	}
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&ftdi->udev->dev, "failed %d to submit urb %p to write %d commands totaling %d bytes to the Uxxx\n",
+			retval, urb, command_size, total_size);
+		usb_free_coherent(ftdi->udev, total_size, buf, urb->transfer_dma);
+		usb_free_urb(urb);
+		return retval;
+	}
+	usb_free_urb(urb);        /* release our reference to this urb,
+				     the USB core will eventually free it entirely */
+	ftdi->command_head += command_size;
+	ftdi_elan_kick_respond_queue(ftdi);
+	return 0;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+				  struct u132_target *target, u8 *buffer, int length)
+{
+	struct urb *urb = target->urb;
+	int halted = target->halted;
+	int skipped = target->skipped;
+	int actual = target->actual;
+	int non_null = target->non_null;
+	int toggle_bits = target->toggle_bits;
+	int error_count = target->error_count;
+	int condition_code = target->condition_code;
+	int repeat_number = target->repeat_number;
+	void (*callback) (void *, struct urb *, u8 *, int, int, int, int, int,
+			  int, int, int, int) = target->callback;
+	target->active -= 1;
+	target->callback = NULL;
+	(*callback) (target->endp, urb, buffer, length, toggle_bits,
+		     error_count, condition_code, repeat_number, halted, skipped,
+		     actual, non_null);
+}
+
+static char *have_ed_set_response(struct usb_ftdi *ftdi,
+				  struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+				  char *b)
+{
+	int payload = (ed_length >> 0) & 0x07FF;
+	mutex_lock(&ftdi->u132_lock);
+	target->actual = 0;
+	target->non_null = (ed_length >> 15) & 0x0001;
+	target->repeat_number = (ed_length >> 11) & 0x000F;
+	if (ed_type == 0x02) {
+		if (payload == 0 || target->abandoning > 0) {
+			target->abandoning = 0;
+			mutex_unlock(&ftdi->u132_lock);
+			ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+					      payload);
+			ftdi->received = 0;
+			ftdi->expected = 4;
+			ftdi->ed_found = 0;
+			return ftdi->response;
+		} else {
+			ftdi->expected = 4 + payload;
+			ftdi->ed_found = 1;
+			mutex_unlock(&ftdi->u132_lock);
+			return b;
+		}
+	} else if (ed_type == 0x03) {
+		if (payload == 0 || target->abandoning > 0) {
+			target->abandoning = 0;
+			mutex_unlock(&ftdi->u132_lock);
+			ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+					      payload);
+			ftdi->received = 0;
+			ftdi->expected = 4;
+			ftdi->ed_found = 0;
+			return ftdi->response;
+		} else {
+			ftdi->expected = 4 + payload;
+			ftdi->ed_found = 1;
+			mutex_unlock(&ftdi->u132_lock);
+			return b;
+		}
+	} else if (ed_type == 0x01) {
+		target->abandoning = 0;
+		mutex_unlock(&ftdi->u132_lock);
+		ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+				      payload);
+		ftdi->received = 0;
+		ftdi->expected = 4;
+		ftdi->ed_found = 0;
+		return ftdi->response;
+	} else {
+		target->abandoning = 0;
+		mutex_unlock(&ftdi->u132_lock);
+		ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+				      payload);
+		ftdi->received = 0;
+		ftdi->expected = 4;
+		ftdi->ed_found = 0;
+		return ftdi->response;
+	}
+}
+
+static char *have_ed_get_response(struct usb_ftdi *ftdi,
+				  struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+				  char *b)
+{
+	mutex_lock(&ftdi->u132_lock);
+	target->condition_code = TD_DEVNOTRESP;
+	target->actual = (ed_length >> 0) & 0x01FF;
+	target->non_null = (ed_length >> 15) & 0x0001;
+	target->repeat_number = (ed_length >> 11) & 0x000F;
+	mutex_unlock(&ftdi->u132_lock);
+	if (target->active)
+		ftdi_elan_do_callback(ftdi, target, NULL, 0);
+	target->abandoning = 0;
+	ftdi->received = 0;
+	ftdi->expected = 4;
+	ftdi->ed_found = 0;
+	return ftdi->response;
+}
+
+
+/*
+ * The engine tries to empty the FTDI fifo
+ *
+ * all responses found in the fifo data are dispatched thus
+ * the response buffer can only ever hold a maximum sized
+ * response from the Uxxx.
+ *
+ */
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi)
+{
+	u8 *b = ftdi->response + ftdi->received;
+	int bytes_read = 0;
+	int retry_on_empty = 1;
+	int retry_on_timeout = 3;
+	int empty_packets = 0;
+read:{
+		int packet_bytes = 0;
+		int retval = usb_bulk_msg(ftdi->udev,
+					  usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+					  ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+					  &packet_bytes, 500);
+		char diag[30 *3 + 4];
+		char *d = diag;
+		int m = packet_bytes;
+		u8 *c = ftdi->bulk_in_buffer;
+		int s = (sizeof(diag) - 1) / 3;
+		diag[0] = 0;
+		while (s-- > 0 && m-- > 0) {
+			if (s > 0 || m == 0) {
+				d += sprintf(d, " %02X", *c++);
+			} else
+				d += sprintf(d, " ..");
+		}
+		if (packet_bytes > 2) {
+			ftdi->bulk_in_left = packet_bytes - 2;
+			ftdi->bulk_in_last = 1;
+			goto have;
+		} else if (retval == -ETIMEDOUT) {
+			if (retry_on_timeout-- > 0) {
+				dev_err(&ftdi->udev->dev, "TIMED OUT with packet_bytes = %d with total %d bytes%s\n",
+					packet_bytes, bytes_read, diag);
+				goto more;
+			} else if (bytes_read > 0) {
+				dev_err(&ftdi->udev->dev, "ONLY %d bytes%s\n",
+					bytes_read, diag);
+				return -ENOMEM;
+			} else {
+				dev_err(&ftdi->udev->dev, "TIMED OUT with packet_bytes = %d with total %d bytes%s\n",
+					packet_bytes, bytes_read, diag);
+				return -ENOMEM;
+			}
+		} else if (retval == -EILSEQ) {
+			dev_err(&ftdi->udev->dev, "error = %d with packet_bytes = %d with total %d bytes%s\n",
+				retval, packet_bytes, bytes_read, diag);
+			return retval;
+		} else if (retval) {
+			dev_err(&ftdi->udev->dev, "error = %d with packet_bytes = %d with total %d bytes%s\n",
+				retval, packet_bytes, bytes_read, diag);
+			return retval;
+		} else if (packet_bytes == 2) {
+			unsigned char s0 = ftdi->bulk_in_buffer[0];
+			unsigned char s1 = ftdi->bulk_in_buffer[1];
+			empty_packets += 1;
+			if (s0 == 0x31 && s1 == 0x60) {
+				if (retry_on_empty-- > 0) {
+					goto more;
+				} else
+					return 0;
+			} else if (s0 == 0x31 && s1 == 0x00) {
+				if (retry_on_empty-- > 0) {
+					goto more;
+				} else
+					return 0;
+			} else {
+				if (retry_on_empty-- > 0) {
+					goto more;
+				} else
+					return 0;
+			}
+		} else if (packet_bytes == 1) {
+			if (retry_on_empty-- > 0) {
+				goto more;
+			} else
+				return 0;
+		} else {
+			if (retry_on_empty-- > 0) {
+				goto more;
+			} else
+				return 0;
+		}
+	}
+more:{
+		goto read;
+	}
+have:if (ftdi->bulk_in_left > 0) {
+		u8 c = ftdi->bulk_in_buffer[++ftdi->bulk_in_last];
+		bytes_read += 1;
+		ftdi->bulk_in_left -= 1;
+		if (ftdi->received == 0 && c == 0xFF) {
+			goto have;
+		} else
+			*b++ = c;
+		if (++ftdi->received < ftdi->expected) {
+			goto have;
+		} else if (ftdi->ed_found) {
+			int ed_number = (ftdi->response[0] >> 5) & 0x03;
+			u16 ed_length = (ftdi->response[2] << 8) |
+				ftdi->response[1];
+			struct u132_target *target = &ftdi->target[ed_number];
+			int payload = (ed_length >> 0) & 0x07FF;
+			char diag[30 *3 + 4];
+			char *d = diag;
+			int m = payload;
+			u8 *c = 4 + ftdi->response;
+			int s = (sizeof(diag) - 1) / 3;
+			diag[0] = 0;
+			while (s-- > 0 && m-- > 0) {
+				if (s > 0 || m == 0) {
+					d += sprintf(d, " %02X", *c++);
+				} else
+					d += sprintf(d, " ..");
+			}
+			ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+					      payload);
+			ftdi->received = 0;
+			ftdi->expected = 4;
+			ftdi->ed_found = 0;
+			b = ftdi->response;
+			goto have;
+		} else if (ftdi->expected == 8) {
+			u8 buscmd;
+			int respond_head = ftdi->respond_head++;
+			struct u132_respond *respond = &ftdi->respond[
+				RESPOND_MASK & respond_head];
+			u32 data = ftdi->response[7];
+			data <<= 8;
+			data |= ftdi->response[6];
+			data <<= 8;
+			data |= ftdi->response[5];
+			data <<= 8;
+			data |= ftdi->response[4];
+			*respond->value = data;
+			*respond->result = 0;
+			complete(&respond->wait_completion);
+			ftdi->received = 0;
+			ftdi->expected = 4;
+			ftdi->ed_found = 0;
+			b = ftdi->response;
+			buscmd = (ftdi->response[0] >> 0) & 0x0F;
+			if (buscmd == 0x00) {
+			} else if (buscmd == 0x02) {
+			} else if (buscmd == 0x06) {
+			} else if (buscmd == 0x0A) {
+			} else
+				dev_err(&ftdi->udev->dev, "Uxxx unknown(%0X) value = %08X\n",
+					buscmd, data);
+			goto have;
+		} else {
+			if ((ftdi->response[0] & 0x80) == 0x00) {
+				ftdi->expected = 8;
+				goto have;
+			} else {
+				int ed_number = (ftdi->response[0] >> 5) & 0x03;
+				int ed_type = (ftdi->response[0] >> 0) & 0x03;
+				u16 ed_length = (ftdi->response[2] << 8) |
+					ftdi->response[1];
+				struct u132_target *target = &ftdi->target[
+					ed_number];
+				target->halted = (ftdi->response[0] >> 3) &
+					0x01;
+				target->skipped = (ftdi->response[0] >> 2) &
+					0x01;
+				target->toggle_bits = (ftdi->response[3] >> 6)
+					& 0x03;
+				target->error_count = (ftdi->response[3] >> 4)
+					& 0x03;
+				target->condition_code = (ftdi->response[
+								  3] >> 0) & 0x0F;
+				if ((ftdi->response[0] & 0x10) == 0x00) {
+					b = have_ed_set_response(ftdi, target,
+								 ed_length, ed_number, ed_type,
+								 b);
+					goto have;
+				} else {
+					b = have_ed_get_response(ftdi, target,
+								 ed_length, ed_number, ed_type,
+								 b);
+					goto have;
+				}
+			}
+		}
+	} else
+		goto more;
+}
+
+
+/*
+ * create a urb, and a buffer for it, and copy the data to the urb
+ *
+ */
+static ssize_t ftdi_elan_write(struct file *file,
+			       const char __user *user_buffer, size_t count,
+			       loff_t *ppos)
+{
+	int retval = 0;
+	struct urb *urb;
+	char *buf;
+	struct usb_ftdi *ftdi = file->private_data;
+
+	if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	}
+	if (count == 0) {
+		goto exit;
+	}
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		retval = -ENOMEM;
+		goto error_1;
+	}
+	buf = usb_alloc_coherent(ftdi->udev, count, GFP_KERNEL,
+				 &urb->transfer_dma);
+	if (!buf) {
+		retval = -ENOMEM;
+		goto error_2;
+	}
+	if (copy_from_user(buf, user_buffer, count)) {
+		retval = -EFAULT;
+		goto error_3;
+	}
+	usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+							   ftdi->bulk_out_endpointAddr), buf, count,
+			  ftdi_elan_write_bulk_callback, ftdi);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&ftdi->udev->dev,
+			"failed submitting write urb, error %d\n", retval);
+		goto error_3;
+	}
+	usb_free_urb(urb);
+
+exit:
+	return count;
+error_3:
+	usb_free_coherent(ftdi->udev, count, buf, urb->transfer_dma);
+error_2:
+	usb_free_urb(urb);
+error_1:
+	return retval;
+}
+
+static const struct file_operations ftdi_elan_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.read = ftdi_elan_read,
+	.write = ftdi_elan_write,
+	.open = ftdi_elan_open,
+	.release = ftdi_elan_release,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver ftdi_elan_jtag_class = {
+	.name = "ftdi-%d-jtag",
+	.fops = &ftdi_elan_fops,
+	.minor_base = USB_FTDI_ELAN_MINOR_BASE,
+};
+
+/*
+ * the following definitions are for the
+ * ELAN FPGA state machgine processor that
+ * lies on the other side of the FTDI chip
+ */
+#define cPCIu132rd 0x0
+#define cPCIu132wr 0x1
+#define cPCIiord 0x2
+#define cPCIiowr 0x3
+#define cPCImemrd 0x6
+#define cPCImemwr 0x7
+#define cPCIcfgrd 0xA
+#define cPCIcfgwr 0xB
+#define cPCInull 0xF
+#define cU132cmd_status 0x0
+#define cU132flash 0x1
+#define cPIDsetup 0x0
+#define cPIDout 0x1
+#define cPIDin 0x2
+#define cPIDinonce 0x3
+#define cCCnoerror 0x0
+#define cCCcrc 0x1
+#define cCCbitstuff 0x2
+#define cCCtoggle 0x3
+#define cCCstall 0x4
+#define cCCnoresp 0x5
+#define cCCbadpid1 0x6
+#define cCCbadpid2 0x7
+#define cCCdataoverrun 0x8
+#define cCCdataunderrun 0x9
+#define cCCbuffoverrun 0xC
+#define cCCbuffunderrun 0xD
+#define cCCnotaccessed 0xF
+static int ftdi_elan_write_reg(struct usb_ftdi *ftdi, u32 data)
+{
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x00 | cPCIu132wr;
+			command->length = 0x04;
+			command->address = 0x00;
+			command->width = 0x00;
+			command->follows = 4;
+			command->value = data;
+			command->buffer = &command->value;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+static int ftdi_elan_write_config(struct usb_ftdi *ftdi, int config_offset,
+				  u8 width, u32 data)
+{
+	u8 addressofs = config_offset / 4;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x00 | (cPCIcfgwr & 0x0F);
+			command->length = 0x04;
+			command->address = addressofs;
+			command->width = 0x00 | (width & 0x0F);
+			command->follows = 4;
+			command->value = data;
+			command->buffer = &command->value;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+static int ftdi_elan_write_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+				  u8 width, u32 data)
+{
+	u8 addressofs = mem_offset / 4;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x00 | (cPCImemwr & 0x0F);
+			command->length = 0x04;
+			command->address = addressofs;
+			command->width = 0x00 | (width & 0x0F);
+			command->follows = 4;
+			command->value = data;
+			command->buffer = &command->value;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
+			       u8 width, u32 data)
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_write_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_write_pcimem);
+static int ftdi_elan_read_reg(struct usb_ftdi *ftdi, u32 *data)
+{
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		int respond_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		respond_size = ftdi->respond_next - ftdi->respond_head;
+		if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+		{
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			struct u132_respond *respond = &ftdi->respond[
+				RESPOND_MASK & ftdi->respond_next];
+			int result = -ENODEV;
+			respond->result = &result;
+			respond->header = command->header = 0x00 | cPCIu132rd;
+			command->length = 0x04;
+			respond->address = command->address = cU132cmd_status;
+			command->width = 0x00;
+			command->follows = 0;
+			command->value = 0;
+			command->buffer = NULL;
+			respond->value = data;
+			init_completion(&respond->wait_completion);
+			ftdi->command_next += 1;
+			ftdi->respond_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			wait_for_completion(&respond->wait_completion);
+			return result;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+static int ftdi_elan_read_config(struct usb_ftdi *ftdi, int config_offset,
+				 u8 width, u32 *data)
+{
+	u8 addressofs = config_offset / 4;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		int respond_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		respond_size = ftdi->respond_next - ftdi->respond_head;
+		if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+		{
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			struct u132_respond *respond = &ftdi->respond[
+				RESPOND_MASK & ftdi->respond_next];
+			int result = -ENODEV;
+			respond->result = &result;
+			respond->header = command->header = 0x00 | (cPCIcfgrd &
+								    0x0F);
+			command->length = 0x04;
+			respond->address = command->address = addressofs;
+			command->width = 0x00 | (width & 0x0F);
+			command->follows = 0;
+			command->value = 0;
+			command->buffer = NULL;
+			respond->value = data;
+			init_completion(&respond->wait_completion);
+			ftdi->command_next += 1;
+			ftdi->respond_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			wait_for_completion(&respond->wait_completion);
+			return result;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+static int ftdi_elan_read_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+				 u8 width, u32 *data)
+{
+	u8 addressofs = mem_offset / 4;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		int respond_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		respond_size = ftdi->respond_next - ftdi->respond_head;
+		if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+		{
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			struct u132_respond *respond = &ftdi->respond[
+				RESPOND_MASK & ftdi->respond_next];
+			int result = -ENODEV;
+			respond->result = &result;
+			respond->header = command->header = 0x00 | (cPCImemrd &
+								    0x0F);
+			command->length = 0x04;
+			respond->address = command->address = addressofs;
+			command->width = 0x00 | (width & 0x0F);
+			command->follows = 0;
+			command->value = 0;
+			command->buffer = NULL;
+			respond->value = data;
+			init_completion(&respond->wait_completion);
+			ftdi->command_next += 1;
+			ftdi->respond_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			wait_for_completion(&respond->wait_completion);
+			return result;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
+			      u8 width, u32 *data)
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else
+		return ftdi_elan_read_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_pcimem);
+static int ftdi_elan_edset_setup(struct usb_ftdi *ftdi, u8 ed_number,
+				 void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+				 void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						   int toggle_bits, int error_count, int condition_code, int repeat_number,
+						   int halted, int skipped, int actual, int non_null))
+{
+	u8 ed = ed_number - 1;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			struct u132_target *target = &ftdi->target[ed];
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x80 | (ed << 5);
+			command->length = 0x8007;
+			command->address = (toggle_bits << 6) | (ep_number << 2)
+				| (address << 0);
+			command->width = usb_maxpacket(urb->dev, urb->pipe,
+						       usb_pipeout(urb->pipe));
+			command->follows = 8;
+			command->value = 0;
+			command->buffer = urb->setup_packet;
+			target->callback = callback;
+			target->endp = endp;
+			target->urb = urb;
+			target->active = 1;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+			      void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+			      void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						int toggle_bits, int error_count, int condition_code, int repeat_number,
+						int halted, int skipped, int actual, int non_null))
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_edset_setup(ftdi, ed_number, endp, urb, address,
+				     ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_setup);
+static int ftdi_elan_edset_input(struct usb_ftdi *ftdi, u8 ed_number,
+				 void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+				 void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						   int toggle_bits, int error_count, int condition_code, int repeat_number,
+						   int halted, int skipped, int actual, int non_null))
+{
+	u8 ed = ed_number - 1;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			struct u132_target *target = &ftdi->target[ed];
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			u32 remaining_length = urb->transfer_buffer_length -
+				urb->actual_length;
+			command->header = 0x82 | (ed << 5);
+			if (remaining_length == 0) {
+				command->length = 0x0000;
+			} else if (remaining_length > 1024) {
+				command->length = 0x8000 | 1023;
+			} else
+				command->length = 0x8000 | (remaining_length -
+							    1);
+			command->address = (toggle_bits << 6) | (ep_number << 2)
+				| (address << 0);
+			command->width = usb_maxpacket(urb->dev, urb->pipe,
+						       usb_pipeout(urb->pipe));
+			command->follows = 0;
+			command->value = 0;
+			command->buffer = NULL;
+			target->callback = callback;
+			target->endp = endp;
+			target->urb = urb;
+			target->active = 1;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+			      void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+			      void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						int toggle_bits, int error_count, int condition_code, int repeat_number,
+						int halted, int skipped, int actual, int non_null))
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_edset_input(ftdi, ed_number, endp, urb, address,
+				     ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_input);
+static int ftdi_elan_edset_empty(struct usb_ftdi *ftdi, u8 ed_number,
+				 void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+				 void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						   int toggle_bits, int error_count, int condition_code, int repeat_number,
+						   int halted, int skipped, int actual, int non_null))
+{
+	u8 ed = ed_number - 1;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			struct u132_target *target = &ftdi->target[ed];
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x81 | (ed << 5);
+			command->length = 0x0000;
+			command->address = (toggle_bits << 6) | (ep_number << 2)
+				| (address << 0);
+			command->width = usb_maxpacket(urb->dev, urb->pipe,
+						       usb_pipeout(urb->pipe));
+			command->follows = 0;
+			command->value = 0;
+			command->buffer = NULL;
+			target->callback = callback;
+			target->endp = endp;
+			target->urb = urb;
+			target->active = 1;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+			      void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+			      void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						int toggle_bits, int error_count, int condition_code, int repeat_number,
+						int halted, int skipped, int actual, int non_null))
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_edset_empty(ftdi, ed_number, endp, urb, address,
+				     ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_empty);
+static int ftdi_elan_edset_output(struct usb_ftdi *ftdi, u8 ed_number,
+				  void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+				  void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						    int toggle_bits, int error_count, int condition_code, int repeat_number,
+						    int halted, int skipped, int actual, int non_null))
+{
+	u8 ed = ed_number - 1;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			u8 *b;
+			u16 urb_size;
+			int i = 0;
+			char data[30 *3 + 4];
+			char *d = data;
+			int m = (sizeof(data) - 1) / 3;
+			int l = 0;
+			struct u132_target *target = &ftdi->target[ed];
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x81 | (ed << 5);
+			command->address = (toggle_bits << 6) | (ep_number << 2)
+				| (address << 0);
+			command->width = usb_maxpacket(urb->dev, urb->pipe,
+						       usb_pipeout(urb->pipe));
+			command->follows = min_t(u32, 1024,
+						 urb->transfer_buffer_length -
+						 urb->actual_length);
+			command->value = 0;
+			command->buffer = urb->transfer_buffer +
+				urb->actual_length;
+			command->length = 0x8000 | (command->follows - 1);
+			b = command->buffer;
+			urb_size = command->follows;
+			data[0] = 0;
+			while (urb_size-- > 0) {
+				if (i > m) {
+				} else if (i++ < m) {
+					int w = sprintf(d, " %02X", *b++);
+					d += w;
+					l += w;
+				} else
+					d += sprintf(d, " ..");
+			}
+			target->callback = callback;
+			target->endp = endp;
+			target->urb = urb;
+			target->active = 1;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+			       void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+			       void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						 int toggle_bits, int error_count, int condition_code, int repeat_number,
+						 int halted, int skipped, int actual, int non_null))
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_edset_output(ftdi, ed_number, endp, urb, address,
+				      ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_output);
+static int ftdi_elan_edset_single(struct usb_ftdi *ftdi, u8 ed_number,
+				  void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+				  void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						    int toggle_bits, int error_count, int condition_code, int repeat_number,
+						    int halted, int skipped, int actual, int non_null))
+{
+	u8 ed = ed_number - 1;
+wait:if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else {
+		int command_size;
+		mutex_lock(&ftdi->u132_lock);
+		command_size = ftdi->command_next - ftdi->command_head;
+		if (command_size < COMMAND_SIZE) {
+			u32 remaining_length = urb->transfer_buffer_length -
+				urb->actual_length;
+			struct u132_target *target = &ftdi->target[ed];
+			struct u132_command *command = &ftdi->command[
+				COMMAND_MASK & ftdi->command_next];
+			command->header = 0x83 | (ed << 5);
+			if (remaining_length == 0) {
+				command->length = 0x0000;
+			} else if (remaining_length > 1024) {
+				command->length = 0x8000 | 1023;
+			} else
+				command->length = 0x8000 | (remaining_length -
+							    1);
+			command->address = (toggle_bits << 6) | (ep_number << 2)
+				| (address << 0);
+			command->width = usb_maxpacket(urb->dev, urb->pipe,
+						       usb_pipeout(urb->pipe));
+			command->follows = 0;
+			command->value = 0;
+			command->buffer = NULL;
+			target->callback = callback;
+			target->endp = endp;
+			target->urb = urb;
+			target->active = 1;
+			ftdi->command_next += 1;
+			ftdi_elan_kick_command_queue(ftdi);
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			mutex_unlock(&ftdi->u132_lock);
+			msleep(100);
+			goto wait;
+		}
+	}
+}
+
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+			       void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+			       void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+						 int toggle_bits, int error_count, int condition_code, int repeat_number,
+						 int halted, int skipped, int actual, int non_null))
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_edset_single(ftdi, ed_number, endp, urb, address,
+				      ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_single);
+static int ftdi_elan_edset_flush(struct usb_ftdi *ftdi, u8 ed_number,
+				 void *endp)
+{
+	u8 ed = ed_number - 1;
+	if (ftdi->disconnected > 0) {
+		return -ENODEV;
+	} else if (ftdi->initialized == 0) {
+		return -ENODEV;
+	} else {
+		struct u132_target *target = &ftdi->target[ed];
+		mutex_lock(&ftdi->u132_lock);
+		if (target->abandoning > 0) {
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		} else {
+			target->abandoning = 1;
+		wait_1:if (target->active == 1) {
+				int command_size = ftdi->command_next -
+					ftdi->command_head;
+				if (command_size < COMMAND_SIZE) {
+					struct u132_command *command =
+						&ftdi->command[COMMAND_MASK &
+							       ftdi->command_next];
+					command->header = 0x80 | (ed << 5) |
+						0x4;
+					command->length = 0x00;
+					command->address = 0x00;
+					command->width = 0x00;
+					command->follows = 0;
+					command->value = 0;
+					command->buffer = &command->value;
+					ftdi->command_next += 1;
+					ftdi_elan_kick_command_queue(ftdi);
+				} else {
+					mutex_unlock(&ftdi->u132_lock);
+					msleep(100);
+					mutex_lock(&ftdi->u132_lock);
+					goto wait_1;
+				}
+			}
+			mutex_unlock(&ftdi->u132_lock);
+			return 0;
+		}
+	}
+}
+
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+			      void *endp)
+{
+	struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+	return ftdi_elan_edset_flush(ftdi, ed_number, endp);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_flush);
+static int ftdi_elan_flush_input_fifo(struct usb_ftdi *ftdi)
+{
+	int retry_on_empty = 10;
+	int retry_on_timeout = 5;
+	int retry_on_status = 20;
+more:{
+		int packet_bytes = 0;
+		int retval = usb_bulk_msg(ftdi->udev,
+					  usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+					  ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+					  &packet_bytes, 100);
+		if (packet_bytes > 2) {
+			char diag[30 *3 + 4];
+			char *d = diag;
+			int m = (sizeof(diag) - 1) / 3;
+			char *b = ftdi->bulk_in_buffer;
+			int bytes_read = 0;
+			diag[0] = 0;
+			while (packet_bytes-- > 0) {
+				char c = *b++;
+				if (bytes_read < m) {
+					d += sprintf(d, " %02X",
+						     0x000000FF & c);
+				} else if (bytes_read > m) {
+				} else
+					d += sprintf(d, " ..");
+				bytes_read += 1;
+				continue;
+			}
+			goto more;
+		} else if (packet_bytes > 1) {
+			char s1 = ftdi->bulk_in_buffer[0];
+			char s2 = ftdi->bulk_in_buffer[1];
+			if (s1 == 0x31 && s2 == 0x60) {
+				return 0;
+			} else if (retry_on_status-- > 0) {
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "STATUS ERROR retry limit reached\n");
+				return -EFAULT;
+			}
+		} else if (packet_bytes > 0) {
+			char b1 = ftdi->bulk_in_buffer[0];
+			dev_err(&ftdi->udev->dev, "only one byte flushed from FTDI = %02X\n",
+				b1);
+			if (retry_on_status-- > 0) {
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "STATUS ERROR retry limit reached\n");
+				return -EFAULT;
+			}
+		} else if (retval == -ETIMEDOUT) {
+			if (retry_on_timeout-- > 0) {
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "TIMED OUT retry limit reached\n");
+				return -ENOMEM;
+			}
+		} else if (retval == 0) {
+			if (retry_on_empty-- > 0) {
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "empty packet retry limit reached\n");
+				return -ENOMEM;
+			}
+		} else {
+			dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+			return retval;
+		}
+	}
+	return -1;
+}
+
+
+/*
+ * send the long flush sequence
+ *
+ */
+static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
+{
+	int retval;
+	struct urb *urb;
+	char *buf;
+	int I = 257;
+	int i = 0;
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&ftdi->udev->dev, "could not alloc a urb for flush sequence\n");
+		return -ENOMEM;
+	}
+	buf = usb_alloc_coherent(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+	if (!buf) {
+		dev_err(&ftdi->udev->dev, "could not get a buffer for flush sequence\n");
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	while (I-- > 0)
+		buf[i++] = 0x55;
+	usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+							   ftdi->bulk_out_endpointAddr), buf, i,
+			  ftdi_elan_write_bulk_callback, ftdi);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&ftdi->udev->dev, "failed to submit urb containing the flush sequence\n");
+		usb_free_coherent(ftdi->udev, i, buf, urb->transfer_dma);
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	usb_free_urb(urb);
+	return 0;
+}
+
+
+/*
+ * send the reset sequence
+ *
+ */
+static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
+{
+	int retval;
+	struct urb *urb;
+	char *buf;
+	int I = 4;
+	int i = 0;
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		dev_err(&ftdi->udev->dev, "could not get a urb for the reset sequence\n");
+		return -ENOMEM;
+	}
+	buf = usb_alloc_coherent(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+	if (!buf) {
+		dev_err(&ftdi->udev->dev, "could not get a buffer for the reset sequence\n");
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	buf[i++] = 0x55;
+	buf[i++] = 0xAA;
+	buf[i++] = 0x5A;
+	buf[i++] = 0xA5;
+	usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+							   ftdi->bulk_out_endpointAddr), buf, i,
+			  ftdi_elan_write_bulk_callback, ftdi);
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&ftdi->udev->dev, "failed to submit urb containing the reset sequence\n");
+		usb_free_coherent(ftdi->udev, i, buf, urb->transfer_dma);
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	usb_free_urb(urb);
+	return 0;
+}
+
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi)
+{
+	int retval;
+	int long_stop = 10;
+	int retry_on_timeout = 5;
+	int retry_on_empty = 10;
+	int err_count = 0;
+	retval = ftdi_elan_flush_input_fifo(ftdi);
+	if (retval)
+		return retval;
+	ftdi->bulk_in_left = 0;
+	ftdi->bulk_in_last = -1;
+	while (long_stop-- > 0) {
+		int read_stop;
+		int read_stuck;
+		retval = ftdi_elan_synchronize_flush(ftdi);
+		if (retval)
+			return retval;
+		retval = ftdi_elan_flush_input_fifo(ftdi);
+		if (retval)
+			return retval;
+	reset:retval = ftdi_elan_synchronize_reset(ftdi);
+		if (retval)
+			return retval;
+		read_stop = 100;
+		read_stuck = 10;
+	read:{
+			int packet_bytes = 0;
+			retval = usb_bulk_msg(ftdi->udev,
+					      usb_rcvbulkpipe(ftdi->udev,
+							      ftdi->bulk_in_endpointAddr),
+					      ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+					      &packet_bytes, 500);
+			if (packet_bytes > 2) {
+				char diag[30 *3 + 4];
+				char *d = diag;
+				int m = (sizeof(diag) - 1) / 3;
+				char *b = ftdi->bulk_in_buffer;
+				int bytes_read = 0;
+				unsigned char c = 0;
+				diag[0] = 0;
+				while (packet_bytes-- > 0) {
+					c = *b++;
+					if (bytes_read < m) {
+						d += sprintf(d, " %02X", c);
+					} else if (bytes_read > m) {
+					} else
+						d += sprintf(d, " ..");
+					bytes_read += 1;
+					continue;
+				}
+				if (c == 0x7E) {
+					return 0;
+				} else {
+					if (c == 0x55) {
+						goto read;
+					} else if (read_stop-- > 0) {
+						goto read;
+					} else {
+						dev_err(&ftdi->udev->dev, "retry limit reached\n");
+						continue;
+					}
+				}
+			} else if (packet_bytes > 1) {
+				unsigned char s1 = ftdi->bulk_in_buffer[0];
+				unsigned char s2 = ftdi->bulk_in_buffer[1];
+				if (s1 == 0x31 && s2 == 0x00) {
+					if (read_stuck-- > 0) {
+						goto read;
+					} else
+						goto reset;
+				} else if (s1 == 0x31 && s2 == 0x60) {
+					if (read_stop-- > 0) {
+						goto read;
+					} else {
+						dev_err(&ftdi->udev->dev, "retry limit reached\n");
+						continue;
+					}
+				} else {
+					if (read_stop-- > 0) {
+						goto read;
+					} else {
+						dev_err(&ftdi->udev->dev, "retry limit reached\n");
+						continue;
+					}
+				}
+			} else if (packet_bytes > 0) {
+				if (read_stop-- > 0) {
+					goto read;
+				} else {
+					dev_err(&ftdi->udev->dev, "retry limit reached\n");
+					continue;
+				}
+			} else if (retval == -ETIMEDOUT) {
+				if (retry_on_timeout-- > 0) {
+					goto read;
+				} else {
+					dev_err(&ftdi->udev->dev, "TIMED OUT retry limit reached\n");
+					continue;
+				}
+			} else if (retval == 0) {
+				if (retry_on_empty-- > 0) {
+					goto read;
+				} else {
+					dev_err(&ftdi->udev->dev, "empty packet retry limit reached\n");
+					continue;
+				}
+			} else {
+				err_count += 1;
+				dev_err(&ftdi->udev->dev, "error = %d\n",
+					retval);
+				if (read_stop-- > 0) {
+					goto read;
+				} else {
+					dev_err(&ftdi->udev->dev, "retry limit reached\n");
+					continue;
+				}
+			}
+		}
+	}
+	dev_err(&ftdi->udev->dev, "failed to synchronize\n");
+	return -EFAULT;
+}
+
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi)
+{
+	int retry_on_empty = 10;
+	int retry_on_timeout = 5;
+	int retry_on_status = 50;
+more:{
+		int packet_bytes = 0;
+		int retval = usb_bulk_msg(ftdi->udev,
+					  usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+					  ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+					  &packet_bytes, 1000);
+		if (packet_bytes > 2) {
+			char diag[30 *3 + 4];
+			char *d = diag;
+			int m = (sizeof(diag) - 1) / 3;
+			char *b = ftdi->bulk_in_buffer;
+			int bytes_read = 0;
+			diag[0] = 0;
+			while (packet_bytes-- > 0) {
+				char c = *b++;
+				if (bytes_read < m) {
+					d += sprintf(d, " %02X",
+						     0x000000FF & c);
+				} else if (bytes_read > m) {
+				} else
+					d += sprintf(d, " ..");
+				bytes_read += 1;
+				continue;
+			}
+			goto more;
+		} else if (packet_bytes > 1) {
+			char s1 = ftdi->bulk_in_buffer[0];
+			char s2 = ftdi->bulk_in_buffer[1];
+			if (s1 == 0x31 && s2 == 0x60) {
+				return 0;
+			} else if (retry_on_status-- > 0) {
+				msleep(5);
+				goto more;
+			} else
+				return -EFAULT;
+		} else if (packet_bytes > 0) {
+			char b1 = ftdi->bulk_in_buffer[0];
+			dev_err(&ftdi->udev->dev, "only one byte flushed from FTDI = %02X\n", b1);
+			if (retry_on_status-- > 0) {
+				msleep(5);
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "STATUS ERROR retry limit reached\n");
+				return -EFAULT;
+			}
+		} else if (retval == -ETIMEDOUT) {
+			if (retry_on_timeout-- > 0) {
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "TIMED OUT retry limit reached\n");
+				return -ENOMEM;
+			}
+		} else if (retval == 0) {
+			if (retry_on_empty-- > 0) {
+				goto more;
+			} else {
+				dev_err(&ftdi->udev->dev, "empty packet retry limit reached\n");
+				return -ENOMEM;
+			}
+		} else {
+			dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+			return -ENOMEM;
+		}
+	}
+	return -1;
+}
+
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
+{
+	int UxxxStatus = ftdi_elan_read_reg(ftdi, &ftdi->controlreg);
+	if (UxxxStatus)
+		return UxxxStatus;
+	if (ftdi->controlreg & 0x00400000) {
+		if (ftdi->card_ejected) {
+		} else {
+			ftdi->card_ejected = 1;
+			dev_err(&ftdi->udev->dev, "CARD EJECTED - controlreg = %08X\n",
+				ftdi->controlreg);
+		}
+		return -ENODEV;
+	} else {
+		u8 fn = ftdi->function - 1;
+		int activePCIfn = fn << 8;
+		u32 pcidata;
+		u32 pciVID;
+		u32 pciPID;
+		int reg = 0;
+		UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+						   &pcidata);
+		if (UxxxStatus)
+			return UxxxStatus;
+		pciVID = pcidata & 0xFFFF;
+		pciPID = (pcidata >> 16) & 0xFFFF;
+		if (pciVID == ftdi->platform_data.vendor && pciPID ==
+		    ftdi->platform_data.device) {
+			return 0;
+		} else {
+			dev_err(&ftdi->udev->dev, "vendor=%04X pciVID=%04X device=%04X pciPID=%04X\n",
+				ftdi->platform_data.vendor, pciVID,
+				ftdi->platform_data.device, pciPID);
+			return -ENODEV;
+		}
+	}
+}
+
+
+#define ftdi_read_pcimem(ftdi, member, data) ftdi_elan_read_pcimem(ftdi, \
+								   offsetof(struct ohci_regs, member), 0, data);
+#define ftdi_write_pcimem(ftdi, member, data) ftdi_elan_write_pcimem(ftdi, \
+								     offsetof(struct ohci_regs, member), 0, data);
+
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD |	\
+			OHCI_INTR_WDH)
+static int ftdi_elan_check_controller(struct usb_ftdi *ftdi, int quirk)
+{
+	int devices = 0;
+	int retval;
+	u32 hc_control;
+	int num_ports;
+	u32 control;
+	u32 rh_a = -1;
+	u32 status;
+	u32 fminterval;
+	u32 hc_fminterval;
+	u32 periodicstart;
+	u32 cmdstatus;
+	u32 roothub_a;
+	int mask = OHCI_INTR_INIT;
+	int sleep_time = 0;
+	int reset_timeout = 30;        /* ... allow extra time */
+	int temp;
+	retval = ftdi_write_pcimem(ftdi, intrdisable, OHCI_INTR_MIE);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, control, &control);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, roothub.a, &rh_a);
+	if (retval)
+		return retval;
+	num_ports = rh_a & RH_A_NDP;
+	retval = ftdi_read_pcimem(ftdi, fminterval, &hc_fminterval);
+	if (retval)
+		return retval;
+	hc_fminterval &= 0x3fff;
+	if (hc_fminterval != FI) {
+	}
+	hc_fminterval |= FSMP(hc_fminterval) << 16;
+	retval = ftdi_read_pcimem(ftdi, control, &hc_control);
+	if (retval)
+		return retval;
+	switch (hc_control & OHCI_CTRL_HCFS) {
+	case OHCI_USB_OPER:
+		sleep_time = 0;
+		break;
+	case OHCI_USB_SUSPEND:
+	case OHCI_USB_RESUME:
+		hc_control &= OHCI_CTRL_RWC;
+		hc_control |= OHCI_USB_RESUME;
+		sleep_time = 10;
+		break;
+	default:
+		hc_control &= OHCI_CTRL_RWC;
+		hc_control |= OHCI_USB_RESET;
+		sleep_time = 50;
+		break;
+	}
+	retval = ftdi_write_pcimem(ftdi, control, hc_control);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, control, &control);
+	if (retval)
+		return retval;
+	msleep(sleep_time);
+	retval = ftdi_read_pcimem(ftdi, roothub.a, &roothub_a);
+	if (retval)
+		return retval;
+	if (!(roothub_a & RH_A_NPS)) {        /* power down each port */
+		for (temp = 0; temp < num_ports; temp++) {
+			retval = ftdi_write_pcimem(ftdi,
+						   roothub.portstatus[temp], RH_PS_LSDA);
+			if (retval)
+				return retval;
+		}
+	}
+	retval = ftdi_read_pcimem(ftdi, control, &control);
+	if (retval)
+		return retval;
+retry:retval = ftdi_read_pcimem(ftdi, cmdstatus, &status);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, cmdstatus, OHCI_HCR);
+	if (retval)
+		return retval;
+extra:{
+		retval = ftdi_read_pcimem(ftdi, cmdstatus, &status);
+		if (retval)
+			return retval;
+		if (0 != (status & OHCI_HCR)) {
+			if (--reset_timeout == 0) {
+				dev_err(&ftdi->udev->dev, "USB HC reset timed out!\n");
+				return -ENODEV;
+			} else {
+				msleep(5);
+				goto extra;
+			}
+		}
+	}
+	if (quirk & OHCI_QUIRK_INITRESET) {
+		retval = ftdi_write_pcimem(ftdi, control, hc_control);
+		if (retval)
+			return retval;
+		retval = ftdi_read_pcimem(ftdi, control, &control);
+		if (retval)
+			return retval;
+	}
+	retval = ftdi_write_pcimem(ftdi, ed_controlhead, 0x00000000);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, ed_bulkhead, 0x11000000);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, hcca, 0x00000000);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, fminterval, &fminterval);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, fminterval,
+				   ((fminterval & FIT) ^ FIT) | hc_fminterval);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, periodicstart,
+				   ((9 *hc_fminterval) / 10) & 0x3fff);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, fminterval, &fminterval);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, periodicstart, &periodicstart);
+	if (retval)
+		return retval;
+	if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
+		if (!(quirk & OHCI_QUIRK_INITRESET)) {
+			quirk |= OHCI_QUIRK_INITRESET;
+			goto retry;
+		} else
+			dev_err(&ftdi->udev->dev, "init err(%08x %04x)\n",
+				fminterval, periodicstart);
+	}                        /* start controller operations */
+	hc_control &= OHCI_CTRL_RWC;
+	hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
+	retval = ftdi_write_pcimem(ftdi, control, hc_control);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, cmdstatus, OHCI_BLF);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, cmdstatus, &cmdstatus);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, control, &control);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, roothub.status, RH_HS_DRWE);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, intrstatus, mask);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, intrdisable,
+				   OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
+				   OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
+				   OHCI_INTR_SO);
+	if (retval)
+		return retval;        /* handle root hub init quirks ... */
+	retval = ftdi_read_pcimem(ftdi, roothub.a, &roothub_a);
+	if (retval)
+		return retval;
+	roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
+	if (quirk & OHCI_QUIRK_SUPERIO) {
+		roothub_a |= RH_A_NOCP;
+		roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
+		retval = ftdi_write_pcimem(ftdi, roothub.a, roothub_a);
+		if (retval)
+			return retval;
+	} else if ((quirk & OHCI_QUIRK_AMD756) || distrust_firmware) {
+		roothub_a |= RH_A_NPS;
+		retval = ftdi_write_pcimem(ftdi, roothub.a, roothub_a);
+		if (retval)
+			return retval;
+	}
+	retval = ftdi_write_pcimem(ftdi, roothub.status, RH_HS_LPSC);
+	if (retval)
+		return retval;
+	retval = ftdi_write_pcimem(ftdi, roothub.b,
+				   (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
+	if (retval)
+		return retval;
+	retval = ftdi_read_pcimem(ftdi, control, &control);
+	if (retval)
+		return retval;
+	mdelay((roothub_a >> 23) & 0x1fe);
+	for (temp = 0; temp < num_ports; temp++) {
+		u32 portstatus;
+		retval = ftdi_read_pcimem(ftdi, roothub.portstatus[temp],
+					  &portstatus);
+		if (retval)
+			return retval;
+		if (1 & portstatus)
+			devices += 1;
+	}
+	return devices;
+}
+
+static int ftdi_elan_setup_controller(struct usb_ftdi *ftdi, int fn)
+{
+	u32 latence_timer;
+	int UxxxStatus;
+	u32 pcidata;
+	int reg = 0;
+	int activePCIfn = fn << 8;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
+	if (UxxxStatus)
+		return UxxxStatus;
+	reg = 16;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+					    0xFFFFFFFF);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+					    0xF0000000);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	reg = 12;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &latence_timer);
+	if (UxxxStatus)
+		return UxxxStatus;
+	latence_timer &= 0xFFFF00FF;
+	latence_timer |= 0x00001600;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+					    latence_timer);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	reg = 4;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+					    0x06);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	for (reg = 0; reg <= 0x54; reg += 4) {
+		UxxxStatus = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+		if (UxxxStatus)
+			return UxxxStatus;
+	}
+	return 0;
+}
+
+static int ftdi_elan_close_controller(struct usb_ftdi *ftdi, int fn)
+{
+	u32 latence_timer;
+	int UxxxStatus;
+	u32 pcidata;
+	int reg = 0;
+	int activePCIfn = fn << 8;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
+	if (UxxxStatus)
+		return UxxxStatus;
+	reg = 16;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+					    0xFFFFFFFF);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+					    0x00000000);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	reg = 12;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &latence_timer);
+	if (UxxxStatus)
+		return UxxxStatus;
+	latence_timer &= 0xFFFF00FF;
+	latence_timer |= 0x00001600;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+					    latence_timer);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+					   &pcidata);
+	if (UxxxStatus)
+		return UxxxStatus;
+	reg = 4;
+	UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+					    0x00);
+	if (UxxxStatus)
+		return UxxxStatus;
+	return ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, &pcidata);
+}
+
+static int ftdi_elan_found_controller(struct usb_ftdi *ftdi, int fn, int quirk)
+{
+	int result;
+	int UxxxStatus;
+	UxxxStatus = ftdi_elan_setup_controller(ftdi, fn);
+	if (UxxxStatus)
+		return UxxxStatus;
+	result = ftdi_elan_check_controller(ftdi, quirk);
+	UxxxStatus = ftdi_elan_close_controller(ftdi, fn);
+	if (UxxxStatus)
+		return UxxxStatus;
+	return result;
+}
+
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
+{
+	u32 controlreg;
+	u8 sensebits;
+	int UxxxStatus;
+	UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L);
+	if (UxxxStatus)
+		return UxxxStatus;
+	msleep(750);
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000);
+	if (UxxxStatus)
+		return UxxxStatus;
+	msleep(250);
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+	if (UxxxStatus)
+		return UxxxStatus;
+	UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+	if (UxxxStatus)
+		return UxxxStatus;
+	msleep(1000);
+	sensebits = (controlreg >> 16) & 0x000F;
+	if (0x0D == sensebits)
+		return 0;
+	else
+		return - ENXIO;
+}
+
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi)
+{
+	int UxxxStatus;
+	u32 pcidata;
+	int reg = 0;
+	u8 fn;
+	int activePCIfn = 0;
+	int max_devices = 0;
+	int controllers = 0;
+	int unrecognized = 0;
+	ftdi->function = 0;
+	for (fn = 0; (fn < 4); fn++) {
+		u32 pciVID = 0;
+		u32 pciPID = 0;
+		int devices = 0;
+		activePCIfn = fn << 8;
+		UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+						   &pcidata);
+		if (UxxxStatus)
+			return UxxxStatus;
+		pciVID = pcidata & 0xFFFF;
+		pciPID = (pcidata >> 16) & 0xFFFF;
+		if ((pciVID == PCI_VENDOR_ID_OPTI) && (pciPID == 0xc861)) {
+			devices = ftdi_elan_found_controller(ftdi, fn, 0);
+			controllers += 1;
+		} else if ((pciVID == PCI_VENDOR_ID_NEC) && (pciPID == 0x0035))
+		{
+			devices = ftdi_elan_found_controller(ftdi, fn, 0);
+			controllers += 1;
+		} else if ((pciVID == PCI_VENDOR_ID_AL) && (pciPID == 0x5237)) {
+			devices = ftdi_elan_found_controller(ftdi, fn, 0);
+			controllers += 1;
+		} else if ((pciVID == PCI_VENDOR_ID_ATT) && (pciPID == 0x5802))
+		{
+			devices = ftdi_elan_found_controller(ftdi, fn, 0);
+			controllers += 1;
+		} else if (pciVID == PCI_VENDOR_ID_AMD && pciPID == 0x740c) {
+			devices = ftdi_elan_found_controller(ftdi, fn,
+							     OHCI_QUIRK_AMD756);
+			controllers += 1;
+		} else if (pciVID == PCI_VENDOR_ID_COMPAQ && pciPID == 0xa0f8) {
+			devices = ftdi_elan_found_controller(ftdi, fn,
+							     OHCI_QUIRK_ZFMICRO);
+			controllers += 1;
+		} else if (0 == pcidata) {
+		} else
+			unrecognized += 1;
+		if (devices > max_devices) {
+			max_devices = devices;
+			ftdi->function = fn + 1;
+			ftdi->platform_data.vendor = pciVID;
+			ftdi->platform_data.device = pciPID;
+		}
+	}
+	if (ftdi->function > 0) {
+		return ftdi_elan_setup_controller(ftdi,	ftdi->function - 1);
+	} else if (controllers > 0) {
+		return -ENXIO;
+	} else if (unrecognized > 0) {
+		return -ENXIO;
+	} else {
+		ftdi->enumerated = 0;
+		return -ENXIO;
+	}
+}
+
+
+/*
+ * we use only the first bulk-in and bulk-out endpoints
+ */
+static int ftdi_elan_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id)
+{
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	size_t buffer_size;
+	int i;
+	int retval = -ENOMEM;
+	struct usb_ftdi *ftdi;
+
+	ftdi = kzalloc(sizeof(struct usb_ftdi), GFP_KERNEL);
+	if (!ftdi)
+		return -ENOMEM;
+
+	mutex_lock(&ftdi_module_lock);
+	list_add_tail(&ftdi->ftdi_list, &ftdi_static_list);
+	ftdi->sequence_num = ++ftdi_instances;
+	mutex_unlock(&ftdi_module_lock);
+	ftdi_elan_init_kref(ftdi);
+	sema_init(&ftdi->sw_lock, 1);
+	ftdi->udev = usb_get_dev(interface_to_usbdev(interface));
+	ftdi->interface = interface;
+	mutex_init(&ftdi->u132_lock);
+	ftdi->expected = 4;
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if (!ftdi->bulk_in_endpointAddr &&
+		    usb_endpoint_is_bulk_in(endpoint)) {
+			buffer_size = usb_endpoint_maxp(endpoint);
+			ftdi->bulk_in_size = buffer_size;
+			ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+			ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+			if (!ftdi->bulk_in_buffer) {
+				dev_err(&ftdi->udev->dev, "Could not allocate bulk_in_buffer\n");
+				retval = -ENOMEM;
+				goto error;
+			}
+		}
+		if (!ftdi->bulk_out_endpointAddr &&
+		    usb_endpoint_is_bulk_out(endpoint)) {
+			ftdi->bulk_out_endpointAddr =
+				endpoint->bEndpointAddress;
+		}
+	}
+	if (!(ftdi->bulk_in_endpointAddr && ftdi->bulk_out_endpointAddr)) {
+		dev_err(&ftdi->udev->dev, "Could not find both bulk-in and bulk-out endpoints\n");
+		retval = -ENODEV;
+		goto error;
+	}
+	dev_info(&ftdi->udev->dev, "interface %d has I=%02X O=%02X\n",
+		 iface_desc->desc.bInterfaceNumber, ftdi->bulk_in_endpointAddr,
+		 ftdi->bulk_out_endpointAddr);
+	usb_set_intfdata(interface, ftdi);
+	if (iface_desc->desc.bInterfaceNumber == 0 &&
+	    ftdi->bulk_in_endpointAddr == 0x81 &&
+	    ftdi->bulk_out_endpointAddr == 0x02) {
+		retval = usb_register_dev(interface, &ftdi_elan_jtag_class);
+		if (retval) {
+			dev_err(&ftdi->udev->dev, "Not able to get a minor for this device\n");
+			usb_set_intfdata(interface, NULL);
+			retval = -ENOMEM;
+			goto error;
+		} else {
+			ftdi->class = &ftdi_elan_jtag_class;
+			dev_info(&ftdi->udev->dev, "USB FDTI=%p JTAG interface %d now attached to ftdi%d\n",
+				 ftdi, iface_desc->desc.bInterfaceNumber,
+				 interface->minor);
+			return 0;
+		}
+	} else if (iface_desc->desc.bInterfaceNumber == 1 &&
+		   ftdi->bulk_in_endpointAddr == 0x83 &&
+		   ftdi->bulk_out_endpointAddr == 0x04) {
+		ftdi->class = NULL;
+		dev_info(&ftdi->udev->dev, "USB FDTI=%p ELAN interface %d now activated\n",
+			 ftdi, iface_desc->desc.bInterfaceNumber);
+		INIT_DELAYED_WORK(&ftdi->status_work, ftdi_elan_status_work);
+		INIT_DELAYED_WORK(&ftdi->command_work, ftdi_elan_command_work);
+		INIT_DELAYED_WORK(&ftdi->respond_work, ftdi_elan_respond_work);
+		ftdi_status_queue_work(ftdi, msecs_to_jiffies(3 *1000));
+		return 0;
+	} else {
+		dev_err(&ftdi->udev->dev,
+			"Could not find ELAN's U132 device\n");
+		retval = -ENODEV;
+		goto error;
+	}
+error:if (ftdi) {
+		ftdi_elan_put_kref(ftdi);
+	}
+	return retval;
+}
+
+static void ftdi_elan_disconnect(struct usb_interface *interface)
+{
+	struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+	ftdi->disconnected += 1;
+	if (ftdi->class) {
+		int minor = interface->minor;
+		struct usb_class_driver *class = ftdi->class;
+		usb_set_intfdata(interface, NULL);
+		usb_deregister_dev(interface, class);
+		dev_info(&ftdi->udev->dev, "USB FTDI U132 jtag interface on minor %d now disconnected\n",
+			 minor);
+	} else {
+		ftdi_status_cancel_work(ftdi);
+		ftdi_command_cancel_work(ftdi);
+		ftdi_response_cancel_work(ftdi);
+		ftdi_elan_abandon_completions(ftdi);
+		ftdi_elan_abandon_targets(ftdi);
+		if (ftdi->registered) {
+			platform_device_unregister(&ftdi->platform_dev);
+			ftdi->synchronized = 0;
+			ftdi->enumerated = 0;
+			ftdi->initialized = 0;
+			ftdi->registered = 0;
+		}
+		flush_workqueue(status_queue);
+		flush_workqueue(command_queue);
+		flush_workqueue(respond_queue);
+		ftdi->disconnected += 1;
+		usb_set_intfdata(interface, NULL);
+		dev_info(&ftdi->udev->dev, "USB FTDI U132 host controller interface now disconnected\n");
+	}
+	ftdi_elan_put_kref(ftdi);
+}
+
+static struct usb_driver ftdi_elan_driver = {
+	.name = "ftdi-elan",
+	.probe = ftdi_elan_probe,
+	.disconnect = ftdi_elan_disconnect,
+	.id_table = ftdi_elan_table,
+};
+static int __init ftdi_elan_init(void)
+{
+	int result;
+	pr_info("driver %s\n", ftdi_elan_driver.name);
+	mutex_init(&ftdi_module_lock);
+	INIT_LIST_HEAD(&ftdi_static_list);
+	status_queue = create_singlethread_workqueue("ftdi-status-control");
+	if (!status_queue)
+		goto err_status_queue;
+	command_queue = create_singlethread_workqueue("ftdi-command-engine");
+	if (!command_queue)
+		goto err_command_queue;
+	respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
+	if (!respond_queue)
+		goto err_respond_queue;
+	result = usb_register(&ftdi_elan_driver);
+	if (result) {
+		destroy_workqueue(status_queue);
+		destroy_workqueue(command_queue);
+		destroy_workqueue(respond_queue);
+		pr_err("usb_register failed. Error number %d\n", result);
+	}
+	return result;
+
+err_respond_queue:
+	destroy_workqueue(command_queue);
+err_command_queue:
+	destroy_workqueue(status_queue);
+err_status_queue:
+	pr_err("%s couldn't create workqueue\n", ftdi_elan_driver.name);
+	return -ENOMEM;
+}
+
+static void __exit ftdi_elan_exit(void)
+{
+	struct usb_ftdi *ftdi;
+	struct usb_ftdi *temp;
+	usb_deregister(&ftdi_elan_driver);
+	pr_info("ftdi_u132 driver deregistered\n");
+	list_for_each_entry_safe(ftdi, temp, &ftdi_static_list, ftdi_list) {
+		ftdi_status_cancel_work(ftdi);
+		ftdi_command_cancel_work(ftdi);
+		ftdi_response_cancel_work(ftdi);
+	} flush_workqueue(status_queue);
+	destroy_workqueue(status_queue);
+	status_queue = NULL;
+	flush_workqueue(command_queue);
+	destroy_workqueue(command_queue);
+	command_queue = NULL;
+	flush_workqueue(respond_queue);
+	destroy_workqueue(respond_queue);
+	respond_queue = NULL;
+}
+
+
+module_init(ftdi_elan_init);
+module_exit(ftdi_elan_exit);
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
new file mode 100644
index 0000000..6d4e757
--- /dev/null
+++ b/drivers/usb/misc/idmouse.c
@@ -0,0 +1,439 @@
+/* Siemens ID Mouse driver v0.6
+
+  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.
+
+  Copyright (C) 2004-5 by Florian 'Floe' Echtler  <echtler@fs.tum.de>
+                      and Andreas  'ad'  Deresch <aderesch@fs.tum.de>
+
+  Derived from the USB Skeleton driver 1.1,
+  Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
+
+  Additional information provided by Martin Reising
+  <Martin.Reising@natural-computing.de>
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+/* image constants */
+#define WIDTH 225
+#define HEIGHT 289
+#define HEADER "P5 225 289 255 "
+#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1)
+
+/* version information */
+#define DRIVER_VERSION "0.6"
+#define DRIVER_SHORT   "idmouse"
+#define DRIVER_AUTHOR  "Florian 'Floe' Echtler <echtler@fs.tum.de>"
+#define DRIVER_DESC    "Siemens ID Mouse FingerTIP Sensor Driver"
+
+/* minor number for misc USB devices */
+#define USB_IDMOUSE_MINOR_BASE 132
+
+/* vendor and device IDs */
+#define ID_SIEMENS 0x0681
+#define ID_IDMOUSE 0x0005
+#define ID_CHERRY  0x0010
+
+/* device ID table */
+static const struct usb_device_id idmouse_table[] = {
+	{USB_DEVICE(ID_SIEMENS, ID_IDMOUSE)}, /* Siemens ID Mouse (Professional) */
+	{USB_DEVICE(ID_SIEMENS, ID_CHERRY )}, /* Cherry FingerTIP ID Board       */
+	{}                                    /* terminating null entry          */
+};
+
+/* sensor commands */
+#define FTIP_RESET   0x20
+#define FTIP_ACQUIRE 0x21
+#define FTIP_RELEASE 0x22
+#define FTIP_BLINK   0x23  /* LSB of value = blink pulse width */
+#define FTIP_SCROLL  0x24
+
+#define ftip_command(dev, command, value, index) \
+	usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \
+	USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000)
+
+MODULE_DEVICE_TABLE(usb, idmouse_table);
+static DEFINE_MUTEX(open_disc_mutex);
+
+/* structure to hold all of our device specific stuff */
+struct usb_idmouse {
+
+	struct usb_device *udev; /* save off the usb device pointer */
+	struct usb_interface *interface; /* the interface for this device */
+
+	unsigned char *bulk_in_buffer; /* the buffer to receive data */
+	size_t bulk_in_size; /* the maximum bulk packet size */
+	size_t orig_bi_size; /* same as above, but reported by the device */
+	__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+
+	int open; /* if the port is open or not */
+	int present; /* if the device is not disconnected */
+	struct mutex lock; /* locks this structure */
+
+};
+
+/* local function prototypes */
+static ssize_t idmouse_read(struct file *file, char __user *buffer,
+				size_t count, loff_t * ppos);
+
+static int idmouse_open(struct inode *inode, struct file *file);
+static int idmouse_release(struct inode *inode, struct file *file);
+
+static int idmouse_probe(struct usb_interface *interface,
+				const struct usb_device_id *id);
+
+static void idmouse_disconnect(struct usb_interface *interface);
+static int idmouse_suspend(struct usb_interface *intf, pm_message_t message);
+static int idmouse_resume(struct usb_interface *intf);
+
+/* file operation pointers */
+static const struct file_operations idmouse_fops = {
+	.owner = THIS_MODULE,
+	.read = idmouse_read,
+	.open = idmouse_open,
+	.release = idmouse_release,
+	.llseek = default_llseek,
+};
+
+/* class driver information */
+static struct usb_class_driver idmouse_class = {
+	.name = "idmouse%d",
+	.fops = &idmouse_fops,
+	.minor_base = USB_IDMOUSE_MINOR_BASE,
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver idmouse_driver = {
+	.name = DRIVER_SHORT,
+	.probe = idmouse_probe,
+	.disconnect = idmouse_disconnect,
+	.suspend = idmouse_suspend,
+	.resume = idmouse_resume,
+	.reset_resume = idmouse_resume,
+	.id_table = idmouse_table,
+	.supports_autosuspend = 1,
+};
+
+static int idmouse_create_image(struct usb_idmouse *dev)
+{
+	int bytes_read;
+	int bulk_read;
+	int result;
+
+	memcpy(dev->bulk_in_buffer, HEADER, sizeof(HEADER)-1);
+	bytes_read = sizeof(HEADER)-1;
+
+	/* reset the device and set a fast blink rate */
+	result = ftip_command(dev, FTIP_RELEASE, 0, 0);
+	if (result < 0)
+		goto reset;
+	result = ftip_command(dev, FTIP_BLINK,   1, 0);
+	if (result < 0)
+		goto reset;
+
+	/* initialize the sensor - sending this command twice */
+	/* significantly reduces the rate of failed reads     */
+	result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
+	if (result < 0)
+		goto reset;
+	result = ftip_command(dev, FTIP_ACQUIRE, 0, 0);
+	if (result < 0)
+		goto reset;
+
+	/* start the readout - sending this command twice */
+	/* presumably enables the high dynamic range mode */
+	result = ftip_command(dev, FTIP_RESET,   0, 0);
+	if (result < 0)
+		goto reset;
+	result = ftip_command(dev, FTIP_RESET,   0, 0);
+	if (result < 0)
+		goto reset;
+
+	/* loop over a blocking bulk read to get data from the device */
+	while (bytes_read < IMGSIZE) {
+		result = usb_bulk_msg (dev->udev,
+				usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
+				dev->bulk_in_buffer + bytes_read,
+				dev->bulk_in_size, &bulk_read, 5000);
+		if (result < 0) {
+			/* Maybe this error was caused by the increased packet size? */
+			/* Reset to the original value and tell userspace to retry.  */
+			if (dev->bulk_in_size != dev->orig_bi_size) {
+				dev->bulk_in_size = dev->orig_bi_size;
+				result = -EAGAIN;
+			}
+			break;
+		}
+		if (signal_pending(current)) {
+			result = -EINTR;
+			break;
+		}
+		bytes_read += bulk_read;
+	}
+
+	/* reset the device */
+reset:
+	ftip_command(dev, FTIP_RELEASE, 0, 0);
+
+	/* check for valid image */
+	/* right border should be black (0x00) */
+	for (bytes_read = sizeof(HEADER)-1 + WIDTH-1; bytes_read < IMGSIZE; bytes_read += WIDTH)
+		if (dev->bulk_in_buffer[bytes_read] != 0x00)
+			return -EAGAIN;
+
+	/* lower border should be white (0xFF) */
+	for (bytes_read = IMGSIZE-WIDTH; bytes_read < IMGSIZE-1; bytes_read++)
+		if (dev->bulk_in_buffer[bytes_read] != 0xFF)
+			return -EAGAIN;
+
+	/* should be IMGSIZE == 65040 */
+	dev_dbg(&dev->interface->dev, "read %d bytes fingerprint data\n",
+		bytes_read);
+	return result;
+}
+
+/* PM operations are nops as this driver does IO only during open() */
+static int idmouse_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	return 0;
+}
+
+static int idmouse_resume(struct usb_interface *intf)
+{
+	return 0;
+}
+
+static inline void idmouse_delete(struct usb_idmouse *dev)
+{
+	kfree(dev->bulk_in_buffer);
+	kfree(dev);
+}
+
+static int idmouse_open(struct inode *inode, struct file *file)
+{
+	struct usb_idmouse *dev;
+	struct usb_interface *interface;
+	int result;
+
+	/* get the interface from minor number and driver information */
+	interface = usb_find_interface (&idmouse_driver, iminor (inode));
+	if (!interface)
+		return -ENODEV;
+
+	mutex_lock(&open_disc_mutex);
+	/* get the device information block from the interface */
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		mutex_unlock(&open_disc_mutex);
+		return -ENODEV;
+	}
+
+	/* lock this device */
+	mutex_lock(&dev->lock);
+	mutex_unlock(&open_disc_mutex);
+
+	/* check if already open */
+	if (dev->open) {
+
+		/* already open, so fail */
+		result = -EBUSY;
+
+	} else {
+
+		/* create a new image and check for success */
+		result = usb_autopm_get_interface(interface);
+		if (result)
+			goto error;
+		result = idmouse_create_image (dev);
+		if (result)
+			goto error;
+		usb_autopm_put_interface(interface);
+
+		/* increment our usage count for the driver */
+		++dev->open;
+
+		/* save our object in the file's private structure */
+		file->private_data = dev;
+
+	} 
+
+error:
+
+	/* unlock this device */
+	mutex_unlock(&dev->lock);
+	return result;
+}
+
+static int idmouse_release(struct inode *inode, struct file *file)
+{
+	struct usb_idmouse *dev;
+
+	dev = file->private_data;
+
+	if (dev == NULL)
+		return -ENODEV;
+
+	mutex_lock(&open_disc_mutex);
+	/* lock our device */
+	mutex_lock(&dev->lock);
+
+	/* are we really open? */
+	if (dev->open <= 0) {
+		mutex_unlock(&dev->lock);
+		mutex_unlock(&open_disc_mutex);
+		return -ENODEV;
+	}
+
+	--dev->open;
+
+	if (!dev->present) {
+		/* the device was unplugged before the file was released */
+		mutex_unlock(&dev->lock);
+		mutex_unlock(&open_disc_mutex);
+		idmouse_delete(dev);
+	} else {
+		mutex_unlock(&dev->lock);
+		mutex_unlock(&open_disc_mutex);
+	}
+	return 0;
+}
+
+static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count,
+				loff_t * ppos)
+{
+	struct usb_idmouse *dev = file->private_data;
+	int result;
+
+	/* lock this object */
+	mutex_lock(&dev->lock);
+
+	/* verify that the device wasn't unplugged */
+	if (!dev->present) {
+		mutex_unlock(&dev->lock);
+		return -ENODEV;
+	}
+
+	result = simple_read_from_buffer(buffer, count, ppos,
+					dev->bulk_in_buffer, IMGSIZE);
+	/* unlock the device */
+	mutex_unlock(&dev->lock);
+	return result;
+}
+
+static int idmouse_probe(struct usb_interface *interface,
+				const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_idmouse *dev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int result;
+
+	/* check if we have gotten the data or the hid interface */
+	iface_desc = &interface->altsetting[0];
+	if (iface_desc->desc.bInterfaceClass != 0x0A)
+		return -ENODEV;
+
+	if (iface_desc->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+
+	mutex_init(&dev->lock);
+	dev->udev = udev;
+	dev->interface = interface;
+
+	/* set up the endpoint information - use only the first bulk-in endpoint */
+	endpoint = &iface_desc->endpoint[0].desc;
+	if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) {
+		/* we found a bulk in endpoint */
+		dev->orig_bi_size = usb_endpoint_maxp(endpoint);
+		dev->bulk_in_size = 0x200; /* works _much_ faster */
+		dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+		dev->bulk_in_buffer =
+			kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
+
+		if (!dev->bulk_in_buffer) {
+			dev_err(&interface->dev, "Unable to allocate input buffer.\n");
+			idmouse_delete(dev);
+			return -ENOMEM;
+		}
+	}
+
+	if (!(dev->bulk_in_endpointAddr)) {
+		dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n");
+		idmouse_delete(dev);
+		return -ENODEV;
+	}
+	/* allow device read, write and ioctl */
+	dev->present = 1;
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, dev);
+	result = usb_register_dev(interface, &idmouse_class);
+	if (result) {
+		/* something prevented us from registering this device */
+		dev_err(&interface->dev, "Unable to allocate minor number.\n");
+		usb_set_intfdata(interface, NULL);
+		idmouse_delete(dev);
+		return result;
+	}
+
+	/* be noisy */
+	dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
+
+	return 0;
+}
+
+static void idmouse_disconnect(struct usb_interface *interface)
+{
+	struct usb_idmouse *dev;
+
+	/* get device structure */
+	dev = usb_get_intfdata(interface);
+
+	/* give back our minor */
+	usb_deregister_dev(interface, &idmouse_class);
+
+	mutex_lock(&open_disc_mutex);
+	usb_set_intfdata(interface, NULL);
+	/* lock the device */
+	mutex_lock(&dev->lock);
+	mutex_unlock(&open_disc_mutex);
+
+	/* prevent device read, write and ioctl */
+	dev->present = 0;
+
+	/* if the device is opened, idmouse_release will clean this up */
+	if (!dev->open) {
+		mutex_unlock(&dev->lock);
+		idmouse_delete(dev);
+	} else {
+		/* unlock */
+		mutex_unlock(&dev->lock);
+	}
+
+	dev_info(&interface->dev, "disconnected\n");
+}
+
+module_usb_driver(idmouse_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
new file mode 100644
index 0000000..5e43fd8
--- /dev/null
+++ b/drivers/usb/misc/iowarrior.c
@@ -0,0 +1,940 @@
+/*
+ *  Native support for the I/O-Warrior USB devices
+ *
+ *  Copyright (c) 2003-2005  Code Mercenaries GmbH
+ *  written by Christian Lucht <lucht@codemercs.com>
+ *
+ *  based on
+
+ *  usb-skeleton.c by Greg Kroah-Hartman  <greg@kroah.com>
+ *  brlvger.c by Stephane Dalton  <sdalton@videotron.ca>
+ *           and St�hane Doyon   <s.doyon@videotron.ca>
+ *
+ *  Released under the GPLv2.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/usb/iowarrior.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v0.4.0"
+#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>"
+#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)"
+
+#define USB_VENDOR_ID_CODEMERCS		1984
+/* low speed iowarrior */
+#define USB_DEVICE_ID_CODEMERCS_IOW40	0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24	0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOWPV1	0x1511
+#define USB_DEVICE_ID_CODEMERCS_IOWPV2	0x1512
+/* full speed iowarrior */
+#define USB_DEVICE_ID_CODEMERCS_IOW56	0x1503
+
+/* Get a minor range for your devices from the usb maintainer */
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define IOWARRIOR_MINOR_BASE	0
+#else
+#define IOWARRIOR_MINOR_BASE	208	// SKELETON_MINOR_BASE 192 + 16, not official yet
+#endif
+
+/* interrupt input queue size */
+#define MAX_INTERRUPT_BUFFER 16
+/*
+   maximum number of urbs that are submitted for writes at the same time,
+   this applies to the IOWarrior56 only!
+   IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls.
+*/
+#define MAX_WRITES_IN_FLIGHT 4
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* Module parameters */
+static DEFINE_MUTEX(iowarrior_mutex);
+
+static struct usb_driver iowarrior_driver;
+static DEFINE_MUTEX(iowarrior_open_disc_lock);
+
+/*--------------*/
+/*     data     */
+/*--------------*/
+
+/* Structure to hold all of our device specific stuff */
+struct iowarrior {
+	struct mutex mutex;			/* locks this structure */
+	struct usb_device *udev;		/* save off the usb device pointer */
+	struct usb_interface *interface;	/* the interface for this device */
+	unsigned char minor;			/* the starting minor number for this device */
+	struct usb_endpoint_descriptor *int_out_endpoint;	/* endpoint for reading (needed for IOW56 only) */
+	struct usb_endpoint_descriptor *int_in_endpoint;	/* endpoint for reading */
+	struct urb *int_in_urb;		/* the urb for reading data */
+	unsigned char *int_in_buffer;	/* buffer for data to be read */
+	unsigned char serial_number;	/* to detect lost packages */
+	unsigned char *read_queue;	/* size is MAX_INTERRUPT_BUFFER * packet size */
+	wait_queue_head_t read_wait;
+	wait_queue_head_t write_wait;	/* wait-queue for writing to the device */
+	atomic_t write_busy;		/* number of write-urbs submitted */
+	atomic_t read_idx;
+	atomic_t intr_idx;
+	spinlock_t intr_idx_lock;	/* protects intr_idx */
+	atomic_t overflow_flag;		/* signals an index 'rollover' */
+	int present;			/* this is 1 as long as the device is connected */
+	int opened;			/* this is 1 if the device is currently open */
+	char chip_serial[9];		/* the serial number string of the chip connected */
+	int report_size;		/* number of bytes in a report */
+	u16 product_id;
+};
+
+/*--------------*/
+/*    globals   */
+/*--------------*/
+
+/*
+ *  USB spec identifies 5 second timeouts.
+ */
+#define GET_TIMEOUT 5
+#define USB_REQ_GET_REPORT  0x01
+//#if 0
+static int usb_get_report(struct usb_device *dev,
+			  struct usb_host_interface *inter, unsigned char type,
+			  unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			       USB_REQ_GET_REPORT,
+			       USB_DIR_IN | USB_TYPE_CLASS |
+			       USB_RECIP_INTERFACE, (type << 8) + id,
+			       inter->desc.bInterfaceNumber, buf, size,
+			       GET_TIMEOUT*HZ);
+}
+//#endif
+
+#define USB_REQ_SET_REPORT 0x09
+
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+			  unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(interface_to_usbdev(intf),
+			       usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+			       USB_REQ_SET_REPORT,
+			       USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			       (type << 8) + id,
+			       intf->cur_altsetting->desc.bInterfaceNumber, buf,
+			       size, HZ);
+}
+
+/*---------------------*/
+/* driver registration */
+/*---------------------*/
+/* table of devices that work with this driver */
+static const struct usb_device_id iowarrior_ids[] = {
+	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)},
+	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)},
+	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)},
+	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)},
+	{USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)},
+	{}			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, iowarrior_ids);
+
+/*
+ * USB callback handler for reading data
+ */
+static void iowarrior_callback(struct urb *urb)
+{
+	struct iowarrior *dev = urb->context;
+	int intr_idx;
+	int read_idx;
+	int aux_idx;
+	int offset;
+	int status = urb->status;
+	int retval;
+
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:
+		goto exit;
+	}
+
+	spin_lock(&dev->intr_idx_lock);
+	intr_idx = atomic_read(&dev->intr_idx);
+	/* aux_idx become previous intr_idx */
+	aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1);
+	read_idx = atomic_read(&dev->read_idx);
+
+	/* queue is not empty and it's interface 0 */
+	if ((intr_idx != read_idx)
+	    && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) {
+		/* + 1 for serial number */
+		offset = aux_idx * (dev->report_size + 1);
+		if (!memcmp
+		    (dev->read_queue + offset, urb->transfer_buffer,
+		     dev->report_size)) {
+			/* equal values on interface 0 will be ignored */
+			spin_unlock(&dev->intr_idx_lock);
+			goto exit;
+		}
+	}
+
+	/* aux_idx become next intr_idx */
+	aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1);
+	if (read_idx == aux_idx) {
+		/* queue full, dropping oldest input */
+		read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx;
+		atomic_set(&dev->read_idx, read_idx);
+		atomic_set(&dev->overflow_flag, 1);
+	}
+
+	/* +1 for serial number */
+	offset = intr_idx * (dev->report_size + 1);
+	memcpy(dev->read_queue + offset, urb->transfer_buffer,
+	       dev->report_size);
+	*(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++;
+
+	atomic_set(&dev->intr_idx, aux_idx);
+	spin_unlock(&dev->intr_idx_lock);
+	/* tell the blocking read about the new data */
+	wake_up_interruptible(&dev->read_wait);
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d\n",
+			__func__, retval);
+
+}
+
+/*
+ * USB Callback handler for write-ops
+ */
+static void iowarrior_write_callback(struct urb *urb)
+{
+	struct iowarrior *dev;
+	int status = urb->status;
+
+	dev = urb->context;
+	/* sync/async unlink faults aren't errors */
+	if (status &&
+	    !(status == -ENOENT ||
+	      status == -ECONNRESET || status == -ESHUTDOWN)) {
+		dev_dbg(&dev->interface->dev,
+			"nonzero write bulk status received: %d\n", status);
+	}
+	/* free up our allocated buffer */
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	/* tell a waiting writer the interrupt-out-pipe is available again */
+	atomic_dec(&dev->write_busy);
+	wake_up_interruptible(&dev->write_wait);
+}
+
+/**
+ *	iowarrior_delete
+ */
+static inline void iowarrior_delete(struct iowarrior *dev)
+{
+	dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
+	kfree(dev->int_in_buffer);
+	usb_free_urb(dev->int_in_urb);
+	kfree(dev->read_queue);
+	kfree(dev);
+}
+
+/*---------------------*/
+/* fops implementation */
+/*---------------------*/
+
+static int read_index(struct iowarrior *dev)
+{
+	int intr_idx, read_idx;
+
+	read_idx = atomic_read(&dev->read_idx);
+	intr_idx = atomic_read(&dev->intr_idx);
+
+	return (read_idx == intr_idx ? -1 : read_idx);
+}
+
+/**
+ *  iowarrior_read
+ */
+static ssize_t iowarrior_read(struct file *file, char __user *buffer,
+			      size_t count, loff_t *ppos)
+{
+	struct iowarrior *dev;
+	int read_idx;
+	int offset;
+
+	dev = file->private_data;
+
+	/* verify that the device wasn't unplugged */
+	if (dev == NULL || !dev->present)
+		return -ENODEV;
+
+	dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
+		dev->minor, count);
+
+	/* read count must be packet size (+ time stamp) */
+	if ((count != dev->report_size)
+	    && (count != (dev->report_size + 1)))
+		return -EINVAL;
+
+	/* repeat until no buffer overrun in callback handler occur */
+	do {
+		atomic_set(&dev->overflow_flag, 0);
+		if ((read_idx = read_index(dev)) == -1) {
+			/* queue empty */
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+			else {
+				//next line will return when there is either new data, or the device is unplugged
+				int r = wait_event_interruptible(dev->read_wait,
+								 (!dev->present
+								  || (read_idx =
+								      read_index
+								      (dev)) !=
+								  -1));
+				if (r) {
+					//we were interrupted by a signal
+					return -ERESTART;
+				}
+				if (!dev->present) {
+					//The device was unplugged
+					return -ENODEV;
+				}
+				if (read_idx == -1) {
+					// Can this happen ???
+					return 0;
+				}
+			}
+		}
+
+		offset = read_idx * (dev->report_size + 1);
+		if (copy_to_user(buffer, dev->read_queue + offset, count)) {
+			return -EFAULT;
+		}
+	} while (atomic_read(&dev->overflow_flag));
+
+	read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx;
+	atomic_set(&dev->read_idx, read_idx);
+	return count;
+}
+
+/*
+ * iowarrior_write
+ */
+static ssize_t iowarrior_write(struct file *file,
+			       const char __user *user_buffer,
+			       size_t count, loff_t *ppos)
+{
+	struct iowarrior *dev;
+	int retval = 0;
+	char *buf = NULL;	/* for IOW24 and IOW56 we need a buffer */
+	struct urb *int_out_urb = NULL;
+
+	dev = file->private_data;
+
+	mutex_lock(&dev->mutex);
+	/* verify that the device wasn't unplugged */
+	if (!dev->present) {
+		retval = -ENODEV;
+		goto exit;
+	}
+	dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
+		dev->minor, count);
+	/* if count is 0 we're already done */
+	if (count == 0) {
+		retval = 0;
+		goto exit;
+	}
+	/* We only accept full reports */
+	if (count != dev->report_size) {
+		retval = -EINVAL;
+		goto exit;
+	}
+	switch (dev->product_id) {
+	case USB_DEVICE_ID_CODEMERCS_IOW24:
+	case USB_DEVICE_ID_CODEMERCS_IOWPV1:
+	case USB_DEVICE_ID_CODEMERCS_IOWPV2:
+	case USB_DEVICE_ID_CODEMERCS_IOW40:
+		/* IOW24 and IOW40 use a synchronous call */
+		buf = kmalloc(count, GFP_KERNEL);
+		if (!buf) {
+			retval = -ENOMEM;
+			goto exit;
+		}
+		if (copy_from_user(buf, user_buffer, count)) {
+			retval = -EFAULT;
+			kfree(buf);
+			goto exit;
+		}
+		retval = usb_set_report(dev->interface, 2, 0, buf, count);
+		kfree(buf);
+		goto exit;
+		break;
+	case USB_DEVICE_ID_CODEMERCS_IOW56:
+		/* The IOW56 uses asynchronous IO and more urbs */
+		if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) {
+			/* Wait until we are below the limit for submitted urbs */
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				goto exit;
+			} else {
+				retval = wait_event_interruptible(dev->write_wait,
+								  (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT)));
+				if (retval) {
+					/* we were interrupted by a signal */
+					retval = -ERESTART;
+					goto exit;
+				}
+				if (!dev->present) {
+					/* The device was unplugged */
+					retval = -ENODEV;
+					goto exit;
+				}
+				if (!dev->opened) {
+					/* We were closed while waiting for an URB */
+					retval = -ENODEV;
+					goto exit;
+				}
+			}
+		}
+		atomic_inc(&dev->write_busy);
+		int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!int_out_urb) {
+			retval = -ENOMEM;
+			dev_dbg(&dev->interface->dev,
+				"Unable to allocate urb\n");
+			goto error_no_urb;
+		}
+		buf = usb_alloc_coherent(dev->udev, dev->report_size,
+					 GFP_KERNEL, &int_out_urb->transfer_dma);
+		if (!buf) {
+			retval = -ENOMEM;
+			dev_dbg(&dev->interface->dev,
+				"Unable to allocate buffer\n");
+			goto error_no_buffer;
+		}
+		usb_fill_int_urb(int_out_urb, dev->udev,
+				 usb_sndintpipe(dev->udev,
+						dev->int_out_endpoint->bEndpointAddress),
+				 buf, dev->report_size,
+				 iowarrior_write_callback, dev,
+				 dev->int_out_endpoint->bInterval);
+		int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		if (copy_from_user(buf, user_buffer, count)) {
+			retval = -EFAULT;
+			goto error;
+		}
+		retval = usb_submit_urb(int_out_urb, GFP_KERNEL);
+		if (retval) {
+			dev_dbg(&dev->interface->dev,
+				"submit error %d for urb nr.%d\n",
+				retval, atomic_read(&dev->write_busy));
+			goto error;
+		}
+		/* submit was ok */
+		retval = count;
+		usb_free_urb(int_out_urb);
+		goto exit;
+		break;
+	default:
+		/* what do we have here ? An unsupported Product-ID ? */
+		dev_err(&dev->interface->dev, "%s - not supported for product=0x%x\n",
+			__func__, dev->product_id);
+		retval = -EFAULT;
+		goto exit;
+		break;
+	}
+error:
+	usb_free_coherent(dev->udev, dev->report_size, buf,
+			  int_out_urb->transfer_dma);
+error_no_buffer:
+	usb_free_urb(int_out_urb);
+error_no_urb:
+	atomic_dec(&dev->write_busy);
+	wake_up_interruptible(&dev->write_wait);
+exit:
+	mutex_unlock(&dev->mutex);
+	return retval;
+}
+
+/**
+ *	iowarrior_ioctl
+ */
+static long iowarrior_ioctl(struct file *file, unsigned int cmd,
+							unsigned long arg)
+{
+	struct iowarrior *dev = NULL;
+	__u8 *buffer;
+	__u8 __user *user_buffer;
+	int retval;
+	int io_res;		/* checks for bytes read/written and copy_to/from_user results */
+
+	dev = file->private_data;
+	if (dev == NULL) {
+		return -ENODEV;
+	}
+
+	buffer = kzalloc(dev->report_size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* lock this object */
+	mutex_lock(&iowarrior_mutex);
+	mutex_lock(&dev->mutex);
+
+	/* verify that the device wasn't unplugged */
+	if (!dev->present) {
+		retval = -ENODEV;
+		goto error_out;
+	}
+
+	dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n",
+		dev->minor, cmd, arg);
+
+	retval = 0;
+	io_res = 0;
+	switch (cmd) {
+	case IOW_WRITE:
+		if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 ||
+		    dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 ||
+		    dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 ||
+		    dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) {
+			user_buffer = (__u8 __user *)arg;
+			io_res = copy_from_user(buffer, user_buffer,
+						dev->report_size);
+			if (io_res) {
+				retval = -EFAULT;
+			} else {
+				io_res = usb_set_report(dev->interface, 2, 0,
+							buffer,
+							dev->report_size);
+				if (io_res < 0)
+					retval = io_res;
+			}
+		} else {
+			retval = -EINVAL;
+			dev_err(&dev->interface->dev,
+				"ioctl 'IOW_WRITE' is not supported for product=0x%x.\n",
+				dev->product_id);
+		}
+		break;
+	case IOW_READ:
+		user_buffer = (__u8 __user *)arg;
+		io_res = usb_get_report(dev->udev,
+					dev->interface->cur_altsetting, 1, 0,
+					buffer, dev->report_size);
+		if (io_res < 0)
+			retval = io_res;
+		else {
+			io_res = copy_to_user(user_buffer, buffer, dev->report_size);
+			if (io_res)
+				retval = -EFAULT;
+		}
+		break;
+	case IOW_GETINFO:
+		{
+			/* Report available information for the device */
+			struct iowarrior_info info;
+			/* needed for power consumption */
+			struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc;
+
+			memset(&info, 0, sizeof(info));
+			/* directly from the descriptor */
+			info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+			info.product = dev->product_id;
+			info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice);
+
+			/* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */
+			info.speed = dev->udev->speed;
+			info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber;
+			info.report_size = dev->report_size;
+
+			/* serial number string has been read earlier 8 chars or empty string */
+			memcpy(info.serial, dev->chip_serial,
+			       sizeof(dev->chip_serial));
+			if (cfg_descriptor == NULL) {
+				info.power = -1;	/* no information available */
+			} else {
+				/* the MaxPower is stored in units of 2mA to make it fit into a byte-value */
+				info.power = cfg_descriptor->bMaxPower * 2;
+			}
+			io_res = copy_to_user((struct iowarrior_info __user *)arg, &info,
+					 sizeof(struct iowarrior_info));
+			if (io_res)
+				retval = -EFAULT;
+			break;
+		}
+	default:
+		/* return that we did not understand this ioctl call */
+		retval = -ENOTTY;
+		break;
+	}
+error_out:
+	/* unlock the device */
+	mutex_unlock(&dev->mutex);
+	mutex_unlock(&iowarrior_mutex);
+	kfree(buffer);
+	return retval;
+}
+
+/**
+ *	iowarrior_open
+ */
+static int iowarrior_open(struct inode *inode, struct file *file)
+{
+	struct iowarrior *dev = NULL;
+	struct usb_interface *interface;
+	int subminor;
+	int retval = 0;
+
+	mutex_lock(&iowarrior_mutex);
+	subminor = iminor(inode);
+
+	interface = usb_find_interface(&iowarrior_driver, subminor);
+	if (!interface) {
+		mutex_unlock(&iowarrior_mutex);
+		printk(KERN_ERR "%s - error, can't find device for minor %d\n",
+		       __func__, subminor);
+		return -ENODEV;
+	}
+
+	mutex_lock(&iowarrior_open_disc_lock);
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		mutex_unlock(&iowarrior_open_disc_lock);
+		mutex_unlock(&iowarrior_mutex);
+		return -ENODEV;
+	}
+
+	mutex_lock(&dev->mutex);
+	mutex_unlock(&iowarrior_open_disc_lock);
+
+	/* Only one process can open each device, no sharing. */
+	if (dev->opened) {
+		retval = -EBUSY;
+		goto out;
+	}
+
+	/* setup interrupt handler for receiving values */
+	if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) {
+		dev_err(&interface->dev, "Error %d while submitting URB\n", retval);
+		retval = -EFAULT;
+		goto out;
+	}
+	/* increment our usage count for the driver */
+	++dev->opened;
+	/* save our object in the file's private structure */
+	file->private_data = dev;
+	retval = 0;
+
+out:
+	mutex_unlock(&dev->mutex);
+	mutex_unlock(&iowarrior_mutex);
+	return retval;
+}
+
+/**
+ *	iowarrior_release
+ */
+static int iowarrior_release(struct inode *inode, struct file *file)
+{
+	struct iowarrior *dev;
+	int retval = 0;
+
+	dev = file->private_data;
+	if (dev == NULL) {
+		return -ENODEV;
+	}
+
+	dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
+
+	/* lock our device */
+	mutex_lock(&dev->mutex);
+
+	if (dev->opened <= 0) {
+		retval = -ENODEV;	/* close called more than once */
+		mutex_unlock(&dev->mutex);
+	} else {
+		dev->opened = 0;	/* we're closing now */
+		retval = 0;
+		if (dev->present) {
+			/*
+			   The device is still connected so we only shutdown
+			   pending read-/write-ops.
+			 */
+			usb_kill_urb(dev->int_in_urb);
+			wake_up_interruptible(&dev->read_wait);
+			wake_up_interruptible(&dev->write_wait);
+			mutex_unlock(&dev->mutex);
+		} else {
+			/* The device was unplugged, cleanup resources */
+			mutex_unlock(&dev->mutex);
+			iowarrior_delete(dev);
+		}
+	}
+	return retval;
+}
+
+static unsigned iowarrior_poll(struct file *file, poll_table * wait)
+{
+	struct iowarrior *dev = file->private_data;
+	unsigned int mask = 0;
+
+	if (!dev->present)
+		return POLLERR | POLLHUP;
+
+	poll_wait(file, &dev->read_wait, wait);
+	poll_wait(file, &dev->write_wait, wait);
+
+	if (!dev->present)
+		return POLLERR | POLLHUP;
+
+	if (read_index(dev) != -1)
+		mask |= POLLIN | POLLRDNORM;
+
+	if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT)
+		mask |= POLLOUT | POLLWRNORM;
+	return mask;
+}
+
+/*
+ * File operations needed when we register this driver.
+ * This assumes that this driver NEEDS file operations,
+ * of course, which means that the driver is expected
+ * to have a node in the /dev directory. If the USB
+ * device were for a network interface then the driver
+ * would use "struct net_driver" instead, and a serial
+ * device would use "struct tty_driver".
+ */
+static const struct file_operations iowarrior_fops = {
+	.owner = THIS_MODULE,
+	.write = iowarrior_write,
+	.read = iowarrior_read,
+	.unlocked_ioctl = iowarrior_ioctl,
+	.open = iowarrior_open,
+	.release = iowarrior_release,
+	.poll = iowarrior_poll,
+	.llseek = noop_llseek,
+};
+
+static char *iowarrior_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
+}
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver iowarrior_class = {
+	.name = "iowarrior%d",
+	.devnode = iowarrior_devnode,
+	.fops = &iowarrior_fops,
+	.minor_base = IOWARRIOR_MINOR_BASE,
+};
+
+/*---------------------------------*/
+/*  probe and disconnect functions */
+/*---------------------------------*/
+/**
+ *	iowarrior_probe
+ *
+ *	Called by the usb core when a new device is connected that it thinks
+ *	this driver might be interested in.
+ */
+static int iowarrior_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct iowarrior *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int i;
+	int retval = -ENOMEM;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&interface->dev, "Out of memory\n");
+		return retval;
+	}
+
+	mutex_init(&dev->mutex);
+
+	atomic_set(&dev->intr_idx, 0);
+	atomic_set(&dev->read_idx, 0);
+	spin_lock_init(&dev->intr_idx_lock);
+	atomic_set(&dev->overflow_flag, 0);
+	init_waitqueue_head(&dev->read_wait);
+	atomic_set(&dev->write_busy, 0);
+	init_waitqueue_head(&dev->write_wait);
+
+	dev->udev = udev;
+	dev->interface = interface;
+
+	iface_desc = interface->cur_altsetting;
+	dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
+
+	/* set up the endpoint information */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(endpoint))
+			dev->int_in_endpoint = endpoint;
+		if (usb_endpoint_is_int_out(endpoint))
+			/* this one will match for the IOWarrior56 only */
+			dev->int_out_endpoint = endpoint;
+	}
+
+	if (!dev->int_in_endpoint) {
+		dev_err(&interface->dev, "no interrupt-in endpoint found\n");
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) {
+		if (!dev->int_out_endpoint) {
+			dev_err(&interface->dev, "no interrupt-out endpoint found\n");
+			retval = -ENODEV;
+			goto error;
+		}
+	}
+
+	/* we have to check the report_size often, so remember it in the endianness suitable for our machine */
+	dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
+	if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
+	    (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56))
+		/* IOWarrior56 has wMaxPacketSize different from report size */
+		dev->report_size = 7;
+
+	/* create the urb and buffer for reading */
+	dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->int_in_urb) {
+		dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+		goto error;
+	}
+	dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
+	if (!dev->int_in_buffer) {
+		dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n");
+		goto error;
+	}
+	usb_fill_int_urb(dev->int_in_urb, dev->udev,
+			 usb_rcvintpipe(dev->udev,
+					dev->int_in_endpoint->bEndpointAddress),
+			 dev->int_in_buffer, dev->report_size,
+			 iowarrior_callback, dev,
+			 dev->int_in_endpoint->bInterval);
+	/* create an internal buffer for interrupt data from the device */
+	dev->read_queue =
+	    kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
+		    GFP_KERNEL);
+	if (!dev->read_queue) {
+		dev_err(&interface->dev, "Couldn't allocate read_queue\n");
+		goto error;
+	}
+	/* Get the serial-number of the chip */
+	memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
+	usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
+		   sizeof(dev->chip_serial));
+	if (strlen(dev->chip_serial) != 8)
+		memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
+
+	/* Set the idle timeout to 0, if this is interface 0 */
+	if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
+	    usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			    0x0A,
+			    USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+			    0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+	}
+	/* allow device read and ioctl */
+	dev->present = 1;
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(interface, dev);
+
+	retval = usb_register_dev(interface, &iowarrior_class);
+	if (retval) {
+		/* something prevented us from registering this driver */
+		dev_err(&interface->dev, "Not able to get a minor for this device.\n");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	dev->minor = interface->minor;
+
+	/* let the user know what node this device is now attached to */
+	dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d "
+		 "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial,
+		 iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE);
+	return retval;
+
+error:
+	iowarrior_delete(dev);
+	return retval;
+}
+
+/**
+ *	iowarrior_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ */
+static void iowarrior_disconnect(struct usb_interface *interface)
+{
+	struct iowarrior *dev;
+	int minor;
+
+	dev = usb_get_intfdata(interface);
+	mutex_lock(&iowarrior_open_disc_lock);
+	usb_set_intfdata(interface, NULL);
+
+	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;
+
+	mutex_unlock(&dev->mutex);
+	mutex_unlock(&iowarrior_open_disc_lock);
+
+	if (dev->opened) {
+		/* There is a process that holds a filedescriptor to the device ,
+		   so we only shutdown read-/write-ops going on.
+		   Deleting the device is postponed until close() was called.
+		 */
+		usb_kill_urb(dev->int_in_urb);
+		wake_up_interruptible(&dev->read_wait);
+		wake_up_interruptible(&dev->write_wait);
+	} else {
+		/* no process is using the device, cleanup now */
+		iowarrior_delete(dev);
+	}
+
+	dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
+		 minor - IOWARRIOR_MINOR_BASE);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver iowarrior_driver = {
+	.name = "iowarrior",
+	.probe = iowarrior_probe,
+	.disconnect = iowarrior_disconnect,
+	.id_table = iowarrior_ids,
+};
+
+module_usb_driver(iowarrior_driver);
diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c
new file mode 100644
index 0000000..1c61830
--- /dev/null
+++ b/drivers/usb/misc/isight_firmware.c
@@ -0,0 +1,134 @@
+/*
+ * Driver for loading USB isight firmware
+ *
+ * Copyright (C) 2008 Matthew Garrett <mjg@redhat.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.
+ *
+ * The USB isight cameras in recent Apples are roughly compatible with the USB
+ * video class specification, and can be driven by uvcvideo. However, they
+ * need firmware to be loaded beforehand. After firmware loading, the device
+ * detaches from the USB bus and reattaches with a new device ID. It can then
+ * be claimed by the uvc driver.
+ *
+ * The firmware is non-free and must be extracted by the user. Tools to do this
+ * are available at http://bersace03.free.fr/ift/
+ *
+ * The isight firmware loading was reverse engineered by Johannes Berg
+ * <johannes@sipsolutions.de>, and this driver is based on code by Ronald
+ * Bultje <rbultje@ronald.bitfreak.net>
+ */
+
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+static const struct usb_device_id id_table[] = {
+	{USB_DEVICE(0x05ac, 0x8300)},
+	{},
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int isight_firmware_load(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	int llen, len, req, ret = 0;
+	const struct firmware *firmware;
+	unsigned char *buf = kmalloc(50, GFP_KERNEL);
+	unsigned char data[4];
+	const u8 *ptr;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (request_firmware(&firmware, "isight.fw", &dev->dev) != 0) {
+		printk(KERN_ERR "Unable to load isight firmware\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	ptr = firmware->data;
+
+	buf[0] = 0x01;
+	if (usb_control_msg
+	    (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, buf, 1,
+	     300) != 1) {
+		printk(KERN_ERR
+		       "Failed to initialise isight firmware loader\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	while (ptr+4 <= firmware->data+firmware->size) {
+		memcpy(data, ptr, 4);
+		len = (data[0] << 8 | data[1]);
+		req = (data[2] << 8 | data[3]);
+		ptr += 4;
+
+		if (len == 0x8001)
+			break;	/* success */
+		else if (len == 0)
+			continue;
+
+		for (; len > 0; req += 50) {
+			llen = min(len, 50);
+			len -= llen;
+			if (ptr+llen > firmware->data+firmware->size) {
+				printk(KERN_ERR
+				       "Malformed isight firmware");
+				ret = -ENODEV;
+				goto out;
+			}
+			memcpy(buf, ptr, llen);
+
+			ptr += llen;
+
+			if (usb_control_msg
+			    (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, req, 0,
+			     buf, llen, 300) != llen) {
+				printk(KERN_ERR
+				       "Failed to load isight firmware\n");
+				ret = -ENODEV;
+				goto out;
+			}
+
+		}
+	}
+
+	buf[0] = 0x00;
+	if (usb_control_msg
+	    (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, buf, 1,
+	     300) != 1) {
+		printk(KERN_ERR "isight firmware loading completion failed\n");
+		ret = -ENODEV;
+	}
+
+out:
+	kfree(buf);
+	release_firmware(firmware);
+	return ret;
+}
+
+MODULE_FIRMWARE("isight.fw");
+
+static void isight_firmware_disconnect(struct usb_interface *intf)
+{
+}
+
+static struct usb_driver isight_firmware_driver = {
+	.name = "isight_firmware",
+	.probe = isight_firmware_load,
+	.disconnect = isight_firmware_disconnect,
+	.id_table = id_table,
+};
+
+module_usb_driver(isight_firmware_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
new file mode 100644
index 0000000..cce22ff
--- /dev/null
+++ b/drivers/usb/misc/ldusb.c
@@ -0,0 +1,806 @@
+/**
+ * Generic USB driver for report based interrupt in/out devices
+ * like LD Didactic's USB devices. LD Didactic's USB devices are
+ * HID devices which do not use HID report definitons (they use
+ * raw interrupt in and our reports only for communication).
+ *
+ * This driver uses a ring buffer for time critical reading of
+ * interrupt in reports and provides read and write methods for
+ * raw interrupt reports (similar to the Windows HID driver).
+ * Devices based on the book USB COMPLETE by Jan Axelson may need
+ * such a compatibility to the Windows HID driver.
+ *
+ * Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de>
+ *
+ *	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.
+ *
+ * Derived from Lego USB Tower driver
+ * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net>
+ *		 2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+
+/* Define these values to match your devices */
+#define USB_VENDOR_ID_LD		0x0f11	/* USB Vendor ID of LD Didactic GmbH */
+#define USB_DEVICE_ID_LD_CASSY		0x1000	/* USB Product ID of CASSY-S modules with 8 bytes endpoint size */
+#define USB_DEVICE_ID_LD_CASSY2		0x1001	/* USB Product ID of CASSY-S modules with 64 bytes endpoint size */
+#define USB_DEVICE_ID_LD_POCKETCASSY	0x1010	/* USB Product ID of Pocket-CASSY */
+#define USB_DEVICE_ID_LD_POCKETCASSY2	0x1011	/* USB Product ID of Pocket-CASSY 2 (reserved) */
+#define USB_DEVICE_ID_LD_MOBILECASSY	0x1020	/* USB Product ID of Mobile-CASSY */
+#define USB_DEVICE_ID_LD_MOBILECASSY2	0x1021	/* USB Product ID of Mobile-CASSY 2 (reserved) */
+#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE	0x1031	/* USB Product ID of Micro-CASSY Voltage */
+#define USB_DEVICE_ID_LD_MICROCASSYCURRENT	0x1032	/* USB Product ID of Micro-CASSY Current */
+#define USB_DEVICE_ID_LD_MICROCASSYTIME		0x1033	/* USB Product ID of Micro-CASSY Time (reserved) */
+#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE	0x1035	/* USB Product ID of Micro-CASSY Temperature */
+#define USB_DEVICE_ID_LD_MICROCASSYPH		0x1038	/* USB Product ID of Micro-CASSY pH */
+#define USB_DEVICE_ID_LD_JWM		0x1080	/* USB Product ID of Joule and Wattmeter */
+#define USB_DEVICE_ID_LD_DMMP		0x1081	/* USB Product ID of Digital Multimeter P (reserved) */
+#define USB_DEVICE_ID_LD_UMIP		0x1090	/* USB Product ID of UMI P */
+#define USB_DEVICE_ID_LD_UMIC		0x10A0	/* USB Product ID of UMI C */
+#define USB_DEVICE_ID_LD_UMIB		0x10B0	/* USB Product ID of UMI B */
+#define USB_DEVICE_ID_LD_XRAY		0x1100	/* USB Product ID of X-Ray Apparatus 55481 */
+#define USB_DEVICE_ID_LD_XRAY2		0x1101	/* USB Product ID of X-Ray Apparatus 554800 */
+#define USB_DEVICE_ID_LD_XRAYCT		0x1110	/* USB Product ID of X-Ray Apparatus CT 554821*/
+#define USB_DEVICE_ID_LD_VIDEOCOM	0x1200	/* USB Product ID of VideoCom */
+#define USB_DEVICE_ID_LD_MOTOR		0x1210	/* USB Product ID of Motor (reserved) */
+#define USB_DEVICE_ID_LD_COM3LAB	0x2000	/* USB Product ID of COM3LAB */
+#define USB_DEVICE_ID_LD_TELEPORT	0x2010	/* USB Product ID of Terminal Adapter */
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020	/* USB Product ID of Network Analyser */
+#define USB_DEVICE_ID_LD_POWERCONTROL	0x2030	/* USB Product ID of Converter Control Unit */
+#define USB_DEVICE_ID_LD_MACHINETEST	0x2040	/* USB Product ID of Machine Test System */
+#define USB_DEVICE_ID_LD_MOSTANALYSER	0x2050	/* USB Product ID of MOST Protocol Analyser */
+#define USB_DEVICE_ID_LD_MOSTANALYSER2	0x2051	/* USB Product ID of MOST Protocol Analyser 2 */
+#define USB_DEVICE_ID_LD_ABSESP		0x2060	/* USB Product ID of ABS ESP */
+#define USB_DEVICE_ID_LD_AUTODATABUS	0x2070	/* USB Product ID of Automotive Data Buses */
+#define USB_DEVICE_ID_LD_MCT		0x2080	/* USB Product ID of Microcontroller technique */
+#define USB_DEVICE_ID_LD_HYBRID		0x2090	/* USB Product ID of Automotive Hybrid */
+#define USB_DEVICE_ID_LD_HEATCONTROL	0x20A0	/* USB Product ID of Heat control */
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define USB_LD_MINOR_BASE	0
+#else
+#define USB_LD_MINOR_BASE	176
+#endif
+
+/* table of devices that work with this driver */
+static const struct usb_device_id ld_usb_table[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) },
+	{ USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, ld_usb_table);
+MODULE_VERSION("V0.14");
+MODULE_AUTHOR("Michael Hund <mhund@ld-didactic.de>");
+MODULE_DESCRIPTION("LD USB Driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("LD USB Devices");
+
+/* All interrupt in transfers are collected in a ring buffer to
+ * avoid racing conditions and get better performance of the driver.
+ */
+static int ring_buffer_size = 128;
+module_param(ring_buffer_size, int, 0);
+MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size in reports");
+
+/* The write_buffer can contain more than one interrupt out transfer.
+ */
+static int write_buffer_size = 10;
+module_param(write_buffer_size, int, 0);
+MODULE_PARM_DESC(write_buffer_size, "Write buffer size in reports");
+
+/* As of kernel version 2.6.4 ehci-hcd uses an
+ * "only one interrupt transfer per frame" shortcut
+ * to simplify the scheduling of periodic transfers.
+ * This conflicts with our standard 1ms intervals for in and out URBs.
+ * We use default intervals of 2ms for in and 2ms for out transfers,
+ * which should be fast enough.
+ * Increase the interval to allow more devices that do interrupt transfers,
+ * or set to 1 to use the standard interval from the endpoint descriptors.
+ */
+static int min_interrupt_in_interval = 2;
+module_param(min_interrupt_in_interval, int, 0);
+MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms");
+
+static int min_interrupt_out_interval = 2;
+module_param(min_interrupt_out_interval, int, 0);
+MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms");
+
+/* Structure to hold all of our device specific stuff */
+struct ld_usb {
+	struct mutex		mutex;		/* locks this structure */
+	struct usb_interface*	intf;		/* save off the usb interface pointer */
+
+	int			open_count;	/* number of times this port has been opened */
+
+	char*			ring_buffer;
+	unsigned int		ring_head;
+	unsigned int		ring_tail;
+
+	wait_queue_head_t	read_wait;
+	wait_queue_head_t	write_wait;
+
+	char*			interrupt_in_buffer;
+	struct usb_endpoint_descriptor* interrupt_in_endpoint;
+	struct urb*		interrupt_in_urb;
+	int			interrupt_in_interval;
+	size_t			interrupt_in_endpoint_size;
+	int			interrupt_in_running;
+	int			interrupt_in_done;
+	int			buffer_overflow;
+	spinlock_t		rbsl;
+
+	char*			interrupt_out_buffer;
+	struct usb_endpoint_descriptor* interrupt_out_endpoint;
+	struct urb*		interrupt_out_urb;
+	int			interrupt_out_interval;
+	size_t			interrupt_out_endpoint_size;
+	int			interrupt_out_busy;
+};
+
+static struct usb_driver ld_usb_driver;
+
+/**
+ *	ld_usb_abort_transfers
+ *      aborts transfers and frees associated data structures
+ */
+static void ld_usb_abort_transfers(struct ld_usb *dev)
+{
+	/* shutdown transfer */
+	if (dev->interrupt_in_running) {
+		dev->interrupt_in_running = 0;
+		if (dev->intf)
+			usb_kill_urb(dev->interrupt_in_urb);
+	}
+	if (dev->interrupt_out_busy)
+		if (dev->intf)
+			usb_kill_urb(dev->interrupt_out_urb);
+}
+
+/**
+ *	ld_usb_delete
+ */
+static void ld_usb_delete(struct ld_usb *dev)
+{
+	ld_usb_abort_transfers(dev);
+
+	/* free data structures */
+	usb_free_urb(dev->interrupt_in_urb);
+	usb_free_urb(dev->interrupt_out_urb);
+	kfree(dev->ring_buffer);
+	kfree(dev->interrupt_in_buffer);
+	kfree(dev->interrupt_out_buffer);
+	kfree(dev);
+}
+
+/**
+ *	ld_usb_interrupt_in_callback
+ */
+static void ld_usb_interrupt_in_callback(struct urb *urb)
+{
+	struct ld_usb *dev = urb->context;
+	size_t *actual_buffer;
+	unsigned int next_ring_head;
+	int status = urb->status;
+	int retval;
+
+	if (status) {
+		if (status == -ENOENT ||
+		    status == -ECONNRESET ||
+		    status == -ESHUTDOWN) {
+			goto exit;
+		} else {
+			dev_dbg(&dev->intf->dev,
+				"%s: nonzero status received: %d\n", __func__,
+				status);
+			spin_lock(&dev->rbsl);
+			goto resubmit; /* maybe we can recover */
+		}
+	}
+
+	spin_lock(&dev->rbsl);
+	if (urb->actual_length > 0) {
+		next_ring_head = (dev->ring_head+1) % ring_buffer_size;
+		if (next_ring_head != dev->ring_tail) {
+			actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_head*(sizeof(size_t)+dev->interrupt_in_endpoint_size));
+			/* actual_buffer gets urb->actual_length + interrupt_in_buffer */
+			*actual_buffer = urb->actual_length;
+			memcpy(actual_buffer+1, dev->interrupt_in_buffer, urb->actual_length);
+			dev->ring_head = next_ring_head;
+			dev_dbg(&dev->intf->dev, "%s: received %d bytes\n",
+				__func__, urb->actual_length);
+		} else {
+			dev_warn(&dev->intf->dev,
+				 "Ring buffer overflow, %d bytes dropped\n",
+				 urb->actual_length);
+			dev->buffer_overflow = 1;
+		}
+	}
+
+resubmit:
+	/* resubmit if we're still running */
+	if (dev->interrupt_in_running && !dev->buffer_overflow && dev->intf) {
+		retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
+		if (retval) {
+			dev_err(&dev->intf->dev,
+				"usb_submit_urb failed (%d)\n", retval);
+			dev->buffer_overflow = 1;
+		}
+	}
+	spin_unlock(&dev->rbsl);
+exit:
+	dev->interrupt_in_done = 1;
+	wake_up_interruptible(&dev->read_wait);
+}
+
+/**
+ *	ld_usb_interrupt_out_callback
+ */
+static void ld_usb_interrupt_out_callback(struct urb *urb)
+{
+	struct ld_usb *dev = urb->context;
+	int status = urb->status;
+
+	/* sync/async unlink faults aren't errors */
+	if (status && !(status == -ENOENT ||
+			status == -ECONNRESET ||
+			status == -ESHUTDOWN))
+		dev_dbg(&dev->intf->dev,
+			"%s - nonzero write interrupt status received: %d\n",
+			__func__, status);
+
+	dev->interrupt_out_busy = 0;
+	wake_up_interruptible(&dev->write_wait);
+}
+
+/**
+ *	ld_usb_open
+ */
+static int ld_usb_open(struct inode *inode, struct file *file)
+{
+	struct ld_usb *dev;
+	int subminor;
+	int retval;
+	struct usb_interface *interface;
+
+	nonseekable_open(inode, file);
+	subminor = iminor(inode);
+
+	interface = usb_find_interface(&ld_usb_driver, subminor);
+
+	if (!interface) {
+		printk(KERN_ERR "%s - error, can't find device for minor %d\n",
+		       __func__, subminor);
+		return -ENODEV;
+	}
+
+	dev = usb_get_intfdata(interface);
+
+	if (!dev)
+		return -ENODEV;
+
+	/* lock this device */
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
+	/* allow opening only once */
+	if (dev->open_count) {
+		retval = -EBUSY;
+		goto unlock_exit;
+	}
+	dev->open_count = 1;
+
+	/* initialize in direction */
+	dev->ring_head = 0;
+	dev->ring_tail = 0;
+	dev->buffer_overflow = 0;
+	usb_fill_int_urb(dev->interrupt_in_urb,
+			 interface_to_usbdev(interface),
+			 usb_rcvintpipe(interface_to_usbdev(interface),
+					dev->interrupt_in_endpoint->bEndpointAddress),
+			 dev->interrupt_in_buffer,
+			 dev->interrupt_in_endpoint_size,
+			 ld_usb_interrupt_in_callback,
+			 dev,
+			 dev->interrupt_in_interval);
+
+	dev->interrupt_in_running = 1;
+	dev->interrupt_in_done = 0;
+
+	retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval);
+		dev->interrupt_in_running = 0;
+		dev->open_count = 0;
+		goto unlock_exit;
+	}
+
+	/* save device in the file's private structure */
+	file->private_data = dev;
+
+unlock_exit:
+	mutex_unlock(&dev->mutex);
+
+	return retval;
+}
+
+/**
+ *	ld_usb_release
+ */
+static int ld_usb_release(struct inode *inode, struct file *file)
+{
+	struct ld_usb *dev;
+	int retval = 0;
+
+	dev = file->private_data;
+
+	if (dev == NULL) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+	if (dev->open_count != 1) {
+		retval = -ENODEV;
+		goto unlock_exit;
+	}
+	if (dev->intf == NULL) {
+		/* the device was unplugged before the file was released */
+		mutex_unlock(&dev->mutex);
+		/* unlock here as ld_usb_delete frees dev */
+		ld_usb_delete(dev);
+		goto exit;
+	}
+
+	/* wait until write transfer is finished */
+	if (dev->interrupt_out_busy)
+		wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ);
+	ld_usb_abort_transfers(dev);
+	dev->open_count = 0;
+
+unlock_exit:
+	mutex_unlock(&dev->mutex);
+
+exit:
+	return retval;
+}
+
+/**
+ *	ld_usb_poll
+ */
+static unsigned int ld_usb_poll(struct file *file, poll_table *wait)
+{
+	struct ld_usb *dev;
+	unsigned int mask = 0;
+
+	dev = file->private_data;
+
+	if (!dev->intf)
+		return POLLERR | POLLHUP;
+
+	poll_wait(file, &dev->read_wait, wait);
+	poll_wait(file, &dev->write_wait, wait);
+
+	if (dev->ring_head != dev->ring_tail)
+		mask |= POLLIN | POLLRDNORM;
+	if (!dev->interrupt_out_busy)
+		mask |= POLLOUT | POLLWRNORM;
+
+	return mask;
+}
+
+/**
+ *	ld_usb_read
+ */
+static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
+			   loff_t *ppos)
+{
+	struct ld_usb *dev;
+	size_t *actual_buffer;
+	size_t bytes_to_read;
+	int retval = 0;
+	int rv;
+
+	dev = file->private_data;
+
+	/* verify that we actually have some data to read */
+	if (count == 0)
+		goto exit;
+
+	/* lock this object */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+	/* verify that the device wasn't unplugged */
+	if (dev->intf == NULL) {
+		retval = -ENODEV;
+		printk(KERN_ERR "ldusb: No device or device unplugged %d\n", retval);
+		goto unlock_exit;
+	}
+
+	/* wait for data */
+	spin_lock_irq(&dev->rbsl);
+	if (dev->ring_head == dev->ring_tail) {
+		dev->interrupt_in_done = 0;
+		spin_unlock_irq(&dev->rbsl);
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto unlock_exit;
+		}
+		retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done);
+		if (retval < 0)
+			goto unlock_exit;
+	} else {
+		spin_unlock_irq(&dev->rbsl);
+	}
+
+	/* actual_buffer contains actual_length + interrupt_in_buffer */
+	actual_buffer = (size_t*)(dev->ring_buffer + dev->ring_tail*(sizeof(size_t)+dev->interrupt_in_endpoint_size));
+	bytes_to_read = min(count, *actual_buffer);
+	if (bytes_to_read < *actual_buffer)
+		dev_warn(&dev->intf->dev, "Read buffer overflow, %zd bytes dropped\n",
+			 *actual_buffer-bytes_to_read);
+
+	/* copy one interrupt_in_buffer from ring_buffer into userspace */
+	if (copy_to_user(buffer, actual_buffer+1, bytes_to_read)) {
+		retval = -EFAULT;
+		goto unlock_exit;
+	}
+	dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size;
+
+	retval = bytes_to_read;
+
+	spin_lock_irq(&dev->rbsl);
+	if (dev->buffer_overflow) {
+		dev->buffer_overflow = 0;
+		spin_unlock_irq(&dev->rbsl);
+		rv = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+		if (rv < 0)
+			dev->buffer_overflow = 1;
+	} else {
+		spin_unlock_irq(&dev->rbsl);
+	}
+
+unlock_exit:
+	/* unlock the device */
+	mutex_unlock(&dev->mutex);
+
+exit:
+	return retval;
+}
+
+/**
+ *	ld_usb_write
+ */
+static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
+			    size_t count, loff_t *ppos)
+{
+	struct ld_usb *dev;
+	size_t bytes_to_write;
+	int retval = 0;
+
+	dev = file->private_data;
+
+	/* verify that we actually have some data to write */
+	if (count == 0)
+		goto exit;
+
+	/* lock this object */
+	if (mutex_lock_interruptible(&dev->mutex)) {
+		retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+	/* verify that the device wasn't unplugged */
+	if (dev->intf == NULL) {
+		retval = -ENODEV;
+		printk(KERN_ERR "ldusb: No device or device unplugged %d\n", retval);
+		goto unlock_exit;
+	}
+
+	/* wait until previous transfer is finished */
+	if (dev->interrupt_out_busy) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto unlock_exit;
+		}
+		retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy);
+		if (retval < 0) {
+			goto unlock_exit;
+		}
+	}
+
+	/* write the data into interrupt_out_buffer from userspace */
+	bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size);
+	if (bytes_to_write < count)
+		dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write);
+	dev_dbg(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n",
+		__func__, count, bytes_to_write);
+
+	if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
+		retval = -EFAULT;
+		goto unlock_exit;
+	}
+
+	if (dev->interrupt_out_endpoint == NULL) {
+		/* try HID_REQ_SET_REPORT=9 on control_endpoint instead of interrupt_out_endpoint */
+		retval = usb_control_msg(interface_to_usbdev(dev->intf),
+					 usb_sndctrlpipe(interface_to_usbdev(dev->intf), 0),
+					 9,
+					 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+					 1 << 8, 0,
+					 dev->interrupt_out_buffer,
+					 bytes_to_write,
+					 USB_CTRL_SET_TIMEOUT * HZ);
+		if (retval < 0)
+			dev_err(&dev->intf->dev,
+				"Couldn't submit HID_REQ_SET_REPORT %d\n",
+				retval);
+		goto unlock_exit;
+	}
+
+	/* send off the urb */
+	usb_fill_int_urb(dev->interrupt_out_urb,
+			 interface_to_usbdev(dev->intf),
+			 usb_sndintpipe(interface_to_usbdev(dev->intf),
+					dev->interrupt_out_endpoint->bEndpointAddress),
+			 dev->interrupt_out_buffer,
+			 bytes_to_write,
+			 ld_usb_interrupt_out_callback,
+			 dev,
+			 dev->interrupt_out_interval);
+
+	dev->interrupt_out_busy = 1;
+	wmb();
+
+	retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
+	if (retval) {
+		dev->interrupt_out_busy = 0;
+		dev_err(&dev->intf->dev,
+			"Couldn't submit interrupt_out_urb %d\n", retval);
+		goto unlock_exit;
+	}
+	retval = bytes_to_write;
+
+unlock_exit:
+	/* unlock the device */
+	mutex_unlock(&dev->mutex);
+
+exit:
+	return retval;
+}
+
+/* file operations needed when we register this driver */
+static const struct file_operations ld_usb_fops = {
+	.owner =	THIS_MODULE,
+	.read  =	ld_usb_read,
+	.write =	ld_usb_write,
+	.open =		ld_usb_open,
+	.release =	ld_usb_release,
+	.poll =		ld_usb_poll,
+	.llseek =	no_llseek,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver ld_usb_class = {
+	.name =		"ldusb%d",
+	.fops =		&ld_usb_fops,
+	.minor_base =	USB_LD_MINOR_BASE,
+};
+
+/**
+ *	ld_usb_probe
+ *
+ *	Called by the usb core when a new device is connected that it thinks
+ *	this driver might be interested in.
+ */
+static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct ld_usb *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	char *buffer;
+	int i;
+	int retval = -ENOMEM;
+
+	/* allocate memory for our device state and initialize it */
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&intf->dev, "Out of memory\n");
+		goto exit;
+	}
+	mutex_init(&dev->mutex);
+	spin_lock_init(&dev->rbsl);
+	dev->intf = intf;
+	init_waitqueue_head(&dev->read_wait);
+	init_waitqueue_head(&dev->write_wait);
+
+	/* workaround for early firmware versions on fast computers */
+	if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VENDOR_ID_LD) &&
+	    ((le16_to_cpu(udev->descriptor.idProduct) == USB_DEVICE_ID_LD_CASSY) ||
+	     (le16_to_cpu(udev->descriptor.idProduct) == USB_DEVICE_ID_LD_COM3LAB)) &&
+	    (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x103)) {
+		buffer = kmalloc(256, GFP_KERNEL);
+		if (buffer == NULL) {
+			dev_err(&intf->dev, "Couldn't allocate string buffer\n");
+			goto error;
+		}
+		/* usb_string makes SETUP+STALL to leave always ControlReadLoop */
+		usb_string(udev, 255, buffer, 256);
+		kfree(buffer);
+	}
+
+	iface_desc = intf->cur_altsetting;
+
+	/* set up the endpoint information */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(endpoint))
+			dev->interrupt_in_endpoint = endpoint;
+
+		if (usb_endpoint_is_int_out(endpoint))
+			dev->interrupt_out_endpoint = endpoint;
+	}
+	if (dev->interrupt_in_endpoint == NULL) {
+		dev_err(&intf->dev, "Interrupt in endpoint not found\n");
+		goto error;
+	}
+	if (dev->interrupt_out_endpoint == NULL)
+		dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");
+
+	dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
+	dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL);
+	if (!dev->ring_buffer) {
+		dev_err(&intf->dev, "Couldn't allocate ring_buffer\n");
+		goto error;
+	}
+	dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);
+	if (!dev->interrupt_in_buffer) {
+		dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n");
+		goto error;
+	}
+	dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->interrupt_in_urb) {
+		dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n");
+		goto error;
+	}
+	dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) :
+									 udev->descriptor.bMaxPacketSize0;
+	dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);
+	if (!dev->interrupt_out_buffer) {
+		dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n");
+		goto error;
+	}
+	dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->interrupt_out_urb) {
+		dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n");
+		goto error;
+	}
+	dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
+	if (dev->interrupt_out_endpoint)
+		dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(intf, dev);
+
+	retval = usb_register_dev(intf, &ld_usb_class);
+	if (retval) {
+		/* something prevented us from registering this driver */
+		dev_err(&intf->dev, "Not able to get a minor for this device.\n");
+		usb_set_intfdata(intf, NULL);
+		goto error;
+	}
+
+	/* let the user know what node this device is now attached to */
+	dev_info(&intf->dev, "LD USB Device #%d now attached to major %d minor %d\n",
+		(intf->minor - USB_LD_MINOR_BASE), USB_MAJOR, intf->minor);
+
+exit:
+	return retval;
+
+error:
+	ld_usb_delete(dev);
+
+	return retval;
+}
+
+/**
+ *	ld_usb_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ */
+static void ld_usb_disconnect(struct usb_interface *intf)
+{
+	struct ld_usb *dev;
+	int minor;
+
+	dev = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+
+	minor = intf->minor;
+
+	/* give back our minor */
+	usb_deregister_dev(intf, &ld_usb_class);
+
+	mutex_lock(&dev->mutex);
+
+	/* if the device is not opened, then we clean up right now */
+	if (!dev->open_count) {
+		mutex_unlock(&dev->mutex);
+		ld_usb_delete(dev);
+	} else {
+		dev->intf = NULL;
+		/* wake up pollers */
+		wake_up_interruptible_all(&dev->read_wait);
+		wake_up_interruptible_all(&dev->write_wait);
+		mutex_unlock(&dev->mutex);
+	}
+
+	dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
+		 (minor - USB_LD_MINOR_BASE));
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver ld_usb_driver = {
+	.name =		"ldusb",
+	.probe =	ld_usb_probe,
+	.disconnect =	ld_usb_disconnect,
+	.id_table =	ld_usb_table,
+};
+
+module_usb_driver(ld_usb_driver);
+
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
new file mode 100644
index 0000000..0ec9ee5
--- /dev/null
+++ b/drivers/usb/misc/legousbtower.c
@@ -0,0 +1,1010 @@
+/*
+ * LEGO USB Tower driver
+ *
+ * Copyright (C) 2003 David Glance <davidgsf@sourceforge.net>
+ *               2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
+ *
+ *	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.
+ *
+ * derived from USB Skeleton driver - 0.5
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * History:
+ *
+ * 2001-10-13 - 0.1 js
+ *   - first version
+ * 2001-11-03 - 0.2 js
+ *   - simplified buffering, one-shot URBs for writing
+ * 2001-11-10 - 0.3 js
+ *   - removed IOCTL (setting power/mode is more complicated, postponed)
+ * 2001-11-28 - 0.4 js
+ *   - added vendor commands for mode of operation and power level in open
+ * 2001-12-04 - 0.5 js
+ *   - set IR mode by default (by oversight 0.4 set VLL mode)
+ * 2002-01-11 - 0.5? pcchan
+ *   - make read buffer reusable and work around bytes_to_write issue between
+ *     uhci and legusbtower
+ * 2002-09-23 - 0.52 david (david@csse.uwa.edu.au)
+ *   - imported into lejos project
+ *   - changed wake_up to wake_up_interruptible
+ *   - changed to use lego0 rather than tower0
+ *   - changed dbg() to use __func__ rather than deprecated __func__
+ * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au)
+ *   - changed read and write to write everything or
+ *     timeout (from a patch by Chris Riesen and Brett Thaeler driver)
+ *   - added ioctl functionality to set timeouts
+ * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au)
+ *   - initial import into LegoUSB project
+ *   - merge of existing LegoUSB.c driver
+ * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au)
+ *   - port to 2.6 style driver
+ * 2004-02-29 - 0.6 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - fix locking
+ *   - unlink read URBs which are no longer needed
+ *   - allow increased buffer size, eliminates need for timeout on write
+ *   - have read URB running continuously
+ *   - added poll
+ *   - forbid seeking
+ *   - added nonblocking I/O
+ *   - changed back __func__ to __func__
+ *   - read and log tower firmware version
+ *   - reset tower on probe, avoids failure of first write
+ * 2004-03-09 - 0.7 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - timeout read now only after inactivity, shorten default accordingly
+ * 2004-03-11 - 0.8 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - log major, minor instead of possibly confusing device filename
+ *   - whitespace cleanup
+ * 2004-03-12 - 0.9 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - normalize whitespace in debug messages
+ *   - take care about endianness in control message responses
+ * 2004-03-13 - 0.91 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - make default intervals longer to accommodate current EHCI driver
+ * 2004-03-19 - 0.92 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - replaced atomic_t by memory barriers
+ * 2004-04-21 - 0.93 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - wait for completion of write urb in release (needed for remotecontrol)
+ *   - corrected poll for write direction (missing negation)
+ * 2004-04-22 - 0.94 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - make device locking interruptible
+ * 2004-04-30 - 0.95 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - check for valid udev on resubmitting and unlinking urbs
+ * 2004-08-03 - 0.96 Juergen Stuber <starblue@users.sourceforge.net>
+ *   - move reset into open to clean out spurious data
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.96"
+#define DRIVER_AUTHOR "Juergen Stuber <starblue@sourceforge.net>"
+#define DRIVER_DESC "LEGO USB Tower Driver"
+
+
+/* The defaults are chosen to work with the latest versions of leJOS and NQC.
+ */
+
+/* Some legacy software likes to receive packets in one piece.
+ * In this case read_buffer_size should exceed the maximal packet length
+ * (417 for datalog uploads), and packet_timeout should be set.
+ */
+static int read_buffer_size = 480;
+module_param(read_buffer_size, int, 0);
+MODULE_PARM_DESC(read_buffer_size, "Read buffer size");
+
+/* Some legacy software likes to send packets in one piece.
+ * In this case write_buffer_size should exceed the maximal packet length
+ * (417 for firmware and program downloads).
+ * A problem with long writes is that the following read may time out
+ * if the software is not prepared to wait long enough.
+ */
+static int write_buffer_size = 480;
+module_param(write_buffer_size, int, 0);
+MODULE_PARM_DESC(write_buffer_size, "Write buffer size");
+
+/* Some legacy software expects reads to contain whole LASM packets.
+ * To achieve this, characters which arrive before a packet timeout
+ * occurs will be returned in a single read operation.
+ * A problem with long reads is that the software may time out
+ * if it is not prepared to wait long enough.
+ * The packet timeout should be greater than the time between the
+ * reception of subsequent characters, which should arrive about
+ * every 5ms for the standard 2400 baud.
+ * Set it to 0 to disable.
+ */
+static int packet_timeout = 50;
+module_param(packet_timeout, int, 0);
+MODULE_PARM_DESC(packet_timeout, "Packet timeout in ms");
+
+/* Some legacy software expects blocking reads to time out.
+ * Timeout occurs after the specified time of read and write inactivity.
+ * Set it to 0 to disable.
+ */
+static int read_timeout = 200;
+module_param(read_timeout, int, 0);
+MODULE_PARM_DESC(read_timeout, "Read timeout in ms");
+
+/* As of kernel version 2.6.4 ehci-hcd uses an
+ * "only one interrupt transfer per frame" shortcut
+ * to simplify the scheduling of periodic transfers.
+ * This conflicts with our standard 1ms intervals for in and out URBs.
+ * We use default intervals of 2ms for in and 8ms for out transfers,
+ * which is fast enough for 2400 baud and allows a small additional load.
+ * Increase the interval to allow more devices that do interrupt transfers,
+ * or set to 0 to use the standard interval from the endpoint descriptors.
+ */
+static int interrupt_in_interval = 2;
+module_param(interrupt_in_interval, int, 0);
+MODULE_PARM_DESC(interrupt_in_interval, "Interrupt in interval in ms");
+
+static int interrupt_out_interval = 8;
+module_param(interrupt_out_interval, int, 0);
+MODULE_PARM_DESC(interrupt_out_interval, "Interrupt out interval in ms");
+
+/* Define these values to match your device */
+#define LEGO_USB_TOWER_VENDOR_ID	0x0694
+#define LEGO_USB_TOWER_PRODUCT_ID	0x0001
+
+/* Vendor requests */
+#define LEGO_USB_TOWER_REQUEST_RESET		0x04
+#define LEGO_USB_TOWER_REQUEST_GET_VERSION	0xFD
+
+struct tower_reset_reply {
+	__le16 size;		/* little-endian */
+	__u8 err_code;
+	__u8 spare;
+} __attribute__ ((packed));
+
+struct tower_get_version_reply {
+	__le16 size;		/* little-endian */
+	__u8 err_code;
+	__u8 spare;
+	__u8 major;
+	__u8 minor;
+	__le16 build_no;		/* little-endian */
+} __attribute__ ((packed));
+
+
+/* table of devices that work with this driver */
+static const struct usb_device_id tower_table[] = {
+	{ USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, tower_table);
+static DEFINE_MUTEX(open_disc_mutex);
+
+#define LEGO_USB_TOWER_MINOR_BASE	160
+
+
+/* Structure to hold all of our device specific stuff */
+struct lego_usb_tower {
+	struct mutex		lock;		/* locks this structure */
+	struct usb_device*	udev;		/* save off the usb device pointer */
+	unsigned char		minor;		/* the starting minor number for this device */
+
+	int			open_count;	/* number of times this port has been opened */
+
+	char*			read_buffer;
+	size_t			read_buffer_length; /* this much came in */
+	size_t			read_packet_length; /* this much will be returned on read */
+	spinlock_t		read_buffer_lock;
+	int			packet_timeout_jiffies;
+	unsigned long		read_last_arrival;
+
+	wait_queue_head_t	read_wait;
+	wait_queue_head_t	write_wait;
+
+	char*			interrupt_in_buffer;
+	struct usb_endpoint_descriptor* interrupt_in_endpoint;
+	struct urb*		interrupt_in_urb;
+	int			interrupt_in_interval;
+	int			interrupt_in_running;
+	int			interrupt_in_done;
+
+	char*			interrupt_out_buffer;
+	struct usb_endpoint_descriptor* interrupt_out_endpoint;
+	struct urb*		interrupt_out_urb;
+	int			interrupt_out_interval;
+	int			interrupt_out_busy;
+
+};
+
+
+/* local function prototypes */
+static ssize_t tower_read	(struct file *file, char __user *buffer, size_t count, loff_t *ppos);
+static ssize_t tower_write	(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
+static inline void tower_delete (struct lego_usb_tower *dev);
+static int tower_open		(struct inode *inode, struct file *file);
+static int tower_release	(struct inode *inode, struct file *file);
+static unsigned int tower_poll	(struct file *file, poll_table *wait);
+static loff_t tower_llseek	(struct file *file, loff_t off, int whence);
+
+static void tower_abort_transfers (struct lego_usb_tower *dev);
+static void tower_check_for_read_packet (struct lego_usb_tower *dev);
+static void tower_interrupt_in_callback (struct urb *urb);
+static void tower_interrupt_out_callback (struct urb *urb);
+
+static int  tower_probe	(struct usb_interface *interface, const struct usb_device_id *id);
+static void tower_disconnect	(struct usb_interface *interface);
+
+
+/* file operations needed when we register this driver */
+static const struct file_operations tower_fops = {
+	.owner =	THIS_MODULE,
+	.read  =	tower_read,
+	.write =	tower_write,
+	.open =		tower_open,
+	.release =	tower_release,
+	.poll =		tower_poll,
+	.llseek =	tower_llseek,
+};
+
+static char *legousbtower_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
+}
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver tower_class = {
+	.name =		"legousbtower%d",
+	.devnode = 	legousbtower_devnode,
+	.fops =		&tower_fops,
+	.minor_base =	LEGO_USB_TOWER_MINOR_BASE,
+};
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver tower_driver = {
+	.name =		"legousbtower",
+	.probe =	tower_probe,
+	.disconnect =	tower_disconnect,
+	.id_table =	tower_table,
+};
+
+
+/**
+ *	lego_usb_tower_debug_data
+ */
+static inline void lego_usb_tower_debug_data(struct device *dev,
+					     const char *function, int size,
+					     const unsigned char *data)
+{
+	dev_dbg(dev, "%s - length = %d, data = %*ph\n",
+		function, size, size, data);
+}
+
+
+/**
+ *	tower_delete
+ */
+static inline void tower_delete (struct lego_usb_tower *dev)
+{
+	tower_abort_transfers (dev);
+
+	/* free data structures */
+	usb_free_urb(dev->interrupt_in_urb);
+	usb_free_urb(dev->interrupt_out_urb);
+	kfree (dev->read_buffer);
+	kfree (dev->interrupt_in_buffer);
+	kfree (dev->interrupt_out_buffer);
+	kfree (dev);
+}
+
+
+/**
+ *	tower_open
+ */
+static int tower_open (struct inode *inode, struct file *file)
+{
+	struct lego_usb_tower *dev = NULL;
+	int subminor;
+	int retval = 0;
+	struct usb_interface *interface;
+	struct tower_reset_reply *reset_reply;
+	int result;
+
+	reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL);
+
+	if (!reset_reply) {
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	nonseekable_open(inode, file);
+	subminor = iminor(inode);
+
+	interface = usb_find_interface (&tower_driver, subminor);
+
+	if (!interface) {
+		pr_err("error, can't find device for minor %d\n", subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	mutex_lock(&open_disc_mutex);
+	dev = usb_get_intfdata(interface);
+
+	if (!dev) {
+		mutex_unlock(&open_disc_mutex);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	/* lock this device */
+	if (mutex_lock_interruptible(&dev->lock)) {
+		mutex_unlock(&open_disc_mutex);
+	        retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+
+	/* allow opening only once */
+	if (dev->open_count) {
+		mutex_unlock(&open_disc_mutex);
+		retval = -EBUSY;
+		goto unlock_exit;
+	}
+	dev->open_count = 1;
+	mutex_unlock(&open_disc_mutex);
+
+	/* reset the tower */
+	result = usb_control_msg (dev->udev,
+				  usb_rcvctrlpipe(dev->udev, 0),
+				  LEGO_USB_TOWER_REQUEST_RESET,
+				  USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+				  0,
+				  0,
+				  reset_reply,
+				  sizeof(*reset_reply),
+				  1000);
+	if (result < 0) {
+		dev_err(&dev->udev->dev,
+			"LEGO USB Tower reset control request failed\n");
+		retval = result;
+		goto unlock_exit;
+	}
+
+	/* initialize in direction */
+	dev->read_buffer_length = 0;
+	dev->read_packet_length = 0;
+	usb_fill_int_urb (dev->interrupt_in_urb,
+			  dev->udev,
+			  usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
+			  dev->interrupt_in_buffer,
+			  usb_endpoint_maxp(dev->interrupt_in_endpoint),
+			  tower_interrupt_in_callback,
+			  dev,
+			  dev->interrupt_in_interval);
+
+	dev->interrupt_in_running = 1;
+	dev->interrupt_in_done = 0;
+	mb();
+
+	retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL);
+	if (retval) {
+		dev_err(&dev->udev->dev,
+			"Couldn't submit interrupt_in_urb %d\n", retval);
+		dev->interrupt_in_running = 0;
+		dev->open_count = 0;
+		goto unlock_exit;
+	}
+
+	/* save device in the file's private structure */
+	file->private_data = dev;
+
+unlock_exit:
+	mutex_unlock(&dev->lock);
+
+exit:
+	kfree(reset_reply);
+	return retval;
+}
+
+/**
+ *	tower_release
+ */
+static int tower_release (struct inode *inode, struct file *file)
+{
+	struct lego_usb_tower *dev;
+	int retval = 0;
+
+	dev = file->private_data;
+
+	if (dev == NULL) {
+		retval = -ENODEV;
+		goto exit_nolock;
+	}
+
+	mutex_lock(&open_disc_mutex);
+	if (mutex_lock_interruptible(&dev->lock)) {
+	        retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+	if (dev->open_count != 1) {
+		dev_dbg(&dev->udev->dev, "%s: device not opened exactly once\n",
+			__func__);
+		retval = -ENODEV;
+		goto unlock_exit;
+	}
+	if (dev->udev == NULL) {
+		/* the device was unplugged before the file was released */
+
+		/* unlock here as tower_delete frees dev */
+		mutex_unlock(&dev->lock);
+		tower_delete (dev);
+		goto exit;
+	}
+
+	/* wait until write transfer is finished */
+	if (dev->interrupt_out_busy) {
+		wait_event_interruptible_timeout (dev->write_wait, !dev->interrupt_out_busy, 2 * HZ);
+	}
+	tower_abort_transfers (dev);
+	dev->open_count = 0;
+
+unlock_exit:
+	mutex_unlock(&dev->lock);
+
+exit:
+	mutex_unlock(&open_disc_mutex);
+exit_nolock:
+	return retval;
+}
+
+
+/**
+ *	tower_abort_transfers
+ *      aborts transfers and frees associated data structures
+ */
+static void tower_abort_transfers (struct lego_usb_tower *dev)
+{
+	if (dev == NULL)
+		return;
+
+	/* shutdown transfer */
+	if (dev->interrupt_in_running) {
+		dev->interrupt_in_running = 0;
+		mb();
+		if (dev->udev)
+			usb_kill_urb (dev->interrupt_in_urb);
+	}
+	if (dev->interrupt_out_busy && dev->udev)
+		usb_kill_urb(dev->interrupt_out_urb);
+}
+
+
+/**
+ *	tower_check_for_read_packet
+ *
+ *      To get correct semantics for signals and non-blocking I/O
+ *      with packetizing we pretend not to see any data in the read buffer
+ *      until it has been there unchanged for at least
+ *      dev->packet_timeout_jiffies, or until the buffer is full.
+ */
+static void tower_check_for_read_packet (struct lego_usb_tower *dev)
+{
+	spin_lock_irq (&dev->read_buffer_lock);
+	if (!packet_timeout
+	    || time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies)
+	    || dev->read_buffer_length == read_buffer_size) {
+		dev->read_packet_length = dev->read_buffer_length;
+	}
+	dev->interrupt_in_done = 0;
+	spin_unlock_irq (&dev->read_buffer_lock);
+}
+
+
+/**
+ *	tower_poll
+ */
+static unsigned int tower_poll (struct file *file, poll_table *wait)
+{
+	struct lego_usb_tower *dev;
+	unsigned int mask = 0;
+
+	dev = file->private_data;
+
+	if (!dev->udev)
+		return POLLERR | POLLHUP;
+
+	poll_wait(file, &dev->read_wait, wait);
+	poll_wait(file, &dev->write_wait, wait);
+
+	tower_check_for_read_packet(dev);
+	if (dev->read_packet_length > 0) {
+		mask |= POLLIN | POLLRDNORM;
+	}
+	if (!dev->interrupt_out_busy) {
+		mask |= POLLOUT | POLLWRNORM;
+	}
+
+	return mask;
+}
+
+
+/**
+ *	tower_llseek
+ */
+static loff_t tower_llseek (struct file *file, loff_t off, int whence)
+{
+	return -ESPIPE;		/* unseekable */
+}
+
+
+/**
+ *	tower_read
+ */
+static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct lego_usb_tower *dev;
+	size_t bytes_to_read;
+	int i;
+	int retval = 0;
+	unsigned long timeout = 0;
+
+	dev = file->private_data;
+
+	/* lock this object */
+	if (mutex_lock_interruptible(&dev->lock)) {
+		retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+		pr_err("No device or device unplugged %d\n", retval);
+		goto unlock_exit;
+	}
+
+	/* verify that we actually have some data to read */
+	if (count == 0) {
+		dev_dbg(&dev->udev->dev, "read request of 0 bytes\n");
+		goto unlock_exit;
+	}
+
+	if (read_timeout) {
+		timeout = jiffies + msecs_to_jiffies(read_timeout);
+	}
+
+	/* wait for data */
+	tower_check_for_read_packet (dev);
+	while (dev->read_packet_length == 0) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto unlock_exit;
+		}
+		retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies);
+		if (retval < 0) {
+			goto unlock_exit;
+		}
+
+		/* reset read timeout during read or write activity */
+		if (read_timeout
+		    && (dev->read_buffer_length || dev->interrupt_out_busy)) {
+			timeout = jiffies + msecs_to_jiffies(read_timeout);
+		}
+		/* check for read timeout */
+		if (read_timeout && time_after (jiffies, timeout)) {
+			retval = -ETIMEDOUT;
+			goto unlock_exit;
+		}
+		tower_check_for_read_packet (dev);
+	}
+
+	/* copy the data from read_buffer into userspace */
+	bytes_to_read = min(count, dev->read_packet_length);
+
+	if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) {
+		retval = -EFAULT;
+		goto unlock_exit;
+	}
+
+	spin_lock_irq (&dev->read_buffer_lock);
+	dev->read_buffer_length -= bytes_to_read;
+	dev->read_packet_length -= bytes_to_read;
+	for (i=0; i<dev->read_buffer_length; i++) {
+		dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read];
+	}
+	spin_unlock_irq (&dev->read_buffer_lock);
+
+	retval = bytes_to_read;
+
+unlock_exit:
+	/* unlock the device */
+	mutex_unlock(&dev->lock);
+
+exit:
+	return retval;
+}
+
+
+/**
+ *	tower_write
+ */
+static ssize_t tower_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct lego_usb_tower *dev;
+	size_t bytes_to_write;
+	int retval = 0;
+
+	dev = file->private_data;
+
+	/* lock this object */
+	if (mutex_lock_interruptible(&dev->lock)) {
+		retval = -ERESTARTSYS;
+		goto exit;
+	}
+
+	/* verify that the device wasn't unplugged */
+	if (dev->udev == NULL) {
+		retval = -ENODEV;
+		pr_err("No device or device unplugged %d\n", retval);
+		goto unlock_exit;
+	}
+
+	/* verify that we actually have some data to write */
+	if (count == 0) {
+		dev_dbg(&dev->udev->dev, "write request of 0 bytes\n");
+		goto unlock_exit;
+	}
+
+	/* wait until previous transfer is finished */
+	while (dev->interrupt_out_busy) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto unlock_exit;
+		}
+		retval = wait_event_interruptible (dev->write_wait, !dev->interrupt_out_busy);
+		if (retval) {
+			goto unlock_exit;
+		}
+	}
+
+	/* write the data into interrupt_out_buffer from userspace */
+	bytes_to_write = min_t(int, count, write_buffer_size);
+	dev_dbg(&dev->udev->dev, "%s: count = %Zd, bytes_to_write = %Zd\n",
+		__func__, count, bytes_to_write);
+
+	if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) {
+		retval = -EFAULT;
+		goto unlock_exit;
+	}
+
+	/* send off the urb */
+	usb_fill_int_urb(dev->interrupt_out_urb,
+			 dev->udev,
+			 usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+			 dev->interrupt_out_buffer,
+			 bytes_to_write,
+			 tower_interrupt_out_callback,
+			 dev,
+			 dev->interrupt_out_interval);
+
+	dev->interrupt_out_busy = 1;
+	wmb();
+
+	retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL);
+	if (retval) {
+		dev->interrupt_out_busy = 0;
+		dev_err(&dev->udev->dev,
+			"Couldn't submit interrupt_out_urb %d\n", retval);
+		goto unlock_exit;
+	}
+	retval = bytes_to_write;
+
+unlock_exit:
+	/* unlock the device */
+	mutex_unlock(&dev->lock);
+
+exit:
+	return retval;
+}
+
+
+/**
+ *	tower_interrupt_in_callback
+ */
+static void tower_interrupt_in_callback (struct urb *urb)
+{
+	struct lego_usb_tower *dev = urb->context;
+	int status = urb->status;
+	int retval;
+
+	lego_usb_tower_debug_data(&dev->udev->dev, __func__,
+				  urb->actual_length, urb->transfer_buffer);
+
+	if (status) {
+		if (status == -ENOENT ||
+		    status == -ECONNRESET ||
+		    status == -ESHUTDOWN) {
+			goto exit;
+		} else {
+			dev_dbg(&dev->udev->dev,
+				"%s: nonzero status received: %d\n", __func__,
+				status);
+			goto resubmit; /* maybe we can recover */
+		}
+	}
+
+	if (urb->actual_length > 0) {
+		spin_lock (&dev->read_buffer_lock);
+		if (dev->read_buffer_length + urb->actual_length < read_buffer_size) {
+			memcpy (dev->read_buffer + dev->read_buffer_length,
+				dev->interrupt_in_buffer,
+				urb->actual_length);
+			dev->read_buffer_length += urb->actual_length;
+			dev->read_last_arrival = jiffies;
+			dev_dbg(&dev->udev->dev, "%s: received %d bytes\n",
+				__func__, urb->actual_length);
+		} else {
+			pr_warn("read_buffer overflow, %d bytes dropped\n",
+				urb->actual_length);
+		}
+		spin_unlock (&dev->read_buffer_lock);
+	}
+
+resubmit:
+	/* resubmit if we're still running */
+	if (dev->interrupt_in_running && dev->udev) {
+		retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC);
+		if (retval)
+			dev_err(&dev->udev->dev,
+				"%s: usb_submit_urb failed (%d)\n",
+				__func__, retval);
+	}
+
+exit:
+	dev->interrupt_in_done = 1;
+	wake_up_interruptible (&dev->read_wait);
+}
+
+
+/**
+ *	tower_interrupt_out_callback
+ */
+static void tower_interrupt_out_callback (struct urb *urb)
+{
+	struct lego_usb_tower *dev = urb->context;
+	int status = urb->status;
+
+	lego_usb_tower_debug_data(&dev->udev->dev, __func__,
+				  urb->actual_length, urb->transfer_buffer);
+
+	/* sync/async unlink faults aren't errors */
+	if (status && !(status == -ENOENT ||
+			status == -ECONNRESET ||
+			status == -ESHUTDOWN)) {
+		dev_dbg(&dev->udev->dev,
+			"%s: nonzero write bulk status received: %d\n", __func__,
+			status);
+	}
+
+	dev->interrupt_out_busy = 0;
+	wake_up_interruptible(&dev->write_wait);
+}
+
+
+/**
+ *	tower_probe
+ *
+ *	Called by the usb core when a new device is connected that it thinks
+ *	this driver might be interested in.
+ */
+static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct device *idev = &interface->dev;
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct lego_usb_tower *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor* endpoint;
+	struct tower_get_version_reply *get_version_reply = NULL;
+	int i;
+	int retval = -ENOMEM;
+	int result;
+
+	/* allocate memory for our device state and initialize it */
+
+	dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
+
+	if (dev == NULL) {
+		dev_err(idev, "Out of memory\n");
+		goto exit;
+	}
+
+	mutex_init(&dev->lock);
+
+	dev->udev = udev;
+	dev->open_count = 0;
+
+	dev->read_buffer = NULL;
+	dev->read_buffer_length = 0;
+	dev->read_packet_length = 0;
+	spin_lock_init (&dev->read_buffer_lock);
+	dev->packet_timeout_jiffies = msecs_to_jiffies(packet_timeout);
+	dev->read_last_arrival = jiffies;
+
+	init_waitqueue_head (&dev->read_wait);
+	init_waitqueue_head (&dev->write_wait);
+
+	dev->interrupt_in_buffer = NULL;
+	dev->interrupt_in_endpoint = NULL;
+	dev->interrupt_in_urb = NULL;
+	dev->interrupt_in_running = 0;
+	dev->interrupt_in_done = 0;
+
+	dev->interrupt_out_buffer = NULL;
+	dev->interrupt_out_endpoint = NULL;
+	dev->interrupt_out_urb = NULL;
+	dev->interrupt_out_busy = 0;
+
+	iface_desc = interface->cur_altsetting;
+
+	/* set up the endpoint information */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_xfer_int(endpoint)) {
+			if (usb_endpoint_dir_in(endpoint))
+				dev->interrupt_in_endpoint = endpoint;
+			else
+				dev->interrupt_out_endpoint = endpoint;
+		}
+	}
+	if(dev->interrupt_in_endpoint == NULL) {
+		dev_err(idev, "interrupt in endpoint not found\n");
+		goto error;
+	}
+	if (dev->interrupt_out_endpoint == NULL) {
+		dev_err(idev, "interrupt out endpoint not found\n");
+		goto error;
+	}
+
+	dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL);
+	if (!dev->read_buffer) {
+		dev_err(idev, "Couldn't allocate read_buffer\n");
+		goto error;
+	}
+	dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL);
+	if (!dev->interrupt_in_buffer) {
+		dev_err(idev, "Couldn't allocate interrupt_in_buffer\n");
+		goto error;
+	}
+	dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->interrupt_in_urb) {
+		dev_err(idev, "Couldn't allocate interrupt_in_urb\n");
+		goto error;
+	}
+	dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL);
+	if (!dev->interrupt_out_buffer) {
+		dev_err(idev, "Couldn't allocate interrupt_out_buffer\n");
+		goto error;
+	}
+	dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->interrupt_out_urb) {
+		dev_err(idev, "Couldn't allocate interrupt_out_urb\n");
+		goto error;
+	}
+	dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
+	dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
+
+	get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL);
+
+	if (!get_version_reply) {
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	/* get the firmware version and log it */
+	result = usb_control_msg (udev,
+				  usb_rcvctrlpipe(udev, 0),
+				  LEGO_USB_TOWER_REQUEST_GET_VERSION,
+				  USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+				  0,
+				  0,
+				  get_version_reply,
+				  sizeof(*get_version_reply),
+				  1000);
+	if (result < 0) {
+		dev_err(idev, "LEGO USB Tower get version control request failed\n");
+		retval = result;
+		goto error;
+	}
+	dev_info(&interface->dev,
+		 "LEGO USB Tower firmware version is %d.%d build %d\n",
+		 get_version_reply->major,
+		 get_version_reply->minor,
+		 le16_to_cpu(get_version_reply->build_no));
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata (interface, dev);
+
+	retval = usb_register_dev (interface, &tower_class);
+
+	if (retval) {
+		/* something prevented us from registering this driver */
+		dev_err(idev, "Not able to get a minor for this device.\n");
+		usb_set_intfdata (interface, NULL);
+		goto error;
+	}
+	dev->minor = interface->minor;
+
+	/* let the user know what node this device is now attached to */
+	dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
+		 "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
+		 USB_MAJOR, dev->minor);
+
+exit:
+	kfree(get_version_reply);
+	return retval;
+
+error:
+	kfree(get_version_reply);
+	tower_delete(dev);
+	return retval;
+}
+
+
+/**
+ *	tower_disconnect
+ *
+ *	Called by the usb core when the device is removed from the system.
+ */
+static void tower_disconnect (struct usb_interface *interface)
+{
+	struct lego_usb_tower *dev;
+	int minor;
+
+	dev = usb_get_intfdata (interface);
+	mutex_lock(&open_disc_mutex);
+	usb_set_intfdata (interface, NULL);
+
+	minor = dev->minor;
+
+	/* give back our minor */
+	usb_deregister_dev (interface, &tower_class);
+
+	mutex_lock(&dev->lock);
+	mutex_unlock(&open_disc_mutex);
+
+	/* if the device is not opened, then we clean up right now */
+	if (!dev->open_count) {
+		mutex_unlock(&dev->lock);
+		tower_delete (dev);
+	} else {
+		dev->udev = NULL;
+		/* wake up pollers */
+		wake_up_interruptible_all(&dev->read_wait);
+		wake_up_interruptible_all(&dev->write_wait);
+		mutex_unlock(&dev->lock);
+	}
+
+	dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n",
+		 (minor - LEGO_USB_TOWER_MINOR_BASE));
+}
+
+module_usb_driver(tower_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
new file mode 100644
index 0000000..383fa00
--- /dev/null
+++ b/drivers/usb/misc/lvstest.c
@@ -0,0 +1,464 @@
+/*
+ * drivers/usb/misc/lvstest.c
+ *
+ * Test pattern generation for Link Layer Validation System Tests
+ *
+ * Copyright (C) 2014 ST Microelectronics
+ * Pratyush Anand <pratyush.anand@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/phy.h>
+
+struct lvs_rh {
+	/* root hub interface */
+	struct usb_interface *intf;
+	/* if lvs device connected */
+	bool present;
+	/* port no at which lvs device is present */
+	int portnum;
+	/* urb buffer */
+	u8 buffer[8];
+	/* class descriptor */
+	struct usb_hub_descriptor descriptor;
+	/* urb for polling interrupt pipe */
+	struct urb *urb;
+	/* LVS RH work queue */
+	struct workqueue_struct *rh_queue;
+	/* LVH RH work */
+	struct work_struct	rh_work;
+	/* RH port status */
+	struct usb_port_status port_status;
+};
+
+static struct usb_device *create_lvs_device(struct usb_interface *intf)
+{
+	struct usb_device *udev, *hdev;
+	struct usb_hcd *hcd;
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+
+	if (!lvs->present) {
+		dev_err(&intf->dev, "No LVS device is present\n");
+		return NULL;
+	}
+
+	hdev = interface_to_usbdev(intf);
+	hcd = bus_to_hcd(hdev->bus);
+
+	udev = usb_alloc_dev(hdev, hdev->bus, lvs->portnum);
+	if (!udev) {
+		dev_err(&intf->dev, "Could not allocate lvs udev\n");
+		return NULL;
+	}
+	udev->speed = USB_SPEED_SUPER;
+	udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
+	usb_set_device_state(udev, USB_STATE_DEFAULT);
+
+	if (hcd->driver->enable_device) {
+		if (hcd->driver->enable_device(hcd, udev) < 0) {
+			dev_err(&intf->dev, "Failed to enable\n");
+			usb_put_dev(udev);
+			return NULL;
+		}
+	}
+
+	return udev;
+}
+
+static void destroy_lvs_device(struct usb_device *udev)
+{
+	struct usb_device *hdev = udev->parent;
+	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+
+	if (hcd->driver->free_dev)
+		hcd->driver->free_dev(hcd, udev);
+
+	usb_put_dev(udev);
+}
+
+static int lvs_rh_clear_port_feature(struct usb_device *hdev,
+		int port1, int feature)
+{
+	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
+		NULL, 0, 1000);
+}
+
+static int lvs_rh_set_port_feature(struct usb_device *hdev,
+		int port1, int feature)
+{
+	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
+		NULL, 0, 1000);
+}
+
+static ssize_t u3_entry_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	struct usb_device *udev;
+	int ret;
+
+	udev = create_lvs_device(intf);
+	if (!udev) {
+		dev_err(dev, "failed to create lvs device\n");
+		return -ENOMEM;
+	}
+
+	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
+			USB_PORT_FEAT_SUSPEND);
+	if (ret < 0)
+		dev_err(dev, "can't issue U3 entry %d\n", ret);
+
+	destroy_lvs_device(udev);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+static DEVICE_ATTR_WO(u3_entry);
+
+static ssize_t u3_exit_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	struct usb_device *udev;
+	int ret;
+
+	udev = create_lvs_device(intf);
+	if (!udev) {
+		dev_err(dev, "failed to create lvs device\n");
+		return -ENOMEM;
+	}
+
+	ret = lvs_rh_clear_port_feature(hdev, lvs->portnum,
+			USB_PORT_FEAT_SUSPEND);
+	if (ret < 0)
+		dev_err(dev, "can't issue U3 exit %d\n", ret);
+
+	destroy_lvs_device(udev);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+static DEVICE_ATTR_WO(u3_exit);
+
+static ssize_t hot_reset_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	int ret;
+
+	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
+			USB_PORT_FEAT_RESET);
+	if (ret < 0) {
+		dev_err(dev, "can't issue hot reset %d\n", ret);
+		return ret;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(hot_reset);
+
+static ssize_t u2_timeout_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret < 0) {
+		dev_err(dev, "couldn't parse string %d\n", ret);
+		return ret;
+	}
+
+	if (val < 0 || val > 127)
+		return -EINVAL;
+
+	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
+			USB_PORT_FEAT_U2_TIMEOUT);
+	if (ret < 0) {
+		dev_err(dev, "Error %d while setting U2 timeout %ld\n", ret, val);
+		return ret;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(u2_timeout);
+
+static ssize_t u1_timeout_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret < 0) {
+		dev_err(dev, "couldn't parse string %d\n", ret);
+		return ret;
+	}
+
+	if (val < 0 || val > 127)
+		return -EINVAL;
+
+	ret = lvs_rh_set_port_feature(hdev, lvs->portnum | (val << 8),
+			USB_PORT_FEAT_U1_TIMEOUT);
+	if (ret < 0) {
+		dev_err(dev, "Error %d while setting U1 timeout %ld\n", ret, val);
+		return ret;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(u1_timeout);
+
+static ssize_t get_dev_desc_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *udev;
+	struct usb_device_descriptor *descriptor;
+	int ret;
+
+	descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
+	if (!descriptor) {
+		dev_err(dev, "failed to allocate descriptor memory\n");
+		return -ENOMEM;
+	}
+
+	udev = create_lvs_device(intf);
+	if (!udev) {
+		dev_err(dev, "failed to create lvs device\n");
+		ret = -ENOMEM;
+		goto free_desc;
+	}
+
+	ret = usb_control_msg(udev, (PIPE_CONTROL << 30) | USB_DIR_IN,
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8,
+			0, descriptor, sizeof(*descriptor),
+			USB_CTRL_GET_TIMEOUT);
+	if (ret < 0)
+		dev_err(dev, "can't read device descriptor %d\n", ret);
+
+	destroy_lvs_device(udev);
+
+free_desc:
+	kfree(descriptor);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+static DEVICE_ATTR_WO(get_dev_desc);
+
+static struct attribute *lvs_attributes[] = {
+	&dev_attr_get_dev_desc.attr,
+	&dev_attr_u1_timeout.attr,
+	&dev_attr_u2_timeout.attr,
+	&dev_attr_hot_reset.attr,
+	&dev_attr_u3_entry.attr,
+	&dev_attr_u3_exit.attr,
+	NULL
+};
+
+static const struct attribute_group lvs_attr_group = {
+	.attrs = lvs_attributes,
+};
+
+static void lvs_rh_work(struct work_struct *work)
+{
+	struct lvs_rh *lvs = container_of(work, struct lvs_rh, rh_work);
+	struct usb_interface *intf = lvs->intf;
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+	struct usb_hub_descriptor *descriptor = &lvs->descriptor;
+	struct usb_port_status *port_status = &lvs->port_status;
+	int i, ret = 0;
+	u16 portchange;
+
+	/* Examine each root port */
+	for (i = 1; i <= descriptor->bNbrPorts; i++) {
+		ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, i,
+			port_status, sizeof(*port_status), 1000);
+		if (ret < 4)
+			continue;
+
+		portchange = le16_to_cpu(port_status->wPortChange);
+
+		if (portchange & USB_PORT_STAT_C_LINK_STATE)
+			lvs_rh_clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_PORT_LINK_STATE);
+		if (portchange & USB_PORT_STAT_C_ENABLE)
+			lvs_rh_clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_ENABLE);
+		if (portchange & USB_PORT_STAT_C_RESET)
+			lvs_rh_clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_RESET);
+		if (portchange & USB_PORT_STAT_C_BH_RESET)
+			lvs_rh_clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_BH_PORT_RESET);
+		if (portchange & USB_PORT_STAT_C_CONNECTION) {
+			lvs_rh_clear_port_feature(hdev, i,
+					USB_PORT_FEAT_C_CONNECTION);
+
+			if (le16_to_cpu(port_status->wPortStatus) &
+					USB_PORT_STAT_CONNECTION) {
+				lvs->present = true;
+				lvs->portnum = i;
+				if (hcd->usb_phy)
+					usb_phy_notify_connect(hcd->usb_phy,
+							USB_SPEED_SUPER);
+			} else {
+				lvs->present = false;
+				if (hcd->usb_phy)
+					usb_phy_notify_disconnect(hcd->usb_phy,
+							USB_SPEED_SUPER);
+			}
+			break;
+		}
+	}
+
+	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
+	if (ret != 0 && ret != -ENODEV && ret != -EPERM)
+		dev_err(&intf->dev, "urb resubmit error %d\n", ret);
+}
+
+static void lvs_rh_irq(struct urb *urb)
+{
+	struct lvs_rh *lvs = urb->context;
+
+	queue_work(lvs->rh_queue, &lvs->rh_work);
+}
+
+static int lvs_rh_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	struct usb_device *hdev;
+	struct usb_host_interface *desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct lvs_rh *lvs;
+	unsigned int pipe;
+	int ret, maxp;
+
+	hdev = interface_to_usbdev(intf);
+	desc = intf->cur_altsetting;
+
+	if (desc->desc.bNumEndpoints < 1)
+		return -ENODEV;
+
+	endpoint = &desc->endpoint[0].desc;
+
+	/* valid only for SS root hub */
+	if (hdev->descriptor.bDeviceProtocol != USB_HUB_PR_SS || hdev->parent) {
+		dev_err(&intf->dev, "Bind LVS driver with SS root Hub only\n");
+		return -EINVAL;
+	}
+
+	lvs = devm_kzalloc(&intf->dev, sizeof(*lvs), GFP_KERNEL);
+	if (!lvs)
+		return -ENOMEM;
+
+	lvs->intf = intf;
+	usb_set_intfdata(intf, lvs);
+
+	/* how many number of ports this root hub has */
+	ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
+			USB_DT_SS_HUB << 8, 0, &lvs->descriptor,
+			USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
+	if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
+		dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
+		return ret;
+	}
+
+	/* submit urb to poll interrupt endpoint */
+	lvs->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!lvs->urb) {
+		dev_err(&intf->dev, "couldn't allocate lvs urb\n");
+		return -ENOMEM;
+	}
+
+	lvs->rh_queue = create_singlethread_workqueue("lvs_rh_queue");
+	if (!lvs->rh_queue) {
+		dev_err(&intf->dev, "couldn't create workqueue\n");
+		ret = -ENOMEM;
+		goto free_urb;
+	}
+
+	INIT_WORK(&lvs->rh_work, lvs_rh_work);
+
+	ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group);
+	if (ret < 0) {
+		dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret);
+		goto destroy_queue;
+	}
+
+	pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
+	usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp,
+			lvs_rh_irq, lvs, endpoint->bInterval);
+
+	ret = usb_submit_urb(lvs->urb, GFP_KERNEL);
+	if (ret < 0) {
+		dev_err(&intf->dev, "couldn't submit lvs urb %d\n", ret);
+		goto sysfs_remove;
+	}
+
+	return ret;
+
+sysfs_remove:
+	sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
+destroy_queue:
+	destroy_workqueue(lvs->rh_queue);
+free_urb:
+	usb_free_urb(lvs->urb);
+	return ret;
+}
+
+static void lvs_rh_disconnect(struct usb_interface *intf)
+{
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+
+	sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
+	destroy_workqueue(lvs->rh_queue);
+	usb_free_urb(lvs->urb);
+}
+
+static struct usb_driver lvs_driver = {
+	.name =		"lvs",
+	.probe =	lvs_rh_probe,
+	.disconnect =	lvs_rh_disconnect,
+};
+
+module_usb_driver(lvs_driver);
+
+MODULE_DESCRIPTION("Link Layer Validation System Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
new file mode 100644
index 0000000..13731d5
--- /dev/null
+++ b/drivers/usb/misc/rio500.c
@@ -0,0 +1,553 @@
+/* -*- linux-c -*- */
+
+/* 
+ * Driver for USB Rio 500
+ *
+ * Cesar Miquel (miquel@df.uba.ar)
+ * 
+ * based on hp_scanner.c by David E. Nelson (dnelson@jump.net)
+ * 
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
+ *
+ * Changelog:
+ * 30/05/2003  replaced lock/unlock kernel with up/down
+ *             Daniele Bellucci  bellucda@tiscali.it
+ * */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+
+#include "rio500_usb.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.1"
+#define DRIVER_AUTHOR "Cesar Miquel <miquel@df.uba.ar>"
+#define DRIVER_DESC "USB Rio 500 driver"
+
+#define RIO_MINOR	64
+
+/* stall/wait timeout for rio */
+#define NAK_TIMEOUT (HZ)
+
+#define IBUF_SIZE 0x1000
+
+/* Size of the rio buffer */
+#define OBUF_SIZE 0x10000
+
+struct rio_usb_data {
+        struct usb_device *rio_dev;     /* init: probe_rio */
+        unsigned int ifnum;             /* Interface number of the USB device */
+        int isopen;                     /* nz if open */
+        int present;                    /* Device is present on the bus */
+        char *obuf, *ibuf;              /* transfer buffers */
+        char bulk_in_ep, bulk_out_ep;   /* Endpoint assignments */
+        wait_queue_head_t wait_q;       /* for timeouts */
+	struct mutex lock;          /* general race avoidance */
+};
+
+static DEFINE_MUTEX(rio500_mutex);
+static struct rio_usb_data rio_instance;
+
+static int open_rio(struct inode *inode, struct file *file)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	/* against disconnect() */
+	mutex_lock(&rio500_mutex);
+	mutex_lock(&(rio->lock));
+
+	if (rio->isopen || !rio->present) {
+		mutex_unlock(&(rio->lock));
+		mutex_unlock(&rio500_mutex);
+		return -EBUSY;
+	}
+	rio->isopen = 1;
+
+	init_waitqueue_head(&rio->wait_q);
+
+	mutex_unlock(&(rio->lock));
+
+	dev_info(&rio->rio_dev->dev, "Rio opened.\n");
+	mutex_unlock(&rio500_mutex);
+
+	return 0;
+}
+
+static int close_rio(struct inode *inode, struct file *file)
+{
+	struct rio_usb_data *rio = &rio_instance;
+
+	rio->isopen = 0;
+
+	dev_info(&rio->rio_dev->dev, "Rio closed.\n");
+	return 0;
+}
+
+static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct RioCommand rio_cmd;
+	struct rio_usb_data *rio = &rio_instance;
+	void __user *data;
+	unsigned char *buffer;
+	int result, requesttype;
+	int retries;
+	int retval=0;
+
+	mutex_lock(&(rio->lock));
+        /* Sanity check to make sure rio is connected, powered, etc */
+        if (rio->present == 0 || rio->rio_dev == NULL) {
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	switch (cmd) {
+	case RIO_RECV_COMMAND:
+		data = (void __user *) arg;
+		if (data == NULL)
+			break;
+		if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand))) {
+			retval = -EFAULT;
+			goto err_out;
+		}
+		if (rio_cmd.length < 0 || rio_cmd.length > PAGE_SIZE) {
+			retval = -EINVAL;
+			goto err_out;
+		}
+		buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+		if (buffer == NULL) {
+			retval = -ENOMEM;
+			goto err_out;
+		}
+		if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length)) {
+			retval = -EFAULT;
+			free_page((unsigned long) buffer);
+			goto err_out;
+		}
+
+		requesttype = rio_cmd.requesttype | USB_DIR_IN |
+		    USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+		dev_dbg(&rio->rio_dev->dev,
+			"sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
+			requesttype, rio_cmd.request, rio_cmd.value,
+			rio_cmd.index, rio_cmd.length);
+		/* Send rio control message */
+		retries = 3;
+		while (retries) {
+			result = usb_control_msg(rio->rio_dev,
+						 usb_rcvctrlpipe(rio-> rio_dev, 0),
+						 rio_cmd.request,
+						 requesttype,
+						 rio_cmd.value,
+						 rio_cmd.index, buffer,
+						 rio_cmd.length,
+						 jiffies_to_msecs(rio_cmd.timeout));
+			if (result == -ETIMEDOUT)
+				retries--;
+			else if (result < 0) {
+				dev_err(&rio->rio_dev->dev,
+					"Error executing ioctrl. code = %d\n",
+					result);
+				retries = 0;
+			} else {
+				dev_dbg(&rio->rio_dev->dev,
+					"Executed ioctl. Result = %d (data=%02x)\n",
+					result, buffer[0]);
+				if (copy_to_user(rio_cmd.buffer, buffer,
+						 rio_cmd.length)) {
+					free_page((unsigned long) buffer);
+					retval = -EFAULT;
+					goto err_out;
+				}
+				retries = 0;
+			}
+
+			/* rio_cmd.buffer contains a raw stream of single byte
+			   data which has been returned from rio.  Data is
+			   interpreted at application level.  For data that
+			   will be cast to data types longer than 1 byte, data
+			   will be little_endian and will potentially need to
+			   be swapped at the app level */
+
+		}
+		free_page((unsigned long) buffer);
+		break;
+
+	case RIO_SEND_COMMAND:
+		data = (void __user *) arg;
+		if (data == NULL)
+			break;
+		if (copy_from_user(&rio_cmd, data, sizeof(struct RioCommand))) {
+			retval = -EFAULT;
+			goto err_out;
+		}
+		if (rio_cmd.length < 0 || rio_cmd.length > PAGE_SIZE) {
+			retval = -EINVAL;
+			goto err_out;
+		}
+		buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+		if (buffer == NULL) {
+			retval = -ENOMEM;
+			goto err_out;
+		}
+		if (copy_from_user(buffer, rio_cmd.buffer, rio_cmd.length)) {
+			free_page((unsigned long)buffer);
+			retval = -EFAULT;
+			goto err_out;
+		}
+
+		requesttype = rio_cmd.requesttype | USB_DIR_OUT |
+		    USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+		dev_dbg(&rio->rio_dev->dev,
+			"sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
+			requesttype, rio_cmd.request, rio_cmd.value,
+			rio_cmd.index, rio_cmd.length);
+		/* Send rio control message */
+		retries = 3;
+		while (retries) {
+			result = usb_control_msg(rio->rio_dev,
+						 usb_sndctrlpipe(rio-> rio_dev, 0),
+						 rio_cmd.request,
+						 requesttype,
+						 rio_cmd.value,
+						 rio_cmd.index, buffer,
+						 rio_cmd.length,
+						 jiffies_to_msecs(rio_cmd.timeout));
+			if (result == -ETIMEDOUT)
+				retries--;
+			else if (result < 0) {
+				dev_err(&rio->rio_dev->dev,
+					"Error executing ioctrl. code = %d\n",
+					result);
+				retries = 0;
+			} else {
+				dev_dbg(&rio->rio_dev->dev,
+					"Executed ioctl. Result = %d\n", result);
+				retries = 0;
+
+			}
+
+		}
+		free_page((unsigned long) buffer);
+		break;
+
+	default:
+		retval = -ENOTTY;
+		break;
+	}
+
+
+err_out:
+	mutex_unlock(&(rio->lock));
+	return retval;
+}
+
+static ssize_t
+write_rio(struct file *file, const char __user *buffer,
+	  size_t count, loff_t * ppos)
+{
+	DEFINE_WAIT(wait);
+	struct rio_usb_data *rio = &rio_instance;
+
+	unsigned long copy_size;
+	unsigned long bytes_written = 0;
+	unsigned int partial;
+
+	int result = 0;
+	int maxretry;
+	int errn = 0;
+	int intr;
+
+	intr = mutex_lock_interruptible(&(rio->lock));
+	if (intr)
+		return -EINTR;
+        /* Sanity check to make sure rio is connected, powered, etc */
+        if (rio->present == 0 || rio->rio_dev == NULL) {
+		mutex_unlock(&(rio->lock));
+		return -ENODEV;
+	}
+
+
+
+	do {
+		unsigned long thistime;
+		char *obuf = rio->obuf;
+
+		thistime = copy_size =
+		    (count >= OBUF_SIZE) ? OBUF_SIZE : count;
+		if (copy_from_user(rio->obuf, buffer, copy_size)) {
+			errn = -EFAULT;
+			goto error;
+		}
+		maxretry = 5;
+		while (thistime) {
+			if (!rio->rio_dev) {
+				errn = -ENODEV;
+				goto error;
+			}
+			if (signal_pending(current)) {
+				mutex_unlock(&(rio->lock));
+				return bytes_written ? bytes_written : -EINTR;
+			}
+
+			result = usb_bulk_msg(rio->rio_dev,
+					 usb_sndbulkpipe(rio->rio_dev, 2),
+					 obuf, thistime, &partial, 5000);
+
+			dev_dbg(&rio->rio_dev->dev,
+				"write stats: result:%d thistime:%lu partial:%u\n",
+				result, thistime, partial);
+
+			if (result == -ETIMEDOUT) {	/* NAK - so hold for a while */
+				if (!maxretry--) {
+					errn = -ETIME;
+					goto error;
+				}
+				prepare_to_wait(&rio->wait_q, &wait, TASK_INTERRUPTIBLE);
+				schedule_timeout(NAK_TIMEOUT);
+				finish_wait(&rio->wait_q, &wait);
+				continue;
+			} else if (!result && partial) {
+				obuf += partial;
+				thistime -= partial;
+			} else
+				break;
+		}
+		if (result) {
+			dev_err(&rio->rio_dev->dev, "Write Whoops - %x\n",
+				result);
+			errn = -EIO;
+			goto error;
+		}
+		bytes_written += copy_size;
+		count -= copy_size;
+		buffer += copy_size;
+	} while (count > 0);
+
+	mutex_unlock(&(rio->lock));
+
+	return bytes_written ? bytes_written : -EIO;
+
+error:
+	mutex_unlock(&(rio->lock));
+	return errn;
+}
+
+static ssize_t
+read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
+{
+	DEFINE_WAIT(wait);
+	struct rio_usb_data *rio = &rio_instance;
+	ssize_t read_count;
+	unsigned int partial;
+	int this_read;
+	int result;
+	int maxretry = 10;
+	char *ibuf;
+	int intr;
+
+	intr = mutex_lock_interruptible(&(rio->lock));
+	if (intr)
+		return -EINTR;
+	/* Sanity check to make sure rio is connected, powered, etc */
+        if (rio->present == 0 || rio->rio_dev == NULL) {
+		mutex_unlock(&(rio->lock));
+		return -ENODEV;
+	}
+
+	ibuf = rio->ibuf;
+
+	read_count = 0;
+
+
+	while (count > 0) {
+		if (signal_pending(current)) {
+			mutex_unlock(&(rio->lock));
+			return read_count ? read_count : -EINTR;
+		}
+		if (!rio->rio_dev) {
+			mutex_unlock(&(rio->lock));
+			return -ENODEV;
+		}
+		this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
+
+		result = usb_bulk_msg(rio->rio_dev,
+				      usb_rcvbulkpipe(rio->rio_dev, 1),
+				      ibuf, this_read, &partial,
+				      8000);
+
+		dev_dbg(&rio->rio_dev->dev,
+			"read stats: result:%d this_read:%u partial:%u\n",
+			result, this_read, partial);
+
+		if (partial) {
+			count = this_read = partial;
+		} else if (result == -ETIMEDOUT || result == 15) {	/* FIXME: 15 ??? */
+			if (!maxretry--) {
+				mutex_unlock(&(rio->lock));
+				dev_err(&rio->rio_dev->dev,
+					"read_rio: maxretry timeout\n");
+				return -ETIME;
+			}
+			prepare_to_wait(&rio->wait_q, &wait, TASK_INTERRUPTIBLE);
+			schedule_timeout(NAK_TIMEOUT);
+			finish_wait(&rio->wait_q, &wait);
+			continue;
+		} else if (result != -EREMOTEIO) {
+			mutex_unlock(&(rio->lock));
+			dev_err(&rio->rio_dev->dev,
+				"Read Whoops - result:%u partial:%u this_read:%u\n",
+				result, partial, this_read);
+			return -EIO;
+		} else {
+			mutex_unlock(&(rio->lock));
+			return (0);
+		}
+
+		if (this_read) {
+			if (copy_to_user(buffer, ibuf, this_read)) {
+				mutex_unlock(&(rio->lock));
+				return -EFAULT;
+			}
+			count -= this_read;
+			read_count += this_read;
+			buffer += this_read;
+		}
+	}
+	mutex_unlock(&(rio->lock));
+	return read_count;
+}
+
+static const struct file_operations usb_rio_fops = {
+	.owner =	THIS_MODULE,
+	.read =		read_rio,
+	.write =	write_rio,
+	.unlocked_ioctl = ioctl_rio,
+	.open =		open_rio,
+	.release =	close_rio,
+	.llseek =	noop_llseek,
+};
+
+static struct usb_class_driver usb_rio_class = {
+	.name =		"rio500%d",
+	.fops =		&usb_rio_fops,
+	.minor_base =	RIO_MINOR,
+};
+
+static int probe_rio(struct usb_interface *intf,
+		     const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct rio_usb_data *rio = &rio_instance;
+	int retval;
+
+	dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
+
+	retval = usb_register_dev(intf, &usb_rio_class);
+	if (retval) {
+		dev_err(&dev->dev,
+			"Not able to get a minor for this device.\n");
+		return -ENOMEM;
+	}
+
+	rio->rio_dev = dev;
+
+	if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
+		dev_err(&dev->dev,
+			"probe_rio: Not enough memory for the output buffer\n");
+		usb_deregister_dev(intf, &usb_rio_class);
+		return -ENOMEM;
+	}
+	dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf);
+
+	if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) {
+		dev_err(&dev->dev,
+			"probe_rio: Not enough memory for the input buffer\n");
+		usb_deregister_dev(intf, &usb_rio_class);
+		kfree(rio->obuf);
+		return -ENOMEM;
+	}
+	dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf);
+
+	mutex_init(&(rio->lock));
+
+	usb_set_intfdata (intf, rio);
+	rio->present = 1;
+
+	return 0;
+}
+
+static void disconnect_rio(struct usb_interface *intf)
+{
+	struct rio_usb_data *rio = usb_get_intfdata (intf);
+
+	usb_set_intfdata (intf, NULL);
+	mutex_lock(&rio500_mutex);
+	if (rio) {
+		usb_deregister_dev(intf, &usb_rio_class);
+
+		mutex_lock(&(rio->lock));
+		if (rio->isopen) {
+			rio->isopen = 0;
+			/* better let it finish - the release will do whats needed */
+			rio->rio_dev = NULL;
+			mutex_unlock(&(rio->lock));
+			mutex_unlock(&rio500_mutex);
+			return;
+		}
+		kfree(rio->ibuf);
+		kfree(rio->obuf);
+
+		dev_info(&intf->dev, "USB Rio disconnected.\n");
+
+		rio->present = 0;
+		mutex_unlock(&(rio->lock));
+	}
+	mutex_unlock(&rio500_mutex);
+}
+
+static const struct usb_device_id rio_table[] = {
+	{ USB_DEVICE(0x0841, 1) }, 		/* Rio 500 */
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, rio_table);
+
+static struct usb_driver rio_driver = {
+	.name =		"rio500",
+	.probe =	probe_rio,
+	.disconnect =	disconnect_rio,
+	.id_table =	rio_table,
+};
+
+module_usb_driver(rio_driver);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/misc/rio500_usb.h b/drivers/usb/misc/rio500_usb.h
new file mode 100644
index 0000000..359abc9
--- /dev/null
+++ b/drivers/usb/misc/rio500_usb.h
@@ -0,0 +1,37 @@
+/*  ----------------------------------------------------------------------
+
+    Copyright (C) 2000  Cesar Miquel  (miquel@df.uba.ar)
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    ---------------------------------------------------------------------- */
+
+
+
+#define RIO_SEND_COMMAND			0x1
+#define RIO_RECV_COMMAND			0x2
+
+#define RIO_DIR_OUT               	        0x0
+#define RIO_DIR_IN				0x1
+
+struct RioCommand {
+	short length;
+	int request;
+	int requesttype;
+	int value;
+	int index;
+	void __user *buffer;
+	int timeout;
+};
diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig
new file mode 100644
index 0000000..36bc28c
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/Kconfig
@@ -0,0 +1,47 @@
+
+config USB_SISUSBVGA
+	tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)"
+	depends on (USB_MUSB_HDRC || USB_EHCI_HCD)
+	select FONT_SUPPORT if USB_SISUSBVGA_CON
+        ---help---
+	  Say Y here if you intend to attach a USB2VGA dongle based on a
+	  Net2280 and a SiS315 chip.
+
+	  Note that this device requires a USB 2.0 host controller. It will not
+	  work with USB 1.x controllers.
+
+	  To compile this driver as a module, choose M here; the module will be
+	  called sisusbvga. If unsure, say N.
+
+config USB_SISUSBVGA_CON
+	bool "Text console and mode switching support" if USB_SISUSBVGA
+	depends on VT
+	select FONT_8x16
+	---help---
+	  Say Y here if you want a VGA text console via the USB dongle or
+	  want to support userland applications that utilize the driver's
+	  display mode switching capabilities.
+
+	  Note that this console supports VGA/EGA text mode only.
+
+	  By default, the console part of the driver will not kick in when
+	  the driver is initialized. If you want the driver to take over
+	  one or more of the consoles, you need to specify the number of
+	  the first and last consoles (starting at 1) as driver parameters.
+
+	  For example, if the driver is compiled as a module:
+
+	     modprobe sisusbvga first=1 last=5
+
+	  If you use hotplug, add this to your modutils config files with
+	  the "options" keyword, such as eg.
+
+	     options sisusbvga first=1 last=5
+
+	  If the driver is compiled into the kernel image, the parameters
+	  must be given in the kernel command like, such as
+
+	     sisusbvga.first=1 sisusbvga.last=5
+
+
+
diff --git a/drivers/usb/misc/sisusbvga/Makefile b/drivers/usb/misc/sisusbvga/Makefile
new file mode 100644
index 0000000..3142476
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the sisusb driver (if driver is inside kernel tree).
+#
+
+obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga.o
+
+sisusbvga-y := sisusb.o sisusb_init.o sisusb_con.o
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
new file mode 100644
index 0000000..306d685
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -0,0 +1,3301 @@
+/*
+ * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
+ *
+ * Main part
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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 the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 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 the author may not be used to endorse or promote products
+ * *    derived from this software without specific psisusbr written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED 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 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.
+ *
+ * Author:	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+
+#include "sisusb.h"
+#include "sisusb_init.h"
+
+#ifdef INCL_SISUSB_CON
+#include <linux/font.h>
+#endif
+
+#define SISUSB_DONTSYNC
+
+/* Forward declarations / clean-up routines */
+
+#ifdef INCL_SISUSB_CON
+static int sisusb_first_vc = 0;
+static int sisusb_last_vc = 0;
+module_param_named(first, sisusb_first_vc, int, 0);
+module_param_named(last, sisusb_last_vc, int, 0);
+MODULE_PARM_DESC(first, "Number of first console to take over (1 - MAX_NR_CONSOLES)");
+MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES)");
+#endif
+
+static struct usb_driver sisusb_driver;
+
+static void
+sisusb_free_buffers(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < NUMOBUFS; i++) {
+		if (sisusb->obuf[i]) {
+			kfree(sisusb->obuf[i]);
+			sisusb->obuf[i] = NULL;
+		}
+	}
+	if (sisusb->ibuf) {
+		kfree(sisusb->ibuf);
+		sisusb->ibuf = NULL;
+	}
+}
+
+static void
+sisusb_free_urbs(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < NUMOBUFS; i++) {
+		usb_free_urb(sisusb->sisurbout[i]);
+		sisusb->sisurbout[i] = NULL;
+	}
+	usb_free_urb(sisusb->sisurbin);
+	sisusb->sisurbin = NULL;
+}
+
+/* Level 0: USB transport layer */
+
+/* 1. out-bulks */
+
+/* out-urb management */
+
+/* Return 1 if all free, 0 otherwise */
+static int
+sisusb_all_free(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+
+		if (sisusb->urbstatus[i] & SU_URB_BUSY)
+			return 0;
+
+	}
+
+	return 1;
+}
+
+/* Kill all busy URBs */
+static void
+sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	if (sisusb_all_free(sisusb))
+		return;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+
+		if (sisusb->urbstatus[i] & SU_URB_BUSY)
+			usb_kill_urb(sisusb->sisurbout[i]);
+
+	}
+}
+
+/* Return 1 if ok, 0 if error (not all complete within timeout) */
+static int
+sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
+{
+	int timeout = 5 * HZ, i = 1;
+
+	wait_event_timeout(sisusb->wait_q,
+				(i = sisusb_all_free(sisusb)),
+				 timeout);
+
+	return i;
+}
+
+static int
+sisusb_outurb_available(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+
+		if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
+			return i;
+
+	}
+
+	return -1;
+}
+
+static int
+sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
+{
+	int i, timeout = 5 * HZ;
+
+	wait_event_timeout(sisusb->wait_q,
+				((i = sisusb_outurb_available(sisusb)) >= 0),
+				timeout);
+
+	return i;
+}
+
+static int
+sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	i = sisusb_outurb_available(sisusb);
+
+	if (i >= 0)
+		sisusb->urbstatus[i] |= SU_URB_ALLOC;
+
+	return i;
+}
+
+static void
+sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
+{
+	if ((index >= 0) && (index < sisusb->numobufs))
+		sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
+}
+
+/* completion callback */
+
+static void
+sisusb_bulk_completeout(struct urb *urb)
+{
+	struct sisusb_urb_context *context = urb->context;
+	struct sisusb_usb_data *sisusb;
+
+	if (!context)
+		return;
+
+	sisusb = context->sisusb;
+
+	if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
+		return;
+
+#ifndef SISUSB_DONTSYNC
+	if (context->actual_length)
+		*(context->actual_length) += urb->actual_length;
+#endif
+
+	sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
+	wake_up(&sisusb->wait_q);
+}
+
+static int
+sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
+		int len, int *actual_length, int timeout, unsigned int tflags)
+{
+	struct urb *urb = sisusb->sisurbout[index];
+	int retval, byteswritten = 0;
+
+	/* Set up URB */
+	urb->transfer_flags = 0;
+
+	usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
+		sisusb_bulk_completeout, &sisusb->urbout_context[index]);
+
+	urb->transfer_flags |= tflags;
+	urb->actual_length = 0;
+
+	/* Set up context */
+	sisusb->urbout_context[index].actual_length = (timeout) ?
+						NULL : actual_length;
+
+	/* Declare this urb/buffer in use */
+	sisusb->urbstatus[index] |= SU_URB_BUSY;
+
+	/* Submit URB */
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+
+	/* If OK, and if timeout > 0, wait for completion */
+	if ((retval == 0) && timeout) {
+		wait_event_timeout(sisusb->wait_q,
+				   (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
+				   timeout);
+		if (sisusb->urbstatus[index] & SU_URB_BUSY) {
+			/* URB timed out... kill it and report error */
+			usb_kill_urb(urb);
+			retval = -ETIMEDOUT;
+		} else {
+			/* Otherwise, report urb status */
+			retval = urb->status;
+			byteswritten = urb->actual_length;
+		}
+	}
+
+	if (actual_length)
+		*actual_length = byteswritten;
+
+	return retval;
+}
+
+/* 2. in-bulks */
+
+/* completion callback */
+
+static void
+sisusb_bulk_completein(struct urb *urb)
+{
+	struct sisusb_usb_data *sisusb = urb->context;
+
+	if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
+		return;
+
+	sisusb->completein = 1;
+	wake_up(&sisusb->wait_q);
+}
+
+static int
+sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data,
+	int len, int *actual_length, int timeout, unsigned int tflags)
+{
+	struct urb *urb = sisusb->sisurbin;
+	int retval, readbytes = 0;
+
+	urb->transfer_flags = 0;
+
+	usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
+			sisusb_bulk_completein, sisusb);
+
+	urb->transfer_flags |= tflags;
+	urb->actual_length = 0;
+
+	sisusb->completein = 0;
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval == 0) {
+		wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
+		if (!sisusb->completein) {
+			/* URB timed out... kill it and report error */
+			usb_kill_urb(urb);
+			retval = -ETIMEDOUT;
+		} else {
+			/* URB completed within timeout */
+			retval = urb->status;
+			readbytes = urb->actual_length;
+		}
+	}
+
+	if (actual_length)
+		*actual_length = readbytes;
+
+	return retval;
+}
+
+
+/* Level 1:  */
+
+/* Send a bulk message of variable size
+ *
+ * To copy the data from userspace, give pointer to "userbuffer",
+ * to copy from (non-DMA) kernel memory, give "kernbuffer". If
+ * both of these are NULL, it is assumed, that the transfer
+ * buffer "sisusb->obuf[index]" is set up with the data to send.
+ * Index is ignored if either kernbuffer or userbuffer is set.
+ * If async is nonzero, URBs will be sent without waiting for
+ * completion of the previous URB.
+ *
+ * (return 0 on success)
+ */
+
+static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
+		char *kernbuffer, const char __user *userbuffer, int index,
+		ssize_t *bytes_written, unsigned int tflags, int async)
+{
+	int result = 0, retry, count = len;
+	int passsize, thispass, transferred_len = 0;
+	int fromuser = (userbuffer != NULL) ? 1 : 0;
+	int fromkern = (kernbuffer != NULL) ? 1 : 0;
+	unsigned int pipe;
+	char *buffer;
+
+	(*bytes_written) = 0;
+
+	/* Sanity check */
+	if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
+		return -ENODEV;
+
+	/* If we copy data from kernel or userspace, force the
+	 * allocation of a buffer/urb. If we have the data in
+	 * the transfer buffer[index] already, reuse the buffer/URB
+	 * if the length is > buffer size. (So, transmitting
+	 * large data amounts directly from the transfer buffer
+	 * treats the buffer as a ring buffer. However, we need
+	 * to sync in this case.)
+	 */
+	if (fromuser || fromkern)
+		index = -1;
+	else if (len > sisusb->obufsize)
+		async = 0;
+
+	pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
+
+	do {
+		passsize = thispass = (sisusb->obufsize < count) ?
+						sisusb->obufsize : count;
+
+		if (index < 0)
+			index = sisusb_get_free_outbuf(sisusb);
+
+		if (index < 0)
+			return -EIO;
+
+		buffer = sisusb->obuf[index];
+
+		if (fromuser) {
+
+			if (copy_from_user(buffer, userbuffer, passsize))
+				return -EFAULT;
+
+			userbuffer += passsize;
+
+		} else if (fromkern) {
+
+			memcpy(buffer, kernbuffer, passsize);
+			kernbuffer += passsize;
+
+		}
+
+		retry = 5;
+		while (thispass) {
+
+			if (!sisusb->sisusb_dev)
+				return -ENODEV;
+
+			result = sisusb_bulkout_msg(sisusb,
+						index,
+						pipe,
+						buffer,
+						thispass,
+						&transferred_len,
+						async ? 0 : 5 * HZ,
+						tflags);
+
+			if (result == -ETIMEDOUT) {
+
+				/* Will not happen if async */
+				if (!retry--)
+					return -ETIME;
+
+				continue;
+			}
+
+			if ((result == 0) && !async && transferred_len) {
+
+				thispass -= transferred_len;
+				buffer += transferred_len;
+
+			} else
+				break;
+		}
+
+		if (result)
+			return result;
+
+		(*bytes_written) += passsize;
+		count            -= passsize;
+
+		/* Force new allocation in next iteration */
+		if (fromuser || fromkern)
+			index = -1;
+
+	} while (count > 0);
+
+	if (async) {
+#ifdef SISUSB_DONTSYNC
+		(*bytes_written) = len;
+		/* Some URBs/buffers might be busy */
+#else
+		sisusb_wait_all_out_complete(sisusb);
+		(*bytes_written) = transferred_len;
+		/* All URBs and all buffers are available */
+#endif
+	}
+
+	return ((*bytes_written) == len) ? 0 : -EIO;
+}
+
+/* Receive a bulk message of variable size
+ *
+ * To copy the data to userspace, give pointer to "userbuffer",
+ * to copy to kernel memory, give "kernbuffer". One of them
+ * MUST be set. (There is no technique for letting the caller
+ * read directly from the ibuf.)
+ *
+ */
+
+static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
+		void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
+		unsigned int tflags)
+{
+	int result = 0, retry, count = len;
+	int bufsize, thispass, transferred_len;
+	unsigned int pipe;
+	char *buffer;
+
+	(*bytes_read) = 0;
+
+	/* Sanity check */
+	if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
+		return -ENODEV;
+
+	pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
+	buffer = sisusb->ibuf;
+	bufsize = sisusb->ibufsize;
+
+	retry = 5;
+
+#ifdef SISUSB_DONTSYNC
+	if (!(sisusb_wait_all_out_complete(sisusb)))
+		return -EIO;
+#endif
+
+	while (count > 0) {
+
+		if (!sisusb->sisusb_dev)
+			return -ENODEV;
+
+		thispass = (bufsize < count) ? bufsize : count;
+
+		result = sisusb_bulkin_msg(sisusb,
+					   pipe,
+					   buffer,
+					   thispass,
+					   &transferred_len,
+					   5 * HZ,
+					   tflags);
+
+		if (transferred_len)
+			thispass = transferred_len;
+
+		else if (result == -ETIMEDOUT) {
+
+			if (!retry--)
+				return -ETIME;
+
+			continue;
+
+		} else
+			return -EIO;
+
+
+		if (thispass) {
+
+			(*bytes_read) += thispass;
+			count         -= thispass;
+
+			if (userbuffer) {
+
+				if (copy_to_user(userbuffer, buffer, thispass))
+					return -EFAULT;
+
+				userbuffer += thispass;
+
+			} else {
+
+				memcpy(kernbuffer, buffer, thispass);
+				kernbuffer += thispass;
+
+			}
+
+		}
+
+	}
+
+	return ((*bytes_read) == len) ? 0 : -EIO;
+}
+
+static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
+						struct sisusb_packet *packet)
+{
+	int ret;
+	ssize_t bytes_transferred = 0;
+	__le32 tmp;
+
+	if (len == 6)
+		packet->data = 0;
+
+#ifdef SISUSB_DONTSYNC
+	if (!(sisusb_wait_all_out_complete(sisusb)))
+		return 1;
+#endif
+
+	/* Eventually correct endianness */
+	SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
+
+	/* 1. send the packet */
+	ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
+			(char *)packet, NULL, 0, &bytes_transferred, 0, 0);
+
+	if ((ret == 0) && (len == 6)) {
+
+		/* 2. if packet len == 6, it means we read, so wait for 32bit
+		 *    return value and write it to packet->data
+		 */
+		ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
+				(char *)&tmp, NULL, &bytes_transferred, 0);
+
+		packet->data = le32_to_cpu(tmp);
+	}
+
+	return ret;
+}
+
+static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
+					struct sisusb_packet *packet,
+					unsigned int tflags)
+{
+	int ret;
+	ssize_t bytes_transferred = 0;
+	__le32 tmp;
+
+	if (len == 6)
+		packet->data = 0;
+
+#ifdef SISUSB_DONTSYNC
+	if (!(sisusb_wait_all_out_complete(sisusb)))
+		return 1;
+#endif
+
+	/* Eventually correct endianness */
+	SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
+
+	/* 1. send the packet */
+	ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
+			(char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
+
+	if ((ret == 0) && (len == 6)) {
+
+		/* 2. if packet len == 6, it means we read, so wait for 32bit
+		 *    return value and write it to packet->data
+		 */
+		ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
+				(char *)&tmp, NULL, &bytes_transferred, 0);
+
+		packet->data = le32_to_cpu(tmp);
+	}
+
+	return ret;
+}
+
+/* access video memory and mmio (return 0 on success) */
+
+/* Low level */
+
+/* The following routines assume being used to transfer byte, word,
+ * long etc.
+ * This means that
+ *   - the write routines expect "data" in machine endianness format.
+ *     The data will be converted to leXX in sisusb_xxx_packet.
+ *   - the read routines can expect read data in machine-endianess.
+ */
+
+static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u8 data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	packet.header  = (1 << (addr & 3)) | (type << 6);
+	packet.address = addr & ~3;
+	packet.data    = data << ((addr & 3) << 3);
+	ret = sisusb_send_packet(sisusb, 10, &packet);
+	return ret;
+}
+
+static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u16 data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header = (type << 6) | 0x0003;
+			packet.data   = (u32)data;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 1:
+			packet.header = (type << 6) | 0x0006;
+			packet.data   = (u32)data << 8;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 2:
+			packet.header = (type << 6) | 0x000c;
+			packet.data   = (u32)data << 16;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 3:
+			packet.header = (type << 6) | 0x0008;
+			packet.data   = (u32)data << 24;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			packet.data   = (u32)data >> 8;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+	}
+
+	return ret;
+}
+
+static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x0007;
+			packet.data    = data & 0x00ffffff;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			packet.data    = data << 8;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			packet.data    = data << 16;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = (data >> 16) & 0x00ff;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			packet.data    = data << 24;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = (data >> 8) & 0xffff;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+	}
+
+	return ret;
+}
+
+static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x000f;
+			packet.data    = data;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			packet.data    = data << 8;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = data >> 24;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			packet.data    = data << 16;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = data >> 16;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			packet.data    = data << 24;
+			ret = sisusb_send_packet(sisusb, 10, &packet);
+			packet.header  = (type << 6) | 0x0007;
+			packet.address = (addr & ~3) + 4;
+			packet.data    = data >> 8;
+			ret |= sisusb_send_packet(sisusb, 10, &packet);
+	}
+
+	return ret;
+}
+
+/* The xxx_bulk routines copy a buffer of variable size. They treat the
+ * buffer as chars, therefore lsb/msb has to be corrected if using the
+ * byte/word/long/etc routines for speed-up
+ *
+ * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
+ * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
+ * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
+ * that the data already is in the transfer buffer "sisusb->obuf[index]".
+ */
+
+static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
+				char *kernbuffer, int length,
+				const char __user *userbuffer, int index,
+				ssize_t *bytes_written)
+{
+	struct sisusb_packet packet;
+	int  ret = 0;
+	static int msgcount = 0;
+	u8   swap8, fromkern = kernbuffer ? 1 : 0;
+	u16  swap16;
+	u32  swap32, flag = (length >> 28) & 1;
+	char buf[4];
+
+	/* if neither kernbuffer not userbuffer are given, assume
+	 * data in obuf
+	 */
+	if (!fromkern && !userbuffer)
+		kernbuffer = sisusb->obuf[index];
+
+	(*bytes_written = 0);
+
+	length &= 0x00ffffff;
+
+	while (length) {
+
+	    switch (length) {
+
+		case 1:
+			if (userbuffer) {
+				if (get_user(swap8, (u8 __user *)userbuffer))
+					return -EFAULT;
+			} else
+				swap8 = kernbuffer[0];
+
+			ret = sisusb_write_memio_byte(sisusb,
+							SISUSB_TYPE_MEM,
+							addr, swap8);
+
+			if (!ret)
+				(*bytes_written)++;
+
+			return ret;
+
+		case 2:
+			if (userbuffer) {
+				if (get_user(swap16, (u16 __user *)userbuffer))
+					return -EFAULT;
+			} else
+				swap16 = *((u16 *)kernbuffer);
+
+			ret = sisusb_write_memio_word(sisusb,
+							SISUSB_TYPE_MEM,
+							addr,
+							swap16);
+
+			if (!ret)
+				(*bytes_written) += 2;
+
+			return ret;
+
+		case 3:
+			if (userbuffer) {
+				if (copy_from_user(&buf, userbuffer, 3))
+					return -EFAULT;
+#ifdef __BIG_ENDIAN
+				swap32 = (buf[0] << 16) |
+					 (buf[1] <<  8) |
+					 buf[2];
+#else
+				swap32 = (buf[2] << 16) |
+					 (buf[1] <<  8) |
+					 buf[0];
+#endif
+			} else
+#ifdef __BIG_ENDIAN
+				swap32 = (kernbuffer[0] << 16) |
+					 (kernbuffer[1] <<  8) |
+					 kernbuffer[2];
+#else
+				swap32 = (kernbuffer[2] << 16) |
+					 (kernbuffer[1] <<  8) |
+					 kernbuffer[0];
+#endif
+
+			ret = sisusb_write_memio_24bit(sisusb,
+							SISUSB_TYPE_MEM,
+							addr,
+							swap32);
+
+			if (!ret)
+				(*bytes_written) += 3;
+
+			return ret;
+
+		case 4:
+			if (userbuffer) {
+				if (get_user(swap32, (u32 __user *)userbuffer))
+					return -EFAULT;
+			} else
+				swap32 = *((u32 *)kernbuffer);
+
+			ret = sisusb_write_memio_long(sisusb,
+							SISUSB_TYPE_MEM,
+							addr,
+							swap32);
+			if (!ret)
+				(*bytes_written) += 4;
+
+			return ret;
+
+		default:
+			if ((length & ~3) > 0x10000) {
+
+			   packet.header  = 0x001f;
+			   packet.address = 0x000001d4;
+			   packet.data    = addr;
+			   ret = sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   packet.header  = 0x001f;
+			   packet.address = 0x000001d0;
+			   packet.data    = (length & ~3);
+			   ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   packet.header  = 0x001f;
+			   packet.address = 0x000001c0;
+			   packet.data    = flag | 0x16;
+			   ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   if (userbuffer) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_LBULK_OUT,
+							(length & ~3),
+							NULL, userbuffer, 0,
+							bytes_written, 0, 1);
+				userbuffer += (*bytes_written);
+			   } else if (fromkern) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_LBULK_OUT,
+							(length & ~3),
+							kernbuffer, NULL, 0,
+							bytes_written, 0, 1);
+				kernbuffer += (*bytes_written);
+			   } else {
+			ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_LBULK_OUT,
+							(length & ~3),
+							NULL, NULL, index,
+							bytes_written, 0, 1);
+				kernbuffer += ((*bytes_written) &
+						(sisusb->obufsize-1));
+			   }
+
+			} else {
+
+			   packet.header  = 0x001f;
+			   packet.address = 0x00000194;
+			   packet.data    = addr;
+			   ret = sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   packet.header  = 0x001f;
+			   packet.address = 0x00000190;
+			   packet.data    = (length & ~3);
+			   ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+			   if (sisusb->flagb0 != 0x16) {
+				packet.header  = 0x001f;
+				packet.address = 0x00000180;
+				packet.data    = flag | 0x16;
+				ret |= sisusb_send_bridge_packet(sisusb, 10,
+								&packet, 0);
+				sisusb->flagb0 = 0x16;
+			   }
+			   if (userbuffer) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_OUT,
+							(length & ~3),
+							NULL, userbuffer, 0,
+							bytes_written, 0, 1);
+				userbuffer += (*bytes_written);
+			   } else if (fromkern) {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_OUT,
+							(length & ~3),
+							kernbuffer, NULL, 0,
+							bytes_written, 0, 1);
+				kernbuffer += (*bytes_written);
+			   } else {
+				ret |= sisusb_send_bulk_msg(sisusb,
+							SISUSB_EP_GFX_BULK_OUT,
+							(length & ~3),
+							NULL, NULL, index,
+							bytes_written, 0, 1);
+				kernbuffer += ((*bytes_written) &
+						(sisusb->obufsize-1));
+			   }
+			}
+			if (ret) {
+				msgcount++;
+				if (msgcount < 500)
+					dev_err(&sisusb->sisusb_dev->dev, "Wrote %zd of %d bytes, error %d\n",
+						*bytes_written, length, ret);
+				else if (msgcount == 500)
+					dev_err(&sisusb->sisusb_dev->dev, "Too many errors, logging stopped\n");
+			}
+			addr += (*bytes_written);
+			length -= (*bytes_written);
+	    }
+
+	    if (ret)
+		break;
+
+	}
+
+	return ret ? -EIO : 0;
+}
+
+/* Remember: Read data in packet is in machine-endianess! So for
+ * byte, word, 24bit, long no endian correction is necessary.
+ */
+
+static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u8 *data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	CLEARPACKET(&packet);
+	packet.header  = (1 << (addr & 3)) | (type << 6);
+	packet.address = addr & ~3;
+	ret = sisusb_send_packet(sisusb, 6, &packet);
+	*data = (u8)(packet.data >> ((addr & 3) << 3));
+	return ret;
+}
+
+static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u16 *data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	CLEARPACKET(&packet);
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header = (type << 6) | 0x0003;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data);
+			break;
+		case 1:
+			packet.header = (type << 6) | 0x0006;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data >> 8);
+			break;
+		case 2:
+			packet.header = (type << 6) | 0x000c;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data >> 16);
+			break;
+		case 3:
+			packet.header = (type << 6) | 0x0008;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = (u16)(packet.data >> 24);
+			packet.header = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (u16)(packet.data << 8);
+	}
+
+	return ret;
+}
+
+static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 *data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x0007;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data & 0x00ffffff;
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 8;
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 16;
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= ((packet.data & 0xff) << 16);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 24;
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= ((packet.data & 0xffff) << 8);
+	}
+
+	return ret;
+}
+
+static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
+							u32 addr, u32 *data)
+{
+	struct sisusb_packet packet;
+	int ret = 0;
+
+	packet.address = addr & ~3;
+
+	switch (addr & 3) {
+		case 0:
+			packet.header  = (type << 6) | 0x000f;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data;
+			break;
+		case 1:
+			packet.header  = (type << 6) | 0x000e;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 8;
+			packet.header  = (type << 6) | 0x0001;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (packet.data << 24);
+			break;
+		case 2:
+			packet.header  = (type << 6) | 0x000c;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 16;
+			packet.header  = (type << 6) | 0x0003;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (packet.data << 16);
+			break;
+		case 3:
+			packet.header  = (type << 6) | 0x0008;
+			ret = sisusb_send_packet(sisusb, 6, &packet);
+			*data = packet.data >> 24;
+			packet.header  = (type << 6) | 0x0007;
+			packet.address = (addr & ~3) + 4;
+			ret |= sisusb_send_packet(sisusb, 6, &packet);
+			*data |= (packet.data << 8);
+	}
+
+	return ret;
+}
+
+static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
+				char *kernbuffer, int length,
+				char __user *userbuffer, ssize_t *bytes_read)
+{
+	int ret = 0;
+	char buf[4];
+	u16 swap16;
+	u32 swap32;
+
+	(*bytes_read = 0);
+
+	length &= 0x00ffffff;
+
+	while (length) {
+
+	    switch (length) {
+
+		case 1:
+
+			ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
+								addr, &buf[0]);
+			if (!ret) {
+				(*bytes_read)++;
+				if (userbuffer) {
+					if (put_user(buf[0],
+						(u8 __user *)userbuffer)) {
+						return -EFAULT;
+					}
+				} else {
+					kernbuffer[0] = buf[0];
+				}
+			}
+			return ret;
+
+		case 2:
+			ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
+								addr, &swap16);
+			if (!ret) {
+				(*bytes_read) += 2;
+				if (userbuffer) {
+					if (put_user(swap16,
+						(u16 __user *)userbuffer))
+						return -EFAULT;
+				} else {
+					*((u16 *)kernbuffer) = swap16;
+				}
+			}
+			return ret;
+
+		case 3:
+			ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
+								addr, &swap32);
+			if (!ret) {
+				(*bytes_read) += 3;
+#ifdef __BIG_ENDIAN
+				buf[0] = (swap32 >> 16) & 0xff;
+				buf[1] = (swap32 >> 8) & 0xff;
+				buf[2] = swap32 & 0xff;
+#else
+				buf[2] = (swap32 >> 16) & 0xff;
+				buf[1] = (swap32 >> 8) & 0xff;
+				buf[0] = swap32 & 0xff;
+#endif
+				if (userbuffer) {
+					if (copy_to_user(userbuffer, &buf[0], 3))
+						return -EFAULT;
+				} else {
+					kernbuffer[0] = buf[0];
+					kernbuffer[1] = buf[1];
+					kernbuffer[2] = buf[2];
+				}
+			}
+			return ret;
+
+		default:
+			ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
+								addr, &swap32);
+			if (!ret) {
+				(*bytes_read) += 4;
+				if (userbuffer) {
+					if (put_user(swap32,
+						(u32 __user *)userbuffer))
+						return -EFAULT;
+
+					userbuffer += 4;
+				} else {
+					*((u32 *)kernbuffer) = swap32;
+					kernbuffer += 4;
+				}
+				addr += 4;
+				length -= 4;
+			}
+	    }
+
+	    if (ret)
+		break;
+	}
+
+	return ret;
+}
+
+/* High level: Gfx (indexed) register access */
+
+#ifdef INCL_SISUSB_CON
+int
+sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
+{
+	return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
+}
+
+int
+sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
+{
+	return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
+}
+#endif
+
+int
+sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
+{
+	int ret;
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
+	ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
+	return ret;
+}
+
+int
+sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
+{
+	int ret;
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
+	ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
+	return ret;
+}
+
+int
+sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
+							u8 myand, u8 myor)
+{
+	int ret;
+	u8 tmp;
+
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
+	ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
+	tmp &= myand;
+	tmp |= myor;
+	ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
+	return ret;
+}
+
+static int
+sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx,
+							u8 data, u8 mask)
+{
+	int ret;
+	u8 tmp;
+	ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
+	ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
+	tmp &= ~(mask);
+	tmp |= (data & mask);
+	ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
+	return ret;
+}
+
+int
+sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
+{
+	return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
+}
+
+int
+sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
+{
+	return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00));
+}
+
+/* Write/read video ram */
+
+#ifdef INCL_SISUSB_CON
+int
+sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data)
+{
+	return(sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
+}
+
+int
+sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data)
+{
+	return(sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data));
+}
+
+int
+sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
+			u32 dest, int length, size_t *bytes_written)
+{
+	return(sisusb_write_mem_bulk(sisusb, dest, src, length, NULL, 0, bytes_written));
+}
+
+#ifdef SISUSBENDIANTEST
+int
+sisusb_read_memory(struct sisusb_usb_data *sisusb, char *dest,
+			u32 src, int length, size_t *bytes_written)
+{
+	return(sisusb_read_mem_bulk(sisusb, src, dest, length, NULL, bytes_written));
+}
+#endif
+#endif
+
+#ifdef SISUSBENDIANTEST
+static void
+sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
+{
+    static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+    char destbuffer[10];
+    size_t dummy;
+    int i,j;
+
+    sisusb_copy_memory(sisusb, srcbuffer, sisusb->vrambase, 7, &dummy);
+
+    for(i = 1; i <= 7; i++) {
+        dev_dbg(&sisusb->sisusb_dev->dev, "sisusb: rwtest %d bytes\n", i);
+	sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy);
+	for(j = 0; j < i; j++) {
+	     dev_dbg(&sisusb->sisusb_dev->dev, "rwtest read[%d] = %x\n", j, destbuffer[j]);
+	}
+    }
+}
+#endif
+
+/* access pci config registers (reg numbers 0, 4, 8, etc) */
+
+static int
+sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	packet.header = 0x008f;
+	packet.address = regnum | 0x10000;
+	packet.data = data;
+	ret = sisusb_send_packet(sisusb, 10, &packet);
+	return ret;
+}
+
+static int
+sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data)
+{
+	struct sisusb_packet packet;
+	int ret;
+
+	packet.header = 0x008f;
+	packet.address = (u32)regnum | 0x10000;
+	ret = sisusb_send_packet(sisusb, 6, &packet);
+	*data = packet.data;
+	return ret;
+}
+
+/* Clear video RAM */
+
+static int
+sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
+{
+	int ret, i;
+	ssize_t j;
+
+	if (address < sisusb->vrambase)
+		return 1;
+
+	if (address >= sisusb->vrambase + sisusb->vramsize)
+		return 1;
+
+	if (address + length > sisusb->vrambase + sisusb->vramsize)
+		length = sisusb->vrambase + sisusb->vramsize - address;
+
+	if (length <= 0)
+		return 0;
+
+	/* allocate free buffer/urb and clear the buffer */
+	if ((i = sisusb_alloc_outbuf(sisusb)) < 0)
+		return -EBUSY;
+
+	memset(sisusb->obuf[i], 0, sisusb->obufsize);
+
+	/* We can write a length > buffer size here. The buffer
+	 * data will simply be re-used (like a ring-buffer).
+	 */
+	ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
+
+	/* Free the buffer/urb */
+	sisusb_free_outbuf(sisusb, i);
+
+	return ret;
+}
+
+/* Initialize the graphics core (return 0 on success)
+ * This resets the graphics hardware and puts it into
+ * a defined mode (640x480@60Hz)
+ */
+
+#define GETREG(r,d)     sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
+#define SETREG(r,d)	sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
+#define SETIREG(r,i,d)	sisusb_setidxreg(sisusb, r, i, d)
+#define GETIREG(r,i,d)  sisusb_getidxreg(sisusb, r, i, d)
+#define SETIREGOR(r,i,o)	sisusb_setidxregor(sisusb, r, i, o)
+#define SETIREGAND(r,i,a)	sisusb_setidxregand(sisusb, r, i, a)
+#define SETIREGANDOR(r,i,a,o)	sisusb_setidxregandor(sisusb, r, i, a, o)
+#define READL(a,d)	sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
+#define WRITEL(a,d)	sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
+#define READB(a,d)	sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
+#define WRITEB(a,d)	sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
+
+static int
+sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
+{
+	int ret;
+	u8 tmp8;
+
+	ret = GETIREG(SISSR, 0x16, &tmp8);
+	if (ramtype <= 1) {
+		tmp8 &= 0x3f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0x80;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+	} else {
+		tmp8 |= 0xc0;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 &= 0x0f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0x80;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 &= 0x0f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0xd0;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 &= 0x0f;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+		tmp8 |= 0xa0;
+		ret |= SETIREG(SISSR, 0x16, tmp8);
+	}
+	return ret;
+}
+
+static int
+sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab)
+{
+	int ret;
+	u8  ramtype, done = 0;
+	u32 t0, t1, t2, t3;
+	u32 ramptr = SISUSB_PCI_MEMBASE;
+
+	ret = GETIREG(SISSR, 0x3a, &ramtype);
+	ramtype &= 3;
+
+	ret |= SETIREG(SISSR, 0x13, 0x00);
+
+	if (ramtype <= 1) {
+		ret |= SETIREG(SISSR, 0x14, 0x12);
+		ret |= SETIREGAND(SISSR, 0x15, 0xef);
+	} else {
+		ret |= SETIREG(SISSR, 0x14, 0x02);
+	}
+
+	ret |= sisusb_triggersr16(sisusb, ramtype);
+	ret |= WRITEL(ramptr +  0, 0x01234567);
+	ret |= WRITEL(ramptr +  4, 0x456789ab);
+	ret |= WRITEL(ramptr +  8, 0x89abcdef);
+	ret |= WRITEL(ramptr + 12, 0xcdef0123);
+	ret |= WRITEL(ramptr + 16, 0x55555555);
+	ret |= WRITEL(ramptr + 20, 0x55555555);
+	ret |= WRITEL(ramptr + 24, 0xffffffff);
+	ret |= WRITEL(ramptr + 28, 0xffffffff);
+	ret |= READL(ramptr +  0, &t0);
+	ret |= READL(ramptr +  4, &t1);
+	ret |= READL(ramptr +  8, &t2);
+	ret |= READL(ramptr + 12, &t3);
+
+	if (ramtype <= 1) {
+
+		*chab = 0; *bw = 64;
+
+		if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
+			if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
+				*chab = 0; *bw = 64;
+				ret |= SETIREGAND(SISSR, 0x14, 0xfd);
+			}
+		}
+		if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
+			*chab = 1; *bw = 64;
+			ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01);
+
+			ret |= sisusb_triggersr16(sisusb, ramtype);
+			ret |= WRITEL(ramptr +  0, 0x89abcdef);
+			ret |= WRITEL(ramptr +  4, 0xcdef0123);
+			ret |= WRITEL(ramptr +  8, 0x55555555);
+			ret |= WRITEL(ramptr + 12, 0x55555555);
+			ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
+			ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
+			ret |= READL(ramptr +  4, &t1);
+
+			if (t1 != 0xcdef0123) {
+				*bw = 32;
+				ret |= SETIREGOR(SISSR, 0x15, 0x10);
+			}
+		}
+
+	} else {
+
+		*chab = 0; *bw = 64;	/* default: cha, bw = 64 */
+
+		done = 0;
+
+		if (t1 == 0x456789ab) {
+			if (t0 == 0x01234567) {
+				*chab = 0; *bw = 64;
+				done = 1;
+			}
+		} else {
+			if (t0 == 0x01234567) {
+				*chab = 0; *bw = 32;
+				ret |= SETIREG(SISSR, 0x14, 0x00);
+				done = 1;
+			}
+		}
+
+		if (!done) {
+			ret |= SETIREG(SISSR, 0x14, 0x03);
+			ret |= sisusb_triggersr16(sisusb, ramtype);
+
+			ret |= WRITEL(ramptr +  0, 0x01234567);
+			ret |= WRITEL(ramptr +  4, 0x456789ab);
+			ret |= WRITEL(ramptr +  8, 0x89abcdef);
+			ret |= WRITEL(ramptr + 12, 0xcdef0123);
+			ret |= WRITEL(ramptr + 16, 0x55555555);
+			ret |= WRITEL(ramptr + 20, 0x55555555);
+			ret |= WRITEL(ramptr + 24, 0xffffffff);
+			ret |= WRITEL(ramptr + 28, 0xffffffff);
+			ret |= READL(ramptr +  0, &t0);
+			ret |= READL(ramptr +  4, &t1);
+
+			if (t1 == 0x456789ab) {
+				if (t0 == 0x01234567) {
+					*chab = 1; *bw = 64;
+					return ret;
+				} /* else error */
+			} else {
+				if (t0 == 0x01234567) {
+					*chab = 1; *bw = 32;
+					ret |= SETIREG(SISSR, 0x14, 0x01);
+				} /* else error */
+			}
+		}
+	}
+	return ret;
+}
+
+static int
+sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
+{
+	int ret = 0;
+	u32 ramptr = SISUSB_PCI_MEMBASE;
+	u8 tmp1, tmp2, i, j;
+
+	ret |= WRITEB(ramptr, 0xaa);
+	ret |= WRITEB(ramptr + 16, 0x55);
+	ret |= READB(ramptr, &tmp1);
+	ret |= READB(ramptr + 16, &tmp2);
+	if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
+		for (i = 0, j = 16; i < 2; i++, j += 16) {
+			ret |= GETIREG(SISSR, 0x21, &tmp1);
+			ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
+			ret |= SETIREGOR(SISSR, 0x3c, 0x01);  /* not on 330 */
+			ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
+			ret |= SETIREG(SISSR, 0x21, tmp1);
+			ret |= WRITEB(ramptr + 16 + j, j);
+			ret |= READB(ramptr + 16 + j, &tmp1);
+			if (tmp1 == j) {
+				ret |= WRITEB(ramptr + j, j);
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+static int
+sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index,
+			u8 rankno, u8 chab, const u8 dramtype[][5],
+			int bw)
+{
+	int ret = 0, ranksize;
+	u8 tmp;
+
+	*iret = 0;
+
+	if ((rankno == 2) && (dramtype[index][0] == 2))
+		return ret;
+
+	ranksize = dramtype[index][3] / 2 * bw / 32;
+
+	if ((ranksize * rankno) > 128)
+		return ret;
+
+	tmp = 0;
+	while ((ranksize >>= 1) > 0) tmp += 0x10;
+	tmp |= ((rankno - 1) << 2);
+	tmp |= ((bw / 64) & 0x02);
+	tmp |= (chab & 0x01);
+
+	ret = SETIREG(SISSR, 0x14, tmp);
+	ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
+
+	*iret = 1;
+
+	return ret;
+}
+
+static int
+sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn)
+{
+	int ret = 0, i;
+	u32 j, tmp;
+
+	*iret = 0;
+
+	for (i = 0, j = 0; i < testn; i++) {
+		ret |= WRITEL(sisusb->vrambase + j, j);
+		j += inc;
+	}
+
+	for (i = 0, j = 0; i < testn; i++) {
+		ret |= READL(sisusb->vrambase + j, &tmp);
+		if (tmp != j) return ret;
+		j += inc;
+	}
+
+	*iret = 1;
+	return ret;
+}
+
+static int
+sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno,
+					int idx, int bw, const u8 rtype[][5])
+{
+	int ret = 0, i, i2ret;
+	u32 inc;
+
+	*iret = 0;
+
+	for (i = rankno; i >= 1; i--) {
+		inc = 1 << (rtype[idx][2] +
+			    rtype[idx][1] +
+			    rtype[idx][0] +
+			    bw / 64 + i);
+		ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
+		if (!i2ret)
+			return ret;
+	}
+
+	inc = 1 << (rtype[idx][2] + bw / 64 + 2);
+	ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
+	if (!i2ret)
+		return ret;
+
+	inc = 1 << (10 + bw / 64);
+	ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
+	if (!i2ret)
+		return ret;
+
+	*iret = 1;
+	return ret;
+}
+
+static int
+sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw,
+								int chab)
+{
+	int ret = 0, i2ret = 0, i, j;
+	static const u8 sdramtype[13][5] = {
+		{ 2, 12, 9, 64, 0x35 },
+		{ 1, 13, 9, 64, 0x44 },
+		{ 2, 12, 8, 32, 0x31 },
+		{ 2, 11, 9, 32, 0x25 },
+		{ 1, 12, 9, 32, 0x34 },
+		{ 1, 13, 8, 32, 0x40 },
+		{ 2, 11, 8, 16, 0x21 },
+		{ 1, 12, 8, 16, 0x30 },
+		{ 1, 11, 9, 16, 0x24 },
+		{ 1, 11, 8,  8, 0x20 },
+		{ 2,  9, 8,  4, 0x01 },
+		{ 1, 10, 8,  4, 0x10 },
+		{ 1,  9, 8,  2, 0x00 }
+	};
+
+	*iret = 1; /* error */
+
+	for (i = 0; i < 13; i++) {
+		ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
+		for (j = 2; j > 0; j--) {
+			ret |= sisusb_set_rank(sisusb, &i2ret, i, j,
+						chab, sdramtype, bw);
+			if (!i2ret)
+				continue;
+
+			ret |= sisusb_check_ranks(sisusb, &i2ret, j, i,
+						bw, sdramtype);
+			if (i2ret) {
+				*iret = 0;	/* ram size found */
+				return ret;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int
+sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
+{
+	int ret = 0;
+	u32 address;
+	int i, length, modex, modey, bpp;
+
+	modex = 640; modey = 480; bpp = 2;
+
+	address = sisusb->vrambase;	/* Clear video ram */
+
+	if (clrall)
+		length = sisusb->vramsize;
+	else
+		length = modex * bpp * modey;
+
+	ret = sisusb_clear_vram(sisusb, address, length);
+
+	if (!ret && drwfr) {
+		for (i = 0; i < modex; i++) {
+			address = sisusb->vrambase + (i * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+			address += (modex * (modey-1) * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+		}
+		for (i = 0; i < modey; i++) {
+			address = sisusb->vrambase + ((i * modex) * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+			address += ((modex - 1) * bpp);
+			ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+							address, 0xf100);
+		}
+	}
+
+	return ret;
+}
+
+static int
+sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
+{
+	int ret = 0, i, j, modex, modey, bpp, du;
+	u8 sr31, cr63, tmp8;
+	static const char attrdata[] = {
+		0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+		0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+		0x01,0x00,0x00,0x00
+	};
+	static const char crtcrdata[] = {
+		0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
+		0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+		0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
+		0xff
+	};
+	static const char grcdata[] = {
+		0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
+		0xff
+	};
+	static const char crtcdata[] = {
+		0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e,
+		0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05,
+		0x00
+	};
+
+	modex = 640; modey = 480; bpp = 2;
+
+	GETIREG(SISSR, 0x31, &sr31);
+	GETIREG(SISCR, 0x63, &cr63);
+	SETIREGOR(SISSR, 0x01, 0x20);
+	SETIREG(SISCR, 0x63, cr63 & 0xbf);
+	SETIREGOR(SISCR, 0x17, 0x80);
+	SETIREGOR(SISSR, 0x1f, 0x04);
+	SETIREGAND(SISSR, 0x07, 0xfb);
+	SETIREG(SISSR, 0x00, 0x03);	/* seq */
+	SETIREG(SISSR, 0x01, 0x21);
+	SETIREG(SISSR, 0x02, 0x0f);
+	SETIREG(SISSR, 0x03, 0x00);
+	SETIREG(SISSR, 0x04, 0x0e);
+	SETREG(SISMISCW, 0x23);		/* misc */
+	for (i = 0; i <= 0x18; i++) {	/* crtc */
+		SETIREG(SISCR, i, crtcrdata[i]);
+	}
+	for (i = 0; i <= 0x13; i++) {	/* att */
+		GETREG(SISINPSTAT, &tmp8);
+		SETREG(SISAR, i);
+		SETREG(SISAR, attrdata[i]);
+	}
+	GETREG(SISINPSTAT, &tmp8);
+	SETREG(SISAR, 0x14);
+	SETREG(SISAR, 0x00);
+	GETREG(SISINPSTAT, &tmp8);
+	SETREG(SISAR, 0x20);
+	GETREG(SISINPSTAT, &tmp8);
+	for (i = 0; i <= 0x08; i++) {	/* grc */
+		SETIREG(SISGR, i, grcdata[i]);
+	}
+	SETIREGAND(SISGR, 0x05, 0xbf);
+	for (i = 0x0A; i <= 0x0E; i++) {	/* clr ext */
+		SETIREG(SISSR, i, 0x00);
+	}
+	SETIREGAND(SISSR, 0x37, 0xfe);
+	SETREG(SISMISCW, 0xef);		/* sync */
+	SETIREG(SISCR, 0x11, 0x00);	/* crtc */
+	for (j = 0x00, i = 0; i <= 7; i++, j++) {
+		SETIREG(SISCR, j, crtcdata[i]);
+	}
+	for (j = 0x10; i <= 10; i++, j++) {
+		SETIREG(SISCR, j, crtcdata[i]);
+	}
+	for (j = 0x15; i <= 12; i++, j++) {
+		SETIREG(SISCR, j, crtcdata[i]);
+	}
+	for (j = 0x0A; i <= 15; i++, j++) {
+		SETIREG(SISSR, j, crtcdata[i]);
+	}
+	SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
+	SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
+	SETIREG(SISCR, 0x14, 0x4f);
+	du = (modex / 16) * (bpp * 2);	/* offset/pitch */
+	if (modex % 16) du += bpp;
+	SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
+	SETIREG(SISCR, 0x13, (du & 0xff));
+	du <<= 5;
+	tmp8 = du >> 8;
+	if (du & 0xff) tmp8++;
+	SETIREG(SISSR, 0x10, tmp8);
+	SETIREG(SISSR, 0x31, 0x00);	/* VCLK */
+	SETIREG(SISSR, 0x2b, 0x1b);
+	SETIREG(SISSR, 0x2c, 0xe1);
+	SETIREG(SISSR, 0x2d, 0x01);
+	SETIREGAND(SISSR, 0x3d, 0xfe);	/* FIFO */
+	SETIREG(SISSR, 0x08, 0xae);
+	SETIREGAND(SISSR, 0x09, 0xf0);
+	SETIREG(SISSR, 0x08, 0x34);
+	SETIREGOR(SISSR, 0x3d, 0x01);
+	SETIREGAND(SISSR, 0x1f, 0x3f);	/* mode regs */
+	SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
+	SETIREG(SISCR, 0x19, 0x00);
+	SETIREGAND(SISCR, 0x1a, 0xfc);
+	SETIREGAND(SISSR, 0x0f, 0xb7);
+	SETIREGAND(SISSR, 0x31, 0xfb);
+	SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
+	SETIREGAND(SISSR, 0x32, 0xf3);
+	SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
+	SETIREG(SISCR, 0x52, 0x6c);
+
+	SETIREG(SISCR, 0x0d, 0x00);	/* adjust frame */
+	SETIREG(SISCR, 0x0c, 0x00);
+	SETIREG(SISSR, 0x0d, 0x00);
+	SETIREGAND(SISSR, 0x37, 0xfe);
+
+	SETIREG(SISCR, 0x32, 0x20);
+	SETIREGAND(SISSR, 0x01, 0xdf);	/* enable display */
+	SETIREG(SISCR, 0x63, (cr63 & 0xbf));
+	SETIREG(SISSR, 0x31, (sr31 & 0xfb));
+
+	if (touchengines) {
+		SETIREG(SISSR, 0x20, 0xa1);	/* enable engines */
+		SETIREGOR(SISSR, 0x1e, 0x5a);
+
+		SETIREG(SISSR, 0x26, 0x01);	/* disable cmdqueue */
+		SETIREG(SISSR, 0x27, 0x1f);
+		SETIREG(SISSR, 0x26, 0x00);
+	}
+
+	SETIREG(SISCR, 0x34, 0x44);	/* we just set std mode #44 */
+
+	return ret;
+}
+
+static int
+sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
+{
+	int ret = 0, i, j, bw, chab, iret, retry = 3;
+	u8 tmp8, ramtype;
+	u32 tmp32;
+	static const char mclktable[] = {
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143
+	};
+	static const char eclktable[] = {
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143,
+		0x3b, 0x22, 0x01, 143
+	};
+	static const char ramtypetable1[] = {
+		0x00, 0x04, 0x60, 0x60,
+		0x0f, 0x0f, 0x1f, 0x1f,
+		0xba, 0xba, 0xba, 0xba,
+		0xa9, 0xa9, 0xac, 0xac,
+		0xa0, 0xa0, 0xa0, 0xa8,
+		0x00, 0x00, 0x02, 0x02,
+		0x30, 0x30, 0x40, 0x40
+	};
+	static const char ramtypetable2[] = {
+		0x77, 0x77, 0x44, 0x44,
+		0x77, 0x77, 0x44, 0x44,
+		0x00, 0x00, 0x00, 0x00,
+		0x5b, 0x5b, 0xab, 0xab,
+		0x00, 0x00, 0xf0, 0xf8
+	};
+
+	while (retry--) {
+
+		/* Enable VGA */
+		ret = GETREG(SISVGAEN, &tmp8);
+		ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
+
+		/* Enable GPU access to VRAM */
+		ret |= GETREG(SISMISCR, &tmp8);
+		ret |= SETREG(SISMISCW, (tmp8 | 0x01));
+
+		if (ret) continue;
+
+		/* Reset registers */
+		ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
+		ret |= SETIREG(SISSR, 0x05, 0x86);
+		ret |= SETIREGOR(SISSR, 0x20, 0x01);
+
+		ret |= SETREG(SISMISCW, 0x67);
+
+		for (i = 0x06; i <= 0x1f; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x21; i <= 0x27; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x31; i <= 0x3d; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x12; i <= 0x1b; i++) {
+			ret |= SETIREG(SISSR, i, 0x00);
+		}
+		for (i = 0x79; i <= 0x7c; i++) {
+			ret |= SETIREG(SISCR, i, 0x00);
+		}
+
+		if (ret) continue;
+
+		ret |= SETIREG(SISCR, 0x63, 0x80);
+
+		ret |= GETIREG(SISSR, 0x3a, &ramtype);
+		ramtype &= 0x03;
+
+		ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
+		ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
+		ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
+
+		ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
+		ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
+		ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
+
+		ret |= SETIREG(SISSR, 0x07, 0x18);
+		ret |= SETIREG(SISSR, 0x11, 0x0f);
+
+		if (ret) continue;
+
+		for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
+			ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]);
+		}
+		for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
+			ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]);
+		}
+
+		ret |= SETIREG(SISCR, 0x49, 0xaa);
+
+		ret |= SETIREG(SISSR, 0x1f, 0x00);
+		ret |= SETIREG(SISSR, 0x20, 0xa0);
+		ret |= SETIREG(SISSR, 0x23, 0xf6);
+		ret |= SETIREG(SISSR, 0x24, 0x0d);
+		ret |= SETIREG(SISSR, 0x25, 0x33);
+
+		ret |= SETIREG(SISSR, 0x11, 0x0f);
+
+		ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
+
+		ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
+
+		if (ret) continue;
+
+		ret |= SETIREG(SISPART1, 0x00, 0x00);
+
+		ret |= GETIREG(SISSR, 0x13, &tmp8);
+		tmp8 >>= 4;
+
+		ret |= SETIREG(SISPART1, 0x02, 0x00);
+		ret |= SETIREG(SISPART1, 0x2e, 0x08);
+
+		ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
+		tmp32 &= 0x00f00000;
+		tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
+		ret |= SETIREG(SISSR, 0x25, tmp8);
+		tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
+		ret |= SETIREG(SISCR, 0x49, tmp8);
+
+		ret |= SETIREG(SISSR, 0x27, 0x1f);
+		ret |= SETIREG(SISSR, 0x31, 0x00);
+		ret |= SETIREG(SISSR, 0x32, 0x11);
+		ret |= SETIREG(SISSR, 0x33, 0x00);
+
+		if (ret) continue;
+
+		ret |= SETIREG(SISCR, 0x83, 0x00);
+
+		ret |= sisusb_set_default_mode(sisusb, 0);
+
+		ret |= SETIREGAND(SISSR, 0x21, 0xdf);
+		ret |= SETIREGOR(SISSR, 0x01, 0x20);
+		ret |= SETIREGOR(SISSR, 0x16, 0x0f);
+
+		ret |= sisusb_triggersr16(sisusb, ramtype);
+
+		/* Disable refresh */
+		ret |= SETIREGAND(SISSR, 0x17, 0xf8);
+		ret |= SETIREGOR(SISSR, 0x19, 0x03);
+
+		ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
+		ret |= sisusb_verify_mclk(sisusb);
+
+		if (ramtype <= 1) {
+			ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
+			if (iret) {
+				dev_err(&sisusb->sisusb_dev->dev,"RAM size detection failed, assuming 8MB video RAM\n");
+				ret |= SETIREG(SISSR,0x14,0x31);
+				/* TODO */
+			}
+		} else {
+			dev_err(&sisusb->sisusb_dev->dev, "DDR RAM device found, assuming 8MB video RAM\n");
+			ret |= SETIREG(SISSR,0x14,0x31);
+			/* *** TODO *** */
+		}
+
+		/* Enable refresh */
+		ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
+		ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
+		ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
+
+		ret |= SETIREGOR(SISSR, 0x21, 0x20);
+
+		ret |= SETIREG(SISSR, 0x22, 0xfb);
+		ret |= SETIREG(SISSR, 0x21, 0xa5);
+
+		if (ret == 0)
+			break;
+	}
+
+	return ret;
+}
+
+#undef SETREG
+#undef GETREG
+#undef SETIREG
+#undef GETIREG
+#undef SETIREGOR
+#undef SETIREGAND
+#undef SETIREGANDOR
+#undef READL
+#undef WRITEL
+
+static void
+sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
+{
+	u8 tmp8, tmp82, ramtype;
+	int bw = 0;
+	char *ramtypetext1 = NULL;
+	static const char ram_datarate[4] = {'S', 'S', 'D', 'D'};
+	static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'};
+	static const int busSDR[4]  = {64, 64, 128, 128};
+	static const int busDDR[4]  = {32, 32,  64,  64};
+	static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
+
+	sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
+	sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
+	sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
+	sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
+	ramtype &= 0x03;
+	switch ((tmp8 >> 2) & 0x03) {
+	case 0: ramtypetext1 = "1 ch/1 r";
+		if (tmp82 & 0x10) {
+			bw = 32;
+		} else {
+			bw = busSDR[(tmp8 & 0x03)];
+		}
+		break;
+	case 1: ramtypetext1 = "1 ch/2 r";
+		sisusb->vramsize <<= 1;
+		bw = busSDR[(tmp8 & 0x03)];
+		break;
+	case 2: ramtypetext1 = "asymmeric";
+		sisusb->vramsize += sisusb->vramsize/2;
+		bw = busDDRA[(tmp8 & 0x03)];
+		break;
+	case 3: ramtypetext1 = "2 channel";
+		sisusb->vramsize <<= 1;
+		bw = busDDR[(tmp8 & 0x03)];
+		break;
+	}
+
+
+	dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %cDR S%cRAM, bus width %d\n",
+		 sisusb->vramsize >> 20, ramtypetext1,
+		 ram_datarate[ramtype], ram_dynamictype[ramtype], bw);
+}
+
+static int
+sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
+{
+	struct sisusb_packet packet;
+	int ret;
+	u32 tmp32;
+
+	/* Do some magic */
+	packet.header  = 0x001f;
+	packet.address = 0x00000324;
+	packet.data    = 0x00000004;
+	ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x001f;
+	packet.address = 0x00000364;
+	packet.data    = 0x00000004;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x001f;
+	packet.address = 0x00000384;
+	packet.data    = 0x00000004;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x001f;
+	packet.address = 0x00000100;
+	packet.data    = 0x00000700;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	packet.header  = 0x000f;
+	packet.address = 0x00000004;
+	ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
+	packet.data |= 0x17;
+	ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+	/* Init BAR 0 (VRAM) */
+	ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+	ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
+	ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+	tmp32 &= 0x0f;
+	tmp32 |= SISUSB_PCI_MEMBASE;
+	ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
+
+	/* Init BAR 1 (MMIO) */
+	ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+	ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
+	ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+	tmp32 &= 0x0f;
+	tmp32 |= SISUSB_PCI_MMIOBASE;
+	ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
+
+	/* Init BAR 2 (i/o ports) */
+	ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+	ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
+	ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+	tmp32 &= 0x0f;
+	tmp32 |= SISUSB_PCI_IOPORTBASE;
+	ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
+
+	/* Enable memory and i/o access */
+	ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
+	tmp32 |= 0x3;
+	ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
+
+	if (ret == 0) {
+		/* Some further magic */
+		packet.header  = 0x001f;
+		packet.address = 0x00000050;
+		packet.data    = 0x000000ff;
+		ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+	}
+
+	return ret;
+}
+
+/* Initialize the graphics device (return 0 on success)
+ * This initializes the net2280 as well as the PCI registers
+ * of the graphics board.
+ */
+
+static int
+sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
+{
+	int ret = 0, test = 0;
+	u32 tmp32;
+
+	if (sisusb->devinit == 1) {
+		/* Read PCI BARs and see if they have been set up */
+		ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+		if (ret) return ret;
+		if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++;
+
+		ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+		if (ret) return ret;
+		if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++;
+
+		ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+		if (ret) return ret;
+		if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++;
+	}
+
+	/* No? So reset the device */
+	if ((sisusb->devinit == 0) || (test != 3)) {
+
+		ret |= sisusb_do_init_gfxdevice(sisusb);
+
+		if (ret == 0)
+			sisusb->devinit = 1;
+
+	}
+
+	if (sisusb->devinit) {
+		/* Initialize the graphics core */
+		if (sisusb_init_gfxcore(sisusb) == 0) {
+			sisusb->gfxinit = 1;
+			sisusb_get_ramconfig(sisusb);
+			ret |= sisusb_set_default_mode(sisusb, 1);
+			ret |= sisusb_setup_screen(sisusb, 1, initscreen);
+		}
+	}
+
+	return ret;
+}
+
+
+#ifdef INCL_SISUSB_CON
+
+/* Set up default text mode:
+   - Set text mode (0x03)
+   - Upload default font
+   - Upload user font (if available)
+*/
+
+int
+sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init)
+{
+	int ret = 0, slot = sisusb->font_slot, i;
+	const struct font_desc *myfont;
+	u8 *tempbuf;
+	u16 *tempbufb;
+	size_t written;
+	static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
+	static const char bootlogo[] = "(o_ //\\ V_/_";
+
+	/* sisusb->lock is down */
+
+	if (!sisusb->SiS_Pr)
+		return 1;
+
+	sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
+	sisusb->SiS_Pr->sisusb = (void *)sisusb;
+
+	/* Set mode 0x03 */
+	SiSUSBSetMode(sisusb->SiS_Pr, 0x03);
+
+	myfont = find_font("VGA8x16");
+	if (!myfont)
+		return 1;
+
+	tempbuf = vmalloc(8192);
+	if (!tempbuf)
+		return 1;
+
+	for (i = 0; i < 256; i++)
+		memcpy(tempbuf + (i * 32), myfont->data + (i * 16), 16);
+
+	/* Upload default font */
+	ret = sisusbcon_do_font_op(sisusb, 1, 0, tempbuf, 8192, 0, 1, NULL, 16, 0);
+
+	vfree(tempbuf);
+
+	/* Upload user font (and reset current slot) */
+	if (sisusb->font_backup) {
+		ret |= sisusbcon_do_font_op(sisusb, 1, 2, sisusb->font_backup,
+				8192, sisusb->font_backup_512, 1, NULL,
+				sisusb->font_backup_height, 0);
+		if (slot != 2)
+			sisusbcon_do_font_op(sisusb, 1, 0, NULL, 0, 0, 1,
+					NULL, 16, 0);
+	}
+
+	if (init && !sisusb->scrbuf) {
+
+		tempbuf = vmalloc(8192);
+		if (tempbuf) {
+
+			i = 4096;
+			tempbufb = (u16 *)tempbuf;
+			while (i--)
+				*(tempbufb++) = 0x0720;
+
+			i = 0;
+			tempbufb = (u16 *)tempbuf;
+			while (bootlogo[i]) {
+				*(tempbufb++) = 0x0700 | bootlogo[i++];
+				if (!(i % 4))
+					tempbufb += 76;
+			}
+
+			i = 0;
+			tempbufb = (u16 *)tempbuf + 6;
+			while (bootstring[i])
+				*(tempbufb++) = 0x0700 | bootstring[i++];
+
+			ret |= sisusb_copy_memory(sisusb, tempbuf,
+				sisusb->vrambase, 8192, &written);
+
+			vfree(tempbuf);
+
+		}
+
+	} else if (sisusb->scrbuf) {
+
+		ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf,
+				sisusb->vrambase, sisusb->scrbuf_size, &written);
+
+	}
+
+	if (sisusb->sisusb_cursor_size_from >= 0 &&
+	    sisusb->sisusb_cursor_size_to >= 0) {
+		sisusb_setidxreg(sisusb, SISCR, 0x0a,
+				sisusb->sisusb_cursor_size_from);
+		sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0,
+				sisusb->sisusb_cursor_size_to);
+	} else {
+		sisusb_setidxreg(sisusb, SISCR, 0x0a, 0x2d);
+		sisusb_setidxreg(sisusb, SISCR, 0x0b, 0x0e);
+		sisusb->sisusb_cursor_size_to = -1;
+	}
+
+	slot = sisusb->sisusb_cursor_loc;
+	if(slot < 0) slot = 0;
+
+	sisusb->sisusb_cursor_loc = -1;
+	sisusb->bad_cursor_pos = 1;
+
+	sisusb_set_cursor(sisusb, slot);
+
+	sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
+	sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
+
+	sisusb->textmodedestroyed = 0;
+
+	/* sisusb->lock is down */
+
+	return ret;
+}
+
+#endif
+
+/* fops */
+
+static int
+sisusb_open(struct inode *inode, struct file *file)
+{
+	struct sisusb_usb_data *sisusb;
+	struct usb_interface *interface;
+	int subminor = iminor(inode);
+
+	interface = usb_find_interface(&sisusb_driver, subminor);
+	if (!interface) {
+		return -ENODEV;
+	}
+
+	sisusb = usb_get_intfdata(interface);
+	if (!sisusb) {
+		return -ENODEV;
+	}
+
+	mutex_lock(&sisusb->lock);
+
+	if (!sisusb->present || !sisusb->ready) {
+		mutex_unlock(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	if (sisusb->isopen) {
+		mutex_unlock(&sisusb->lock);
+		return -EBUSY;
+	}
+
+	if (!sisusb->devinit) {
+		if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH ||
+		    sisusb->sisusb_dev->speed == USB_SPEED_SUPER) {
+			if (sisusb_init_gfxdevice(sisusb, 0)) {
+				mutex_unlock(&sisusb->lock);
+				dev_err(&sisusb->sisusb_dev->dev, "Failed to initialize device\n");
+				return -EIO;
+			}
+		} else {
+			mutex_unlock(&sisusb->lock);
+			dev_err(&sisusb->sisusb_dev->dev, "Device not attached to USB 2.0 hub\n");
+			return -EIO;
+		}
+	}
+
+	/* Increment usage count for our sisusb */
+	kref_get(&sisusb->kref);
+
+	sisusb->isopen = 1;
+
+	file->private_data = sisusb;
+
+	mutex_unlock(&sisusb->lock);
+
+	return 0;
+}
+
+void
+sisusb_delete(struct kref *kref)
+{
+	struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
+
+	if (!sisusb)
+		return;
+
+	usb_put_dev(sisusb->sisusb_dev);
+
+	sisusb->sisusb_dev = NULL;
+	sisusb_free_buffers(sisusb);
+	sisusb_free_urbs(sisusb);
+#ifdef INCL_SISUSB_CON
+	kfree(sisusb->SiS_Pr);
+#endif
+	kfree(sisusb);
+}
+
+static int
+sisusb_release(struct inode *inode, struct file *file)
+{
+	struct sisusb_usb_data *sisusb;
+
+	sisusb = file->private_data;
+	if (!sisusb)
+		return -ENODEV;
+
+	mutex_lock(&sisusb->lock);
+
+	if (sisusb->present) {
+		/* Wait for all URBs to finish if device still present */
+		if (!sisusb_wait_all_out_complete(sisusb))
+			sisusb_kill_all_busy(sisusb);
+	}
+
+	sisusb->isopen = 0;
+	file->private_data = NULL;
+
+	mutex_unlock(&sisusb->lock);
+
+	/* decrement the usage count on our device */
+	kref_put(&sisusb->kref, sisusb_delete);
+
+	return 0;
+}
+
+static ssize_t
+sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct sisusb_usb_data *sisusb;
+	ssize_t bytes_read = 0;
+	int errno = 0;
+	u8 buf8;
+	u16 buf16;
+	u32 buf32, address;
+
+	sisusb = file->private_data;
+	if (!sisusb)
+		return -ENODEV;
+
+	mutex_lock(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		mutex_unlock(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
+	    (*ppos) <  SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_IOPORTBASE +
+			SISUSB_PCI_IOPORTBASE;
+
+		/* Read i/o ports
+		 * Byte, word and long(32) can be read. As this
+		 * emulates inX instructions, the data returned is
+		 * in machine-endianness.
+		 */
+		switch (count) {
+
+			case 1:
+				if (sisusb_read_memio_byte(sisusb,
+							SISUSB_TYPE_IO,
+							address, &buf8))
+					errno = -EIO;
+				else if (put_user(buf8, (u8 __user *)buffer))
+					errno = -EFAULT;
+				else
+					bytes_read = 1;
+
+				break;
+
+			case 2:
+				if (sisusb_read_memio_word(sisusb,
+							SISUSB_TYPE_IO,
+							address, &buf16))
+					errno = -EIO;
+				else if (put_user(buf16, (u16 __user *)buffer))
+					errno = -EFAULT;
+				else
+					bytes_read = 2;
+
+				break;
+
+			case 4:
+				if (sisusb_read_memio_long(sisusb,
+							SISUSB_TYPE_IO,
+							address, &buf32))
+					errno = -EIO;
+				else if (put_user(buf32, (u32 __user *)buffer))
+					errno = -EFAULT;
+				else
+					bytes_read = 4;
+
+				break;
+
+			default:
+				errno = -EIO;
+
+		}
+
+	} else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
+		   (*ppos) <  SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MEMBASE +
+			SISUSB_PCI_MEMBASE;
+
+		/* Read video ram
+		 * Remember: Data delivered is never endian-corrected
+		 */
+		errno = sisusb_read_mem_bulk(sisusb, address,
+					NULL, count, buffer, &bytes_read);
+
+		if (bytes_read)
+			errno = bytes_read;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
+		    (*ppos) <  SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MMIOBASE +
+			SISUSB_PCI_MMIOBASE;
+
+		/* Read MMIO
+		 * Remember: Data delivered is never endian-corrected
+		 */
+		errno = sisusb_read_mem_bulk(sisusb, address,
+					NULL, count, buffer, &bytes_read);
+
+		if (bytes_read)
+			errno = bytes_read;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
+		    (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
+
+		if (count != 4) {
+			mutex_unlock(&sisusb->lock);
+			return -EINVAL;
+		}
+
+		address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
+
+		/* Read PCI config register
+		 * Return value delivered in machine endianness.
+		 */
+		if (sisusb_read_pci_config(sisusb, address, &buf32))
+			errno = -EIO;
+		else if (put_user(buf32, (u32 __user *)buffer))
+			errno = -EFAULT;
+		else
+			bytes_read = 4;
+
+	} else {
+
+		errno = -EBADFD;
+
+	}
+
+	(*ppos) += bytes_read;
+
+	mutex_unlock(&sisusb->lock);
+
+	return errno ? errno : bytes_read;
+}
+
+static ssize_t
+sisusb_write(struct file *file, const char __user *buffer, size_t count,
+								loff_t *ppos)
+{
+	struct sisusb_usb_data *sisusb;
+	int errno = 0;
+	ssize_t bytes_written = 0;
+	u8 buf8;
+	u16 buf16;
+	u32 buf32, address;
+
+	sisusb = file->private_data;
+	if (!sisusb)
+		return -ENODEV;
+
+	mutex_lock(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		mutex_unlock(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
+	    (*ppos) <  SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_IOPORTBASE +
+			SISUSB_PCI_IOPORTBASE;
+
+		/* Write i/o ports
+		 * Byte, word and long(32) can be written. As this
+		 * emulates outX instructions, the data is expected
+		 * in machine-endianness.
+		 */
+		switch (count) {
+
+			case 1:
+				if (get_user(buf8, (u8 __user *)buffer))
+					errno = -EFAULT;
+				else if (sisusb_write_memio_byte(sisusb,
+							SISUSB_TYPE_IO,
+							address, buf8))
+					errno = -EIO;
+				else
+					bytes_written = 1;
+
+				break;
+
+			case 2:
+				if (get_user(buf16, (u16 __user *)buffer))
+					errno = -EFAULT;
+				else if (sisusb_write_memio_word(sisusb,
+							SISUSB_TYPE_IO,
+							address, buf16))
+					errno = -EIO;
+				else
+					bytes_written = 2;
+
+				break;
+
+			case 4:
+				if (get_user(buf32, (u32 __user *)buffer))
+					errno = -EFAULT;
+				else if (sisusb_write_memio_long(sisusb,
+							SISUSB_TYPE_IO,
+							address, buf32))
+					errno = -EIO;
+				else
+					bytes_written = 4;
+
+				break;
+
+			default:
+				errno = -EIO;
+		}
+
+	} else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
+		   (*ppos) <  SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MEMBASE +
+			SISUSB_PCI_MEMBASE;
+
+		/* Write video ram.
+		 * Buffer is copied 1:1, therefore, on big-endian
+		 * machines, the data must be swapped by userland
+		 * in advance (if applicable; no swapping in 8bpp
+		 * mode or if YUV data is being transferred).
+		 */
+		errno = sisusb_write_mem_bulk(sisusb, address, NULL,
+					count, buffer, 0, &bytes_written);
+
+		if (bytes_written)
+			errno = bytes_written;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
+		    (*ppos) <  SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
+
+		address = (*ppos) -
+			SISUSB_PCI_PSEUDO_MMIOBASE +
+			SISUSB_PCI_MMIOBASE;
+
+		/* Write MMIO.
+		 * Buffer is copied 1:1, therefore, on big-endian
+		 * machines, the data must be swapped by userland
+		 * in advance.
+		 */
+		errno = sisusb_write_mem_bulk(sisusb, address, NULL,
+					count, buffer, 0, &bytes_written);
+
+		if (bytes_written)
+			errno = bytes_written;
+
+	} else  if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
+		    (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
+
+		if (count != 4) {
+			mutex_unlock(&sisusb->lock);
+			return -EINVAL;
+		}
+
+		address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
+
+		/* Write PCI config register.
+		 * Given value expected in machine endianness.
+		 */
+		if (get_user(buf32, (u32 __user *)buffer))
+			errno = -EFAULT;
+		else if (sisusb_write_pci_config(sisusb, address, buf32))
+			errno = -EIO;
+		else
+			bytes_written = 4;
+
+
+	} else {
+
+		/* Error */
+		errno = -EBADFD;
+
+	}
+
+	(*ppos) += bytes_written;
+
+	mutex_unlock(&sisusb->lock);
+
+	return errno ? errno : bytes_written;
+}
+
+static loff_t
+sisusb_lseek(struct file *file, loff_t offset, int orig)
+{
+	struct sisusb_usb_data *sisusb;
+	loff_t ret;
+
+	sisusb = file->private_data;
+	if (!sisusb)
+		return -ENODEV;
+
+	mutex_lock(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		mutex_unlock(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	switch (orig) {
+		case 0:
+			file->f_pos = offset;
+			ret = file->f_pos;
+			/* never negative, no force_successful_syscall needed */
+			break;
+		case 1:
+			file->f_pos += offset;
+			ret = file->f_pos;
+			/* never negative, no force_successful_syscall needed */
+			break;
+		default:
+			/* seeking relative to "end of file" is not supported */
+			ret = -EINVAL;
+	}
+
+	mutex_unlock(&sisusb->lock);
+	return ret;
+}
+
+static int
+sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
+							unsigned long arg)
+{
+	int	retval, port, length;
+	u32	address;
+
+	/* All our commands require the device
+	 * to be initialized.
+	 */
+	if (!sisusb->devinit)
+		return -ENODEV;
+
+	port = y->data3 -
+		SISUSB_PCI_PSEUDO_IOPORTBASE +
+		SISUSB_PCI_IOPORTBASE;
+
+	switch (y->operation) {
+		case SUCMD_GET:
+			retval = sisusb_getidxreg(sisusb, port,
+							 y->data0, &y->data1);
+			if (!retval) {
+				if (copy_to_user((void __user *)arg, y,
+							sizeof(*y)))
+					retval = -EFAULT;
+			}
+			break;
+
+		case SUCMD_SET:
+			retval = sisusb_setidxreg(sisusb, port,
+						y->data0, y->data1);
+			break;
+
+		case SUCMD_SETOR:
+			retval = sisusb_setidxregor(sisusb, port,
+						y->data0, y->data1);
+			break;
+
+		case SUCMD_SETAND:
+			retval = sisusb_setidxregand(sisusb, port,
+						y->data0, y->data1);
+			break;
+
+		case SUCMD_SETANDOR:
+			retval = sisusb_setidxregandor(sisusb, port,
+						y->data0, y->data1, y->data2);
+			break;
+
+		case SUCMD_SETMASK:
+			retval = sisusb_setidxregmask(sisusb, port,
+						y->data0, y->data1, y->data2);
+			break;
+
+		case SUCMD_CLRSCR:
+			/* Gfx core must be initialized */
+			if (!sisusb->gfxinit)
+				return -ENODEV;
+
+			length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
+			address = y->data3 -
+				SISUSB_PCI_PSEUDO_MEMBASE +
+				SISUSB_PCI_MEMBASE;
+			retval = sisusb_clear_vram(sisusb, address, length);
+			break;
+
+		case SUCMD_HANDLETEXTMODE:
+			retval = 0;
+#ifdef INCL_SISUSB_CON
+			/* Gfx core must be initialized, SiS_Pr must exist */
+			if (!sisusb->gfxinit || !sisusb->SiS_Pr)
+				return -ENODEV;
+
+			switch (y->data0) {
+			case 0:
+				retval = sisusb_reset_text_mode(sisusb, 0);
+				break;
+			case 1:
+				sisusb->textmodedestroyed = 1;
+				break;
+			}
+#endif
+			break;
+
+#ifdef INCL_SISUSB_CON
+		case SUCMD_SETMODE:
+			/* Gfx core must be initialized, SiS_Pr must exist */
+			if (!sisusb->gfxinit || !sisusb->SiS_Pr)
+				return -ENODEV;
+
+			retval = 0;
+
+			sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
+			sisusb->SiS_Pr->sisusb = (void *)sisusb;
+
+			if (SiSUSBSetMode(sisusb->SiS_Pr, y->data3))
+				retval = -EINVAL;
+
+			break;
+
+		case SUCMD_SETVESAMODE:
+			/* Gfx core must be initialized, SiS_Pr must exist */
+			if (!sisusb->gfxinit || !sisusb->SiS_Pr)
+				return -ENODEV;
+
+			retval = 0;
+
+			sisusb->SiS_Pr->IOAddress = SISUSB_PCI_IOPORTBASE + 0x30;
+			sisusb->SiS_Pr->sisusb = (void *)sisusb;
+
+			if (SiSUSBSetVESAMode(sisusb->SiS_Pr, y->data3))
+				retval = -EINVAL;
+
+			break;
+#endif
+
+		default:
+			retval = -EINVAL;
+	}
+
+	if (retval > 0)
+		retval = -EIO;
+
+	return retval;
+}
+
+static long
+sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct sisusb_usb_data *sisusb;
+	struct sisusb_info x;
+	struct sisusb_command y;
+	long retval = 0;
+	u32 __user *argp = (u32 __user *)arg;
+
+	sisusb = file->private_data;
+	if (!sisusb)
+		return -ENODEV;
+
+	mutex_lock(&sisusb->lock);
+
+	/* Sanity check */
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	switch (cmd) {
+
+		case SISUSB_GET_CONFIG_SIZE:
+
+			if (put_user(sizeof(x), argp))
+				retval = -EFAULT;
+
+			break;
+
+		case SISUSB_GET_CONFIG:
+
+			x.sisusb_id	    = SISUSB_ID;
+			x.sisusb_version    = SISUSB_VERSION;
+			x.sisusb_revision   = SISUSB_REVISION;
+			x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
+			x.sisusb_gfxinit    = sisusb->gfxinit;
+			x.sisusb_vrambase   = SISUSB_PCI_PSEUDO_MEMBASE;
+			x.sisusb_mmiobase   = SISUSB_PCI_PSEUDO_MMIOBASE;
+			x.sisusb_iobase     = SISUSB_PCI_PSEUDO_IOPORTBASE;
+			x.sisusb_pcibase    = SISUSB_PCI_PSEUDO_PCIBASE;
+			x.sisusb_vramsize   = sisusb->vramsize;
+			x.sisusb_minor	    = sisusb->minor;
+			x.sisusb_fbdevactive= 0;
+#ifdef INCL_SISUSB_CON
+			x.sisusb_conactive  = sisusb->haveconsole ? 1 : 0;
+#else
+			x.sisusb_conactive  = 0;
+#endif
+			memset(x.sisusb_reserved, 0, sizeof(x.sisusb_reserved));
+
+			if (copy_to_user((void __user *)arg, &x, sizeof(x)))
+				retval = -EFAULT;
+
+			break;
+
+		case SISUSB_COMMAND:
+
+			if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
+				retval = -EFAULT;
+			else
+				retval = sisusb_handle_command(sisusb, &y, arg);
+
+			break;
+
+		default:
+			retval = -ENOTTY;
+			break;
+	}
+
+err_out:
+	mutex_unlock(&sisusb->lock);
+	return retval;
+}
+
+#ifdef SISUSB_NEW_CONFIG_COMPAT
+static long
+sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+	long retval;
+
+	switch (cmd) {
+		case SISUSB_GET_CONFIG_SIZE:
+		case SISUSB_GET_CONFIG:
+		case SISUSB_COMMAND:
+			retval = sisusb_ioctl(f, cmd, arg);
+			return retval;
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+#endif
+
+static const struct file_operations usb_sisusb_fops = {
+	.owner =	THIS_MODULE,
+	.open =		sisusb_open,
+	.release =	sisusb_release,
+	.read =		sisusb_read,
+	.write =	sisusb_write,
+	.llseek =	sisusb_lseek,
+#ifdef SISUSB_NEW_CONFIG_COMPAT
+	.compat_ioctl = sisusb_compat_ioctl,
+#endif
+	.unlocked_ioctl = sisusb_ioctl
+};
+
+static struct usb_class_driver usb_sisusb_class = {
+	.name =		"sisusbvga%d",
+	.fops =		&usb_sisusb_fops,
+	.minor_base =	SISUSB_MINOR
+};
+
+static int sisusb_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct sisusb_usb_data *sisusb;
+	int retval = 0, i;
+
+	dev_info(&dev->dev, "USB2VGA dongle found at address %d\n",
+		dev->devnum);
+
+	/* Allocate memory for our private */
+	sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL);
+	if (!sisusb) {
+		dev_err(&dev->dev, "Failed to allocate memory for private data\n");
+		return -ENOMEM;
+	}
+	kref_init(&sisusb->kref);
+
+	mutex_init(&(sisusb->lock));
+
+	/* Register device */
+	retval = usb_register_dev(intf, &usb_sisusb_class);
+	if (retval) {
+		dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n",
+			dev->devnum);
+		retval = -ENODEV;
+		goto error_1;
+	}
+
+	sisusb->sisusb_dev = dev;
+	sisusb->minor      = intf->minor;
+	sisusb->vrambase   = SISUSB_PCI_MEMBASE;
+	sisusb->mmiobase   = SISUSB_PCI_MMIOBASE;
+	sisusb->mmiosize   = SISUSB_PCI_MMIOSIZE;
+	sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
+	/* Everything else is zero */
+
+	/* Allocate buffers */
+	sisusb->ibufsize = SISUSB_IBUF_SIZE;
+	if (!(sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL))) {
+		dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer");
+		retval = -ENOMEM;
+		goto error_2;
+	}
+
+	sisusb->numobufs = 0;
+	sisusb->obufsize = SISUSB_OBUF_SIZE;
+	for (i = 0; i < NUMOBUFS; i++) {
+		if (!(sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL))) {
+			if (i == 0) {
+				dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n");
+				retval = -ENOMEM;
+				goto error_3;
+			}
+			break;
+		} else
+			sisusb->numobufs++;
+
+	}
+
+	/* Allocate URBs */
+	if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
+		dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
+		retval = -ENOMEM;
+		goto error_3;
+	}
+	sisusb->completein = 1;
+
+	for (i = 0; i < sisusb->numobufs; i++) {
+		if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
+			dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
+			retval = -ENOMEM;
+			goto error_4;
+		}
+		sisusb->urbout_context[i].sisusb = (void *)sisusb;
+		sisusb->urbout_context[i].urbindex = i;
+		sisusb->urbstatus[i] = 0;
+	}
+
+	dev_info(&sisusb->sisusb_dev->dev, "Allocated %d output buffers\n", sisusb->numobufs);
+
+#ifdef INCL_SISUSB_CON
+	/* Allocate our SiS_Pr */
+	if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
+		dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate SiS_Pr\n");
+	}
+#endif
+
+	/* Do remaining init stuff */
+
+	init_waitqueue_head(&sisusb->wait_q);
+
+	usb_set_intfdata(intf, sisusb);
+
+	usb_get_dev(sisusb->sisusb_dev);
+
+	sisusb->present = 1;
+
+	if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) {
+		int initscreen = 1;
+#ifdef INCL_SISUSB_CON
+		if (sisusb_first_vc > 0 &&
+		    sisusb_last_vc > 0 &&
+		    sisusb_first_vc <= sisusb_last_vc &&
+		    sisusb_last_vc <= MAX_NR_CONSOLES)
+			initscreen = 0;
+#endif
+		if (sisusb_init_gfxdevice(sisusb, initscreen))
+			dev_err(&sisusb->sisusb_dev->dev, "Failed to early initialize device\n");
+
+	} else
+		dev_info(&sisusb->sisusb_dev->dev, "Not attached to USB 2.0 hub, deferring init\n");
+
+	sisusb->ready = 1;
+
+#ifdef SISUSBENDIANTEST
+	dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST ***\n");
+	sisusb_testreadwrite(sisusb);
+	dev_dbg(&sisusb->sisusb_dev->dev, "*** RWTEST END ***\n");
+#endif
+
+#ifdef INCL_SISUSB_CON
+	sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
+#endif
+
+	return 0;
+
+error_4:
+	sisusb_free_urbs(sisusb);
+error_3:
+	sisusb_free_buffers(sisusb);
+error_2:
+	usb_deregister_dev(intf, &usb_sisusb_class);
+error_1:
+	kfree(sisusb);
+	return retval;
+}
+
+static void sisusb_disconnect(struct usb_interface *intf)
+{
+	struct sisusb_usb_data *sisusb;
+
+	/* This should *not* happen */
+	sisusb = usb_get_intfdata(intf);
+	if (!sisusb)
+		return;
+
+#ifdef INCL_SISUSB_CON
+	sisusb_console_exit(sisusb);
+#endif
+
+	usb_deregister_dev(intf, &usb_sisusb_class);
+
+	mutex_lock(&sisusb->lock);
+
+	/* Wait for all URBs to complete and kill them in case (MUST do) */
+	if (!sisusb_wait_all_out_complete(sisusb))
+		sisusb_kill_all_busy(sisusb);
+
+	usb_set_intfdata(intf, NULL);
+
+	sisusb->present = 0;
+	sisusb->ready = 0;
+
+	mutex_unlock(&sisusb->lock);
+
+	/* decrement our usage count */
+	kref_put(&sisusb->kref, sisusb_delete);
+}
+
+static const struct usb_device_id sisusb_table[] = {
+	{ USB_DEVICE(0x0711, 0x0550) },
+	{ USB_DEVICE(0x0711, 0x0900) },
+	{ USB_DEVICE(0x0711, 0x0901) },
+	{ USB_DEVICE(0x0711, 0x0902) },
+	{ USB_DEVICE(0x0711, 0x0903) },
+	{ USB_DEVICE(0x0711, 0x0918) },
+	{ USB_DEVICE(0x0711, 0x0920) },
+	{ USB_DEVICE(0x0711, 0x0950) },
+	{ USB_DEVICE(0x0711, 0x5200) },
+	{ USB_DEVICE(0x182d, 0x021c) },
+	{ USB_DEVICE(0x182d, 0x0269) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE (usb, sisusb_table);
+
+static struct usb_driver sisusb_driver = {
+	.name =		"sisusb",
+	.probe =	sisusb_probe,
+	.disconnect =	sisusb_disconnect,
+	.id_table =	sisusb_table,
+};
+
+static int __init usb_sisusb_init(void)
+{
+
+#ifdef INCL_SISUSB_CON
+	sisusb_init_concode();
+#endif
+
+	return usb_register(&sisusb_driver);
+}
+
+static void __exit usb_sisusb_exit(void)
+{
+	usb_deregister(&sisusb_driver);
+}
+
+module_init(usb_sisusb_init);
+module_exit(usb_sisusb_exit);
+
+MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
+MODULE_DESCRIPTION("sisusbvga - Driver for Net2280/SiS315-based USB2VGA dongles");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h
new file mode 100644
index 0000000..55492a5
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb.h
@@ -0,0 +1,311 @@
+/*
+ * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * 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 the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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 the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED 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 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.
+ *
+ * Author:	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#ifndef _SISUSB_H_
+#define _SISUSB_H_
+
+#ifdef CONFIG_COMPAT
+#define SISUSB_NEW_CONFIG_COMPAT
+#endif
+
+#include <linux/mutex.h>
+
+/* For older kernels, support for text consoles is by default
+ * off. To enable text console support, change the following:
+ */
+/* #define CONFIG_USB_SISUSBVGA_CON */
+
+/* Version Information */
+
+#define SISUSB_VERSION		0
+#define SISUSB_REVISION		0
+#define SISUSB_PATCHLEVEL	8
+
+/* Include console and mode switching code? */
+
+#ifdef CONFIG_USB_SISUSBVGA_CON
+#define INCL_SISUSB_CON		1
+#endif
+
+#include <linux/console.h>
+#include <linux/vt_kern.h>
+#include "sisusb_struct.h"
+
+/* USB related */
+
+#define SISUSB_MINOR		133	/* official */
+
+/* Size of the sisusb input/output buffers */
+#define SISUSB_IBUF_SIZE  0x01000
+#define SISUSB_OBUF_SIZE  0x10000	/* fixed */
+
+#define NUMOBUFS 8		/* max number of output buffers/output URBs */
+
+/* About endianness:
+ *
+ * 1) I/O ports, PCI config registers. The read/write()
+ *    calls emulate inX/outX. Hence, the data is
+ *    expected/delivered in machine endiannes by this
+ *    driver.
+ * 2) Video memory. The data is copied 1:1. There is
+ *    no swapping. Ever. This means for userland that
+ *    the data has to be prepared properly. (Hint:
+ *    think graphics data format, command queue,
+ *    hardware cursor.)
+ * 3) MMIO. Data is copied 1:1. MMIO must be swapped
+ *    properly by userland.
+ *
+ */
+
+#ifdef __BIG_ENDIAN
+#define SISUSB_CORRECT_ENDIANNESS_PACKET(p)		\
+	do {						\
+		p->header  = cpu_to_le16(p->header);	\
+		p->address = cpu_to_le32(p->address);	\
+		p->data    = cpu_to_le32(p->data);	\
+	} while(0)
+#else
+#define SISUSB_CORRECT_ENDIANNESS_PACKET(p)
+#endif
+
+struct sisusb_usb_data;
+
+struct sisusb_urb_context {	/* urb->context for outbound bulk URBs */
+	struct sisusb_usb_data *sisusb;
+	int urbindex;
+	int *actual_length;
+};
+
+struct sisusb_usb_data {
+	struct usb_device *sisusb_dev;
+	struct usb_interface *interface;
+	struct kref kref;
+	wait_queue_head_t wait_q;	/* for syncind and timeouts */
+	struct mutex lock;	/* general race avoidance */
+	unsigned int ifnum;	/* interface number of the USB device */
+	int minor;		/* minor (for logging clarity) */
+	int isopen;		/* !=0 if open */
+	int present;		/* !=0 if device is present on the bus */
+	int ready;		/* !=0 if device is ready for userland */
+	int numobufs;		/* number of obufs = number of out urbs */
+	char *obuf[NUMOBUFS], *ibuf;	/* transfer buffers */
+	int obufsize, ibufsize;
+	struct urb *sisurbout[NUMOBUFS];
+	struct urb *sisurbin;
+	unsigned char urbstatus[NUMOBUFS];
+	unsigned char completein;
+	struct sisusb_urb_context urbout_context[NUMOBUFS];
+	unsigned long flagb0;
+	unsigned long vrambase;	/* framebuffer base */
+	unsigned int vramsize;	/* framebuffer size (bytes) */
+	unsigned long mmiobase;
+	unsigned int mmiosize;
+	unsigned long ioportbase;
+	unsigned char devinit;	/* device initialized? */
+	unsigned char gfxinit;	/* graphics core initialized? */
+	unsigned short chipid, chipvendor;
+	unsigned short chiprevision;
+#ifdef INCL_SISUSB_CON
+	struct SiS_Private *SiS_Pr;
+	unsigned long scrbuf;
+	unsigned int scrbuf_size;
+	int haveconsole, con_first, con_last;
+	int havethisconsole[MAX_NR_CONSOLES];
+	int textmodedestroyed;
+	unsigned int sisusb_num_columns;	/* real number, not vt's idea */
+	int cur_start_addr, con_rolled_over;
+	int sisusb_cursor_loc, bad_cursor_pos;
+	int sisusb_cursor_size_from;
+	int sisusb_cursor_size_to;
+	int current_font_height, current_font_512;
+	int font_backup_size, font_backup_height, font_backup_512;
+	char *font_backup;
+	int font_slot;
+	struct vc_data *sisusb_display_fg;
+	int is_gfx;
+	int con_blanked;
+#endif
+};
+
+#define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref)
+
+/* USB transport related */
+
+/* urbstatus */
+#define SU_URB_BUSY   1
+#define SU_URB_ALLOC  2
+
+/* Endpoints */
+
+#define SISUSB_EP_GFX_IN	0x0e	/* gfx std packet out(0e)/in(8e) */
+#define SISUSB_EP_GFX_OUT	0x0e
+
+#define SISUSB_EP_GFX_BULK_OUT	0x01	/* gfx mem bulk out/in */
+#define SISUSB_EP_GFX_BULK_IN	0x02	/* ? 2 is "OUT" ? */
+
+#define SISUSB_EP_GFX_LBULK_OUT	0x03	/* gfx large mem bulk out */
+
+#define SISUSB_EP_UNKNOWN_04	0x04	/* ? 4 is "OUT" ? - unused */
+
+#define SISUSB_EP_BRIDGE_IN	0x0d	/* Net2280 out(0d)/in(8d) */
+#define SISUSB_EP_BRIDGE_OUT	0x0d
+
+#define SISUSB_TYPE_MEM		0
+#define SISUSB_TYPE_IO		1
+
+struct sisusb_packet {
+	unsigned short header;
+	u32 address;
+	u32 data;
+} __attribute__ ((__packed__));
+
+#define CLEARPACKET(packet) memset(packet, 0, 10)
+
+/* PCI bridge related */
+
+#define SISUSB_PCI_MEMBASE	0xd0000000
+#define SISUSB_PCI_MMIOBASE	0xe4000000
+#define SISUSB_PCI_IOPORTBASE	0x0000d000
+
+#define SISUSB_PCI_PSEUDO_MEMBASE	0x10000000
+#define SISUSB_PCI_PSEUDO_MMIOBASE	0x20000000
+#define SISUSB_PCI_PSEUDO_IOPORTBASE	0x0000d000
+#define SISUSB_PCI_PSEUDO_PCIBASE	0x00010000
+
+#define SISUSB_PCI_MMIOSIZE	(128*1024)
+#define SISUSB_PCI_PCONFSIZE	0x5c
+
+/* graphics core related */
+
+#define AROFFSET	0x40
+#define ARROFFSET	0x41
+#define GROFFSET	0x4e
+#define SROFFSET	0x44
+#define CROFFSET	0x54
+#define MISCROFFSET	0x4c
+#define MISCWOFFSET	0x42
+#define INPUTSTATOFFSET 0x5A
+#define PART1OFFSET	0x04
+#define PART2OFFSET	0x10
+#define PART3OFFSET	0x12
+#define PART4OFFSET	0x14
+#define PART5OFFSET	0x16
+#define CAPTUREOFFSET	0x00
+#define VIDEOOFFSET	0x02
+#define COLREGOFFSET	0x48
+#define PELMASKOFFSET	0x46
+#define VGAENABLE	0x43
+
+#define SISAR		SISUSB_PCI_IOPORTBASE + AROFFSET
+#define SISARR		SISUSB_PCI_IOPORTBASE + ARROFFSET
+#define SISGR		SISUSB_PCI_IOPORTBASE + GROFFSET
+#define SISSR		SISUSB_PCI_IOPORTBASE + SROFFSET
+#define SISCR		SISUSB_PCI_IOPORTBASE + CROFFSET
+#define SISMISCR	SISUSB_PCI_IOPORTBASE + MISCROFFSET
+#define SISMISCW	SISUSB_PCI_IOPORTBASE + MISCWOFFSET
+#define SISINPSTAT	SISUSB_PCI_IOPORTBASE + INPUTSTATOFFSET
+#define SISPART1	SISUSB_PCI_IOPORTBASE + PART1OFFSET
+#define SISPART2	SISUSB_PCI_IOPORTBASE + PART2OFFSET
+#define SISPART3	SISUSB_PCI_IOPORTBASE + PART3OFFSET
+#define SISPART4	SISUSB_PCI_IOPORTBASE + PART4OFFSET
+#define SISPART5	SISUSB_PCI_IOPORTBASE + PART5OFFSET
+#define SISCAP		SISUSB_PCI_IOPORTBASE + CAPTUREOFFSET
+#define SISVID		SISUSB_PCI_IOPORTBASE + VIDEOOFFSET
+#define SISCOLIDXR	SISUSB_PCI_IOPORTBASE + COLREGOFFSET - 1
+#define SISCOLIDX	SISUSB_PCI_IOPORTBASE + COLREGOFFSET
+#define SISCOLDATA	SISUSB_PCI_IOPORTBASE + COLREGOFFSET + 1
+#define SISCOL2IDX	SISPART5
+#define SISCOL2DATA	SISPART5 + 1
+#define SISPEL		SISUSB_PCI_IOPORTBASE + PELMASKOFFSET
+#define SISVGAEN	SISUSB_PCI_IOPORTBASE + VGAENABLE
+#define SISDACA		SISCOLIDX
+#define SISDACD		SISCOLDATA
+
+/* ioctl related */
+
+/* Structure argument for SISUSB_GET_INFO ioctl  */
+struct sisusb_info {
+	__u32 sisusb_id;	/* for identifying sisusb */
+#define SISUSB_ID  0x53495355	/* Identify myself with 'SISU' */
+	__u8 sisusb_version;
+	__u8 sisusb_revision;
+	__u8 sisusb_patchlevel;
+	__u8 sisusb_gfxinit;	/* graphics core initialized? */
+
+	__u32 sisusb_vrambase;
+	__u32 sisusb_mmiobase;
+	__u32 sisusb_iobase;
+	__u32 sisusb_pcibase;
+
+	__u32 sisusb_vramsize;	/* framebuffer size in bytes */
+
+	__u32 sisusb_minor;
+
+	__u32 sisusb_fbdevactive;	/* != 0 if framebuffer device active */
+
+	__u32 sisusb_conactive;	/* != 0 if console driver active */
+
+	__u8 sisusb_reserved[28];	/* for future use */
+};
+
+struct sisusb_command {
+	__u8 operation;		/* see below */
+	__u8 data0;		/* operation dependent */
+	__u8 data1;		/* operation dependent */
+	__u8 data2;		/* operation dependent */
+	__u32 data3;		/* operation dependent */
+	__u32 data4;		/* for future use */
+};
+
+#define SUCMD_GET	0x01	/* for all: data0 = index, data3 = port */
+#define SUCMD_SET	0x02	/* data1 = value */
+#define SUCMD_SETOR	0x03	/* data1 = or */
+#define SUCMD_SETAND	0x04	/* data1 = and */
+#define SUCMD_SETANDOR	0x05	/* data1 = and, data2 = or */
+#define SUCMD_SETMASK	0x06	/* data1 = data, data2 = mask */
+
+#define SUCMD_CLRSCR	0x07	/* data0:1:2 = length, data3 = address */
+
+#define SUCMD_HANDLETEXTMODE 0x08	/* Reset/destroy text mode */
+
+#define SUCMD_SETMODE	0x09	/* Set a display mode (data3 = SiS mode) */
+#define SUCMD_SETVESAMODE 0x0a	/* Set a display mode (data3 = VESA mode) */
+
+#define SISUSB_COMMAND		_IOWR(0xF3,0x3D,struct sisusb_command)
+#define SISUSB_GET_CONFIG_SIZE	_IOR(0xF3,0x3E,__u32)
+#define SISUSB_GET_CONFIG	_IOR(0xF3,0x3F,struct sisusb_info)
+
+#endif /* SISUSB_H */
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
new file mode 100644
index 0000000..ace3430
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -0,0 +1,1586 @@
+/*
+ * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
+ *
+ * VGA text mode console part
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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 the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 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 the author may not be used to endorse or promote products
+ * *    derived from this software without specific psisusbr written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED 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 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.
+ *
+ * Author: Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ * Portions based on vgacon.c which are
+ *	Created 28 Sep 1997 by Geert Uytterhoeven
+ *      Rewritten by Martin Mares <mj@ucw.cz>, July 1998
+ *      based on code Copyright (C) 1991, 1992  Linus Torvalds
+ *			    1995  Jay Estabrook
+ *
+ * A note on using in_atomic() in here: We can't handle console
+ * calls from non-schedulable context due to our USB-dependend
+ * nature. For now, this driver just ignores any calls if it
+ * detects this state.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/usb.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/kd.h>
+#include <linux/init.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+
+#include "sisusb.h"
+#include "sisusb_init.h"
+
+#ifdef INCL_SISUSB_CON
+
+#define sisusbcon_writew(val, addr)	(*(addr) = (val))
+#define sisusbcon_readw(addr)		(*(addr))
+#define sisusbcon_memmovew(d, s, c)	memmove(d, s, c)
+#define sisusbcon_memcpyw(d, s, c)	memcpy(d, s, c)
+
+/* vc_data -> sisusb conversion table */
+static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES];
+
+/* Forward declaration */
+static const struct consw sisusb_con;
+
+static inline void
+sisusbcon_memsetw(u16 *s, u16 c, unsigned int count)
+{
+	count /= 2;
+	while (count--)
+		sisusbcon_writew(c, s++);
+}
+
+static inline void
+sisusb_initialize(struct sisusb_usb_data *sisusb)
+{
+	/* Reset cursor and start address */
+	if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00))
+		return;
+	if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00))
+		return;
+	if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00))
+		return;
+	sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00);
+}
+
+static inline void
+sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c)
+{
+	sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2;
+
+	sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
+	sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
+}
+
+void
+sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location)
+{
+	if (sisusb->sisusb_cursor_loc == location)
+		return;
+
+	sisusb->sisusb_cursor_loc = location;
+
+	/* Hardware bug: Text cursor appears twice or not at all
+	 * at some positions. Work around it with the cursor skew
+	 * bits.
+	 */
+
+	if ((location & 0x0007) == 0x0007) {
+		sisusb->bad_cursor_pos = 1;
+		location--;
+		if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20))
+			return;
+	} else if (sisusb->bad_cursor_pos) {
+		if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f))
+			return;
+		sisusb->bad_cursor_pos = 0;
+	}
+
+	if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8)))
+		return;
+	sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff));
+}
+
+static inline struct sisusb_usb_data *
+sisusb_get_sisusb(unsigned short console)
+{
+	return mysisusbs[console];
+}
+
+static inline int
+sisusb_sisusb_valid(struct sisusb_usb_data *sisusb)
+{
+	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev)
+		return 0;
+
+	return 1;
+}
+
+static struct sisusb_usb_data *
+sisusb_get_sisusb_lock_and_check(unsigned short console)
+{
+	struct sisusb_usb_data *sisusb;
+
+	/* We can't handle console calls in non-schedulable
+	 * context due to our locks and the USB transport.
+	 * So we simply ignore them. This should only affect
+	 * some calls to printk.
+	 */
+	if (in_atomic())
+		return NULL;
+
+	sisusb = sisusb_get_sisusb(console);
+	if (!sisusb)
+		return NULL;
+
+	mutex_lock(&sisusb->lock);
+
+	if (!sisusb_sisusb_valid(sisusb) ||
+	    !sisusb->havethisconsole[console]) {
+		mutex_unlock(&sisusb->lock);
+		return NULL;
+	}
+
+	return sisusb;
+}
+
+static int
+sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb)
+{
+	if (sisusb->is_gfx ||
+	    sisusb->textmodedestroyed ||
+	    c->vc_mode != KD_TEXT)
+		return 1;
+
+	return 0;
+}
+
+/* con_startup console interface routine */
+static const char *
+sisusbcon_startup(void)
+{
+	return "SISUSBCON";
+}
+
+/* con_init console interface routine */
+static void
+sisusbcon_init(struct vc_data *c, int init)
+{
+	struct sisusb_usb_data *sisusb;
+	int cols, rows;
+
+	/* This is called by do_take_over_console(),
+	 * ie by us/under our control. It is
+	 * only called after text mode and fonts
+	 * are set up/restored.
+	 */
+
+	sisusb = sisusb_get_sisusb(c->vc_num);
+	if (!sisusb)
+		return;
+
+	mutex_lock(&sisusb->lock);
+
+	if (!sisusb_sisusb_valid(sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	c->vc_can_do_color = 1;
+
+	c->vc_complement_mask = 0x7700;
+
+	c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0;
+
+	sisusb->haveconsole = 1;
+
+	sisusb->havethisconsole[c->vc_num] = 1;
+
+	/* We only support 640x400 */
+	c->vc_scan_lines = 400;
+
+	c->vc_font.height = sisusb->current_font_height;
+
+	/* We only support width = 8 */
+	cols = 80;
+	rows = c->vc_scan_lines / c->vc_font.height;
+
+	/* Increment usage count for our sisusb.
+	 * Doing so saves us from upping/downing
+	 * the disconnect semaphore; we can't
+	 * lose our sisusb until this is undone
+	 * in con_deinit. For all other console
+	 * interface functions, it suffices to
+	 * use sisusb->lock and do a quick check
+	 * of sisusb for device disconnection.
+	 */
+	kref_get(&sisusb->kref);
+
+	if (!*c->vc_uni_pagedir_loc)
+		con_set_default_unimap(c);
+
+	mutex_unlock(&sisusb->lock);
+
+	if (init) {
+		c->vc_cols = cols;
+		c->vc_rows = rows;
+	} else
+		vc_resize(c, cols, rows);
+}
+
+/* con_deinit console interface routine */
+static void
+sisusbcon_deinit(struct vc_data *c)
+{
+	struct sisusb_usb_data *sisusb;
+	int i;
+
+	/* This is called by do_take_over_console()
+	 * and others, ie not under our control.
+	 */
+
+	sisusb = sisusb_get_sisusb(c->vc_num);
+	if (!sisusb)
+		return;
+
+	mutex_lock(&sisusb->lock);
+
+	/* Clear ourselves in mysisusbs */
+	mysisusbs[c->vc_num] = NULL;
+
+	sisusb->havethisconsole[c->vc_num] = 0;
+
+	/* Free our font buffer if all consoles are gone */
+	if (sisusb->font_backup) {
+		for(i = 0; i < MAX_NR_CONSOLES; i++) {
+			if (sisusb->havethisconsole[c->vc_num])
+				break;
+		}
+		if (i == MAX_NR_CONSOLES) {
+			vfree(sisusb->font_backup);
+			sisusb->font_backup = NULL;
+		}
+	}
+
+	mutex_unlock(&sisusb->lock);
+
+	/* decrement the usage count on our sisusb */
+	kref_put(&sisusb->kref, sisusb_delete);
+}
+
+/* interface routine */
+static u8
+sisusbcon_build_attr(struct vc_data *c, u8 color, u8 intensity,
+			    u8 blink, u8 underline, u8 reverse, u8 unused)
+{
+	u8 attr = color;
+
+	if (underline)
+		attr = (attr & 0xf0) | c->vc_ulcolor;
+	else if (intensity == 0)
+		attr = (attr & 0xf0) | c->vc_halfcolor;
+
+	if (reverse)
+		attr = ((attr) & 0x88) |
+		       ((((attr) >> 4) |
+		       ((attr) << 4)) & 0x77);
+
+	if (blink)
+		attr ^= 0x80;
+
+	if (intensity == 2)
+		attr ^= 0x08;
+
+	return attr;
+}
+
+/* Interface routine */
+static void
+sisusbcon_invert_region(struct vc_data *vc, u16 *p, int count)
+{
+	/* Invert a region. This is called with a pointer
+	 * to the console's internal screen buffer. So we
+	 * simply do the inversion there and rely on
+	 * a call to putc(s) to update the real screen.
+	 */
+
+	while (count--) {
+		u16 a = sisusbcon_readw(p);
+
+		a = ((a) & 0x88ff)        |
+		    (((a) & 0x7000) >> 4) |
+		    (((a) & 0x0700) << 4);
+
+		sisusbcon_writew(a, p++);
+	}
+}
+
+#define SISUSB_VADDR(x,y) \
+	((u16 *)c->vc_origin + \
+	(y) * sisusb->sisusb_num_columns + \
+	(x))
+
+#define SISUSB_HADDR(x,y) \
+	((u16 *)(sisusb->vrambase + (c->vc_origin - sisusb->scrbuf)) + \
+	(y) * sisusb->sisusb_num_columns + \
+	(x))
+
+/* Interface routine */
+static void
+sisusbcon_putc(struct vc_data *c, int ch, int y, int x)
+{
+	struct sisusb_usb_data *sisusb;
+	ssize_t written;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return;
+
+	/* sisusb->lock is down */
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+
+	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
+				(long)SISUSB_HADDR(x, y), 2, &written);
+
+	mutex_unlock(&sisusb->lock);
+}
+
+/* Interface routine */
+static void
+sisusbcon_putcs(struct vc_data *c, const unsigned short *s,
+		         int count, int y, int x)
+{
+	struct sisusb_usb_data *sisusb;
+	ssize_t written;
+	u16 *dest;
+	int i;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return;
+
+	/* sisusb->lock is down */
+
+	/* Need to put the characters into the buffer ourselves,
+	 * because the vt does this AFTER calling us.
+	 */
+
+	dest = SISUSB_VADDR(x, y);
+
+	for (i = count; i > 0; i--)
+		sisusbcon_writew(sisusbcon_readw(s++), dest++);
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
+				(long)SISUSB_HADDR(x, y), count * 2, &written);
+
+	mutex_unlock(&sisusb->lock);
+}
+
+/* Interface routine */
+static void
+sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width)
+{
+	struct sisusb_usb_data *sisusb;
+	u16 eattr = c->vc_video_erase_char;
+	ssize_t written;
+	int i, length, cols;
+	u16 *dest;
+
+	if (width <= 0 || height <= 0)
+		return;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return;
+
+	/* sisusb->lock is down */
+
+	/* Need to clear buffer ourselves, because the vt does
+	 * this AFTER calling us.
+	 */
+
+	dest = SISUSB_VADDR(x, y);
+
+	cols = sisusb->sisusb_num_columns;
+
+	if (width > cols)
+		width = cols;
+
+	if (x == 0 && width >= c->vc_cols) {
+
+		sisusbcon_memsetw(dest, eattr, height * cols * 2);
+
+	} else {
+
+		for (i = height; i > 0; i--, dest += cols)
+			sisusbcon_memsetw(dest, eattr, width * 2);
+
+	}
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	length = ((height * cols) - x - (cols - width - x)) * 2;
+
+
+	sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y),
+				(long)SISUSB_HADDR(x, y), length, &written);
+
+	mutex_unlock(&sisusb->lock);
+}
+
+/* Interface routine */
+static void
+sisusbcon_bmove(struct vc_data *c, int sy, int sx,
+			 int dy, int dx, int height, int width)
+{
+	struct sisusb_usb_data *sisusb;
+	ssize_t written;
+	int cols, length;
+
+	if (width <= 0 || height <= 0)
+		return;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return;
+
+	/* sisusb->lock is down */
+
+	cols = sisusb->sisusb_num_columns;
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	length = ((height * cols) - dx - (cols - width - dx)) * 2;
+
+
+	sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy),
+				(long)SISUSB_HADDR(dx, dy), length, &written);
+
+	mutex_unlock(&sisusb->lock);
+}
+
+/* interface routine */
+static int
+sisusbcon_switch(struct vc_data *c)
+{
+	struct sisusb_usb_data *sisusb;
+	ssize_t written;
+	int length;
+
+	/* Returnvalue 0 means we have fully restored screen,
+	 *	and vt doesn't need to call do_update_region().
+	 * Returnvalue != 0 naturally means the opposite.
+	 */
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return 0;
+
+	/* sisusb->lock is down */
+
+	/* Don't write to screen if in gfx mode */
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	/* That really should not happen. It would mean we are
+	 * being called while the vc is using its private buffer
+	 * as origin.
+	 */
+	if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
+		mutex_unlock(&sisusb->lock);
+		dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n");
+		return 0;
+	}
+
+	/* Check that we don't copy too much */
+	length = min((int)c->vc_screenbuf_size,
+			(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
+
+	/* Restore the screen contents */
+	sisusbcon_memcpyw((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf,
+								length);
+
+	sisusb_copy_memory(sisusb, (unsigned char *)c->vc_origin,
+				(long)SISUSB_HADDR(0, 0),
+				length, &written);
+
+	mutex_unlock(&sisusb->lock);
+
+	return 0;
+}
+
+/* interface routine */
+static void
+sisusbcon_save_screen(struct vc_data *c)
+{
+	struct sisusb_usb_data *sisusb;
+	int length;
+
+	/* Save the current screen contents to vc's private
+	 * buffer.
+	 */
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	/* Check that we don't copy too much */
+	length = min((int)c->vc_screenbuf_size,
+			(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
+
+	/* Save the screen contents to vc's private buffer */
+	sisusbcon_memcpyw((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin,
+								length);
+
+	mutex_unlock(&sisusb->lock);
+}
+
+/* interface routine */
+static int
+sisusbcon_set_palette(struct vc_data *c, unsigned char *table)
+{
+	struct sisusb_usb_data *sisusb;
+	int i, j;
+
+	/* Return value not used by vt */
+
+	if (!CON_IS_VISIBLE(c))
+		return -EINVAL;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return -EINVAL;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return -EINVAL;
+	}
+
+	for (i = j = 0; i < 16; i++) {
+		if (sisusb_setreg(sisusb, SISCOLIDX, table[i]))
+			break;
+		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
+			break;
+		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
+			break;
+		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
+			break;
+	}
+
+	mutex_unlock(&sisusb->lock);
+
+	return 0;
+}
+
+/* interface routine */
+static int
+sisusbcon_blank(struct vc_data *c, int blank, int mode_switch)
+{
+	struct sisusb_usb_data *sisusb;
+	u8 sr1, cr17, pmreg, cr63;
+	ssize_t written;
+	int ret = 0;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (mode_switch)
+		sisusb->is_gfx = blank ? 1 : 0;
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	switch (blank) {
+
+	case 1:		/* Normal blanking: Clear screen */
+	case -1:
+		sisusbcon_memsetw((u16 *)c->vc_origin,
+				c->vc_video_erase_char,
+				c->vc_screenbuf_size);
+		sisusb_copy_memory(sisusb,
+				(unsigned char *)c->vc_origin,
+				(u32)(sisusb->vrambase +
+					(c->vc_origin - sisusb->scrbuf)),
+				c->vc_screenbuf_size, &written);
+		sisusb->con_blanked = 1;
+		ret = 1;
+		break;
+
+	default:	/* VESA blanking */
+		switch (blank) {
+		case 0: /* Unblank */
+			sr1   = 0x00;
+			cr17  = 0x80;
+			pmreg = 0x00;
+			cr63  = 0x00;
+			ret = 1;
+			sisusb->con_blanked = 0;
+			break;
+		case VESA_VSYNC_SUSPEND + 1:
+			sr1   = 0x20;
+			cr17  = 0x80;
+			pmreg = 0x80;
+			cr63  = 0x40;
+			break;
+		case VESA_HSYNC_SUSPEND + 1:
+			sr1   = 0x20;
+			cr17  = 0x80;
+			pmreg = 0x40;
+			cr63  = 0x40;
+			break;
+		case VESA_POWERDOWN + 1:
+			sr1   = 0x20;
+			cr17  = 0x00;
+			pmreg = 0xc0;
+			cr63  = 0x40;
+			break;
+		default:
+			mutex_unlock(&sisusb->lock);
+			return -EINVAL;
+		}
+
+		sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1);
+		sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17);
+		sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg);
+		sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63);
+
+	}
+
+	mutex_unlock(&sisusb->lock);
+
+	return ret;
+}
+
+/* interface routine */
+static int
+sisusbcon_scrolldelta(struct vc_data *c, int lines)
+{
+	struct sisusb_usb_data *sisusb;
+	int margin = c->vc_size_row * 4;
+	int ul, we, p, st;
+
+	/* The return value does not seem to be used */
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	if (!lines)		/* Turn scrollback off */
+		c->vc_visible_origin = c->vc_origin;
+	else {
+
+		if (sisusb->con_rolled_over >
+				(c->vc_scr_end - sisusb->scrbuf) + margin) {
+
+			ul = c->vc_scr_end - sisusb->scrbuf;
+			we = sisusb->con_rolled_over + c->vc_size_row;
+
+		} else {
+
+			ul = 0;
+			we = sisusb->scrbuf_size;
+
+		}
+
+		p = (c->vc_visible_origin - sisusb->scrbuf - ul + we) % we +
+				lines * c->vc_size_row;
+
+		st = (c->vc_origin - sisusb->scrbuf - ul + we) % we;
+
+		if (st < 2 * margin)
+			margin = 0;
+
+		if (p < margin)
+			p = 0;
+
+		if (p > st - margin)
+			p = st;
+
+		c->vc_visible_origin = sisusb->scrbuf + (p + ul) % we;
+	}
+
+	sisusbcon_set_start_address(sisusb, c);
+
+	mutex_unlock(&sisusb->lock);
+
+	return 1;
+}
+
+/* Interface routine */
+static void
+sisusbcon_cursor(struct vc_data *c, int mode)
+{
+	struct sisusb_usb_data *sisusb;
+	int from, to, baseline;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	if (c->vc_origin != c->vc_visible_origin) {
+		c->vc_visible_origin = c->vc_origin;
+		sisusbcon_set_start_address(sisusb, c);
+	}
+
+	if (mode == CM_ERASE) {
+		sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20);
+		sisusb->sisusb_cursor_size_to = -1;
+		mutex_unlock(&sisusb->lock);
+		return;
+	}
+
+	sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2);
+
+	baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2);
+
+	switch (c->vc_cursor_type & 0x0f) {
+		case CUR_BLOCK:		from = 1;
+					to   = c->vc_font.height;
+					break;
+		case CUR_TWO_THIRDS:	from = c->vc_font.height / 3;
+					to   = baseline;
+					break;
+		case CUR_LOWER_HALF:	from = c->vc_font.height / 2;
+					to   = baseline;
+					break;
+		case CUR_LOWER_THIRD:	from = (c->vc_font.height * 2) / 3;
+					to   = baseline;
+					break;
+		case CUR_NONE:		from = 31;
+					to = 30;
+					break;
+		default:
+		case CUR_UNDERLINE:	from = baseline - 1;
+					to   = baseline;
+					break;
+	}
+
+	if (sisusb->sisusb_cursor_size_from != from ||
+	    sisusb->sisusb_cursor_size_to != to) {
+
+		sisusb_setidxreg(sisusb, SISCR, 0x0a, from);
+		sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to);
+
+		sisusb->sisusb_cursor_size_from = from;
+		sisusb->sisusb_cursor_size_to   = to;
+	}
+
+	mutex_unlock(&sisusb->lock);
+}
+
+static int
+sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb,
+					int t, int b, int dir, int lines)
+{
+	int cols = sisusb->sisusb_num_columns;
+	int length = ((b - t) * cols) * 2;
+	u16 eattr = c->vc_video_erase_char;
+	ssize_t written;
+
+	/* sisusb->lock is down */
+
+	/* Scroll an area which does not match the
+	 * visible screen's dimensions. This needs
+	 * to be done separately, as it does not
+	 * use hardware panning.
+	 */
+
+	switch (dir) {
+
+		case SM_UP:
+			sisusbcon_memmovew(SISUSB_VADDR(0, t),
+					   SISUSB_VADDR(0, t + lines),
+					   (b - t - lines) * cols * 2);
+			sisusbcon_memsetw(SISUSB_VADDR(0, b - lines), eattr,
+					  lines * cols * 2);
+			break;
+
+		case SM_DOWN:
+			sisusbcon_memmovew(SISUSB_VADDR(0, t + lines),
+					   SISUSB_VADDR(0, t),
+					   (b - t - lines) * cols * 2);
+			sisusbcon_memsetw(SISUSB_VADDR(0, t), eattr,
+					  lines * cols * 2);
+			break;
+	}
+
+	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(0, t),
+				(long)SISUSB_HADDR(0, t), length, &written);
+
+	mutex_unlock(&sisusb->lock);
+
+	return 1;
+}
+
+/* Interface routine */
+static int
+sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
+{
+	struct sisusb_usb_data *sisusb;
+	u16 eattr = c->vc_video_erase_char;
+	ssize_t written;
+	int copyall = 0;
+	unsigned long oldorigin;
+	unsigned int delta = lines * c->vc_size_row;
+	u32 originoffset;
+
+	/* Returning != 0 means we have done the scrolling successfully.
+	 * Returning 0 makes vt do the scrolling on its own.
+	 * Note that con_scroll is only called if the console is
+	 * visible. In that case, the origin should be our buffer,
+	 * not the vt's private one.
+	 */
+
+	if (!lines)
+		return 1;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	/* Special case */
+	if (t || b != c->vc_rows)
+		return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines);
+
+	if (c->vc_origin != c->vc_visible_origin) {
+		c->vc_visible_origin = c->vc_origin;
+		sisusbcon_set_start_address(sisusb, c);
+	}
+
+	/* limit amount to maximum realistic size */
+	if (lines > c->vc_rows)
+		lines = c->vc_rows;
+
+	oldorigin = c->vc_origin;
+
+	switch (dir) {
+
+	case SM_UP:
+
+		if (c->vc_scr_end + delta >=
+				sisusb->scrbuf + sisusb->scrbuf_size) {
+			sisusbcon_memcpyw((u16 *)sisusb->scrbuf,
+					  (u16 *)(oldorigin + delta),
+					  c->vc_screenbuf_size - delta);
+			c->vc_origin = sisusb->scrbuf;
+			sisusb->con_rolled_over = oldorigin - sisusb->scrbuf;
+			copyall = 1;
+		} else
+			c->vc_origin += delta;
+
+		sisusbcon_memsetw(
+			(u16 *)(c->vc_origin + c->vc_screenbuf_size - delta),
+					eattr, delta);
+
+		break;
+
+	case SM_DOWN:
+
+		if (oldorigin - delta < sisusb->scrbuf) {
+			sisusbcon_memmovew((u16 *)(sisusb->scrbuf +
+							sisusb->scrbuf_size -
+							c->vc_screenbuf_size +
+							delta),
+					   (u16 *)oldorigin,
+					   c->vc_screenbuf_size - delta);
+			c->vc_origin = sisusb->scrbuf +
+					sisusb->scrbuf_size -
+					c->vc_screenbuf_size;
+			sisusb->con_rolled_over = 0;
+			copyall = 1;
+		} else
+			c->vc_origin -= delta;
+
+		c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
+
+		scr_memsetw((u16 *)(c->vc_origin), eattr, delta);
+
+		break;
+	}
+
+	originoffset = (u32)(c->vc_origin - sisusb->scrbuf);
+
+	if (copyall)
+		sisusb_copy_memory(sisusb,
+			(char *)c->vc_origin,
+			(u32)(sisusb->vrambase + originoffset),
+			c->vc_screenbuf_size, &written);
+	else if (dir == SM_UP)
+		sisusb_copy_memory(sisusb,
+			(char *)c->vc_origin + c->vc_screenbuf_size - delta,
+			(u32)sisusb->vrambase + originoffset +
+					c->vc_screenbuf_size - delta,
+			delta, &written);
+	else
+		sisusb_copy_memory(sisusb,
+			(char *)c->vc_origin,
+			(u32)(sisusb->vrambase + originoffset),
+			delta, &written);
+
+	c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
+	c->vc_visible_origin = c->vc_origin;
+
+	sisusbcon_set_start_address(sisusb, c);
+
+	c->vc_pos = c->vc_pos - oldorigin + c->vc_origin;
+
+	mutex_unlock(&sisusb->lock);
+
+	return 1;
+}
+
+/* Interface routine */
+static int
+sisusbcon_set_origin(struct vc_data *c)
+{
+	struct sisusb_usb_data *sisusb;
+
+	/* Returning != 0 means we were successful.
+	 * Returning 0 will vt make to use its own
+	 *	screenbuffer as the origin.
+	 */
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	c->vc_origin = c->vc_visible_origin = sisusb->scrbuf;
+
+	sisusbcon_set_start_address(sisusb, c);
+
+	sisusb->con_rolled_over = 0;
+
+	mutex_unlock(&sisusb->lock);
+
+	return 1;
+}
+
+/* Interface routine */
+static int
+sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows,
+		 unsigned int user)
+{
+	struct sisusb_usb_data *sisusb;
+	int fh;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return -ENODEV;
+
+	fh = sisusb->current_font_height;
+
+	mutex_unlock(&sisusb->lock);
+
+	/* We are quite unflexible as regards resizing. The vt code
+	 * handles sizes where the line length isn't equal the pitch
+	 * quite badly. As regards the rows, our panning tricks only
+	 * work well if the number of rows equals the visible number
+	 * of rows.
+	 */
+
+	if (newcols != 80 || c->vc_scan_lines / fh != newrows)
+		return -EINVAL;
+
+	return 0;
+}
+
+int
+sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
+			u8 *arg, int cmapsz, int ch512, int dorecalc,
+			struct vc_data *c, int fh, int uplock)
+{
+	int font_select = 0x00, i, err = 0;
+	u32 offset = 0;
+	u8 dummy;
+
+	/* sisusb->lock is down */
+
+	/*
+	 * The default font is kept in slot 0.
+	 * A user font is loaded in slot 2 (256 ch)
+	 * or 2+3 (512 ch).
+	 */
+
+	if ((slot != 0 && slot != 2) || !fh) {
+		if (uplock)
+			mutex_unlock(&sisusb->lock);
+		return -EINVAL;
+	}
+
+	if (set)
+		sisusb->font_slot = slot;
+
+	/* Default font is always 256 */
+	if (slot == 0)
+		ch512 = 0;
+	else
+		offset = 4 * cmapsz;
+
+	font_select = (slot == 0) ? 0x00 : (ch512 ? 0x0e : 0x0a);
+
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x04); /* Write to plane 2 */
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x07); /* Memory mode a0-bf */
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset */
+
+	if (err)
+		goto font_op_error;
+
+	err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x03); /* Select plane read 2 */
+	err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x00); /* Disable odd/even */
+	err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x00); /* Address range a0-bf */
+
+	if (err)
+		goto font_op_error;
+
+	if (arg) {
+		if (set)
+			for (i = 0; i < cmapsz; i++) {
+				err |= sisusb_writeb(sisusb,
+					sisusb->vrambase + offset + i,
+					arg[i]);
+				if (err)
+					break;
+			}
+		else
+			for (i = 0; i < cmapsz; i++) {
+				err |= sisusb_readb(sisusb,
+					sisusb->vrambase + offset + i,
+					&arg[i]);
+				if (err)
+					break;
+			}
+
+		/*
+		 * In 512-character mode, the character map is not contiguous if
+		 * we want to remain EGA compatible -- which we do
+		 */
+
+		if (ch512) {
+			if (set)
+				for (i = 0; i < cmapsz; i++) {
+					err |= sisusb_writeb(sisusb,
+						sisusb->vrambase + offset +
+							(2 * cmapsz) + i,
+						arg[cmapsz + i]);
+					if (err)
+						break;
+				}
+			else
+				for (i = 0; i < cmapsz; i++) {
+					err |= sisusb_readb(sisusb,
+						sisusb->vrambase + offset +
+							(2 * cmapsz) + i,
+						&arg[cmapsz + i]);
+					if (err)
+						break;
+				}
+		}
+	}
+
+	if (err)
+		goto font_op_error;
+
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x03); /* Write to planes 0+1 */
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x03); /* Memory mode a0-bf */
+	if (set)
+		sisusb_setidxreg(sisusb, SISSR, 0x03, font_select);
+	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset end */
+
+	if (err)
+		goto font_op_error;
+
+	err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x00); /* Select plane read 0 */
+	err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x10); /* Enable odd/even */
+	err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x06); /* Address range b8-bf */
+
+	if (err)
+		goto font_op_error;
+
+	if ((set) && (ch512 != sisusb->current_font_512)) {
+
+		/* Font is shared among all our consoles.
+		 * And so is the hi_font_mask.
+		 */
+		for (i = 0; i < MAX_NR_CONSOLES; i++) {
+			struct vc_data *d = vc_cons[i].d;
+			if (d && d->vc_sw == &sisusb_con)
+				d->vc_hi_font_mask = ch512 ? 0x0800 : 0;
+		}
+
+		sisusb->current_font_512 = ch512;
+
+		/* color plane enable register:
+			256-char: enable intensity bit
+			512-char: disable intensity bit */
+		sisusb_getreg(sisusb, SISINPSTAT, &dummy);
+		sisusb_setreg(sisusb, SISAR, 0x12);
+		sisusb_setreg(sisusb, SISAR, ch512 ? 0x07 : 0x0f);
+
+		sisusb_getreg(sisusb, SISINPSTAT, &dummy);
+		sisusb_setreg(sisusb, SISAR, 0x20);
+		sisusb_getreg(sisusb, SISINPSTAT, &dummy);
+	}
+
+	if (dorecalc) {
+
+		/*
+		 * Adjust the screen to fit a font of a certain height
+		 */
+
+		unsigned char ovr, vde, fsr;
+		int rows = 0, maxscan = 0;
+
+		if (c) {
+
+			/* Number of video rows */
+			rows = c->vc_scan_lines / fh;
+			/* Scan lines to actually display-1 */
+			maxscan = rows * fh - 1;
+
+			/*printk(KERN_DEBUG "sisusb recalc rows %d maxscan %d fh %d sl %d\n",
+				rows, maxscan, fh, c->vc_scan_lines);*/
+
+			sisusb_getidxreg(sisusb, SISCR, 0x07, &ovr);
+			vde = maxscan & 0xff;
+			ovr = (ovr & 0xbd) |
+			      ((maxscan & 0x100) >> 7) |
+			      ((maxscan & 0x200) >> 3);
+			sisusb_setidxreg(sisusb, SISCR, 0x07, ovr);
+			sisusb_setidxreg(sisusb, SISCR, 0x12, vde);
+
+		}
+
+		sisusb_getidxreg(sisusb, SISCR, 0x09, &fsr);
+		fsr = (fsr & 0xe0) | (fh - 1);
+		sisusb_setidxreg(sisusb, SISCR, 0x09, fsr);
+		sisusb->current_font_height = fh;
+
+		sisusb->sisusb_cursor_size_from = -1;
+		sisusb->sisusb_cursor_size_to   = -1;
+
+	}
+
+	if (uplock)
+		mutex_unlock(&sisusb->lock);
+
+	if (dorecalc && c) {
+		int rows = c->vc_scan_lines / fh;
+
+		/* Now adjust our consoles' size */
+
+		for (i = 0; i < MAX_NR_CONSOLES; i++) {
+			struct vc_data *vc = vc_cons[i].d;
+
+			if (vc && vc->vc_sw == &sisusb_con) {
+				if (CON_IS_VISIBLE(vc)) {
+					vc->vc_sw->con_cursor(vc, CM_DRAW);
+				}
+				vc->vc_font.height = fh;
+				vc_resize(vc, 0, rows);
+			}
+		}
+	}
+
+	return 0;
+
+font_op_error:
+	if (uplock)
+		mutex_unlock(&sisusb->lock);
+
+	return -EIO;
+}
+
+/* Interface routine */
+static int
+sisusbcon_font_set(struct vc_data *c, struct console_font *font,
+							unsigned flags)
+{
+	struct sisusb_usb_data *sisusb;
+	unsigned charcount = font->charcount;
+
+	if (font->width != 8 || (charcount != 256 && charcount != 512))
+		return -EINVAL;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return -ENODEV;
+
+	/* sisusb->lock is down */
+
+	/* Save the user-provided font into a buffer. This
+	 * is used for restoring text mode after quitting
+	 * from X and for the con_getfont routine.
+	 */
+	if (sisusb->font_backup) {
+		if (sisusb->font_backup_size < charcount) {
+			vfree(sisusb->font_backup);
+			sisusb->font_backup = NULL;
+		}
+	}
+
+	if (!sisusb->font_backup)
+		sisusb->font_backup = vmalloc(charcount * 32);
+
+	if (sisusb->font_backup) {
+		memcpy(sisusb->font_backup, font->data, charcount * 32);
+		sisusb->font_backup_size = charcount;
+		sisusb->font_backup_height = font->height;
+		sisusb->font_backup_512 = (charcount == 512) ? 1 : 0;
+	}
+
+	/* do_font_op ups sisusb->lock */
+
+	return sisusbcon_do_font_op(sisusb, 1, 2, font->data,
+			8192, (charcount == 512),
+			(!(flags & KD_FONT_FLAG_DONT_RECALC)) ? 1 : 0,
+			c, font->height, 1);
+}
+
+/* Interface routine */
+static int
+sisusbcon_font_get(struct vc_data *c, struct console_font *font)
+{
+	struct sisusb_usb_data *sisusb;
+
+	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
+	if (!sisusb)
+		return -ENODEV;
+
+	/* sisusb->lock is down */
+
+	font->width = 8;
+	font->height = c->vc_font.height;
+	font->charcount = 256;
+
+	if (!font->data) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	if (!sisusb->font_backup) {
+		mutex_unlock(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	/* Copy 256 chars only, like vgacon */
+	memcpy(font->data, sisusb->font_backup, 256 * 32);
+
+	mutex_unlock(&sisusb->lock);
+
+	return 0;
+}
+
+/*
+ *  The console `switch' structure for the sisusb console
+ */
+
+static const struct consw sisusb_con = {
+	.owner =		THIS_MODULE,
+	.con_startup =		sisusbcon_startup,
+	.con_init =		sisusbcon_init,
+	.con_deinit =		sisusbcon_deinit,
+	.con_clear =		sisusbcon_clear,
+	.con_putc =		sisusbcon_putc,
+	.con_putcs =		sisusbcon_putcs,
+	.con_cursor =		sisusbcon_cursor,
+	.con_scroll =		sisusbcon_scroll,
+	.con_bmove =		sisusbcon_bmove,
+	.con_switch =		sisusbcon_switch,
+	.con_blank =		sisusbcon_blank,
+	.con_font_set =		sisusbcon_font_set,
+	.con_font_get =		sisusbcon_font_get,
+	.con_set_palette =	sisusbcon_set_palette,
+	.con_scrolldelta =	sisusbcon_scrolldelta,
+	.con_build_attr =	sisusbcon_build_attr,
+	.con_invert_region =	sisusbcon_invert_region,
+	.con_set_origin =	sisusbcon_set_origin,
+	.con_save_screen =	sisusbcon_save_screen,
+	.con_resize =		sisusbcon_resize,
+};
+
+/* Our very own dummy console driver */
+
+static const char *sisusbdummycon_startup(void)
+{
+    return "SISUSBVGADUMMY";
+}
+
+static void sisusbdummycon_init(struct vc_data *vc, int init)
+{
+    vc->vc_can_do_color = 1;
+    if (init) {
+	vc->vc_cols = 80;
+	vc->vc_rows = 25;
+    } else
+	vc_resize(vc, 80, 25);
+}
+
+static int sisusbdummycon_dummy(void)
+{
+    return 0;
+}
+
+#define SISUSBCONDUMMY	(void *)sisusbdummycon_dummy
+
+static const struct consw sisusb_dummy_con = {
+	.owner =		THIS_MODULE,
+	.con_startup =		sisusbdummycon_startup,
+	.con_init =		sisusbdummycon_init,
+	.con_deinit =		SISUSBCONDUMMY,
+	.con_clear =		SISUSBCONDUMMY,
+	.con_putc =		SISUSBCONDUMMY,
+	.con_putcs =		SISUSBCONDUMMY,
+	.con_cursor =		SISUSBCONDUMMY,
+	.con_scroll =		SISUSBCONDUMMY,
+	.con_bmove =		SISUSBCONDUMMY,
+	.con_switch =		SISUSBCONDUMMY,
+	.con_blank =		SISUSBCONDUMMY,
+	.con_font_set =		SISUSBCONDUMMY,
+	.con_font_get =		SISUSBCONDUMMY,
+	.con_font_default =	SISUSBCONDUMMY,
+	.con_font_copy =	SISUSBCONDUMMY,
+	.con_set_palette =	SISUSBCONDUMMY,
+	.con_scrolldelta =	SISUSBCONDUMMY,
+};
+
+int
+sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
+{
+	int i, ret;
+
+	mutex_lock(&sisusb->lock);
+
+	/* Erm.. that should not happen */
+	if (sisusb->haveconsole || !sisusb->SiS_Pr) {
+		mutex_unlock(&sisusb->lock);
+		return 1;
+	}
+
+	sisusb->con_first = first;
+	sisusb->con_last  = last;
+
+	if (first > last ||
+	    first > MAX_NR_CONSOLES ||
+	    last > MAX_NR_CONSOLES) {
+		mutex_unlock(&sisusb->lock);
+		return 1;
+	}
+
+	/* If gfxcore not initialized or no consoles given, quit graciously */
+	if (!sisusb->gfxinit || first < 1 || last < 1) {
+		mutex_unlock(&sisusb->lock);
+		return 0;
+	}
+
+	sisusb->sisusb_cursor_loc       = -1;
+	sisusb->sisusb_cursor_size_from = -1;
+	sisusb->sisusb_cursor_size_to   = -1;
+
+	/* Set up text mode (and upload  default font) */
+	if (sisusb_reset_text_mode(sisusb, 1)) {
+		mutex_unlock(&sisusb->lock);
+		dev_err(&sisusb->sisusb_dev->dev, "Failed to set up text mode\n");
+		return 1;
+	}
+
+	/* Initialize some gfx registers */
+	sisusb_initialize(sisusb);
+
+	for (i = first - 1; i <= last - 1; i++) {
+		/* Save sisusb for our interface routines */
+		mysisusbs[i] = sisusb;
+	}
+
+	/* Initial console setup */
+	sisusb->sisusb_num_columns = 80;
+
+	/* Use a 32K buffer (matches b8000-bffff area) */
+	sisusb->scrbuf_size = 32 * 1024;
+
+	/* Allocate screen buffer */
+	if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
+		mutex_unlock(&sisusb->lock);
+		dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate screen buffer\n");
+		return 1;
+	}
+
+	mutex_unlock(&sisusb->lock);
+
+	/* Now grab the desired console(s) */
+	console_lock();
+	ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0);
+	console_unlock();
+	if (!ret)
+		sisusb->haveconsole = 1;
+	else {
+		for (i = first - 1; i <= last - 1; i++)
+			mysisusbs[i] = NULL;
+	}
+
+	return ret;
+}
+
+void
+sisusb_console_exit(struct sisusb_usb_data *sisusb)
+{
+	int i;
+
+	/* This is called if the device is disconnected
+	 * and while disconnect and lock semaphores
+	 * are up. This should be save because we
+	 * can't lose our sisusb any other way but by
+	 * disconnection (and hence, the disconnect
+	 * sema is for protecting all other access
+	 * functions from disconnection, not the
+	 * other way round).
+	 */
+
+	/* Now what do we do in case of disconnection:
+	 * One alternative would be to simply call
+	 * give_up_console(). Nah, not a good idea.
+	 * give_up_console() is obviously buggy as it
+	 * only discards the consw pointer from the
+	 * driver_map, but doesn't adapt vc->vc_sw
+	 * of the affected consoles. Hence, the next
+	 * call to any of the console functions will
+	 * eventually take a trip to oops county.
+	 * Also, give_up_console for some reason
+	 * doesn't decrement our module refcount.
+	 * Instead, we switch our consoles to a private
+	 * dummy console. This, of course, keeps our
+	 * refcount up as well, but it works perfectly.
+	 */
+
+	if (sisusb->haveconsole) {
+		for (i = 0; i < MAX_NR_CONSOLES; i++)
+			if (sisusb->havethisconsole[i]) {
+				console_lock();
+				do_take_over_console(&sisusb_dummy_con, i, i, 0);
+				console_unlock();
+				/* At this point, con_deinit for all our
+				 * consoles is executed by do_take_over_console().
+				 */
+			}
+		sisusb->haveconsole = 0;
+	}
+
+	vfree((void *)sisusb->scrbuf);
+	sisusb->scrbuf = 0;
+
+	vfree(sisusb->font_backup);
+	sisusb->font_backup = NULL;
+}
+
+void __init sisusb_init_concode(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_NR_CONSOLES; i++)
+		mysisusbs[i] = NULL;
+}
+
+#endif /* INCL_CON */
+
+
+
diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.c b/drivers/usb/misc/sisusbvga/sisusb_init.c
new file mode 100644
index 0000000..bf0032c
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.c
@@ -0,0 +1,958 @@
+/*
+ * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
+ *
+ * Display mode initializing code
+ *
+ * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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 the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 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 the author may not be used to endorse or promote products
+ * *    derived from this software without specific prior written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ * Author:	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+
+#include "sisusb.h"
+
+#ifdef INCL_SISUSB_CON
+
+#include "sisusb_init.h"
+
+/*********************************************/
+/*         POINTER INITIALIZATION            */
+/*********************************************/
+
+static void SiSUSB_InitPtr(struct SiS_Private *SiS_Pr)
+{
+	SiS_Pr->SiS_ModeResInfo = SiSUSB_ModeResInfo;
+	SiS_Pr->SiS_StandTable = SiSUSB_StandTable;
+
+	SiS_Pr->SiS_SModeIDTable = SiSUSB_SModeIDTable;
+	SiS_Pr->SiS_EModeIDTable = SiSUSB_EModeIDTable;
+	SiS_Pr->SiS_RefIndex = SiSUSB_RefIndex;
+	SiS_Pr->SiS_CRT1Table = SiSUSB_CRT1Table;
+
+	SiS_Pr->SiS_VCLKData = SiSUSB_VCLKData;
+}
+
+/*********************************************/
+/*          HELPER: SetReg, GetReg           */
+/*********************************************/
+
+static void
+SiS_SetReg(struct SiS_Private *SiS_Pr, unsigned long port,
+	   unsigned short index, unsigned short data)
+{
+	sisusb_setidxreg(SiS_Pr->sisusb, port, index, data);
+}
+
+static void
+SiS_SetRegByte(struct SiS_Private *SiS_Pr, unsigned long port,
+	       unsigned short data)
+{
+	sisusb_setreg(SiS_Pr->sisusb, port, data);
+}
+
+static unsigned char
+SiS_GetReg(struct SiS_Private *SiS_Pr, unsigned long port, unsigned short index)
+{
+	u8 data;
+
+	sisusb_getidxreg(SiS_Pr->sisusb, port, index, &data);
+
+	return data;
+}
+
+static unsigned char
+SiS_GetRegByte(struct SiS_Private *SiS_Pr, unsigned long port)
+{
+	u8 data;
+
+	sisusb_getreg(SiS_Pr->sisusb, port, &data);
+
+	return data;
+}
+
+static void
+SiS_SetRegANDOR(struct SiS_Private *SiS_Pr, unsigned long port,
+		unsigned short index, unsigned short DataAND,
+		unsigned short DataOR)
+{
+	sisusb_setidxregandor(SiS_Pr->sisusb, port, index, DataAND, DataOR);
+}
+
+static void
+SiS_SetRegAND(struct SiS_Private *SiS_Pr, unsigned long port,
+	      unsigned short index, unsigned short DataAND)
+{
+	sisusb_setidxregand(SiS_Pr->sisusb, port, index, DataAND);
+}
+
+static void
+SiS_SetRegOR(struct SiS_Private *SiS_Pr, unsigned long port,
+	     unsigned short index, unsigned short DataOR)
+{
+	sisusb_setidxregor(SiS_Pr->sisusb, port, index, DataOR);
+}
+
+/*********************************************/
+/*      HELPER: DisplayOn, DisplayOff        */
+/*********************************************/
+
+static void SiS_DisplayOn(struct SiS_Private *SiS_Pr)
+{
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0xDF);
+}
+
+/*********************************************/
+/*        HELPER: Init Port Addresses        */
+/*********************************************/
+
+static void SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr)
+{
+	SiS_Pr->SiS_P3c4 = BaseAddr + 0x14;
+	SiS_Pr->SiS_P3d4 = BaseAddr + 0x24;
+	SiS_Pr->SiS_P3c0 = BaseAddr + 0x10;
+	SiS_Pr->SiS_P3ce = BaseAddr + 0x1e;
+	SiS_Pr->SiS_P3c2 = BaseAddr + 0x12;
+	SiS_Pr->SiS_P3ca = BaseAddr + 0x1a;
+	SiS_Pr->SiS_P3c6 = BaseAddr + 0x16;
+	SiS_Pr->SiS_P3c7 = BaseAddr + 0x17;
+	SiS_Pr->SiS_P3c8 = BaseAddr + 0x18;
+	SiS_Pr->SiS_P3c9 = BaseAddr + 0x19;
+	SiS_Pr->SiS_P3cb = BaseAddr + 0x1b;
+	SiS_Pr->SiS_P3cc = BaseAddr + 0x1c;
+	SiS_Pr->SiS_P3cd = BaseAddr + 0x1d;
+	SiS_Pr->SiS_P3da = BaseAddr + 0x2a;
+	SiS_Pr->SiS_Part1Port = BaseAddr + SIS_CRT2_PORT_04;
+}
+
+/*********************************************/
+/*             HELPER: GetSysFlags           */
+/*********************************************/
+
+static void SiS_GetSysFlags(struct SiS_Private *SiS_Pr)
+{
+	SiS_Pr->SiS_MyCR63 = 0x63;
+}
+
+/*********************************************/
+/*         HELPER: Init PCI & Engines        */
+/*********************************************/
+
+static void SiSInitPCIetc(struct SiS_Private *SiS_Pr)
+{
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x20, 0xa1);
+	/*  - Enable 2D (0x40)
+	 *  - Enable 3D (0x02)
+	 *  - Enable 3D vertex command fetch (0x10)
+	 *  - Enable 3D command parser (0x08)
+	 *  - Enable 3D G/L transformation engine (0x80)
+	 */
+	SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1E, 0xDA);
+}
+
+/*********************************************/
+/*        HELPER: SET SEGMENT REGISTERS      */
+/*********************************************/
+
+static void SiS_SetSegRegLower(struct SiS_Private *SiS_Pr, unsigned short value)
+{
+	unsigned short temp;
+
+	value &= 0x00ff;
+	temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb) & 0xf0;
+	temp |= (value >> 4);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb, temp);
+	temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd) & 0xf0;
+	temp |= (value & 0x0f);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp);
+}
+
+static void SiS_SetSegRegUpper(struct SiS_Private *SiS_Pr, unsigned short value)
+{
+	unsigned short temp;
+
+	value &= 0x00ff;
+	temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb) & 0x0f;
+	temp |= (value & 0xf0);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cb, temp);
+	temp = SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd) & 0x0f;
+	temp |= (value << 4);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3cd, temp);
+}
+
+static void SiS_SetSegmentReg(struct SiS_Private *SiS_Pr, unsigned short value)
+{
+	SiS_SetSegRegLower(SiS_Pr, value);
+	SiS_SetSegRegUpper(SiS_Pr, value);
+}
+
+static void SiS_ResetSegmentReg(struct SiS_Private *SiS_Pr)
+{
+	SiS_SetSegmentReg(SiS_Pr, 0);
+}
+
+static void
+SiS_SetSegmentRegOver(struct SiS_Private *SiS_Pr, unsigned short value)
+{
+	unsigned short temp = value >> 8;
+
+	temp &= 0x07;
+	temp |= (temp << 4);
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1d, temp);
+	SiS_SetSegmentReg(SiS_Pr, value);
+}
+
+static void SiS_ResetSegmentRegOver(struct SiS_Private *SiS_Pr)
+{
+	SiS_SetSegmentRegOver(SiS_Pr, 0);
+}
+
+static void SiS_ResetSegmentRegisters(struct SiS_Private *SiS_Pr)
+{
+	SiS_ResetSegmentReg(SiS_Pr);
+	SiS_ResetSegmentRegOver(SiS_Pr);
+}
+
+/*********************************************/
+/*           HELPER: SearchModeID            */
+/*********************************************/
+
+static int
+SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo,
+		 unsigned short *ModeIdIndex)
+{
+	if ((*ModeNo) <= 0x13) {
+
+		if ((*ModeNo) != 0x03)
+			return 0;
+
+		(*ModeIdIndex) = 0;
+
+	} else {
+
+		for (*ModeIdIndex = 0;; (*ModeIdIndex)++) {
+
+			if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID ==
+			    (*ModeNo))
+				break;
+
+			if (SiS_Pr->SiS_EModeIDTable[*ModeIdIndex].Ext_ModeID ==
+			    0xFF)
+				return 0;
+		}
+
+	}
+
+	return 1;
+}
+
+/*********************************************/
+/*            HELPER: ENABLE CRT1            */
+/*********************************************/
+
+static void SiS_HandleCRT1(struct SiS_Private *SiS_Pr)
+{
+	/* Enable CRT1 gating */
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, SiS_Pr->SiS_MyCR63, 0xbf);
+}
+
+/*********************************************/
+/*           HELPER: GetColorDepth           */
+/*********************************************/
+
+static unsigned short
+SiS_GetColorDepth(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		  unsigned short ModeIdIndex)
+{
+	static const unsigned short ColorDepth[6] = { 1, 2, 4, 4, 6, 8 };
+	unsigned short modeflag;
+	short index;
+
+	if (ModeNo <= 0x13) {
+		modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag;
+	} else {
+		modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+	}
+
+	index = (modeflag & ModeTypeMask) - ModeEGA;
+	if (index < 0)
+		index = 0;
+	return ColorDepth[index];
+}
+
+/*********************************************/
+/*             HELPER: GetOffset             */
+/*********************************************/
+
+static unsigned short
+SiS_GetOffset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+	      unsigned short ModeIdIndex, unsigned short rrti)
+{
+	unsigned short xres, temp, colordepth, infoflag;
+
+	infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
+	xres = SiS_Pr->SiS_RefIndex[rrti].XRes;
+
+	colordepth = SiS_GetColorDepth(SiS_Pr, ModeNo, ModeIdIndex);
+
+	temp = xres / 16;
+
+	if (infoflag & InterlaceMode)
+		temp <<= 1;
+
+	temp *= colordepth;
+
+	if (xres % 16)
+		temp += (colordepth >> 1);
+
+	return temp;
+}
+
+/*********************************************/
+/*                   SEQ                     */
+/*********************************************/
+
+static void
+SiS_SetSeqRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
+{
+	unsigned char SRdata;
+	int i;
+
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x00, 0x03);
+
+	SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[0] | 0x20;
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, SRdata);
+
+	for (i = 2; i <= 4; i++) {
+		SRdata = SiS_Pr->SiS_StandTable[StandTableIndex].SR[i - 1];
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, SRdata);
+	}
+}
+
+/*********************************************/
+/*                  MISC                     */
+/*********************************************/
+
+static void
+SiS_SetMiscRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
+{
+	unsigned char Miscdata = SiS_Pr->SiS_StandTable[StandTableIndex].MISC;
+
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c2, Miscdata);
+}
+
+/*********************************************/
+/*                  CRTC                     */
+/*********************************************/
+
+static void
+SiS_SetCRTCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
+{
+	unsigned char CRTCdata;
+	unsigned short i;
+
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f);
+
+	for (i = 0; i <= 0x18; i++) {
+		CRTCdata = SiS_Pr->SiS_StandTable[StandTableIndex].CRTC[i];
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, i, CRTCdata);
+	}
+}
+
+/*********************************************/
+/*                   ATT                     */
+/*********************************************/
+
+static void
+SiS_SetATTRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
+{
+	unsigned char ARdata;
+	unsigned short i;
+
+	for (i = 0; i <= 0x13; i++) {
+		ARdata = SiS_Pr->SiS_StandTable[StandTableIndex].ATTR[i];
+		SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
+		SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, i);
+		SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, ARdata);
+	}
+	SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x14);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x00);
+
+	SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c0, 0x20);
+	SiS_GetRegByte(SiS_Pr, SiS_Pr->SiS_P3da);
+}
+
+/*********************************************/
+/*                   GRC                     */
+/*********************************************/
+
+static void
+SiS_SetGRCRegs(struct SiS_Private *SiS_Pr, unsigned short StandTableIndex)
+{
+	unsigned char GRdata;
+	unsigned short i;
+
+	for (i = 0; i <= 0x08; i++) {
+		GRdata = SiS_Pr->SiS_StandTable[StandTableIndex].GRC[i];
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3ce, i, GRdata);
+	}
+
+	if (SiS_Pr->SiS_ModeType > ModeVGA) {
+		/* 256 color disable */
+		SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3ce, 0x05, 0xBF);
+	}
+}
+
+/*********************************************/
+/*          CLEAR EXTENDED REGISTERS         */
+/*********************************************/
+
+static void SiS_ClearExt1Regs(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
+{
+	int i;
+
+	for (i = 0x0A; i <= 0x0E; i++) {
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, i, 0x00);
+	}
+
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x37, 0xFE);
+}
+
+/*********************************************/
+/*              Get rate index               */
+/*********************************************/
+
+static unsigned short
+SiS_GetRatePtr(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+	       unsigned short ModeIdIndex)
+{
+	unsigned short rrti, i, index, temp;
+
+	if (ModeNo <= 0x13)
+		return 0xFFFF;
+
+	index = SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x33) & 0x0F;
+	if (index > 0)
+		index--;
+
+	rrti = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex;
+	ModeNo = SiS_Pr->SiS_RefIndex[rrti].ModeID;
+
+	i = 0;
+	do {
+		if (SiS_Pr->SiS_RefIndex[rrti + i].ModeID != ModeNo)
+			break;
+
+		temp =
+		    SiS_Pr->SiS_RefIndex[rrti + i].Ext_InfoFlag & ModeTypeMask;
+		if (temp < SiS_Pr->SiS_ModeType)
+			break;
+
+		i++;
+		index--;
+	} while (index != 0xFFFF);
+
+	i--;
+
+	return (rrti + i);
+}
+
+/*********************************************/
+/*                  SYNC                     */
+/*********************************************/
+
+static void SiS_SetCRT1Sync(struct SiS_Private *SiS_Pr, unsigned short rrti)
+{
+	unsigned short sync = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag >> 8;
+	sync &= 0xC0;
+	sync |= 0x2f;
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c2, sync);
+}
+
+/*********************************************/
+/*                  CRTC/2                   */
+/*********************************************/
+
+static void
+SiS_SetCRT1CRTC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		unsigned short ModeIdIndex, unsigned short rrti)
+{
+	unsigned char index;
+	unsigned short temp, i, j, modeflag;
+
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3d4, 0x11, 0x7f);
+
+	modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+	index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRT1CRTC;
+
+	for (i = 0, j = 0; i <= 7; i++, j++) {
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
+			   SiS_Pr->SiS_CRT1Table[index].CR[i]);
+	}
+	for (j = 0x10; i <= 10; i++, j++) {
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
+			   SiS_Pr->SiS_CRT1Table[index].CR[i]);
+	}
+	for (j = 0x15; i <= 12; i++, j++) {
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, j,
+			   SiS_Pr->SiS_CRT1Table[index].CR[i]);
+	}
+	for (j = 0x0A; i <= 15; i++, j++) {
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, j,
+			   SiS_Pr->SiS_CRT1Table[index].CR[i]);
+	}
+
+	temp = SiS_Pr->SiS_CRT1Table[index].CR[16] & 0xE0;
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0E, temp);
+
+	temp = ((SiS_Pr->SiS_CRT1Table[index].CR[16]) & 0x01) << 5;
+	if (modeflag & DoubleScanMode)
+		temp |= 0x80;
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x09, 0x5F, temp);
+
+	if (SiS_Pr->SiS_ModeType > ModeVGA)
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x14, 0x4F);
+}
+
+/*********************************************/
+/*               OFFSET & PITCH              */
+/*********************************************/
+/*  (partly overruled by SetPitch() in XF86) */
+/*********************************************/
+
+static void
+SiS_SetCRT1Offset(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		  unsigned short ModeIdIndex, unsigned short rrti)
+{
+	unsigned short du = SiS_GetOffset(SiS_Pr, ModeNo, ModeIdIndex, rrti);
+	unsigned short infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
+	unsigned short temp;
+
+	temp = (du >> 8) & 0x0f;
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0E, 0xF0, temp);
+
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x13, (du & 0xFF));
+
+	if (infoflag & InterlaceMode)
+		du >>= 1;
+
+	du <<= 5;
+	temp = (du >> 8) & 0xff;
+	if (du & 0xff)
+		temp++;
+	temp++;
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x10, temp);
+}
+
+/*********************************************/
+/*                  VCLK                     */
+/*********************************************/
+
+static void
+SiS_SetCRT1VCLK(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		unsigned short rrti)
+{
+	unsigned short index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK;
+	unsigned short clka = SiS_Pr->SiS_VCLKData[index].SR2B;
+	unsigned short clkb = SiS_Pr->SiS_VCLKData[index].SR2C;
+
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x31, 0xCF);
+
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2B, clka);
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2C, clkb);
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x2D, 0x01);
+}
+
+/*********************************************/
+/*                  FIFO                     */
+/*********************************************/
+
+static void
+SiS_SetCRT1FIFO_310(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		    unsigned short mi)
+{
+	unsigned short modeflag = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag;
+
+	/* disable auto-threshold */
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x3D, 0xFE);
+
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x08, 0xAE);
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x09, 0xF0);
+
+	if (ModeNo <= 0x13)
+		return;
+
+	if ((!(modeflag & DoubleScanMode)) || (!(modeflag & HalfDCLK))) {
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x08, 0x34);
+		SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x3D, 0x01);
+	}
+}
+
+/*********************************************/
+/*              MODE REGISTERS               */
+/*********************************************/
+
+static void
+SiS_SetVCLKState(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		 unsigned short rrti)
+{
+	unsigned short data = 0, VCLK = 0, index = 0;
+
+	if (ModeNo > 0x13) {
+		index = SiS_Pr->SiS_RefIndex[rrti].Ext_CRTVCLK;
+		VCLK = SiS_Pr->SiS_VCLKData[index].CLOCK;
+	}
+
+	if (VCLK >= 166)
+		data |= 0x0c;
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x32, 0xf3, data);
+
+	if (VCLK >= 166)
+		SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1f, 0xe7);
+
+	/* DAC speed */
+	data = 0x03;
+	if (VCLK >= 260)
+		data = 0x00;
+	else if (VCLK >= 160)
+		data = 0x01;
+	else if (VCLK >= 135)
+		data = 0x02;
+
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x07, 0xF8, data);
+}
+
+static void
+SiS_SetCRT1ModeRegs(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		    unsigned short ModeIdIndex, unsigned short rrti)
+{
+	unsigned short data, infoflag = 0, modeflag;
+
+	if (ModeNo <= 0x13)
+		modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag;
+	else {
+		modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+		infoflag = SiS_Pr->SiS_RefIndex[rrti].Ext_InfoFlag;
+	}
+
+	/* Disable DPMS */
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x1F, 0x3F);
+
+	data = 0;
+	if (ModeNo > 0x13) {
+		if (SiS_Pr->SiS_ModeType > ModeEGA) {
+			data |= 0x02;
+			data |= ((SiS_Pr->SiS_ModeType - ModeVGA) << 2);
+		}
+		if (infoflag & InterlaceMode)
+			data |= 0x20;
+	}
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x06, 0xC0, data);
+
+	data = 0;
+	if (infoflag & InterlaceMode) {
+		/* data = (Hsync / 8) - ((Htotal / 8) / 2) + 3 */
+		unsigned short hrs =
+		    (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x04) |
+		     ((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0xc0) << 2))
+		    - 3;
+		unsigned short hto =
+		    (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x00) |
+		     ((SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0b) & 0x03) << 8))
+		    + 5;
+		data = hrs - (hto >> 1) + 3;
+	}
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x19, (data & 0xFF));
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3d4, 0x1a, 0xFC, (data >> 8));
+
+	if (modeflag & HalfDCLK)
+		SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x01, 0x08);
+
+	data = 0;
+	if (modeflag & LineCompareOff)
+		data = 0x08;
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0F, 0xB7, data);
+
+	if ((SiS_Pr->SiS_ModeType == ModeEGA) && (ModeNo > 0x13))
+		SiS_SetRegOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x0F, 0x40);
+
+	SiS_SetRegAND(SiS_Pr, SiS_Pr->SiS_P3c4, 0x31, 0xfb);
+
+	data = 0x60;
+	if (SiS_Pr->SiS_ModeType != ModeText) {
+		data ^= 0x60;
+		if (SiS_Pr->SiS_ModeType != ModeEGA)
+			data ^= 0xA0;
+	}
+	SiS_SetRegANDOR(SiS_Pr, SiS_Pr->SiS_P3c4, 0x21, 0x1F, data);
+
+	SiS_SetVCLKState(SiS_Pr, ModeNo, rrti);
+
+	if (SiS_GetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x31) & 0x40)
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x52, 0x2c);
+	else
+		SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x52, 0x6c);
+}
+
+/*********************************************/
+/*                 LOAD DAC                  */
+/*********************************************/
+
+static void
+SiS_WriteDAC(struct SiS_Private *SiS_Pr, unsigned long DACData,
+	     unsigned short shiftflag, unsigned short dl, unsigned short ah,
+	     unsigned short al, unsigned short dh)
+{
+	unsigned short d1, d2, d3;
+
+	switch (dl) {
+	case 0:
+		d1 = dh;
+		d2 = ah;
+		d3 = al;
+		break;
+	case 1:
+		d1 = ah;
+		d2 = al;
+		d3 = dh;
+		break;
+	default:
+		d1 = al;
+		d2 = dh;
+		d3 = ah;
+	}
+	SiS_SetRegByte(SiS_Pr, DACData, (d1 << shiftflag));
+	SiS_SetRegByte(SiS_Pr, DACData, (d2 << shiftflag));
+	SiS_SetRegByte(SiS_Pr, DACData, (d3 << shiftflag));
+}
+
+static void
+SiS_LoadDAC(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+	    unsigned short mi)
+{
+	unsigned short data, data2, time, i, j, k, m, n, o;
+	unsigned short si, di, bx, sf;
+	unsigned long DACAddr, DACData;
+	const unsigned char *table = NULL;
+
+	if (ModeNo < 0x13)
+		data = SiS_Pr->SiS_SModeIDTable[mi].St_ModeFlag;
+	else
+		data = SiS_Pr->SiS_EModeIDTable[mi].Ext_ModeFlag;
+
+	data &= DACInfoFlag;
+
+	j = time = 64;
+	if (data == 0x00)
+		table = SiS_MDA_DAC;
+	else if (data == 0x08)
+		table = SiS_CGA_DAC;
+	else if (data == 0x10)
+		table = SiS_EGA_DAC;
+	else {
+		j = 16;
+		time = 256;
+		table = SiS_VGA_DAC;
+	}
+
+	DACAddr = SiS_Pr->SiS_P3c8;
+	DACData = SiS_Pr->SiS_P3c9;
+	sf = 0;
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c6, 0xFF);
+
+	SiS_SetRegByte(SiS_Pr, DACAddr, 0x00);
+
+	for (i = 0; i < j; i++) {
+		data = table[i];
+		for (k = 0; k < 3; k++) {
+			data2 = 0;
+			if (data & 0x01)
+				data2 += 0x2A;
+			if (data & 0x02)
+				data2 += 0x15;
+			SiS_SetRegByte(SiS_Pr, DACData, (data2 << sf));
+			data >>= 2;
+		}
+	}
+
+	if (time == 256) {
+		for (i = 16; i < 32; i++) {
+			data = table[i] << sf;
+			for (k = 0; k < 3; k++)
+				SiS_SetRegByte(SiS_Pr, DACData, data);
+		}
+		si = 32;
+		for (m = 0; m < 9; m++) {
+			di = si;
+			bx = si + 4;
+			for (n = 0; n < 3; n++) {
+				for (o = 0; o < 5; o++) {
+					SiS_WriteDAC(SiS_Pr, DACData, sf, n,
+						     table[di], table[bx],
+						     table[si]);
+					si++;
+				}
+				si -= 2;
+				for (o = 0; o < 3; o++) {
+					SiS_WriteDAC(SiS_Pr, DACData, sf, n,
+						     table[di], table[si],
+						     table[bx]);
+					si--;
+				}
+			}
+			si += 5;
+		}
+	}
+}
+
+/*********************************************/
+/*         SET CRT1 REGISTER GROUP           */
+/*********************************************/
+
+static void
+SiS_SetCRT1Group(struct SiS_Private *SiS_Pr, unsigned short ModeNo,
+		 unsigned short ModeIdIndex)
+{
+	unsigned short StandTableIndex, rrti;
+
+	SiS_Pr->SiS_CRT1Mode = ModeNo;
+
+	if (ModeNo <= 0x13)
+		StandTableIndex = 0;
+	else
+		StandTableIndex = 1;
+
+	SiS_ResetSegmentRegisters(SiS_Pr);
+	SiS_SetSeqRegs(SiS_Pr, StandTableIndex);
+	SiS_SetMiscRegs(SiS_Pr, StandTableIndex);
+	SiS_SetCRTCRegs(SiS_Pr, StandTableIndex);
+	SiS_SetATTRegs(SiS_Pr, StandTableIndex);
+	SiS_SetGRCRegs(SiS_Pr, StandTableIndex);
+	SiS_ClearExt1Regs(SiS_Pr, ModeNo);
+
+	rrti = SiS_GetRatePtr(SiS_Pr, ModeNo, ModeIdIndex);
+
+	if (rrti != 0xFFFF) {
+		SiS_SetCRT1Sync(SiS_Pr, rrti);
+		SiS_SetCRT1CRTC(SiS_Pr, ModeNo, ModeIdIndex, rrti);
+		SiS_SetCRT1Offset(SiS_Pr, ModeNo, ModeIdIndex, rrti);
+		SiS_SetCRT1VCLK(SiS_Pr, ModeNo, rrti);
+	}
+
+	SiS_SetCRT1FIFO_310(SiS_Pr, ModeNo, ModeIdIndex);
+
+	SiS_SetCRT1ModeRegs(SiS_Pr, ModeNo, ModeIdIndex, rrti);
+
+	SiS_LoadDAC(SiS_Pr, ModeNo, ModeIdIndex);
+
+	SiS_DisplayOn(SiS_Pr);
+}
+
+/*********************************************/
+/*                 SiSSetMode()              */
+/*********************************************/
+
+int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
+{
+	unsigned short ModeIdIndex;
+	unsigned long BaseAddr = SiS_Pr->IOAddress;
+
+	SiSUSB_InitPtr(SiS_Pr);
+	SiSUSBRegInit(SiS_Pr, BaseAddr);
+	SiS_GetSysFlags(SiS_Pr);
+
+	if (!(SiS_SearchModeID(SiS_Pr, &ModeNo, &ModeIdIndex)))
+		return 0;
+
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3c4, 0x05, 0x86);
+
+	SiSInitPCIetc(SiS_Pr);
+
+	ModeNo &= 0x7f;
+
+	SiS_Pr->SiS_ModeType =
+	    SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag & ModeTypeMask;
+
+	SiS_Pr->SiS_SetFlag = LowModeTests;
+
+	/* Set mode on CRT1 */
+	SiS_SetCRT1Group(SiS_Pr, ModeNo, ModeIdIndex);
+
+	SiS_HandleCRT1(SiS_Pr);
+
+	SiS_DisplayOn(SiS_Pr);
+	SiS_SetRegByte(SiS_Pr, SiS_Pr->SiS_P3c6, 0xFF);
+
+	/* Store mode number */
+	SiS_SetReg(SiS_Pr, SiS_Pr->SiS_P3d4, 0x34, ModeNo);
+
+	return 1;
+}
+
+int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo)
+{
+	unsigned short ModeNo = 0;
+	int i;
+
+	SiSUSB_InitPtr(SiS_Pr);
+
+	if (VModeNo == 0x03) {
+
+		ModeNo = 0x03;
+
+	} else {
+
+		i = 0;
+		do {
+
+			if (SiS_Pr->SiS_EModeIDTable[i].Ext_VESAID == VModeNo) {
+				ModeNo = SiS_Pr->SiS_EModeIDTable[i].Ext_ModeID;
+				break;
+			}
+
+		} while (SiS_Pr->SiS_EModeIDTable[i++].Ext_ModeID != 0xff);
+
+	}
+
+	if (!ModeNo)
+		return 0;
+
+	return SiSUSBSetMode(SiS_Pr, ModeNo);
+}
+
+#endif /* INCL_SISUSB_CON */
diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h
new file mode 100644
index 0000000..c46ce42
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.h
@@ -0,0 +1,841 @@
+/* $XFree86$ */
+/* $XdotOrg$ */
+/*
+ * Data and prototypes for init.c
+ *
+ * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, the following license terms
+ * apply:
+ *
+ * * 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 named License,
+ * * or 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
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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 the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 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 the author may not be used to endorse or promote products
+ * *    derived from this software without specific prior written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ * Author:	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#ifndef _SISUSB_INIT_H_
+#define _SISUSB_INIT_H_
+
+/* SiS_ModeType */
+#define ModeText		0x00
+#define ModeCGA			0x01
+#define ModeEGA			0x02
+#define ModeVGA			0x03
+#define Mode15Bpp		0x04
+#define Mode16Bpp		0x05
+#define Mode24Bpp		0x06
+#define Mode32Bpp		0x07
+
+#define ModeTypeMask		0x07
+#define IsTextMode		0x07
+
+#define DACInfoFlag		0x0018
+#define MemoryInfoFlag		0x01E0
+#define MemorySizeShift		5
+
+/* modeflag */
+#define Charx8Dot		0x0200
+#define LineCompareOff		0x0400
+#define CRT2Mode		0x0800
+#define HalfDCLK		0x1000
+#define NoSupportSimuTV		0x2000
+#define NoSupportLCDScale	0x4000	/* SiS bridge: No scaling possible (no matter what panel) */
+#define DoubleScanMode		0x8000
+
+/* Infoflag */
+#define SupportTV		0x0008
+#define SupportTV1024		0x0800
+#define SupportCHTV		0x0800
+#define Support64048060Hz	0x0800	/* Special for 640x480 LCD */
+#define SupportHiVision		0x0010
+#define SupportYPbPr750p	0x1000
+#define SupportLCD		0x0020
+#define SupportRAMDAC2		0x0040	/* All           (<= 100Mhz) */
+#define SupportRAMDAC2_135	0x0100	/* All except DH (<= 135Mhz) */
+#define SupportRAMDAC2_162	0x0200	/* B, C          (<= 162Mhz) */
+#define SupportRAMDAC2_202	0x0400	/* C             (<= 202Mhz) */
+#define InterlaceMode		0x0080
+#define SyncPP			0x0000
+#define SyncPN			0x4000
+#define SyncNP			0x8000
+#define SyncNN			0xc000
+
+/* SetFlag */
+#define ProgrammingCRT2		0x0001
+#define LowModeTests		0x0002
+#define LCDVESATiming		0x0008
+#define EnableLVDSDDA		0x0010
+#define SetDispDevSwitchFlag	0x0020
+#define CheckWinDos		0x0040
+#define SetDOSMode		0x0080
+
+/* Index in ModeResInfo table */
+#define SIS_RI_320x200		0
+#define SIS_RI_320x240		1
+#define SIS_RI_320x400		2
+#define SIS_RI_400x300		3
+#define SIS_RI_512x384		4
+#define SIS_RI_640x400		5
+#define SIS_RI_640x480		6
+#define SIS_RI_800x600		7
+#define SIS_RI_1024x768		8
+#define SIS_RI_1280x1024	9
+#define SIS_RI_1600x1200	10
+#define SIS_RI_1920x1440	11
+#define SIS_RI_2048x1536	12
+#define SIS_RI_720x480		13
+#define SIS_RI_720x576		14
+#define SIS_RI_1280x960		15
+#define SIS_RI_800x480		16
+#define SIS_RI_1024x576		17
+#define SIS_RI_1280x720		18
+#define SIS_RI_856x480		19
+#define SIS_RI_1280x768		20
+#define SIS_RI_1400x1050	21
+#define SIS_RI_1152x864		22	/* Up to here SiS conforming */
+#define SIS_RI_848x480		23
+#define SIS_RI_1360x768		24
+#define SIS_RI_1024x600		25
+#define SIS_RI_1152x768		26
+#define SIS_RI_768x576		27
+#define SIS_RI_1360x1024	28
+#define SIS_RI_1680x1050	29
+#define SIS_RI_1280x800		30
+#define SIS_RI_1920x1080	31
+#define SIS_RI_960x540		32
+#define SIS_RI_960x600		33
+
+#define SIS_VIDEO_CAPTURE	0x00 - 0x30
+#define SIS_VIDEO_PLAYBACK	0x02 - 0x30
+#define SIS_CRT2_PORT_04	0x04 - 0x30
+
+/* Mode numbers */
+static const unsigned short ModeIndex_320x200[] = { 0x59, 0x41, 0x00, 0x4f };
+static const unsigned short ModeIndex_320x240[] = { 0x50, 0x56, 0x00, 0x53 };
+static const unsigned short ModeIndex_400x300[] = { 0x51, 0x57, 0x00, 0x54 };
+static const unsigned short ModeIndex_512x384[] = { 0x52, 0x58, 0x00, 0x5c };
+static const unsigned short ModeIndex_640x400[] = { 0x2f, 0x5d, 0x00, 0x5e };
+static const unsigned short ModeIndex_640x480[] = { 0x2e, 0x44, 0x00, 0x62 };
+static const unsigned short ModeIndex_720x480[] = { 0x31, 0x33, 0x00, 0x35 };
+static const unsigned short ModeIndex_720x576[] = { 0x32, 0x34, 0x00, 0x36 };
+static const unsigned short ModeIndex_768x576[] = { 0x5f, 0x60, 0x00, 0x61 };
+static const unsigned short ModeIndex_800x480[] = { 0x70, 0x7a, 0x00, 0x76 };
+static const unsigned short ModeIndex_800x600[] = { 0x30, 0x47, 0x00, 0x63 };
+static const unsigned short ModeIndex_848x480[] = { 0x39, 0x3b, 0x00, 0x3e };
+static const unsigned short ModeIndex_856x480[] = { 0x3f, 0x42, 0x00, 0x45 };
+static const unsigned short ModeIndex_960x540[] = { 0x1d, 0x1e, 0x00, 0x1f };
+static const unsigned short ModeIndex_960x600[] = { 0x20, 0x21, 0x00, 0x22 };
+static const unsigned short ModeIndex_1024x768[] = { 0x38, 0x4a, 0x00, 0x64 };
+static const unsigned short ModeIndex_1024x576[] = { 0x71, 0x74, 0x00, 0x77 };
+static const unsigned short ModeIndex_1152x864[] = { 0x29, 0x2a, 0x00, 0x2b };
+static const unsigned short ModeIndex_1280x720[] = { 0x79, 0x75, 0x00, 0x78 };
+static const unsigned short ModeIndex_1280x768[] = { 0x23, 0x24, 0x00, 0x25 };
+static const unsigned short ModeIndex_1280x1024[] = { 0x3a, 0x4d, 0x00, 0x65 };
+
+static const unsigned char SiS_MDA_DAC[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+	0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+	0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+	0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+	0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F
+};
+
+static const unsigned char SiS_CGA_DAC[] = {
+	0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+	0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+	0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+	0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+	0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+	0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+	0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+	0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
+};
+
+static const unsigned char SiS_EGA_DAC[] = {
+	0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x05, 0x15,
+	0x20, 0x30, 0x24, 0x34, 0x21, 0x31, 0x25, 0x35,
+	0x08, 0x18, 0x0C, 0x1C, 0x09, 0x19, 0x0D, 0x1D,
+	0x28, 0x38, 0x2C, 0x3C, 0x29, 0x39, 0x2D, 0x3D,
+	0x02, 0x12, 0x06, 0x16, 0x03, 0x13, 0x07, 0x17,
+	0x22, 0x32, 0x26, 0x36, 0x23, 0x33, 0x27, 0x37,
+	0x0A, 0x1A, 0x0E, 0x1E, 0x0B, 0x1B, 0x0F, 0x1F,
+	0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
+};
+
+static const unsigned char SiS_VGA_DAC[] = {
+	0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+	0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+	0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18,
+	0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F,
+	0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x1F, 0x27, 0x2F,
+	0x37, 0x3F, 0x2D, 0x31, 0x36, 0x3A, 0x3F, 0x00,
+	0x07, 0x0E, 0x15, 0x1C, 0x0E, 0x11, 0x15, 0x18,
+	0x1C, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x04,
+	0x08, 0x0C, 0x10, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
+	0x0B, 0x0C, 0x0D, 0x0F, 0x10
+};
+
+static const struct SiS_St SiSUSB_SModeIDTable[] = {
+	{0x03, 0x0010, 0x18, 0x02, 0x02, 0x00, 0x01, 0x03, 0x40},
+	{0xff, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+static const struct SiS_StResInfo_S SiSUSB_StResInfo[] = {
+	{640, 400},
+	{640, 350},
+	{720, 400},
+	{720, 350},
+	{640, 480}
+};
+
+static const struct SiS_ModeResInfo SiSUSB_ModeResInfo[] = {
+	{320, 200, 8, 8},	/* 0x00 */
+	{320, 240, 8, 8},	/* 0x01 */
+	{320, 400, 8, 8},	/* 0x02 */
+	{400, 300, 8, 8},	/* 0x03 */
+	{512, 384, 8, 8},	/* 0x04 */
+	{640, 400, 8, 16},	/* 0x05 */
+	{640, 480, 8, 16},	/* 0x06 */
+	{800, 600, 8, 16},	/* 0x07 */
+	{1024, 768, 8, 16},	/* 0x08 */
+	{1280, 1024, 8, 16},	/* 0x09 */
+	{1600, 1200, 8, 16},	/* 0x0a */
+	{1920, 1440, 8, 16},	/* 0x0b */
+	{2048, 1536, 8, 16},	/* 0x0c */
+	{720, 480, 8, 16},	/* 0x0d */
+	{720, 576, 8, 16},	/* 0x0e */
+	{1280, 960, 8, 16},	/* 0x0f */
+	{800, 480, 8, 16},	/* 0x10 */
+	{1024, 576, 8, 16},	/* 0x11 */
+	{1280, 720, 8, 16},	/* 0x12 */
+	{856, 480, 8, 16},	/* 0x13 */
+	{1280, 768, 8, 16},	/* 0x14 */
+	{1400, 1050, 8, 16},	/* 0x15 */
+	{1152, 864, 8, 16},	/* 0x16 */
+	{848, 480, 8, 16},	/* 0x17 */
+	{1360, 768, 8, 16},	/* 0x18 */
+	{1024, 600, 8, 16},	/* 0x19 */
+	{1152, 768, 8, 16},	/* 0x1a */
+	{768, 576, 8, 16},	/* 0x1b */
+	{1360, 1024, 8, 16},	/* 0x1c */
+	{1680, 1050, 8, 16},	/* 0x1d */
+	{1280, 800, 8, 16},	/* 0x1e */
+	{1920, 1080, 8, 16},	/* 0x1f */
+	{960, 540, 8, 16},	/* 0x20 */
+	{960, 600, 8, 16}	/* 0x21 */
+};
+
+static const struct SiS_StandTable SiSUSB_StandTable[] = {
+	/* MD_3_400 - mode 0x03 - 400 */
+	{
+	 0x50, 0x18, 0x10, 0x1000,
+	 {0x00, 0x03, 0x00, 0x02},
+	 0x67,
+	 {0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+	  0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+	  0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3,
+	  0xff},
+	 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+	  0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+	  0x0c, 0x00, 0x0f, 0x08},
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x00, 0xff}
+	 },
+	/* Generic for VGA and higher */
+	{
+	 0x00, 0x00, 0x00, 0x0000,
+	 {0x01, 0x0f, 0x00, 0x0e},
+	 0x23,
+	 {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+	  0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3,
+	  0xff},
+	 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	  0x01, 0x00, 0x00, 0x00},
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff}
+	 }
+};
+
+static const struct SiS_Ext SiSUSB_EModeIDTable[] = {
+	{0x2e, 0x0a1b, 0x0101, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2},	/* 640x480x8 */
+	{0x2f, 0x0a1b, 0x0100, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x05, 0x10, 0},	/* 640x400x8 */
+	{0x30, 0x2a1b, 0x0103, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3},	/* 800x600x8 */
+	{0x31, 0x4a1b, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1},	/* 720x480x8 */
+	{0x32, 0x4a1b, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1},	/* 720x576x8 */
+	{0x33, 0x4a1d, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1},	/* 720x480x16 */
+	{0x34, 0x6a1d, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1},	/* 720x576x16 */
+	{0x35, 0x4a1f, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1},	/* 720x480x32 */
+	{0x36, 0x6a1f, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1},	/* 720x576x32 */
+	{0x38, 0x0a1b, 0x0105, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4},	/* 1024x768x8 */
+	{0x3a, 0x0e3b, 0x0107, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8},	/* 1280x1024x8 */
+	{0x41, 0x9a1d, 0x010e, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0},	/* 320x200x16 */
+	{0x44, 0x0a1d, 0x0111, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2},	/* 640x480x16 */
+	{0x47, 0x2a1d, 0x0114, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3},	/* 800x600x16 */
+	{0x4a, 0x0a3d, 0x0117, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4},	/* 1024x768x16 */
+	{0x4d, 0x0e7d, 0x011a, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8},	/* 1280x1024x16 */
+	{0x50, 0x9a1b, 0x0132, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2},	/* 320x240x8  */
+	{0x51, 0xba1b, 0x0133, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3},	/* 400x300x8  */
+	{0x52, 0xba1b, 0x0134, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4},	/* 512x384x8  */
+	{0x56, 0x9a1d, 0x0135, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2},	/* 320x240x16 */
+	{0x57, 0xba1d, 0x0136, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3},	/* 400x300x16 */
+	{0x58, 0xba1d, 0x0137, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4},	/* 512x384x16 */
+	{0x59, 0x9a1b, 0x0138, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0},	/* 320x200x8  */
+	{0x5c, 0xba1f, 0x0000, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4},	/* 512x384x32 */
+	{0x5d, 0x0a1d, 0x0139, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0},	/* 640x400x16 */
+	{0x5e, 0x0a1f, 0x0000, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0},	/* 640x400x32 */
+	{0x62, 0x0a3f, 0x013a, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2},	/* 640x480x32 */
+	{0x63, 0x2a3f, 0x013b, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3},	/* 800x600x32 */
+	{0x64, 0x0a7f, 0x013c, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4},	/* 1024x768x32 */
+	{0x65, 0x0eff, 0x013d, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8},	/* 1280x1024x32 */
+	{0x70, 0x6a1b, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1},	/* 800x480x8 */
+	{0x71, 0x4a1b, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1},	/* 1024x576x8 */
+	{0x74, 0x4a1d, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1},	/* 1024x576x16 */
+	{0x75, 0x0a3d, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5},	/* 1280x720x16 */
+	{0x76, 0x6a1f, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1},	/* 800x480x32 */
+	{0x77, 0x4a1f, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1},	/* 1024x576x32 */
+	{0x78, 0x0a3f, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5},	/* 1280x720x32 */
+	{0x79, 0x0a3b, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5},	/* 1280x720x8 */
+	{0x7a, 0x6a1d, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1},	/* 800x480x16 */
+	{0x23, 0x0e3b, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6},	/* 1280x768x8 */
+	{0x24, 0x0e7d, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6},	/* 1280x768x16 */
+	{0x25, 0x0eff, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6},	/* 1280x768x32 */
+	{0x39, 0x6a1b, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28, -1},	/* 848x480 */
+	{0x3b, 0x6a3d, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
+	 -1},
+	{0x3e, 0x6a7f, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
+	 -1},
+	{0x3f, 0x6a1b, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a, -1},	/* 856x480 */
+	{0x42, 0x6a3d, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
+	 -1},
+	{0x45, 0x6a7f, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
+	 -1},
+	{0x4f, 0x9a1f, 0x0000, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0},	/* 320x200x32 */
+	{0x53, 0x9a1f, 0x0000, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2},	/* 320x240x32 */
+	{0x54, 0xba1f, 0x0000, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3},	/* 400x300x32 */
+	{0x5f, 0x6a1b, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c, -1},	/* 768x576 */
+	{0x60, 0x6a1d, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
+	 -1},
+	{0x61, 0x6a3f, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
+	 -1},
+	{0x1d, 0x6a1b, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d, -1},	/* 960x540 */
+	{0x1e, 0x6a3d, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
+	 -1},
+	{0x1f, 0x6a7f, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
+	 -1},
+	{0x20, 0x6a1b, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e, -1},	/* 960x600 */
+	{0x21, 0x6a3d, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
+	 -1},
+	{0x22, 0x6a7f, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
+	 -1},
+	{0x29, 0x4e1b, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33, -1},	/* 1152x864 */
+	{0x2a, 0x4e3d, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
+	 -1},
+	{0x2b, 0x4e7f, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
+	 -1},
+	{0xff, 0x0000, 0x0000, 0, 0x00, 0x00, 0x00, 0x00, 0x00, -1}
+};
+
+static const struct SiS_Ext2 SiSUSB_RefIndex[] = {
+	{0x085f, 0x0d, 0x03, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00},	/* 0x0 */
+	{0x0067, 0x0e, 0x04, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00},	/* 0x1 */
+	{0x0067, 0x0f, 0x08, 0x48, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00},	/* 0x2 */
+	{0x0067, 0x10, 0x07, 0x8b, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00},	/* 0x3 */
+	{0x0047, 0x11, 0x0a, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00},	/* 0x4 */
+	{0x0047, 0x12, 0x0d, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00},	/* 0x5 */
+	{0x0047, 0x13, 0x13, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00},	/* 0x6 */
+	{0x0107, 0x14, 0x1c, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00},	/* 0x7 */
+	{0xc85f, 0x05, 0x00, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0x8 */
+	{0xc067, 0x06, 0x02, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0x9 */
+	{0xc067, 0x07, 0x02, 0x47, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0xa */
+	{0xc067, 0x08, 0x03, 0x8a, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0xb */
+	{0xc047, 0x09, 0x05, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0xc */
+	{0xc047, 0x0a, 0x09, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0xd */
+	{0xc047, 0x0b, 0x0e, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0xe */
+	{0xc047, 0x0c, 0x15, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00},	/* 0xf */
+	{0x487f, 0x04, 0x00, 0x00, 0x00, 0x2f, 640, 400, 0x30, 0x55, 0x6e},	/* 0x10 */
+	{0xc06f, 0x3c, 0x01, 0x06, 0x13, 0x31, 720, 480, 0x30, 0x00, 0x00},	/* 0x11 */
+	{0x006f, 0x3d, 0x6f, 0x06, 0x14, 0x32, 720, 576, 0x30, 0x00, 0x00},	/* 0x12 (6f was 03) */
+	{0x0087, 0x15, 0x06, 0x00, 0x06, 0x38, 1024, 768, 0x30, 0x00, 0x00},	/* 0x13 */
+	{0xc877, 0x16, 0x0b, 0x06, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00},	/* 0x14 */
+	{0xc067, 0x17, 0x0f, 0x49, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00},	/* 0x15 */
+	{0x0067, 0x18, 0x11, 0x00, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00},	/* 0x16 */
+	{0x0047, 0x19, 0x16, 0x8c, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00},	/* 0x17 */
+	{0x0107, 0x1a, 0x1b, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00},	/* 0x18 */
+	{0x0107, 0x1b, 0x1f, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00},	/* 0x19 */
+	{0x407f, 0x00, 0x00, 0x00, 0x00, 0x41, 320, 200, 0x30, 0x56, 0x4e},	/* 0x1a */
+	{0xc07f, 0x01, 0x00, 0x04, 0x04, 0x50, 320, 240, 0x30, 0x00, 0x00},	/* 0x1b */
+	{0x007f, 0x02, 0x04, 0x05, 0x05, 0x51, 400, 300, 0x30, 0x00, 0x00},	/* 0x1c */
+	{0xc077, 0x03, 0x0b, 0x06, 0x06, 0x52, 512, 384, 0x30, 0x00, 0x00},	/* 0x1d */
+	{0x0077, 0x32, 0x40, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00},	/* 0x1e */
+	{0x0047, 0x33, 0x07, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00},	/* 0x1f */
+	{0x0047, 0x34, 0x0a, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00},	/* 0x20 */
+	{0x0077, 0x35, 0x0b, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00},	/* 0x21 */
+	{0x0047, 0x36, 0x11, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00},	/* 0x22 */
+	{0x0047, 0x37, 0x16, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00},	/* 0x23 */
+	{0x1137, 0x38, 0x19, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00},	/* 0x24 */
+	{0x1107, 0x39, 0x1e, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00},	/* 0x25 */
+	{0x1307, 0x3a, 0x20, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00},	/* 0x26 */
+	{0x0077, 0x42, 0x5b, 0x08, 0x11, 0x23, 1280, 768, 0x30, 0x00, 0x00},	/* 0x27 */
+	{0x0087, 0x45, 0x57, 0x00, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00},	/* 0x28 38Hzi  */
+	{0xc067, 0x46, 0x55, 0x0b, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00},	/* 0x29 848x480-60Hz   */
+	{0x0087, 0x47, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00},	/* 0x2a 856x480-38Hzi  */
+	{0xc067, 0x48, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00},	/* 0x2b 856x480-60Hz   */
+	{0x006f, 0x4d, 0x71, 0x06, 0x15, 0x5f, 768, 576, 0x30, 0x00, 0x00},	/* 0x2c 768x576-56Hz   */
+	{0x0067, 0x52, 0x6a, 0x00, 0x1c, 0x1d, 960, 540, 0x30, 0x00, 0x00},	/* 0x2d 960x540 60Hz */
+	{0x0077, 0x53, 0x6b, 0x0b, 0x1d, 0x20, 960, 600, 0x30, 0x00, 0x00},	/* 0x2e 960x600 60Hz */
+	{0x0087, 0x1c, 0x11, 0x00, 0x07, 0x3a, 1280, 1024, 0x30, 0x00, 0x00},	/* 0x2f */
+	{0x0137, 0x1d, 0x19, 0x07, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00},	/* 0x30 */
+	{0x0107, 0x1e, 0x1e, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00},	/* 0x31 */
+	{0x0207, 0x1f, 0x20, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00},	/* 0x32 */
+	{0x0127, 0x54, 0x6d, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00},	/* 0x33 1152x864-60Hz  */
+	{0x0127, 0x44, 0x19, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00},	/* 0x34 1152x864-75Hz  */
+	{0x0127, 0x4a, 0x1e, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00},	/* 0x35 1152x864-85Hz  */
+	{0xffff, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x00, 0x00}
+};
+
+static const struct SiS_CRT1Table SiSUSB_CRT1Table[] = {
+	{{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0xbf, 0x1f,
+	  0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x00,
+	  0x00}},		/* 0x0 */
+	{{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
+	  0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
+	  0x00}},		/* 0x1 */
+	{{0x3d, 0x31, 0x31, 0x81, 0x37, 0x1f, 0x72, 0xf0,
+	  0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x05,
+	  0x01}},		/* 0x2 */
+	{{0x4f, 0x3f, 0x3f, 0x93, 0x45, 0x0d, 0x24, 0xf5,
+	  0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x01,
+	  0x01}},		/* 0x3 */
+	{{0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+	  0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x05,
+	  0x00}},		/* 0x4 */
+	{{0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e,
+	  0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05,
+	  0x00}},		/* 0x5 */
+	{{0x63, 0x4f, 0x4f, 0x87, 0x56, 0x9b, 0x06, 0x3e,
+	  0xe8, 0x8a, 0xdf, 0xe7, 0x07, 0x00, 0x00, 0x01,
+	  0x00}},		/* 0x6 */
+	{{0x64, 0x4f, 0x4f, 0x88, 0x55, 0x9d, 0xf2, 0x1f,
+	  0xe0, 0x83, 0xdf, 0xdf, 0xf3, 0x10, 0x00, 0x01,
+	  0x00}},		/* 0x7 */
+	{{0x63, 0x4f, 0x4f, 0x87, 0x5a, 0x81, 0xfb, 0x1f,
+	  0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
+	  0x00}},		/* 0x8 */
+	{{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0xfb, 0x1f,
+	  0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
+	  0x61}},		/* 0x9 */
+	{{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0x01, 0x3e,
+	  0xe0, 0x83, 0xdf, 0xdf, 0x02, 0x00, 0x00, 0x05,
+	  0x61}},		/* 0xa */
+	{{0x67, 0x4f, 0x4f, 0x8b, 0x58, 0x81, 0x0d, 0x3e,
+	  0xe0, 0x83, 0xdf, 0xdf, 0x0e, 0x00, 0x00, 0x05,
+	  0x61}},		/* 0xb */
+	{{0x65, 0x4f, 0x4f, 0x89, 0x57, 0x9f, 0xfb, 0x1f,
+	  0xe6, 0x8a, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x01,
+	  0x00}},		/* 0xc */
+	{{0x7b, 0x63, 0x63, 0x9f, 0x6a, 0x93, 0x6f, 0xf0,
+	  0x58, 0x8a, 0x57, 0x57, 0x70, 0x20, 0x00, 0x05,
+	  0x01}},		/* 0xd */
+	{{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xf0,
+	  0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x06,
+	  0x01}},		/* 0xe */
+	{{0x7d, 0x63, 0x63, 0x81, 0x6e, 0x1d, 0x98, 0xf0,
+	  0x7c, 0x82, 0x57, 0x57, 0x99, 0x00, 0x00, 0x06,
+	  0x01}},		/* 0xf */
+	{{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xf0,
+	  0x58, 0x8b, 0x57, 0x57, 0x70, 0x20, 0x00, 0x06,
+	  0x01}},		/* 0x10 */
+	{{0x7e, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xf0,
+	  0x58, 0x8b, 0x57, 0x57, 0x76, 0x20, 0x00, 0x06,
+	  0x01}},		/* 0x11 */
+	{{0x81, 0x63, 0x63, 0x85, 0x6d, 0x18, 0x7a, 0xf0,
+	  0x58, 0x8b, 0x57, 0x57, 0x7b, 0x20, 0x00, 0x06,
+	  0x61}},		/* 0x12 */
+	{{0x83, 0x63, 0x63, 0x87, 0x6e, 0x19, 0x81, 0xf0,
+	  0x58, 0x8b, 0x57, 0x57, 0x82, 0x20, 0x00, 0x06,
+	  0x61}},		/* 0x13 */
+	{{0x85, 0x63, 0x63, 0x89, 0x6f, 0x1a, 0x91, 0xf0,
+	  0x58, 0x8b, 0x57, 0x57, 0x92, 0x20, 0x00, 0x06,
+	  0x61}},		/* 0x14 */
+	{{0x99, 0x7f, 0x7f, 0x9d, 0x84, 0x1a, 0x96, 0x1f,
+	  0x7f, 0x83, 0x7f, 0x7f, 0x97, 0x10, 0x00, 0x02,
+	  0x00}},		/* 0x15 */
+	{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
+	  0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
+	  0x01}},		/* 0x16 */
+	{{0xa1, 0x7f, 0x7f, 0x85, 0x86, 0x97, 0x24, 0xf5,
+	  0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
+	  0x01}},		/* 0x17 */
+	{{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf5,
+	  0x00, 0x83, 0xff, 0xff, 0x1f, 0x10, 0x00, 0x02,
+	  0x01}},		/* 0x18 */
+	{{0xa7, 0x7f, 0x7f, 0x8b, 0x89, 0x95, 0x26, 0xf5,
+	  0x00, 0x83, 0xff, 0xff, 0x27, 0x10, 0x00, 0x02,
+	  0x01}},		/* 0x19 */
+	{{0xa9, 0x7f, 0x7f, 0x8d, 0x8c, 0x9a, 0x2c, 0xf5,
+	  0x00, 0x83, 0xff, 0xff, 0x2d, 0x14, 0x00, 0x02,
+	  0x62}},		/* 0x1a */
+	{{0xab, 0x7f, 0x7f, 0x8f, 0x8d, 0x9b, 0x35, 0xf5,
+	  0x00, 0x83, 0xff, 0xff, 0x36, 0x14, 0x00, 0x02,
+	  0x62}},		/* 0x1b */
+	{{0xcf, 0x9f, 0x9f, 0x93, 0xb2, 0x01, 0x14, 0xba,
+	  0x00, 0x83, 0xff, 0xff, 0x15, 0x00, 0x00, 0x03,
+	  0x00}},		/* 0x1c */
+	{{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0x5a,
+	  0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
+	  0x01}},		/* 0x1d */
+	{{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0x5a,
+	  0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
+	  0x01}},		/* 0x1e */
+	{{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0x5a,
+	  0x00, 0x83, 0xff, 0xff, 0x2f, 0x09, 0x00, 0x07,
+	  0x01}},		/* 0x1f */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x20 */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x21 */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x22 */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x23 */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x24 */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x25 */
+	{{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+	  0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+	  0x00}},		/* 0x26 */
+	{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+	  0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+	  0x00}},		/* 0x27 */
+	{{0x43, 0xef, 0xef, 0x87, 0x06, 0x00, 0xd4, 0x1f,
+	  0xa0, 0x83, 0x9f, 0x9f, 0xd5, 0x1f, 0x41, 0x05,
+	  0x63}},		/* 0x28 */
+	{{0x45, 0xef, 0xef, 0x89, 0x07, 0x01, 0xd9, 0x1f,
+	  0xa0, 0x83, 0x9f, 0x9f, 0xda, 0x1f, 0x41, 0x05,
+	  0x63}},		/* 0x29 */
+	{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+	  0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+	  0x00}},		/* 0x2a */
+	{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+	  0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+	  0x00}},		/* 0x2b */
+	{{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+	  0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+	  0x00}},		/* 0x2c */
+	{{0x59, 0xff, 0xff, 0x9d, 0x17, 0x13, 0x33, 0xba,
+	  0x00, 0x83, 0xff, 0xff, 0x34, 0x0f, 0x41, 0x05,
+	  0x44}},		/* 0x2d */
+	{{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x38, 0xba,
+	  0x00, 0x83, 0xff, 0xff, 0x39, 0x0f, 0x41, 0x05,
+	  0x44}},		/* 0x2e */
+	{{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x3d, 0xba,
+	  0x00, 0x83, 0xff, 0xff, 0x3e, 0x0f, 0x41, 0x05,
+	  0x44}},		/* 0x2f */
+	{{0x5d, 0xff, 0xff, 0x81, 0x19, 0x95, 0x41, 0xba,
+	  0x00, 0x84, 0xff, 0xff, 0x42, 0x0f, 0x41, 0x05,
+	  0x44}},		/* 0x30 */
+	{{0x55, 0xff, 0xff, 0x99, 0x0d, 0x0c, 0x3e, 0xba,
+	  0x00, 0x84, 0xff, 0xff, 0x3f, 0x0f, 0x41, 0x05,
+	  0x00}},		/* 0x31 */
+	{{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xba,
+	  0x27, 0x8b, 0xdf, 0xdf, 0x73, 0x00, 0x00, 0x06,
+	  0x01}},		/* 0x32 */
+	{{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xba,
+	  0x26, 0x89, 0xdf, 0xdf, 0x6f, 0x00, 0x00, 0x06,
+	  0x01}},		/* 0x33 */
+	{{0x7f, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xba,
+	  0x29, 0x8c, 0xdf, 0xdf, 0x75, 0x00, 0x00, 0x06,
+	  0x01}},		/* 0x34 */
+	{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf1,
+	  0xaf, 0x85, 0x3f, 0x3f, 0x25, 0x30, 0x00, 0x02,
+	  0x01}},		/* 0x35 */
+	{{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf1,
+	  0xad, 0x81, 0x3f, 0x3f, 0x1f, 0x30, 0x00, 0x02,
+	  0x01}},		/* 0x36 */
+	{{0xa7, 0x7f, 0x7f, 0x88, 0x89, 0x95, 0x26, 0xf1,
+	  0xb1, 0x85, 0x3f, 0x3f, 0x27, 0x30, 0x00, 0x02,
+	  0x01}},		/* 0x37 */
+	{{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0xc4,
+	  0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
+	  0x01}},		/* 0x38 */
+	{{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0xd4,
+	  0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
+	  0x01}},		/* 0x39 */
+	{{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0xd4,
+	  0x7d, 0x81, 0xcf, 0xcf, 0x2f, 0x21, 0x00, 0x07,
+	  0x01}},		/* 0x3a */
+	{{0xdc, 0x9f, 0x9f, 0x80, 0xaf, 0x9d, 0xe6, 0xff,
+	  0xc0, 0x83, 0xbf, 0xbf, 0xe7, 0x10, 0x00, 0x07,
+	  0x01}},		/* 0x3b */
+	{{0x6b, 0x59, 0x59, 0x8f, 0x5e, 0x8c, 0x0b, 0x3e,
+	  0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x05,
+	  0x00}},		/* 0x3c */
+	{{0x6d, 0x59, 0x59, 0x91, 0x60, 0x89, 0x53, 0xf0,
+	  0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
+	  0x41}},		/* 0x3d */
+	{{0x86, 0x6a, 0x6a, 0x8a, 0x74, 0x06, 0x8c, 0x15,
+	  0x4f, 0x83, 0xef, 0xef, 0x8d, 0x30, 0x00, 0x02,
+	  0x00}},		/* 0x3e */
+	{{0x81, 0x6a, 0x6a, 0x85, 0x70, 0x00, 0x0f, 0x3e,
+	  0xeb, 0x8e, 0xdf, 0xdf, 0x10, 0x00, 0x00, 0x02,
+	  0x00}},		/* 0x3f */
+	{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x1e, 0xf1,
+	  0xae, 0x85, 0x57, 0x57, 0x1f, 0x30, 0x00, 0x02,
+	  0x01}},		/* 0x40 */
+	{{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
+	  0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
+	  0x01}},		/* 0x41 */
+	{{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x20, 0xf5,
+	  0x03, 0x88, 0xff, 0xff, 0x21, 0x10, 0x00, 0x07,
+	  0x01}},		/* 0x42 */
+	{{0xe6, 0xae, 0xae, 0x8a, 0xbd, 0x90, 0x3d, 0x10,
+	  0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x00, 0x03,
+	  0x00}},		/* 0x43 */
+	{{0xc3, 0x8f, 0x8f, 0x87, 0x9b, 0x0b, 0x82, 0xef,
+	  0x60, 0x83, 0x5f, 0x5f, 0x83, 0x10, 0x00, 0x07,
+	  0x01}},		/* 0x44 */
+	{{0x86, 0x69, 0x69, 0x8A, 0x74, 0x06, 0x8C, 0x15,
+	  0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
+	  0x00}},		/* 0x45 */
+	{{0x83, 0x69, 0x69, 0x87, 0x6f, 0x1d, 0x03, 0x3E,
+	  0xE5, 0x8d, 0xDF, 0xe4, 0x04, 0x00, 0x00, 0x06,
+	  0x00}},		/* 0x46 */
+	{{0x86, 0x6A, 0x6A, 0x8A, 0x74, 0x06, 0x8C, 0x15,
+	  0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
+	  0x00}},		/* 0x47 */
+	{{0x81, 0x6A, 0x6A, 0x85, 0x70, 0x00, 0x0F, 0x3E,
+	  0xEB, 0x8E, 0xDF, 0xDF, 0x10, 0x00, 0x00, 0x02,
+	  0x00}},		/* 0x48 */
+	{{0xdd, 0xa9, 0xa9, 0x81, 0xb4, 0x97, 0x26, 0xfd,
+	  0x01, 0x8d, 0xff, 0x00, 0x27, 0x10, 0x00, 0x03,
+	  0x01}},		/* 0x49 */
+	{{0xd9, 0x8f, 0x8f, 0x9d, 0xba, 0x0a, 0x8a, 0xff,
+	  0x60, 0x8b, 0x5f, 0x5f, 0x8b, 0x10, 0x00, 0x03,
+	  0x01}},		/* 0x4a */
+	{{0xea, 0xae, 0xae, 0x8e, 0xba, 0x82, 0x40, 0x10,
+	  0x1b, 0x87, 0x19, 0x1a, 0x41, 0x0f, 0x00, 0x03,
+	  0x00}},		/* 0x4b */
+	{{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0xf1, 0xff,
+	  0xc0, 0x83, 0xbf, 0xbf, 0xf2, 0x10, 0x00, 0x07,
+	  0x01}},		/* 0x4c */
+	{{0x75, 0x5f, 0x5f, 0x99, 0x66, 0x90, 0x53, 0xf0,
+	  0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
+	  0x41}},
+	{{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
+	  0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
+	  0x00}},		/* 0x4e */
+	{{0xcd, 0x9f, 0x9f, 0x91, 0xab, 0x1c, 0x3a, 0xff,
+	  0x20, 0x83, 0x1f, 0x1f, 0x3b, 0x10, 0x00, 0x07,
+	  0x21}},		/* 0x4f */
+	{{0x15, 0xd1, 0xd1, 0x99, 0xe2, 0x19, 0x3d, 0x10,
+	  0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x01, 0x0c,
+	  0x20}},		/* 0x50 */
+	{{0x0e, 0xef, 0xef, 0x92, 0xfe, 0x03, 0x30, 0xf0,
+	  0x1e, 0x83, 0x1b, 0x1c, 0x31, 0x00, 0x01, 0x00,
+	  0x61}},		/* 0x51 */
+	{{0x85, 0x77, 0x77, 0x89, 0x7d, 0x01, 0x31, 0xf0,
+	  0x1e, 0x84, 0x1b, 0x1c, 0x32, 0x00, 0x00, 0x02,
+	  0x41}},		/* 0x52 */
+	{{0x87, 0x77, 0x77, 0x8b, 0x81, 0x0b, 0x68, 0xf0,
+	  0x5a, 0x80, 0x57, 0x57, 0x69, 0x00, 0x00, 0x02,
+	  0x01}},		/* 0x53 */
+	{{0xcd, 0x8f, 0x8f, 0x91, 0x9b, 0x1b, 0x7a, 0xff,
+	  0x64, 0x8c, 0x5f, 0x62, 0x7b, 0x10, 0x00, 0x07,
+	  0x41}}		/* 0x54 */
+};
+
+static const struct SiS_VCLKData SiSUSB_VCLKData[] = {
+	{0x1b, 0xe1, 25},	/* 0x00 */
+	{0x4e, 0xe4, 28},	/* 0x01 */
+	{0x57, 0xe4, 31},	/* 0x02 */
+	{0xc3, 0xc8, 36},	/* 0x03 */
+	{0x42, 0xe2, 40},	/* 0x04 */
+	{0xfe, 0xcd, 43},	/* 0x05 */
+	{0x5d, 0xc4, 44},	/* 0x06 */
+	{0x52, 0xe2, 49},	/* 0x07 */
+	{0x53, 0xe2, 50},	/* 0x08 */
+	{0x74, 0x67, 52},	/* 0x09 */
+	{0x6d, 0x66, 56},	/* 0x0a */
+	{0x5a, 0x64, 65},	/* 0x0b */
+	{0x46, 0x44, 67},	/* 0x0c */
+	{0xb1, 0x46, 68},	/* 0x0d */
+	{0xd3, 0x4a, 72},	/* 0x0e */
+	{0x29, 0x61, 75},	/* 0x0f */
+	{0x6e, 0x46, 76},	/* 0x10 */
+	{0x2b, 0x61, 78},	/* 0x11 */
+	{0x31, 0x42, 79},	/* 0x12 */
+	{0xab, 0x44, 83},	/* 0x13 */
+	{0x46, 0x25, 84},	/* 0x14 */
+	{0x78, 0x29, 86},	/* 0x15 */
+	{0x62, 0x44, 94},	/* 0x16 */
+	{0x2b, 0x41, 104},	/* 0x17 */
+	{0x3a, 0x23, 105},	/* 0x18 */
+	{0x70, 0x44, 108},	/* 0x19 */
+	{0x3c, 0x23, 109},	/* 0x1a */
+	{0x5e, 0x43, 113},	/* 0x1b */
+	{0xbc, 0x44, 116},	/* 0x1c */
+	{0xe0, 0x46, 132},	/* 0x1d */
+	{0x54, 0x42, 135},	/* 0x1e */
+	{0xea, 0x2a, 139},	/* 0x1f */
+	{0x41, 0x22, 157},	/* 0x20 */
+	{0x70, 0x24, 162},	/* 0x21 */
+	{0x30, 0x21, 175},	/* 0x22 */
+	{0x4e, 0x22, 189},	/* 0x23 */
+	{0xde, 0x26, 194},	/* 0x24 */
+	{0x62, 0x06, 202},	/* 0x25 */
+	{0x3f, 0x03, 229},	/* 0x26 */
+	{0xb8, 0x06, 234},	/* 0x27 */
+	{0x34, 0x02, 253},	/* 0x28 */
+	{0x58, 0x04, 255},	/* 0x29 */
+	{0x24, 0x01, 265},	/* 0x2a */
+	{0x9b, 0x02, 267},	/* 0x2b */
+	{0x70, 0x05, 270},	/* 0x2c */
+	{0x25, 0x01, 272},	/* 0x2d */
+	{0x9c, 0x02, 277},	/* 0x2e */
+	{0x27, 0x01, 286},	/* 0x2f */
+	{0x3c, 0x02, 291},	/* 0x30 */
+	{0xef, 0x0a, 292},	/* 0x31 */
+	{0xf6, 0x0a, 310},	/* 0x32 */
+	{0x95, 0x01, 315},	/* 0x33 */
+	{0xf0, 0x09, 324},	/* 0x34 */
+	{0xfe, 0x0a, 331},	/* 0x35 */
+	{0xf3, 0x09, 332},	/* 0x36 */
+	{0xea, 0x08, 340},	/* 0x37 */
+	{0xe8, 0x07, 376},	/* 0x38 */
+	{0xde, 0x06, 389},	/* 0x39 */
+	{0x52, 0x2a, 54},	/* 0x3a 301 TV */
+	{0x52, 0x6a, 27},	/* 0x3b 301 TV */
+	{0x62, 0x24, 70},	/* 0x3c 301 TV */
+	{0x62, 0x64, 70},	/* 0x3d 301 TV */
+	{0xa8, 0x4c, 30},	/* 0x3e 301 TV */
+	{0x20, 0x26, 33},	/* 0x3f 301 TV */
+	{0x31, 0xc2, 39},	/* 0x40 */
+	{0x60, 0x36, 30},	/* 0x41 Chrontel */
+	{0x40, 0x4a, 28},	/* 0x42 Chrontel */
+	{0x9f, 0x46, 44},	/* 0x43 Chrontel */
+	{0x97, 0x2c, 26},	/* 0x44 */
+	{0x44, 0xe4, 25},	/* 0x45 Chrontel */
+	{0x7e, 0x32, 47},	/* 0x46 Chrontel */
+	{0x8a, 0x24, 31},	/* 0x47 Chrontel */
+	{0x97, 0x2c, 26},	/* 0x48 Chrontel */
+	{0xce, 0x3c, 39},	/* 0x49 */
+	{0x52, 0x4a, 36},	/* 0x4a Chrontel */
+	{0x34, 0x61, 95},	/* 0x4b */
+	{0x78, 0x27, 108},	/* 0x4c - was 102 */
+	{0x66, 0x43, 123},	/* 0x4d Modes 0x26-0x28 (1400x1050) */
+	{0x41, 0x4e, 21},	/* 0x4e */
+	{0xa1, 0x4a, 29},	/* 0x4f Chrontel */
+	{0x19, 0x42, 42},	/* 0x50 */
+	{0x54, 0x46, 58},	/* 0x51 Chrontel */
+	{0x25, 0x42, 61},	/* 0x52 */
+	{0x44, 0x44, 66},	/* 0x53 Chrontel */
+	{0x3a, 0x62, 70},	/* 0x54 Chrontel */
+	{0x62, 0xc6, 34},	/* 0x55 848x480-60 */
+	{0x6a, 0xc6, 37},	/* 0x56 848x480-75 - TEMP */
+	{0xbf, 0xc8, 35},	/* 0x57 856x480-38i,60 */
+	{0x30, 0x23, 88},	/* 0x58 1360x768-62 (is 60Hz!) */
+	{0x52, 0x07, 149},	/* 0x59 1280x960-85 */
+	{0x56, 0x07, 156},	/* 0x5a 1400x1050-75 */
+	{0x70, 0x29, 81},	/* 0x5b 1280x768 LCD */
+	{0x45, 0x25, 83},	/* 0x5c 1280x800  */
+	{0x70, 0x0a, 147},	/* 0x5d 1680x1050 */
+	{0x70, 0x24, 162},	/* 0x5e 1600x1200 */
+	{0x5a, 0x64, 65},	/* 0x5f 1280x720 - temp */
+	{0x63, 0x46, 68},	/* 0x60 1280x768_2 */
+	{0x31, 0x42, 79},	/* 0x61 1280x768_3 - temp */
+	{0, 0, 0},		/* 0x62 - custom (will be filled out at run-time) */
+	{0x5a, 0x64, 65},	/* 0x63 1280x720 (LCD LVDS) */
+	{0x70, 0x28, 90},	/* 0x64 1152x864@60 */
+	{0x41, 0xc4, 32},	/* 0x65 848x480@60 */
+	{0x5c, 0xc6, 32},	/* 0x66 856x480@60 */
+	{0x76, 0xe7, 27},	/* 0x67 720x480@60 */
+	{0x5f, 0xc6, 33},	/* 0x68 720/768x576@60 */
+	{0x52, 0x27, 75},	/* 0x69 1920x1080i 60Hz interlaced */
+	{0x7c, 0x6b, 38},	/* 0x6a 960x540@60 */
+	{0xe3, 0x56, 41},	/* 0x6b 960x600@60 */
+	{0x45, 0x25, 83},	/* 0x6c 1280x800 */
+	{0x70, 0x28, 90},	/* 0x6d 1152x864@60 */
+	{0x15, 0xe1, 20},	/* 0x6e 640x400@60 (fake, not actually used) */
+	{0x5f, 0xc6, 33},	/* 0x6f 720x576@60 */
+	{0x37, 0x5a, 10},	/* 0x70 320x200@60 (fake, not actually used) */
+	{0x2b, 0xc2, 35}	/* 0x71 768@576@60 */
+};
+
+int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
+int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
+
+extern int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
+extern int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 * data);
+extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
+			    u8 index, u8 data);
+extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
+			    u8 index, u8 * data);
+extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port,
+				 u8 idx, u8 myand, u8 myor);
+extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
+			      u8 index, u8 myor);
+extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
+			       u8 idx, u8 myand);
+
+void sisusb_delete(struct kref *kref);
+int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
+int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 * data);
+int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
+		       u32 dest, int length, size_t * bytes_written);
+int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
+int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
+			 u8 * arg, int cmapsz, int ch512, int dorecalc,
+			 struct vc_data *c, int fh, int uplock);
+void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);
+int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);
+void sisusb_console_exit(struct sisusb_usb_data *sisusb);
+void sisusb_init_concode(void);
+
+#endif
diff --git a/drivers/usb/misc/sisusbvga/sisusb_struct.h b/drivers/usb/misc/sisusbvga/sisusb_struct.h
new file mode 100644
index 0000000..1c4240e
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_struct.h
@@ -0,0 +1,161 @@
+/*
+ * General structure definitions for universal mode switching modules
+ *
+ * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, the following license terms
+ * apply:
+ *
+ * * 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 named License,
+ * * or 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
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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 the above copyright
+ * *    notice, this list of conditions and the following disclaimer.
+ * * 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 the author may not be used to endorse or promote products
+ * *    derived from this software without specific prior written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ * Author:	Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#ifndef _SISUSB_STRUCT_H_
+#define _SISUSB_STRUCT_H_
+
+struct SiS_St {
+	unsigned char St_ModeID;
+	unsigned short St_ModeFlag;
+	unsigned char St_StTableIndex;
+	unsigned char St_CRT2CRTC;
+	unsigned char St_ResInfo;
+	unsigned char VB_StTVFlickerIndex;
+	unsigned char VB_StTVEdgeIndex;
+	unsigned char VB_StTVYFilterIndex;
+	unsigned char St_PDC;
+};
+
+struct SiS_StandTable {
+	unsigned char CRT_COLS;
+	unsigned char ROWS;
+	unsigned char CHAR_HEIGHT;
+	unsigned short CRT_LEN;
+	unsigned char SR[4];
+	unsigned char MISC;
+	unsigned char CRTC[0x19];
+	unsigned char ATTR[0x14];
+	unsigned char GRC[9];
+};
+
+struct SiS_StResInfo_S {
+	unsigned short HTotal;
+	unsigned short VTotal;
+};
+
+struct SiS_Ext {
+	unsigned char Ext_ModeID;
+	unsigned short Ext_ModeFlag;
+	unsigned short Ext_VESAID;
+	unsigned char Ext_RESINFO;
+	unsigned char VB_ExtTVFlickerIndex;
+	unsigned char VB_ExtTVEdgeIndex;
+	unsigned char VB_ExtTVYFilterIndex;
+	unsigned char VB_ExtTVYFilterIndexROM661;
+	unsigned char REFindex;
+	char ROMMODEIDX661;
+};
+
+struct SiS_Ext2 {
+	unsigned short Ext_InfoFlag;
+	unsigned char Ext_CRT1CRTC;
+	unsigned char Ext_CRTVCLK;
+	unsigned char Ext_CRT2CRTC;
+	unsigned char Ext_CRT2CRTC_NS;
+	unsigned char ModeID;
+	unsigned short XRes;
+	unsigned short YRes;
+	unsigned char Ext_PDC;
+	unsigned char Ext_FakeCRT2CRTC;
+	unsigned char Ext_FakeCRT2Clk;
+};
+
+struct SiS_CRT1Table {
+	unsigned char CR[17];
+};
+
+struct SiS_VCLKData {
+	unsigned char SR2B, SR2C;
+	unsigned short CLOCK;
+};
+
+struct SiS_ModeResInfo {
+	unsigned short HTotal;
+	unsigned short VTotal;
+	unsigned char XChar;
+	unsigned char YChar;
+};
+
+struct SiS_Private {
+	void *sisusb;
+
+	unsigned long IOAddress;
+
+	unsigned long SiS_P3c4;
+	unsigned long SiS_P3d4;
+	unsigned long SiS_P3c0;
+	unsigned long SiS_P3ce;
+	unsigned long SiS_P3c2;
+	unsigned long SiS_P3ca;
+	unsigned long SiS_P3c6;
+	unsigned long SiS_P3c7;
+	unsigned long SiS_P3c8;
+	unsigned long SiS_P3c9;
+	unsigned long SiS_P3cb;
+	unsigned long SiS_P3cc;
+	unsigned long SiS_P3cd;
+	unsigned long SiS_P3da;
+	unsigned long SiS_Part1Port;
+
+	unsigned char SiS_MyCR63;
+	unsigned short SiS_CRT1Mode;
+	unsigned short SiS_ModeType;
+	unsigned short SiS_SetFlag;
+
+	const struct SiS_StandTable *SiS_StandTable;
+	const struct SiS_St *SiS_SModeIDTable;
+	const struct SiS_Ext *SiS_EModeIDTable;
+	const struct SiS_Ext2 *SiS_RefIndex;
+	const struct SiS_CRT1Table *SiS_CRT1Table;
+	const struct SiS_VCLKData *SiS_VCLKData;
+	const struct SiS_ModeResInfo *SiS_ModeResInfo;
+};
+
+#endif
diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c
new file mode 100644
index 0000000..4145314
--- /dev/null
+++ b/drivers/usb/misc/trancevibrator.c
@@ -0,0 +1,143 @@
+/*
+ * PlayStation 2 Trance Vibrator driver
+ *
+ * Copyright (C) 2006 Sam Hocevar <sam@zoy.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Standard include files */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v1.1"
+#define DRIVER_AUTHOR "Sam Hocevar, sam@zoy.org"
+#define DRIVER_DESC "PlayStation 2 Trance Vibrator driver"
+
+#define TRANCEVIBRATOR_VENDOR_ID	0x0b49	/* ASCII Corporation */
+#define TRANCEVIBRATOR_PRODUCT_ID	0x064f	/* Trance Vibrator */
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(TRANCEVIBRATOR_VENDOR_ID, TRANCEVIBRATOR_PRODUCT_ID) },
+	{ },
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+/* Driver-local specific stuff */
+struct trancevibrator {
+	struct usb_device *udev;
+	unsigned int speed;
+};
+
+static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct trancevibrator *tv = usb_get_intfdata(intf);
+
+	return sprintf(buf, "%d\n", tv->speed);
+}
+
+static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct trancevibrator *tv = usb_get_intfdata(intf);
+	int temp, retval, old;
+
+	temp = simple_strtoul(buf, NULL, 10);
+	if (temp > 255)
+		temp = 255;
+	else if (temp < 0)
+		temp = 0;
+	old = tv->speed;
+	tv->speed = temp;
+
+	dev_dbg(&tv->udev->dev, "speed = %d\n", tv->speed);
+
+	/* Set speed */
+	retval = usb_control_msg(tv->udev, usb_sndctrlpipe(tv->udev, 0),
+				 0x01, /* vendor request: set speed */
+				 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+				 tv->speed, /* speed value */
+				 0, NULL, 0, USB_CTRL_GET_TIMEOUT);
+	if (retval) {
+		tv->speed = old;
+		dev_dbg(&tv->udev->dev, "retval = %d\n", retval);
+		return retval;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR, show_speed, set_speed);
+
+static int tv_probe(struct usb_interface *interface,
+		    const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct trancevibrator *dev;
+	int retval;
+
+	dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&interface->dev, "Out of memory\n");
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	dev->udev = usb_get_dev(udev);
+	usb_set_intfdata(interface, dev);
+	retval = device_create_file(&interface->dev, &dev_attr_speed);
+	if (retval)
+		goto error_create_file;
+
+	return 0;
+
+error_create_file:
+	usb_put_dev(udev);
+	usb_set_intfdata(interface, NULL);
+error:
+	kfree(dev);
+	return retval;
+}
+
+static void tv_disconnect(struct usb_interface *interface)
+{
+	struct trancevibrator *dev;
+
+	dev = usb_get_intfdata (interface);
+	device_remove_file(&interface->dev, &dev_attr_speed);
+	usb_set_intfdata(interface, NULL);
+	usb_put_dev(dev->udev);
+	kfree(dev);
+}
+
+/* USB subsystem object */
+static struct usb_driver tv_driver = {
+	.name =		"trancevibrator",
+	.probe =	tv_probe,
+	.disconnect =	tv_disconnect,
+	.id_table =	id_table,
+};
+
+module_usb_driver(tv_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
new file mode 100644
index 0000000..b45cb77
--- /dev/null
+++ b/drivers/usb/misc/usb3503.c
@@ -0,0 +1,435 @@
+/*
+ * Driver for SMSC USB3503 USB 2.0 hub controller driver
+ *
+ * Copyright (c) 2012-2013 Dongjin Kim (tobetter@gmail.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, 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/clk.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb3503.h>
+#include <linux/regmap.h>
+
+#define USB3503_VIDL		0x00
+#define USB3503_VIDM		0x01
+#define USB3503_PIDL		0x02
+#define USB3503_PIDM		0x03
+#define USB3503_DIDL		0x04
+#define USB3503_DIDM		0x05
+
+#define USB3503_CFG1		0x06
+#define USB3503_SELF_BUS_PWR	(1 << 7)
+
+#define USB3503_CFG2		0x07
+#define USB3503_CFG3		0x08
+#define USB3503_NRD		0x09
+
+#define USB3503_PDS		0x0a
+
+#define USB3503_SP_ILOCK	0xe7
+#define USB3503_SPILOCK_CONNECT	(1 << 1)
+#define USB3503_SPILOCK_CONFIG	(1 << 0)
+
+#define USB3503_CFGP		0xee
+#define USB3503_CLKSUSP		(1 << 7)
+
+#define USB3503_RESET		0xff
+
+struct usb3503 {
+	enum usb3503_mode	mode;
+	struct regmap		*regmap;
+	struct device		*dev;
+	struct clk		*clk;
+	u8	port_off_mask;
+	int	gpio_intn;
+	int	gpio_reset;
+	int	gpio_connect;
+	bool	secondary_ref_clk;
+};
+
+static int usb3503_reset(struct usb3503 *hub, int state)
+{
+	if (!state && gpio_is_valid(hub->gpio_connect))
+		gpio_set_value_cansleep(hub->gpio_connect, 0);
+
+	if (gpio_is_valid(hub->gpio_reset))
+		gpio_set_value_cansleep(hub->gpio_reset, state);
+
+	/* Wait T_HUBINIT == 4ms for hub logic to stabilize */
+	if (state)
+		usleep_range(4000, 10000);
+
+	return 0;
+}
+
+static int usb3503_connect(struct usb3503 *hub)
+{
+	struct device *dev = hub->dev;
+	int err;
+
+	usb3503_reset(hub, 1);
+
+	if (hub->regmap) {
+		/* SP_ILOCK: set connect_n, config_n for config */
+		err = regmap_write(hub->regmap, USB3503_SP_ILOCK,
+			   (USB3503_SPILOCK_CONNECT
+				 | USB3503_SPILOCK_CONFIG));
+		if (err < 0) {
+			dev_err(dev, "SP_ILOCK failed (%d)\n", err);
+			return err;
+		}
+
+		/* PDS : Set the ports which are disabled in self-powered mode. */
+		if (hub->port_off_mask) {
+			err = regmap_update_bits(hub->regmap, USB3503_PDS,
+					hub->port_off_mask,
+					hub->port_off_mask);
+			if (err < 0) {
+				dev_err(dev, "PDS failed (%d)\n", err);
+				return err;
+			}
+		}
+
+		/* CFG1 : Set SELF_BUS_PWR, this enables self-powered operation. */
+		err = regmap_update_bits(hub->regmap, USB3503_CFG1,
+					 USB3503_SELF_BUS_PWR,
+					 USB3503_SELF_BUS_PWR);
+		if (err < 0) {
+			dev_err(dev, "CFG1 failed (%d)\n", err);
+			return err;
+		}
+
+		/* SP_LOCK: clear connect_n, config_n for hub connect */
+		err = regmap_update_bits(hub->regmap, USB3503_SP_ILOCK,
+					 (USB3503_SPILOCK_CONNECT
+					  | USB3503_SPILOCK_CONFIG), 0);
+		if (err < 0) {
+			dev_err(dev, "SP_ILOCK failed (%d)\n", err);
+			return err;
+		}
+	}
+
+	if (gpio_is_valid(hub->gpio_connect))
+		gpio_set_value_cansleep(hub->gpio_connect, 1);
+
+	hub->mode = USB3503_MODE_HUB;
+	dev_info(dev, "switched to HUB mode\n");
+
+	return 0;
+}
+
+static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
+{
+	struct device *dev = hub->dev;
+	int err = 0;
+
+	switch (mode) {
+	case USB3503_MODE_HUB:
+		err = usb3503_connect(hub);
+		break;
+
+	case USB3503_MODE_STANDBY:
+		usb3503_reset(hub, 0);
+		dev_info(dev, "switched to STANDBY mode\n");
+		break;
+
+	default:
+		dev_err(dev, "unknown mode is requested\n");
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static const struct regmap_config usb3503_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = USB3503_RESET,
+};
+
+static int usb3503_probe(struct usb3503 *hub)
+{
+	struct device *dev = hub->dev;
+	struct usb3503_platform_data *pdata = dev_get_platdata(dev);
+	struct device_node *np = dev->of_node;
+	int err;
+	u32 mode = USB3503_MODE_HUB;
+	const u32 *property;
+	int len;
+
+	if (pdata) {
+		hub->port_off_mask	= pdata->port_off_mask;
+		hub->gpio_intn		= pdata->gpio_intn;
+		hub->gpio_connect	= pdata->gpio_connect;
+		hub->gpio_reset		= pdata->gpio_reset;
+		hub->mode		= pdata->initial_mode;
+	} else if (np) {
+		struct clk *clk;
+		u32 rate = 0;
+		hub->port_off_mask = 0;
+
+		if (!of_property_read_u32(np, "refclk-frequency", &rate)) {
+			switch (rate) {
+			case 38400000:
+			case 26000000:
+			case 19200000:
+			case 12000000:
+				hub->secondary_ref_clk = 0;
+				break;
+			case 24000000:
+			case 27000000:
+			case 25000000:
+			case 50000000:
+				hub->secondary_ref_clk = 1;
+				break;
+			default:
+				dev_err(dev,
+					"unsupported reference clock rate (%d)\n",
+					(int) rate);
+				return -EINVAL;
+			}
+		}
+
+		clk = devm_clk_get(dev, "refclk");
+		if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) {
+			dev_err(dev, "unable to request refclk (%ld)\n",
+					PTR_ERR(clk));
+			return PTR_ERR(clk);
+		}
+
+		if (!IS_ERR(clk)) {
+			hub->clk = clk;
+
+			if (rate != 0) {
+				err = clk_set_rate(hub->clk, rate);
+				if (err) {
+					dev_err(dev,
+						"unable to set reference clock rate to %d\n",
+						(int) rate);
+					return err;
+				}
+			}
+
+			err = clk_prepare_enable(hub->clk);
+			if (err) {
+				dev_err(dev,
+					"unable to enable reference clock\n");
+				return err;
+			}
+		}
+
+		property = of_get_property(np, "disabled-ports", &len);
+		if (property && (len / sizeof(u32)) > 0) {
+			int i;
+			for (i = 0; i < len / sizeof(u32); i++) {
+				u32 port = be32_to_cpu(property[i]);
+				if ((1 <= port) && (port <= 3))
+					hub->port_off_mask |= (1 << port);
+			}
+		}
+
+		hub->gpio_intn	= of_get_named_gpio(np, "intn-gpios", 0);
+		if (hub->gpio_intn == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		hub->gpio_connect = of_get_named_gpio(np, "connect-gpios", 0);
+		if (hub->gpio_connect == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0);
+		if (hub->gpio_reset == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		of_property_read_u32(np, "initial-mode", &mode);
+		hub->mode = mode;
+	}
+
+	if (hub->port_off_mask && !hub->regmap)
+		dev_err(dev, "Ports disabled with no control interface\n");
+
+	if (gpio_is_valid(hub->gpio_intn)) {
+		int val = hub->secondary_ref_clk ? GPIOF_OUT_INIT_LOW :
+						   GPIOF_OUT_INIT_HIGH;
+		err = devm_gpio_request_one(dev, hub->gpio_intn, val,
+					    "usb3503 intn");
+		if (err) {
+			dev_err(dev,
+				"unable to request GPIO %d as interrupt pin (%d)\n",
+				hub->gpio_intn, err);
+			return err;
+		}
+	}
+
+	if (gpio_is_valid(hub->gpio_connect)) {
+		err = devm_gpio_request_one(dev, hub->gpio_connect,
+				GPIOF_OUT_INIT_LOW, "usb3503 connect");
+		if (err) {
+			dev_err(dev,
+				"unable to request GPIO %d as connect pin (%d)\n",
+				hub->gpio_connect, err);
+			return err;
+		}
+	}
+
+	if (gpio_is_valid(hub->gpio_reset)) {
+		err = devm_gpio_request_one(dev, hub->gpio_reset,
+				GPIOF_OUT_INIT_LOW, "usb3503 reset");
+		if (err) {
+			dev_err(dev,
+				"unable to request GPIO %d as reset pin (%d)\n",
+				hub->gpio_reset, err);
+			return err;
+		}
+	}
+
+	usb3503_switch_mode(hub, hub->mode);
+
+	dev_info(dev, "%s: probed in %s mode\n", __func__,
+			(hub->mode == USB3503_MODE_HUB) ? "hub" : "standby");
+
+	return 0;
+}
+
+static int usb3503_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct usb3503 *hub;
+	int err;
+
+	hub = devm_kzalloc(&i2c->dev, sizeof(struct usb3503), GFP_KERNEL);
+	if (!hub)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, hub);
+	hub->regmap = devm_regmap_init_i2c(i2c, &usb3503_regmap_config);
+	if (IS_ERR(hub->regmap)) {
+		err = PTR_ERR(hub->regmap);
+		dev_err(&i2c->dev, "Failed to initialise regmap: %d\n", err);
+		return err;
+	}
+	hub->dev = &i2c->dev;
+
+	return usb3503_probe(hub);
+}
+
+static int usb3503_platform_probe(struct platform_device *pdev)
+{
+	struct usb3503 *hub;
+
+	hub = devm_kzalloc(&pdev->dev, sizeof(struct usb3503), GFP_KERNEL);
+	if (!hub)
+		return -ENOMEM;
+	hub->dev = &pdev->dev;
+
+	return usb3503_probe(hub);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int usb3503_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct usb3503 *hub = i2c_get_clientdata(client);
+
+	usb3503_switch_mode(hub, USB3503_MODE_STANDBY);
+
+	if (hub->clk)
+		clk_disable_unprepare(hub->clk);
+
+	return 0;
+}
+
+static int usb3503_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct usb3503 *hub = i2c_get_clientdata(client);
+
+	if (hub->clk)
+		clk_prepare_enable(hub->clk);
+
+	usb3503_switch_mode(hub, hub->mode);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend,
+		usb3503_i2c_resume);
+
+static const struct i2c_device_id usb3503_id[] = {
+	{ USB3503_I2C_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, usb3503_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id usb3503_of_match[] = {
+	{ .compatible = "smsc,usb3503", },
+	{ .compatible = "smsc,usb3503a", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, usb3503_of_match);
+#endif
+
+static struct i2c_driver usb3503_i2c_driver = {
+	.driver = {
+		.name = USB3503_I2C_NAME,
+		.pm = &usb3503_i2c_pm_ops,
+		.of_match_table = of_match_ptr(usb3503_of_match),
+	},
+	.probe		= usb3503_i2c_probe,
+	.id_table	= usb3503_id,
+};
+
+static struct platform_driver usb3503_platform_driver = {
+	.driver = {
+		.name = USB3503_I2C_NAME,
+		.of_match_table = of_match_ptr(usb3503_of_match),
+	},
+	.probe		= usb3503_platform_probe,
+};
+
+static int __init usb3503_init(void)
+{
+	int err;
+
+	err = i2c_add_driver(&usb3503_i2c_driver);
+	if (err != 0)
+		pr_err("usb3503: Failed to register I2C driver: %d\n", err);
+
+	err = platform_driver_register(&usb3503_platform_driver);
+	if (err != 0)
+		pr_err("usb3503: Failed to register platform driver: %d\n",
+		       err);
+
+	return 0;
+}
+module_init(usb3503_init);
+
+static void __exit usb3503_exit(void)
+{
+	platform_driver_unregister(&usb3503_platform_driver);
+	i2c_del_driver(&usb3503_i2c_driver);
+}
+module_exit(usb3503_exit);
+
+MODULE_AUTHOR("Dongjin Kim <tobetter@gmail.com>");
+MODULE_DESCRIPTION("USB3503 USB HUB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
new file mode 100644
index 0000000..dc2e5a3
--- /dev/null
+++ b/drivers/usb/misc/usb_u132.h
@@ -0,0 +1,101 @@
+/*
+* Common Header File for the Elan Digital Systems U132 adapter
+* this file should be included by both the "ftdi-u132" and
+* the "u132-hcd" modules.
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+*(http://www.elandigitalsystems.com)
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+*(tony.olech@elandigitalsystems.com)
+*
+* This program is free software;you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation, version 2.
+*
+*
+* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+* The driver consists of two modules, the "ftdi-u132" module is
+* a USB client driver that interfaces to the FTDI chip within
+* the U132 adapter manufactured by Elan Digital Systems, and the
+* "u132-hcd" module is a USB host controller driver that talks
+* to the OHCI controller within CardBus card that are inserted
+* in the U132 adapter.
+*
+* The "ftdi-u132" module should be loaded automatically by the
+* hot plug system when the U132 adapter is plugged in. The module
+* initialises the adapter which mostly consists of synchronising
+* the FTDI chip, before continuously polling the adapter to detect
+* PC card insertions. As soon as a PC card containing a recognised
+* OHCI controller is seen the "ftdi-u132" module explicitly requests
+* the kernel to load the "u132-hcd" module.
+*
+* The "ftdi-u132" module provides the interface to the inserted
+* PC card and the "u132-hcd" module uses the API to send and receive
+* data. The API features call-backs, so that part of the "u132-hcd"
+* module code will run in the context of one of the kernel threads
+* of the "ftdi-u132" module.
+*
+*/
+int ftdi_elan_switch_on_diagnostics(int number);
+void ftdi_elan_gone_away(struct platform_device *pdev);
+void start_usb_lock_device_tracing(void);
+struct u132_platform_data {
+        u16 vendor;
+        u16 device;
+        u8 potpg;
+        void (*port_power) (struct device *dev, int is_on);
+        void (*reset) (struct device *dev);
+};
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+        void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+        void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+        int toggle_bits, int error_count, int condition_code, int repeat_number,
+         int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+        void *endp);
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
+			      u8 width, u32 *data);
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
+			       u8 width, u32 data);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
new file mode 100644
index 0000000..1184390
--- /dev/null
+++ b/drivers/usb/misc/usblcd.c
@@ -0,0 +1,460 @@
+/*****************************************************************************
+ *                          USBLCD Kernel Driver                             *
+ *                            Version 1.05                                   *
+ *             (C) 2005 Georges Toth <g.toth@e-biz.lu>                       *
+ *                                                                           *
+ *     This file is licensed under the GPL. See COPYING in the package.      *
+ * Based on usb-skeleton.c 2.0 by Greg Kroah-Hartman (greg@kroah.com)        *
+ *                                                                           *
+ *                                                                           *
+ * 28.02.05 Complete rewrite of the original usblcd.c driver,                *
+ *          based on usb_skeleton.c.                                         *
+ *          This new driver allows more than one USB-LCD to be connected     *
+ *          and controlled, at once                                          *
+ *****************************************************************************/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#define DRIVER_VERSION "USBLCD Driver Version 1.05"
+
+#define USBLCD_MINOR		144
+
+#define IOCTL_GET_HARD_VERSION	1
+#define IOCTL_GET_DRV_VERSION	2
+
+
+static DEFINE_MUTEX(lcd_mutex);
+static const struct usb_device_id id_table[] = {
+	{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static DEFINE_MUTEX(open_disc_mutex);
+
+
+struct usb_lcd {
+	struct usb_device	*udev;			/* init: probe_lcd */
+	struct usb_interface	*interface;		/* the interface for
+							   this device */
+	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 */
+	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 void lcd_delete(struct kref *kref)
+{
+	struct usb_lcd *dev = to_lcd_dev(kref);
+
+	usb_put_dev(dev->udev);
+	kfree(dev->bulk_in_buffer);
+	kfree(dev);
+}
+
+
+static int lcd_open(struct inode *inode, struct file *file)
+{
+	struct usb_lcd *dev;
+	struct usb_interface *interface;
+	int subminor, r;
+
+	mutex_lock(&lcd_mutex);
+	subminor = iminor(inode);
+
+	interface = usb_find_interface(&lcd_driver, subminor);
+	if (!interface) {
+		mutex_unlock(&lcd_mutex);
+		printk(KERN_ERR "USBLCD: %s - error, can't find device for minor %d\n",
+		       __func__, subminor);
+		return -ENODEV;
+	}
+
+	mutex_lock(&open_disc_mutex);
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		mutex_unlock(&open_disc_mutex);
+		mutex_unlock(&lcd_mutex);
+		return -ENODEV;
+	}
+
+	/* increment our usage count for the device */
+	kref_get(&dev->kref);
+	mutex_unlock(&open_disc_mutex);
+
+	/* grab a power reference */
+	r = usb_autopm_get_interface(interface);
+	if (r < 0) {
+		kref_put(&dev->kref, lcd_delete);
+		mutex_unlock(&lcd_mutex);
+		return r;
+	}
+
+	/* save our object in the file's private structure */
+	file->private_data = dev;
+	mutex_unlock(&lcd_mutex);
+
+	return 0;
+}
+
+static int lcd_release(struct inode *inode, struct file *file)
+{
+	struct usb_lcd *dev;
+
+	dev = file->private_data;
+	if (dev == NULL)
+		return -ENODEV;
+
+	/* decrement the count on our device */
+	usb_autopm_put_interface(dev->interface);
+	kref_put(&dev->kref, lcd_delete);
+	return 0;
+}
+
+static ssize_t lcd_read(struct file *file, char __user * buffer,
+			size_t count, loff_t *ppos)
+{
+	struct usb_lcd *dev;
+	int retval = 0;
+	int bytes_read;
+
+	dev = file->private_data;
+
+	/* do a blocking bulk read to get data from the device */
+	retval = usb_bulk_msg(dev->udev,
+			      usb_rcvbulkpipe(dev->udev,
+					      dev->bulk_in_endpointAddr),
+			      dev->bulk_in_buffer,
+			      min(dev->bulk_in_size, count),
+			      &bytes_read, 10000);
+
+	/* if the read was successful, copy the data to userspace */
+	if (!retval) {
+		if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read))
+			retval = -EFAULT;
+		else
+			retval = bytes_read;
+	}
+
+	return retval;
+}
+
+static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_lcd *dev;
+	u16 bcdDevice;
+	char buf[30];
+
+	dev = file->private_data;
+	if (dev == NULL)
+		return -ENODEV;
+
+	switch (cmd) {
+	case IOCTL_GET_HARD_VERSION:
+		mutex_lock(&lcd_mutex);
+		bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
+		sprintf(buf, "%1d%1d.%1d%1d",
+			(bcdDevice & 0xF000)>>12,
+			(bcdDevice & 0xF00)>>8,
+			(bcdDevice & 0xF0)>>4,
+			(bcdDevice & 0xF));
+		mutex_unlock(&lcd_mutex);
+		if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
+			return -EFAULT;
+		break;
+	case IOCTL_GET_DRV_VERSION:
+		sprintf(buf, DRIVER_VERSION);
+		if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0)
+			return -EFAULT;
+		break;
+	default:
+		return -ENOTTY;
+		break;
+	}
+
+	return 0;
+}
+
+static void lcd_write_bulk_callback(struct urb *urb)
+{
+	struct usb_lcd *dev;
+	int status = urb->status;
+
+	dev = urb->context;
+
+	/* sync/async unlink faults aren't errors */
+	if (status &&
+	    !(status == -ENOENT ||
+	      status == -ECONNRESET ||
+	      status == -ESHUTDOWN)) {
+		dev_dbg(&dev->interface->dev,
+			"nonzero write bulk status received: %d\n", status);
+	}
+
+	/* free up our allocated buffer */
+	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+			  urb->transfer_buffer, urb->transfer_dma);
+	up(&dev->limit_sem);
+}
+
+static ssize_t lcd_write(struct file *file, const char __user * user_buffer,
+			 size_t count, loff_t *ppos)
+{
+	struct usb_lcd *dev;
+	int retval = 0, r;
+	struct urb *urb = NULL;
+	char *buf = NULL;
+
+	dev = file->private_data;
+
+	/* verify that we actually have some data to write */
+	if (count == 0)
+		goto exit;
+
+	r = down_interruptible(&dev->limit_sem);
+	if (r < 0)
+		return -EINTR;
+
+	/* create a urb, and a buffer for it, and copy the data to the urb */
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		retval = -ENOMEM;
+		goto err_no_buf;
+	}
+
+	buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL,
+				 &urb->transfer_dma);
+	if (!buf) {
+		retval = -ENOMEM;
+		goto error;
+	}
+
+	if (copy_from_user(buf, user_buffer, count)) {
+		retval = -EFAULT;
+		goto error;
+	}
+
+	/* initialize the urb properly */
+	usb_fill_bulk_urb(urb, dev->udev,
+			  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) {
+		dev_err(&dev->udev->dev,
+			"%s - failed submitting write urb, error %d\n",
+			__func__, retval);
+		goto error_unanchor;
+	}
+
+	/* release our reference to this urb,
+	   the USB core will eventually free it entirely */
+	usb_free_urb(urb);
+
+exit:
+	return count;
+error_unanchor:
+	usb_unanchor_urb(urb);
+error:
+	usb_free_coherent(dev->udev, count, buf, urb->transfer_dma);
+	usb_free_urb(urb);
+err_no_buf:
+	up(&dev->limit_sem);
+	return retval;
+}
+
+static const struct file_operations lcd_fops = {
+	.owner =        THIS_MODULE,
+	.read =         lcd_read,
+	.write =        lcd_write,
+	.open =         lcd_open,
+	.unlocked_ioctl = lcd_ioctl,
+	.release =      lcd_release,
+	.llseek =	 noop_llseek,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver lcd_class = {
+	.name =         "lcd%d",
+	.fops =         &lcd_fops,
+	.minor_base =   USBLCD_MINOR,
+};
+
+static int lcd_probe(struct usb_interface *interface,
+		     const struct usb_device_id *id)
+{
+	struct usb_lcd *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	size_t buffer_size;
+	int i;
+	int retval = -ENOMEM;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&interface->dev, "Out of memory\n");
+		goto error;
+	}
+	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;
+
+	if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) {
+		dev_warn(&interface->dev, "USBLCD model not supported.\n");
+		retval = -ENODEV;
+		goto error;
+	}
+
+	/* set up the endpoint information */
+	/* use only the first bulk-in and bulk-out endpoints */
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (!dev->bulk_in_endpointAddr &&
+		    usb_endpoint_is_bulk_in(endpoint)) {
+			/* we found a bulk in endpoint */
+			buffer_size = usb_endpoint_maxp(endpoint);
+			dev->bulk_in_size = buffer_size;
+			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+			if (!dev->bulk_in_buffer) {
+				dev_err(&interface->dev,
+					"Could not allocate bulk_in_buffer\n");
+				goto error;
+			}
+		}
+
+		if (!dev->bulk_out_endpointAddr &&
+		    usb_endpoint_is_bulk_out(endpoint)) {
+			/* we found a bulk out endpoint */
+			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+		}
+	}
+	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
+		dev_err(&interface->dev,
+			"Could not find both bulk-in and bulk-out endpoints\n");
+		goto error;
+	}
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, dev);
+
+	/* we can register the device now, as it is ready */
+	retval = usb_register_dev(interface, &lcd_class);
+	if (retval) {
+		/* something prevented us from registering this driver */
+		dev_err(&interface->dev,
+			"Not able to get a minor for this device.\n");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	i = le16_to_cpu(dev->udev->descriptor.bcdDevice);
+
+	dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found "
+		 "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8,
+		 (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum);
+
+	/* let the user know what node this device is now attached to */
+	dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n",
+		 interface->minor);
+	return 0;
+
+error:
+	if (dev)
+		kref_put(&dev->kref, lcd_delete);
+	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;
+
+	mutex_lock(&open_disc_mutex);
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+	mutex_unlock(&open_disc_mutex);
+
+	/* give back our minor */
+	usb_deregister_dev(interface, &lcd_class);
+
+	/* decrement our usage count */
+	kref_put(&dev->kref, lcd_delete);
+
+	dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor);
+}
+
+static struct usb_driver lcd_driver = {
+	.name =		"usblcd",
+	.probe =	lcd_probe,
+	.disconnect =	lcd_disconnect,
+	.suspend =	lcd_suspend,
+	.resume =	lcd_resume,
+	.id_table =	id_table,
+	.supports_autosuspend = 1,
+};
+
+module_usb_driver(lcd_driver);
+
+MODULE_AUTHOR("Georges Toth <g.toth@e-biz.lu>");
+MODULE_DESCRIPTION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
new file mode 100644
index 0000000..bdef0d6
--- /dev/null
+++ b/drivers/usb/misc/usbled.c
@@ -0,0 +1,273 @@
+/*
+ * USB LED driver
+ *
+ * Copyright (C) 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, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
+#define DRIVER_DESC "USB LED Driver"
+
+enum led_type {
+	DELCOM_VISUAL_SIGNAL_INDICATOR,
+	DREAM_CHEEKY_WEBMAIL_NOTIFIER,
+	RISO_KAGAKU_LED
+};
+
+/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index
+   internally, we want to keep the red+green+blue sysfs api, so we decode
+   from 1-bit RGB to the riso kagaku color index according to this table... */
+
+static unsigned const char riso_kagaku_tbl[] = {
+/* R+2G+4B -> riso kagaku color index */
+	[0] = 0, /* black   */
+	[1] = 2, /* red     */
+	[2] = 1, /* green   */
+	[3] = 5, /* yellow  */
+	[4] = 3, /* blue    */
+	[5] = 6, /* magenta */
+	[6] = 4, /* cyan    */
+	[7] = 7  /* white   */
+};
+
+#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
+
+/* table of devices that work with this driver */
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x0fc5, 0x1223),
+			.driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
+	{ USB_DEVICE(0x1d34, 0x0004),
+			.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
+	{ USB_DEVICE(0x1d34, 0x000a),
+			.driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
+	{ USB_DEVICE(0x1294, 0x1320),
+			.driver_info = RISO_KAGAKU_LED },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct usb_led {
+	struct usb_device	*udev;
+	unsigned char		blue;
+	unsigned char		red;
+	unsigned char		green;
+	enum led_type		type;
+};
+
+static void change_color(struct usb_led *led)
+{
+	int retval = 0;
+	unsigned char *buffer;
+	int actlength;
+
+	buffer = kmalloc(8, GFP_KERNEL);
+	if (!buffer) {
+		dev_err(&led->udev->dev, "out of memory\n");
+		return;
+	}
+
+	switch (led->type) {
+	case DELCOM_VISUAL_SIGNAL_INDICATOR: {
+		unsigned char color = 0x07;
+
+		if (led->blue)
+			color &= ~0x04;
+		if (led->red)
+			color &= ~0x02;
+		if (led->green)
+			color &= ~0x01;
+		dev_dbg(&led->udev->dev,
+			"blue = %d, red = %d, green = %d, color = %.2x\n",
+			led->blue, led->red, led->green, color);
+
+		retval = usb_control_msg(led->udev,
+					usb_sndctrlpipe(led->udev, 0),
+					0x12,
+					0xc8,
+					(0x02 * 0x100) + 0x0a,
+					(0x00 * 0x100) + color,
+					buffer,
+					8,
+					2000);
+		break;
+	}
+
+	case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
+		dev_dbg(&led->udev->dev,
+			"red = %d, green = %d, blue = %d\n",
+			led->red, led->green, led->blue);
+
+		buffer[0] = led->red;
+		buffer[1] = led->green;
+		buffer[2] = led->blue;
+		buffer[3] = buffer[4] = buffer[5] = 0;
+		buffer[6] = 0x1a;
+		buffer[7] = 0x05;
+
+		retval = usb_control_msg(led->udev,
+					usb_sndctrlpipe(led->udev, 0),
+					0x09,
+					0x21,
+					0x200,
+					0,
+					buffer,
+					8,
+					2000);
+		break;
+
+	case RISO_KAGAKU_LED:
+		buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue);
+		buffer[1] = 0;
+		buffer[2] = 0;
+		buffer[3] = 0;
+		buffer[4] = 0;
+
+		retval = usb_interrupt_msg(led->udev,
+			usb_sndctrlpipe(led->udev, 2),
+			buffer, 5, &actlength, 1000 /*ms timeout*/);
+		break;
+
+	default:
+		dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
+	}
+
+	if (retval)
+		dev_dbg(&led->udev->dev, "retval = %d\n", retval);
+	kfree(buffer);
+}
+
+#define show_set(value)	\
+static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
+			    char *buf)					\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usb_led *led = usb_get_intfdata(intf);			\
+									\
+	return sprintf(buf, "%d\n", led->value);			\
+}									\
+static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
+			   const char *buf, size_t count)		\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct usb_led *led = usb_get_intfdata(intf);			\
+	int temp = simple_strtoul(buf, NULL, 10);			\
+									\
+	led->value = temp;						\
+	change_color(led);						\
+	return count;							\
+}									\
+static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
+show_set(blue);
+show_set(red);
+show_set(green);
+
+static int led_probe(struct usb_interface *interface,
+		     const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_led *dev = NULL;
+	int retval = -ENOMEM;
+
+	dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
+	if (dev == NULL) {
+		dev_err(&interface->dev, "out of memory\n");
+		goto error_mem;
+	}
+
+	dev->udev = usb_get_dev(udev);
+	dev->type = id->driver_info;
+
+	usb_set_intfdata(interface, dev);
+
+	retval = device_create_file(&interface->dev, &dev_attr_blue);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_red);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_green);
+	if (retval)
+		goto error;
+
+	if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
+		unsigned char *enable;
+
+		enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
+		if (!enable) {
+			dev_err(&interface->dev, "out of memory\n");
+			retval = -ENOMEM;
+			goto error;
+		}
+
+		retval = usb_control_msg(udev,
+					usb_sndctrlpipe(udev, 0),
+					0x09,
+					0x21,
+					0x200,
+					0,
+					enable,
+					8,
+					2000);
+
+		kfree(enable);
+		if (retval != 8)
+			goto error;
+	}
+
+	dev_info(&interface->dev, "USB LED device now attached\n");
+	return 0;
+
+error:
+	device_remove_file(&interface->dev, &dev_attr_blue);
+	device_remove_file(&interface->dev, &dev_attr_red);
+	device_remove_file(&interface->dev, &dev_attr_green);
+	usb_set_intfdata(interface, NULL);
+	usb_put_dev(dev->udev);
+	kfree(dev);
+error_mem:
+	return retval;
+}
+
+static void led_disconnect(struct usb_interface *interface)
+{
+	struct usb_led *dev;
+
+	dev = usb_get_intfdata(interface);
+
+	device_remove_file(&interface->dev, &dev_attr_blue);
+	device_remove_file(&interface->dev, &dev_attr_red);
+	device_remove_file(&interface->dev, &dev_attr_green);
+
+	/* first remove the files, then set the pointer to NULL */
+	usb_set_intfdata(interface, NULL);
+
+	usb_put_dev(dev->udev);
+
+	kfree(dev);
+
+	dev_info(&interface->dev, "USB LED now disconnected\n");
+}
+
+static struct usb_driver led_driver = {
+	.name =		"usbled",
+	.probe =	led_probe,
+	.disconnect =	led_disconnect,
+	.id_table =	id_table,
+};
+
+module_usb_driver(led_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c
new file mode 100644
index 0000000..1fe6b73
--- /dev/null
+++ b/drivers/usb/misc/usbsevseg.c
@@ -0,0 +1,443 @@
+/*
+ * USB 7 Segment Driver
+ *
+ * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com>
+ * Based on usbled.c by 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, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+
+
+#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>"
+#define DRIVER_DESC "USB 7 Segment Driver"
+
+#define VENDOR_ID	0x0fc5
+#define PRODUCT_ID	0x1227
+#define MAXLEN		8
+
+/* table of devices that work with this driver */
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+/* the different text display modes the device is capable of */
+static char *display_textmodes[] = {"raw", "hex", "ascii", NULL};
+
+struct usb_sevsegdev {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+
+	u8 powered;
+	u8 mode_msb;
+	u8 mode_lsb;
+	u8 decimals[MAXLEN];
+	u8 textmode;
+	u8 text[MAXLEN];
+	u16 textlength;
+
+	u8 shadow_power; /* for PM */
+	u8 has_interface_pm;
+};
+
+/* sysfs_streq can't replace this completely
+ * If the device was in hex mode, and the user wanted a 0,
+ * if str commands are used, we would assume the end of string
+ * so mem commands are used.
+ */
+static inline size_t my_memlen(const char *buf, size_t count)
+{
+	if (count > 0 && buf[count-1] == '\n')
+		return count - 1;
+	else
+		return count;
+}
+
+static void update_display_powered(struct usb_sevsegdev *mydev)
+{
+	int rc;
+
+	if (mydev->powered && !mydev->has_interface_pm) {
+		rc = usb_autopm_get_interface(mydev->intf);
+		if (rc < 0)
+			return;
+		mydev->has_interface_pm = 1;
+	}
+
+	if (mydev->shadow_power != 1)
+		return;
+
+	rc = usb_control_msg(mydev->udev,
+			usb_sndctrlpipe(mydev->udev, 0),
+			0x12,
+			0x48,
+			(80 * 0x100) + 10, /*  (power mode) */
+			(0x00 * 0x100) + (mydev->powered ? 1 : 0),
+			NULL,
+			0,
+			2000);
+	if (rc < 0)
+		dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
+
+	if (!mydev->powered && mydev->has_interface_pm) {
+		usb_autopm_put_interface(mydev->intf);
+		mydev->has_interface_pm = 0;
+	}
+}
+
+static void update_display_mode(struct usb_sevsegdev *mydev)
+{
+	int rc;
+
+	if(mydev->shadow_power != 1)
+		return;
+
+	rc = usb_control_msg(mydev->udev,
+			usb_sndctrlpipe(mydev->udev, 0),
+			0x12,
+			0x48,
+			(82 * 0x100) + 10, /* (set mode) */
+			(mydev->mode_msb * 0x100) + mydev->mode_lsb,
+			NULL,
+			0,
+			2000);
+
+	if (rc < 0)
+		dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
+}
+
+static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
+{
+	int rc;
+	int i;
+	unsigned char *buffer;
+	u8 decimals = 0;
+
+	if(mydev->shadow_power != 1)
+		return;
+
+	buffer = kzalloc(MAXLEN, mf);
+	if (!buffer) {
+		dev_err(&mydev->udev->dev, "out of memory\n");
+		return;
+	}
+
+	/* The device is right to left, where as you write left to right */
+	for (i = 0; i < mydev->textlength; i++)
+		buffer[i] = mydev->text[mydev->textlength-1-i];
+
+	rc = usb_control_msg(mydev->udev,
+			usb_sndctrlpipe(mydev->udev, 0),
+			0x12,
+			0x48,
+			(85 * 0x100) + 10, /* (write text) */
+			(0 * 0x100) + mydev->textmode, /* mode  */
+			buffer,
+			mydev->textlength,
+			2000);
+
+	if (rc < 0)
+		dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
+
+	kfree(buffer);
+
+	/* The device is right to left, where as you write left to right */
+	for (i = 0; i < sizeof(mydev->decimals); i++)
+		decimals |= mydev->decimals[i] << i;
+
+	rc = usb_control_msg(mydev->udev,
+			usb_sndctrlpipe(mydev->udev, 0),
+			0x12,
+			0x48,
+			(86 * 0x100) + 10, /* (set decimal) */
+			(0 * 0x100) + decimals, /* decimals */
+			NULL,
+			0,
+			2000);
+
+	if (rc < 0)
+		dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
+}
+
+#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)		\
+static ssize_t show_attr_##name(struct device *dev, 		\
+	struct device_attribute *attr, char *buf) 		\
+{								\
+	struct usb_interface *intf = to_usb_interface(dev);	\
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
+								\
+	return sprintf(buf, "%u\n", mydev->name);		\
+}								\
+								\
+static ssize_t set_attr_##name(struct device *dev, 		\
+	struct device_attribute *attr, const char *buf, size_t count) \
+{								\
+	struct usb_interface *intf = to_usb_interface(dev);	\
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
+								\
+	mydev->name = simple_strtoul(buf, NULL, 10);		\
+	update_fcn(mydev); 					\
+								\
+	return count;						\
+}								\
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_attr_##name, set_attr_##name);
+
+static ssize_t show_attr_text(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
+
+	return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
+}
+
+static ssize_t set_attr_text(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
+	size_t end = my_memlen(buf, count);
+
+	if (end > sizeof(mydev->text))
+		return -EINVAL;
+
+	memset(mydev->text, 0, sizeof(mydev->text));
+	mydev->textlength = end;
+
+	if (end > 0)
+		memcpy(mydev->text, buf, end);
+
+	update_display_visual(mydev, GFP_KERNEL);
+	return count;
+}
+
+static DEVICE_ATTR(text, S_IRUGO | S_IWUSR, show_attr_text, set_attr_text);
+
+static ssize_t show_attr_decimals(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
+	int i;
+	int pos;
+
+	for (i = 0; i < sizeof(mydev->decimals); i++) {
+		pos = sizeof(mydev->decimals) - 1 - i;
+		if (mydev->decimals[i] == 0)
+			buf[pos] = '0';
+		else if (mydev->decimals[i] == 1)
+			buf[pos] = '1';
+		else
+			buf[pos] = 'x';
+	}
+
+	buf[sizeof(mydev->decimals)] = '\n';
+	return sizeof(mydev->decimals) + 1;
+}
+
+static ssize_t set_attr_decimals(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
+	size_t end = my_memlen(buf, count);
+	int i;
+
+	if (end > sizeof(mydev->decimals))
+		return -EINVAL;
+
+	for (i = 0; i < end; i++)
+		if (buf[i] != '0' && buf[i] != '1')
+			return -EINVAL;
+
+	memset(mydev->decimals, 0, sizeof(mydev->decimals));
+	for (i = 0; i < end; i++)
+		if (buf[i] == '1')
+			mydev->decimals[end-1-i] = 1;
+
+	update_display_visual(mydev, GFP_KERNEL);
+
+	return count;
+}
+
+static DEVICE_ATTR(decimals, S_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals);
+
+static ssize_t show_attr_textmode(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
+	int i;
+
+	buf[0] = 0;
+
+	for (i = 0; display_textmodes[i]; i++) {
+		if (mydev->textmode == i) {
+			strcat(buf, " [");
+			strcat(buf, display_textmodes[i]);
+			strcat(buf, "] ");
+		} else {
+			strcat(buf, " ");
+			strcat(buf, display_textmodes[i]);
+			strcat(buf, " ");
+		}
+	}
+	strcat(buf, "\n");
+
+
+	return strlen(buf);
+}
+
+static ssize_t set_attr_textmode(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
+	int i;
+
+	for (i = 0; display_textmodes[i]; i++) {
+		if (sysfs_streq(display_textmodes[i], buf)) {
+			mydev->textmode = i;
+			update_display_visual(mydev, GFP_KERNEL);
+			return count;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode);
+
+
+MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
+MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
+MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
+
+static struct attribute *dev_attrs[] = {
+	&dev_attr_powered.attr,
+	&dev_attr_text.attr,
+	&dev_attr_textmode.attr,
+	&dev_attr_decimals.attr,
+	&dev_attr_mode_msb.attr,
+	&dev_attr_mode_lsb.attr,
+	NULL
+};
+
+static struct attribute_group dev_attr_grp = {
+	.attrs = dev_attrs,
+};
+
+static int sevseg_probe(struct usb_interface *interface,
+	const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_sevsegdev *mydev = NULL;
+	int rc = -ENOMEM;
+
+	mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
+	if (mydev == NULL) {
+		dev_err(&interface->dev, "Out of memory\n");
+		goto error_mem;
+	}
+
+	mydev->udev = usb_get_dev(udev);
+	mydev->intf = interface;
+	usb_set_intfdata(interface, mydev);
+
+	/* PM */
+	mydev->shadow_power = 1; /* currently active */
+	mydev->has_interface_pm = 0; /* have not issued autopm_get */
+
+	/*set defaults */
+	mydev->textmode = 0x02; /* ascii mode */
+	mydev->mode_msb = 0x06; /* 6 characters */
+	mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
+
+	rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp);
+	if (rc)
+		goto error;
+
+	dev_info(&interface->dev, "USB 7 Segment device now attached\n");
+	return 0;
+
+error:
+	usb_set_intfdata(interface, NULL);
+	usb_put_dev(mydev->udev);
+	kfree(mydev);
+error_mem:
+	return rc;
+}
+
+static void sevseg_disconnect(struct usb_interface *interface)
+{
+	struct usb_sevsegdev *mydev;
+
+	mydev = usb_get_intfdata(interface);
+	sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp);
+	usb_set_intfdata(interface, NULL);
+	usb_put_dev(mydev->udev);
+	kfree(mydev);
+	dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
+}
+
+static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_sevsegdev *mydev;
+
+	mydev = usb_get_intfdata(intf);
+	mydev->shadow_power = 0;
+
+	return 0;
+}
+
+static int sevseg_resume(struct usb_interface *intf)
+{
+	struct usb_sevsegdev *mydev;
+
+	mydev = usb_get_intfdata(intf);
+	mydev->shadow_power = 1;
+	update_display_mode(mydev);
+	update_display_visual(mydev, GFP_NOIO);
+
+	return 0;
+}
+
+static int sevseg_reset_resume(struct usb_interface *intf)
+{
+	struct usb_sevsegdev *mydev;
+
+	mydev = usb_get_intfdata(intf);
+	mydev->shadow_power = 1;
+	update_display_mode(mydev);
+	update_display_visual(mydev, GFP_NOIO);
+
+	return 0;
+}
+
+static struct usb_driver sevseg_driver = {
+	.name =		"usbsevseg",
+	.probe =	sevseg_probe,
+	.disconnect =	sevseg_disconnect,
+	.suspend =	sevseg_suspend,
+	.resume =	sevseg_resume,
+	.reset_resume =	sevseg_reset_resume,
+	.id_table =	id_table,
+	.supports_autosuspend = 1,
+};
+
+module_usb_driver(sevseg_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
new file mode 100644
index 0000000..bc92a49
--- /dev/null
+++ b/drivers/usb/misc/usbtest.c
@@ -0,0 +1,2899 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/scatterlist.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+
+#define SIMPLE_IO_TIMEOUT	10000	/* in milliseconds */
+
+/*-------------------------------------------------------------------------*/
+
+static int override_alt = -1;
+module_param_named(alt, override_alt, int, 0644);
+MODULE_PARM_DESC(alt, ">= 0 to override altsetting selection");
+static void complicated_callback(struct urb *urb);
+
+/*-------------------------------------------------------------------------*/
+
+/* FIXME make these public somewhere; usbdevfs.h? */
+struct usbtest_param {
+	/* inputs */
+	unsigned		test_num;	/* 0..(TEST_CASES-1) */
+	unsigned		iterations;
+	unsigned		length;
+	unsigned		vary;
+	unsigned		sglen;
+
+	/* outputs */
+	struct timeval		duration;
+};
+#define USBTEST_REQUEST	_IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+#define	GENERIC		/* let probe() bind using module params */
+
+/* Some devices that can be used for testing will have "real" drivers.
+ * Entries for those need to be enabled here by hand, after disabling
+ * that "real" driver.
+ */
+//#define	IBOT2		/* grab iBOT2 webcams */
+//#define	KEYSPAN_19Qi	/* grab un-renumerated serial adapter */
+
+/*-------------------------------------------------------------------------*/
+
+struct usbtest_info {
+	const char		*name;
+	u8			ep_in;		/* bulk/intr source */
+	u8			ep_out;		/* bulk/intr sink */
+	unsigned		autoconf:1;
+	unsigned		ctrl_out:1;
+	unsigned		iso:1;		/* try iso in/out */
+	unsigned		intr:1;		/* try interrupt in/out */
+	int			alt;
+};
+
+/* this is accessed only through usbfs ioctl calls.
+ * one ioctl to issue a test ... one lock per device.
+ * tests create other threads if they need them.
+ * urbs and buffers are allocated dynamically,
+ * and data generated deterministically.
+ */
+struct usbtest_dev {
+	struct usb_interface	*intf;
+	struct usbtest_info	*info;
+	int			in_pipe;
+	int			out_pipe;
+	int			in_iso_pipe;
+	int			out_iso_pipe;
+	int			in_int_pipe;
+	int			out_int_pipe;
+	struct usb_endpoint_descriptor	*iso_in, *iso_out;
+	struct usb_endpoint_descriptor	*int_in, *int_out;
+	struct mutex		lock;
+
+#define TBUF_SIZE	256
+	u8			*buf;
+};
+
+static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test)
+{
+	return interface_to_usbdev(test->intf);
+}
+
+/* set up all urbs so they can be used with either bulk or interrupt */
+#define	INTERRUPT_RATE		1	/* msec/transfer */
+
+#define ERROR(tdev, fmt, args...) \
+	dev_err(&(tdev)->intf->dev , fmt , ## args)
+#define WARNING(tdev, fmt, args...) \
+	dev_warn(&(tdev)->intf->dev , fmt , ## args)
+
+#define GUARD_BYTE	0xA5
+#define MAX_SGLEN	128
+
+/*-------------------------------------------------------------------------*/
+
+static int
+get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
+{
+	int				tmp;
+	struct usb_host_interface	*alt;
+	struct usb_host_endpoint	*in, *out;
+	struct usb_host_endpoint	*iso_in, *iso_out;
+	struct usb_host_endpoint	*int_in, *int_out;
+	struct usb_device		*udev;
+
+	for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
+		unsigned	ep;
+
+		in = out = NULL;
+		iso_in = iso_out = NULL;
+		int_in = int_out = NULL;
+		alt = intf->altsetting + tmp;
+
+		if (override_alt >= 0 &&
+				override_alt != alt->desc.bAlternateSetting)
+			continue;
+
+		/* take the first altsetting with in-bulk + out-bulk;
+		 * ignore other endpoints and altsettings.
+		 */
+		for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
+			struct usb_host_endpoint	*e;
+
+			e = alt->endpoint + ep;
+			switch (usb_endpoint_type(&e->desc)) {
+			case USB_ENDPOINT_XFER_BULK:
+				break;
+			case USB_ENDPOINT_XFER_INT:
+				if (dev->info->intr)
+					goto try_intr;
+				continue;
+			case USB_ENDPOINT_XFER_ISOC:
+				if (dev->info->iso)
+					goto try_iso;
+				/* FALLTHROUGH */
+			default:
+				continue;
+			}
+			if (usb_endpoint_dir_in(&e->desc)) {
+				if (!in)
+					in = e;
+			} else {
+				if (!out)
+					out = e;
+			}
+			continue;
+try_intr:
+			if (usb_endpoint_dir_in(&e->desc)) {
+				if (!int_in)
+					int_in = e;
+			} else {
+				if (!int_out)
+					int_out = e;
+			}
+			continue;
+try_iso:
+			if (usb_endpoint_dir_in(&e->desc)) {
+				if (!iso_in)
+					iso_in = e;
+			} else {
+				if (!iso_out)
+					iso_out = e;
+			}
+		}
+		if ((in && out)  ||  iso_in || iso_out || int_in || int_out)
+			goto found;
+	}
+	return -EINVAL;
+
+found:
+	udev = testdev_to_usbdev(dev);
+	dev->info->alt = alt->desc.bAlternateSetting;
+	if (alt->desc.bAlternateSetting != 0) {
+		tmp = usb_set_interface(udev,
+				alt->desc.bInterfaceNumber,
+				alt->desc.bAlternateSetting);
+		if (tmp < 0)
+			return tmp;
+	}
+
+	if (in)
+		dev->in_pipe = usb_rcvbulkpipe(udev,
+			in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	if (out)
+		dev->out_pipe = usb_sndbulkpipe(udev,
+			out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+	if (iso_in) {
+		dev->iso_in = &iso_in->desc;
+		dev->in_iso_pipe = usb_rcvisocpipe(udev,
+				iso_in->desc.bEndpointAddress
+					& USB_ENDPOINT_NUMBER_MASK);
+	}
+
+	if (iso_out) {
+		dev->iso_out = &iso_out->desc;
+		dev->out_iso_pipe = usb_sndisocpipe(udev,
+				iso_out->desc.bEndpointAddress
+					& USB_ENDPOINT_NUMBER_MASK);
+	}
+
+	if (int_in) {
+		dev->int_in = &int_in->desc;
+		dev->in_int_pipe = usb_rcvintpipe(udev,
+				int_in->desc.bEndpointAddress
+					& USB_ENDPOINT_NUMBER_MASK);
+	}
+
+	if (int_out) {
+		dev->int_out = &int_out->desc;
+		dev->out_int_pipe = usb_sndintpipe(udev,
+				int_out->desc.bEndpointAddress
+					& USB_ENDPOINT_NUMBER_MASK);
+	}
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Support for testing basic non-queued I/O streams.
+ *
+ * These just package urbs as requests that can be easily canceled.
+ * Each urb's data buffer is dynamically allocated; callers can fill
+ * them with non-zero test data (or test for it) when appropriate.
+ */
+
+static void simple_callback(struct urb *urb)
+{
+	complete(urb->context);
+}
+
+static struct urb *usbtest_alloc_urb(
+	struct usb_device	*udev,
+	int			pipe,
+	unsigned long		bytes,
+	unsigned		transfer_flags,
+	unsigned		offset,
+	u8			bInterval,
+	usb_complete_t		complete_fn)
+{
+	struct urb		*urb;
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return urb;
+
+	if (bInterval)
+		usb_fill_int_urb(urb, udev, pipe, NULL, bytes, complete_fn,
+				NULL, bInterval);
+	else
+		usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, complete_fn,
+				NULL);
+
+	urb->interval = (udev->speed == USB_SPEED_HIGH)
+			? (INTERRUPT_RATE << 3)
+			: INTERRUPT_RATE;
+	urb->transfer_flags = transfer_flags;
+	if (usb_pipein(pipe))
+		urb->transfer_flags |= URB_SHORT_NOT_OK;
+
+	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+		urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
+			GFP_KERNEL, &urb->transfer_dma);
+	else
+		urb->transfer_buffer = kmalloc(bytes + offset, GFP_KERNEL);
+
+	if (!urb->transfer_buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+
+	/* To test unaligned transfers add an offset and fill the
+		unused memory with a guard value */
+	if (offset) {
+		memset(urb->transfer_buffer, GUARD_BYTE, offset);
+		urb->transfer_buffer += offset;
+		if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+			urb->transfer_dma += offset;
+	}
+
+	/* For inbound transfers use guard byte so that test fails if
+		data not correctly copied */
+	memset(urb->transfer_buffer,
+			usb_pipein(urb->pipe) ? GUARD_BYTE : 0,
+			bytes);
+	return urb;
+}
+
+static struct urb *simple_alloc_urb(
+	struct usb_device	*udev,
+	int			pipe,
+	unsigned long		bytes,
+	u8			bInterval)
+{
+	return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
+			bInterval, simple_callback);
+}
+
+static struct urb *complicated_alloc_urb(
+	struct usb_device	*udev,
+	int			pipe,
+	unsigned long		bytes,
+	u8			bInterval)
+{
+	return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0,
+			bInterval, complicated_callback);
+}
+
+static unsigned pattern;
+static unsigned mod_pattern;
+module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)");
+
+static unsigned get_maxpacket(struct usb_device *udev, int pipe)
+{
+	struct usb_host_endpoint	*ep;
+
+	ep = usb_pipe_endpoint(udev, pipe);
+	return le16_to_cpup(&ep->desc.wMaxPacketSize);
+}
+
+static void simple_fill_buf(struct urb *urb)
+{
+	unsigned	i;
+	u8		*buf = urb->transfer_buffer;
+	unsigned	len = urb->transfer_buffer_length;
+	unsigned	maxpacket;
+
+	switch (pattern) {
+	default:
+		/* FALLTHROUGH */
+	case 0:
+		memset(buf, 0, len);
+		break;
+	case 1:			/* mod63 */
+		maxpacket = get_maxpacket(urb->dev, urb->pipe);
+		for (i = 0; i < len; i++)
+			*buf++ = (u8) ((i % maxpacket) % 63);
+		break;
+	}
+}
+
+static inline unsigned long buffer_offset(void *buf)
+{
+	return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1);
+}
+
+static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb)
+{
+	u8 *buf = urb->transfer_buffer;
+	u8 *guard = buf - buffer_offset(buf);
+	unsigned i;
+
+	for (i = 0; guard < buf; i++, guard++) {
+		if (*guard != GUARD_BYTE) {
+			ERROR(tdev, "guard byte[%d] %d (not %d)\n",
+				i, *guard, GUARD_BYTE);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb)
+{
+	unsigned	i;
+	u8		expected;
+	u8		*buf = urb->transfer_buffer;
+	unsigned	len = urb->actual_length;
+	unsigned	maxpacket = get_maxpacket(urb->dev, urb->pipe);
+
+	int ret = check_guard_bytes(tdev, urb);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < len; i++, buf++) {
+		switch (pattern) {
+		/* all-zeroes has no synchronization issues */
+		case 0:
+			expected = 0;
+			break;
+		/* mod63 stays in sync with short-terminated transfers,
+		 * or otherwise when host and gadget agree on how large
+		 * each usb transfer request should be.  resync is done
+		 * with set_interface or set_config.
+		 */
+		case 1:			/* mod63 */
+			expected = (i % maxpacket) % 63;
+			break;
+		/* always fail unsupported patterns */
+		default:
+			expected = !*buf;
+			break;
+		}
+		if (*buf == expected)
+			continue;
+		ERROR(tdev, "buf[%d] = %d (not %d)\n", i, *buf, expected);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void simple_free_urb(struct urb *urb)
+{
+	unsigned long offset = buffer_offset(urb->transfer_buffer);
+
+	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+		usb_free_coherent(
+			urb->dev,
+			urb->transfer_buffer_length + offset,
+			urb->transfer_buffer - offset,
+			urb->transfer_dma - offset);
+	else
+		kfree(urb->transfer_buffer - offset);
+	usb_free_urb(urb);
+}
+
+static int simple_io(
+	struct usbtest_dev	*tdev,
+	struct urb		*urb,
+	int			iterations,
+	int			vary,
+	int			expected,
+	const char		*label
+)
+{
+	struct usb_device	*udev = urb->dev;
+	int			max = urb->transfer_buffer_length;
+	struct completion	completion;
+	int			retval = 0;
+	unsigned long		expire;
+
+	urb->context = &completion;
+	while (retval == 0 && iterations-- > 0) {
+		init_completion(&completion);
+		if (usb_pipeout(urb->pipe)) {
+			simple_fill_buf(urb);
+			urb->transfer_flags |= URB_ZERO_PACKET;
+		}
+		retval = usb_submit_urb(urb, GFP_KERNEL);
+		if (retval != 0)
+			break;
+
+		expire = msecs_to_jiffies(SIMPLE_IO_TIMEOUT);
+		if (!wait_for_completion_timeout(&completion, expire)) {
+			usb_kill_urb(urb);
+			retval = (urb->status == -ENOENT ?
+				  -ETIMEDOUT : urb->status);
+		} else {
+			retval = urb->status;
+		}
+
+		urb->dev = udev;
+		if (retval == 0 && usb_pipein(urb->pipe))
+			retval = simple_check_buf(tdev, urb);
+
+		if (vary) {
+			int	len = urb->transfer_buffer_length;
+
+			len += vary;
+			len %= max;
+			if (len == 0)
+				len = (vary < max) ? vary : max;
+			urb->transfer_buffer_length = len;
+		}
+
+		/* FIXME if endpoint halted, clear halt (and log) */
+	}
+	urb->transfer_buffer_length = max;
+
+	if (expected != retval)
+		dev_err(&udev->dev,
+			"%s failed, iterations left %d, status %d (not %d)\n",
+				label, iterations, retval, expected);
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* We use scatterlist primitives to test queued I/O.
+ * Yes, this also tests the scatterlist primitives.
+ */
+
+static void free_sglist(struct scatterlist *sg, int nents)
+{
+	unsigned		i;
+
+	if (!sg)
+		return;
+	for (i = 0; i < nents; i++) {
+		if (!sg_page(&sg[i]))
+			continue;
+		kfree(sg_virt(&sg[i]));
+	}
+	kfree(sg);
+}
+
+static struct scatterlist *
+alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
+{
+	struct scatterlist	*sg;
+	unsigned int		n_size = 0;
+	unsigned		i;
+	unsigned		size = max;
+	unsigned		maxpacket =
+		get_maxpacket(interface_to_usbdev(dev->intf), pipe);
+
+	if (max == 0)
+		return NULL;
+
+	sg = kmalloc_array(nents, sizeof(*sg), GFP_KERNEL);
+	if (!sg)
+		return NULL;
+	sg_init_table(sg, nents);
+
+	for (i = 0; i < nents; i++) {
+		char		*buf;
+		unsigned	j;
+
+		buf = kzalloc(size, GFP_KERNEL);
+		if (!buf) {
+			free_sglist(sg, i);
+			return NULL;
+		}
+
+		/* kmalloc pages are always physically contiguous! */
+		sg_set_buf(&sg[i], buf, size);
+
+		switch (pattern) {
+		case 0:
+			/* already zeroed */
+			break;
+		case 1:
+			for (j = 0; j < size; j++)
+				*buf++ = (u8) (((j + n_size) % maxpacket) % 63);
+			n_size += size;
+			break;
+		}
+
+		if (vary) {
+			size += vary;
+			size %= max;
+			if (size == 0)
+				size = (vary < max) ? vary : max;
+		}
+	}
+
+	return sg;
+}
+
+static void sg_timeout(unsigned long _req)
+{
+	struct usb_sg_request	*req = (struct usb_sg_request *) _req;
+
+	usb_sg_cancel(req);
+}
+
+static int perform_sglist(
+	struct usbtest_dev	*tdev,
+	unsigned		iterations,
+	int			pipe,
+	struct usb_sg_request	*req,
+	struct scatterlist	*sg,
+	int			nents
+)
+{
+	struct usb_device	*udev = testdev_to_usbdev(tdev);
+	int			retval = 0;
+	struct timer_list	sg_timer;
+
+	setup_timer_on_stack(&sg_timer, sg_timeout, (unsigned long) req);
+
+	while (retval == 0 && iterations-- > 0) {
+		retval = usb_sg_init(req, udev, pipe,
+				(udev->speed == USB_SPEED_HIGH)
+					? (INTERRUPT_RATE << 3)
+					: INTERRUPT_RATE,
+				sg, nents, 0, GFP_KERNEL);
+
+		if (retval)
+			break;
+		mod_timer(&sg_timer, jiffies +
+				msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
+		usb_sg_wait(req);
+		if (!del_timer_sync(&sg_timer))
+			retval = -ETIMEDOUT;
+		else
+			retval = req->status;
+
+		/* FIXME check resulting data pattern */
+
+		/* FIXME if endpoint halted, clear halt (and log) */
+	}
+
+	/* FIXME for unlink or fault handling tests, don't report
+	 * failure if retval is as we expected ...
+	 */
+	if (retval)
+		ERROR(tdev, "perform_sglist failed, "
+				"iterations left %d, status %d\n",
+				iterations, retval);
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* unqueued control message testing
+ *
+ * there's a nice set of device functional requirements in chapter 9 of the
+ * usb 2.0 spec, which we can apply to ANY device, even ones that don't use
+ * special test firmware.
+ *
+ * we know the device is configured (or suspended) by the time it's visible
+ * through usbfs.  we can't change that, so we won't test enumeration (which
+ * worked 'well enough' to get here, this time), power management (ditto),
+ * or remote wakeup (which needs human interaction).
+ */
+
+static unsigned realworld = 1;
+module_param(realworld, uint, 0);
+MODULE_PARM_DESC(realworld, "clear to demand stricter spec compliance");
+
+static int get_altsetting(struct usbtest_dev *dev)
+{
+	struct usb_interface	*iface = dev->intf;
+	struct usb_device	*udev = interface_to_usbdev(iface);
+	int			retval;
+
+	retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			USB_REQ_GET_INTERFACE, USB_DIR_IN|USB_RECIP_INTERFACE,
+			0, iface->altsetting[0].desc.bInterfaceNumber,
+			dev->buf, 1, USB_CTRL_GET_TIMEOUT);
+	switch (retval) {
+	case 1:
+		return dev->buf[0];
+	case 0:
+		retval = -ERANGE;
+		/* FALLTHROUGH */
+	default:
+		return retval;
+	}
+}
+
+static int set_altsetting(struct usbtest_dev *dev, int alternate)
+{
+	struct usb_interface		*iface = dev->intf;
+	struct usb_device		*udev;
+
+	if (alternate < 0 || alternate >= 256)
+		return -EINVAL;
+
+	udev = interface_to_usbdev(iface);
+	return usb_set_interface(udev,
+			iface->altsetting[0].desc.bInterfaceNumber,
+			alternate);
+}
+
+static int is_good_config(struct usbtest_dev *tdev, int len)
+{
+	struct usb_config_descriptor	*config;
+
+	if (len < sizeof(*config))
+		return 0;
+	config = (struct usb_config_descriptor *) tdev->buf;
+
+	switch (config->bDescriptorType) {
+	case USB_DT_CONFIG:
+	case USB_DT_OTHER_SPEED_CONFIG:
+		if (config->bLength != 9) {
+			ERROR(tdev, "bogus config descriptor length\n");
+			return 0;
+		}
+		/* this bit 'must be 1' but often isn't */
+		if (!realworld && !(config->bmAttributes & 0x80)) {
+			ERROR(tdev, "high bit of config attributes not set\n");
+			return 0;
+		}
+		if (config->bmAttributes & 0x1f) {	/* reserved == 0 */
+			ERROR(tdev, "reserved config bits set\n");
+			return 0;
+		}
+		break;
+	default:
+		return 0;
+	}
+
+	if (le16_to_cpu(config->wTotalLength) == len)	/* read it all */
+		return 1;
+	if (le16_to_cpu(config->wTotalLength) >= TBUF_SIZE)	/* max partial read */
+		return 1;
+	ERROR(tdev, "bogus config descriptor read size\n");
+	return 0;
+}
+
+static int is_good_ext(struct usbtest_dev *tdev, u8 *buf)
+{
+	struct usb_ext_cap_descriptor *ext;
+	u32 attr;
+
+	ext = (struct usb_ext_cap_descriptor *) buf;
+
+	if (ext->bLength != USB_DT_USB_EXT_CAP_SIZE) {
+		ERROR(tdev, "bogus usb 2.0 extension descriptor length\n");
+		return 0;
+	}
+
+	attr = le32_to_cpu(ext->bmAttributes);
+	/* bits[1:15] is used and others are reserved */
+	if (attr & ~0xfffe) {	/* reserved == 0 */
+		ERROR(tdev, "reserved bits set\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static int is_good_ss_cap(struct usbtest_dev *tdev, u8 *buf)
+{
+	struct usb_ss_cap_descriptor *ss;
+
+	ss = (struct usb_ss_cap_descriptor *) buf;
+
+	if (ss->bLength != USB_DT_USB_SS_CAP_SIZE) {
+		ERROR(tdev, "bogus superspeed device capability descriptor length\n");
+		return 0;
+	}
+
+	/*
+	 * only bit[1] of bmAttributes is used for LTM and others are
+	 * reserved
+	 */
+	if (ss->bmAttributes & ~0x02) {	/* reserved == 0 */
+		ERROR(tdev, "reserved bits set in bmAttributes\n");
+		return 0;
+	}
+
+	/* bits[0:3] of wSpeedSupported is used and others are reserved */
+	if (le16_to_cpu(ss->wSpeedSupported) & ~0x0f) {	/* reserved == 0 */
+		ERROR(tdev, "reserved bits set in wSpeedSupported\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static int is_good_con_id(struct usbtest_dev *tdev, u8 *buf)
+{
+	struct usb_ss_container_id_descriptor *con_id;
+
+	con_id = (struct usb_ss_container_id_descriptor *) buf;
+
+	if (con_id->bLength != USB_DT_USB_SS_CONTN_ID_SIZE) {
+		ERROR(tdev, "bogus container id descriptor length\n");
+		return 0;
+	}
+
+	if (con_id->bReserved) {	/* reserved == 0 */
+		ERROR(tdev, "reserved bits set\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+/* sanity test for standard requests working with usb_control_mesg() and some
+ * of the utility functions which use it.
+ *
+ * this doesn't test how endpoint halts behave or data toggles get set, since
+ * we won't do I/O to bulk/interrupt endpoints here (which is how to change
+ * halt or toggle).  toggle testing is impractical without support from hcds.
+ *
+ * this avoids failing devices linux would normally work with, by not testing
+ * config/altsetting operations for devices that only support their defaults.
+ * such devices rarely support those needless operations.
+ *
+ * NOTE that since this is a sanity test, it's not examining boundary cases
+ * to see if usbcore, hcd, and device all behave right.  such testing would
+ * involve varied read sizes and other operation sequences.
+ */
+static int ch9_postconfig(struct usbtest_dev *dev)
+{
+	struct usb_interface	*iface = dev->intf;
+	struct usb_device	*udev = interface_to_usbdev(iface);
+	int			i, alt, retval;
+
+	/* [9.2.3] if there's more than one altsetting, we need to be able to
+	 * set and get each one.  mostly trusts the descriptors from usbcore.
+	 */
+	for (i = 0; i < iface->num_altsetting; i++) {
+
+		/* 9.2.3 constrains the range here */
+		alt = iface->altsetting[i].desc.bAlternateSetting;
+		if (alt < 0 || alt >= iface->num_altsetting) {
+			dev_err(&iface->dev,
+					"invalid alt [%d].bAltSetting = %d\n",
+					i, alt);
+		}
+
+		/* [real world] get/set unimplemented if there's only one */
+		if (realworld && iface->num_altsetting == 1)
+			continue;
+
+		/* [9.4.10] set_interface */
+		retval = set_altsetting(dev, alt);
+		if (retval) {
+			dev_err(&iface->dev, "can't set_interface = %d, %d\n",
+					alt, retval);
+			return retval;
+		}
+
+		/* [9.4.4] get_interface always works */
+		retval = get_altsetting(dev);
+		if (retval != alt) {
+			dev_err(&iface->dev, "get alt should be %d, was %d\n",
+					alt, retval);
+			return (retval < 0) ? retval : -EDOM;
+		}
+
+	}
+
+	/* [real world] get_config unimplemented if there's only one */
+	if (!realworld || udev->descriptor.bNumConfigurations != 1) {
+		int	expected = udev->actconfig->desc.bConfigurationValue;
+
+		/* [9.4.2] get_configuration always works
+		 * ... although some cheap devices (like one TI Hub I've got)
+		 * won't return config descriptors except before set_config.
+		 */
+		retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				USB_REQ_GET_CONFIGURATION,
+				USB_DIR_IN | USB_RECIP_DEVICE,
+				0, 0, dev->buf, 1, USB_CTRL_GET_TIMEOUT);
+		if (retval != 1 || dev->buf[0] != expected) {
+			dev_err(&iface->dev, "get config --> %d %d (1 %d)\n",
+				retval, dev->buf[0], expected);
+			return (retval < 0) ? retval : -EDOM;
+		}
+	}
+
+	/* there's always [9.4.3] a device descriptor [9.6.1] */
+	retval = usb_get_descriptor(udev, USB_DT_DEVICE, 0,
+			dev->buf, sizeof(udev->descriptor));
+	if (retval != sizeof(udev->descriptor)) {
+		dev_err(&iface->dev, "dev descriptor --> %d\n", retval);
+		return (retval < 0) ? retval : -EDOM;
+	}
+
+	/*
+	 * there's always [9.4.3] a bos device descriptor [9.6.2] in USB
+	 * 3.0 spec
+	 */
+	if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0210) {
+		struct usb_bos_descriptor *bos = NULL;
+		struct usb_dev_cap_header *header = NULL;
+		unsigned total, num, length;
+		u8 *buf;
+
+		retval = usb_get_descriptor(udev, USB_DT_BOS, 0, dev->buf,
+				sizeof(*udev->bos->desc));
+		if (retval != sizeof(*udev->bos->desc)) {
+			dev_err(&iface->dev, "bos descriptor --> %d\n", retval);
+			return (retval < 0) ? retval : -EDOM;
+		}
+
+		bos = (struct usb_bos_descriptor *)dev->buf;
+		total = le16_to_cpu(bos->wTotalLength);
+		num = bos->bNumDeviceCaps;
+
+		if (total > TBUF_SIZE)
+			total = TBUF_SIZE;
+
+		/*
+		 * get generic device-level capability descriptors [9.6.2]
+		 * in USB 3.0 spec
+		 */
+		retval = usb_get_descriptor(udev, USB_DT_BOS, 0, dev->buf,
+				total);
+		if (retval != total) {
+			dev_err(&iface->dev, "bos descriptor set --> %d\n",
+					retval);
+			return (retval < 0) ? retval : -EDOM;
+		}
+
+		length = sizeof(*udev->bos->desc);
+		buf = dev->buf;
+		for (i = 0; i < num; i++) {
+			buf += length;
+			if (buf + sizeof(struct usb_dev_cap_header) >
+					dev->buf + total)
+				break;
+
+			header = (struct usb_dev_cap_header *)buf;
+			length = header->bLength;
+
+			if (header->bDescriptorType !=
+					USB_DT_DEVICE_CAPABILITY) {
+				dev_warn(&udev->dev, "not device capability descriptor, skip\n");
+				continue;
+			}
+
+			switch (header->bDevCapabilityType) {
+			case USB_CAP_TYPE_EXT:
+				if (buf + USB_DT_USB_EXT_CAP_SIZE >
+						dev->buf + total ||
+						!is_good_ext(dev, buf)) {
+					dev_err(&iface->dev, "bogus usb 2.0 extension descriptor\n");
+					return -EDOM;
+				}
+				break;
+			case USB_SS_CAP_TYPE:
+				if (buf + USB_DT_USB_SS_CAP_SIZE >
+						dev->buf + total ||
+						!is_good_ss_cap(dev, buf)) {
+					dev_err(&iface->dev, "bogus superspeed device capability descriptor\n");
+					return -EDOM;
+				}
+				break;
+			case CONTAINER_ID_TYPE:
+				if (buf + USB_DT_USB_SS_CONTN_ID_SIZE >
+						dev->buf + total ||
+						!is_good_con_id(dev, buf)) {
+					dev_err(&iface->dev, "bogus container id descriptor\n");
+					return -EDOM;
+				}
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	/* there's always [9.4.3] at least one config descriptor [9.6.3] */
+	for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
+		retval = usb_get_descriptor(udev, USB_DT_CONFIG, i,
+				dev->buf, TBUF_SIZE);
+		if (!is_good_config(dev, retval)) {
+			dev_err(&iface->dev,
+					"config [%d] descriptor --> %d\n",
+					i, retval);
+			return (retval < 0) ? retval : -EDOM;
+		}
+
+		/* FIXME cross-checking udev->config[i] to make sure usbcore
+		 * parsed it right (etc) would be good testing paranoia
+		 */
+	}
+
+	/* and sometimes [9.2.6.6] speed dependent descriptors */
+	if (le16_to_cpu(udev->descriptor.bcdUSB) == 0x0200) {
+		struct usb_qualifier_descriptor *d = NULL;
+
+		/* device qualifier [9.6.2] */
+		retval = usb_get_descriptor(udev,
+				USB_DT_DEVICE_QUALIFIER, 0, dev->buf,
+				sizeof(struct usb_qualifier_descriptor));
+		if (retval == -EPIPE) {
+			if (udev->speed == USB_SPEED_HIGH) {
+				dev_err(&iface->dev,
+						"hs dev qualifier --> %d\n",
+						retval);
+				return (retval < 0) ? retval : -EDOM;
+			}
+			/* usb2.0 but not high-speed capable; fine */
+		} else if (retval != sizeof(struct usb_qualifier_descriptor)) {
+			dev_err(&iface->dev, "dev qualifier --> %d\n", retval);
+			return (retval < 0) ? retval : -EDOM;
+		} else
+			d = (struct usb_qualifier_descriptor *) dev->buf;
+
+		/* might not have [9.6.2] any other-speed configs [9.6.4] */
+		if (d) {
+			unsigned max = d->bNumConfigurations;
+			for (i = 0; i < max; i++) {
+				retval = usb_get_descriptor(udev,
+					USB_DT_OTHER_SPEED_CONFIG, i,
+					dev->buf, TBUF_SIZE);
+				if (!is_good_config(dev, retval)) {
+					dev_err(&iface->dev,
+						"other speed config --> %d\n",
+						retval);
+					return (retval < 0) ? retval : -EDOM;
+				}
+			}
+		}
+	}
+	/* FIXME fetch strings from at least the device descriptor */
+
+	/* [9.4.5] get_status always works */
+	retval = usb_get_status(udev, USB_RECIP_DEVICE, 0, dev->buf);
+	if (retval) {
+		dev_err(&iface->dev, "get dev status --> %d\n", retval);
+		return retval;
+	}
+
+	/* FIXME configuration.bmAttributes says if we could try to set/clear
+	 * the device's remote wakeup feature ... if we can, test that here
+	 */
+
+	retval = usb_get_status(udev, USB_RECIP_INTERFACE,
+			iface->altsetting[0].desc.bInterfaceNumber, dev->buf);
+	if (retval) {
+		dev_err(&iface->dev, "get interface status --> %d\n", retval);
+		return retval;
+	}
+	/* FIXME get status for each endpoint in the interface */
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* use ch9 requests to test whether:
+ *   (a) queues work for control, keeping N subtests queued and
+ *       active (auto-resubmit) for M loops through the queue.
+ *   (b) protocol stalls (control-only) will autorecover.
+ *       it's not like bulk/intr; no halt clearing.
+ *   (c) short control reads are reported and handled.
+ *   (d) queues are always processed in-order
+ */
+
+struct ctrl_ctx {
+	spinlock_t		lock;
+	struct usbtest_dev	*dev;
+	struct completion	complete;
+	unsigned		count;
+	unsigned		pending;
+	int			status;
+	struct urb		**urb;
+	struct usbtest_param	*param;
+	int			last;
+};
+
+#define NUM_SUBCASES	16		/* how many test subcases here? */
+
+struct subcase {
+	struct usb_ctrlrequest	setup;
+	int			number;
+	int			expected;
+};
+
+static void ctrl_complete(struct urb *urb)
+{
+	struct ctrl_ctx		*ctx = urb->context;
+	struct usb_ctrlrequest	*reqp;
+	struct subcase		*subcase;
+	int			status = urb->status;
+
+	reqp = (struct usb_ctrlrequest *)urb->setup_packet;
+	subcase = container_of(reqp, struct subcase, setup);
+
+	spin_lock(&ctx->lock);
+	ctx->count--;
+	ctx->pending--;
+
+	/* queue must transfer and complete in fifo order, unless
+	 * usb_unlink_urb() is used to unlink something not at the
+	 * physical queue head (not tested).
+	 */
+	if (subcase->number > 0) {
+		if ((subcase->number - ctx->last) != 1) {
+			ERROR(ctx->dev,
+				"subcase %d completed out of order, last %d\n",
+				subcase->number, ctx->last);
+			status = -EDOM;
+			ctx->last = subcase->number;
+			goto error;
+		}
+	}
+	ctx->last = subcase->number;
+
+	/* succeed or fault in only one way? */
+	if (status == subcase->expected)
+		status = 0;
+
+	/* async unlink for cleanup? */
+	else if (status != -ECONNRESET) {
+
+		/* some faults are allowed, not required */
+		if (subcase->expected > 0 && (
+			  ((status == -subcase->expected	/* happened */
+			   || status == 0))))			/* didn't */
+			status = 0;
+		/* sometimes more than one fault is allowed */
+		else if (subcase->number == 12 && status == -EPIPE)
+			status = 0;
+		else
+			ERROR(ctx->dev, "subtest %d error, status %d\n",
+					subcase->number, status);
+	}
+
+	/* unexpected status codes mean errors; ideally, in hardware */
+	if (status) {
+error:
+		if (ctx->status == 0) {
+			int		i;
+
+			ctx->status = status;
+			ERROR(ctx->dev, "control queue %02x.%02x, err %d, "
+					"%d left, subcase %d, len %d/%d\n",
+					reqp->bRequestType, reqp->bRequest,
+					status, ctx->count, subcase->number,
+					urb->actual_length,
+					urb->transfer_buffer_length);
+
+			/* FIXME this "unlink everything" exit route should
+			 * be a separate test case.
+			 */
+
+			/* unlink whatever's still pending */
+			for (i = 1; i < ctx->param->sglen; i++) {
+				struct urb *u = ctx->urb[
+							(i + subcase->number)
+							% ctx->param->sglen];
+
+				if (u == urb || !u->dev)
+					continue;
+				spin_unlock(&ctx->lock);
+				status = usb_unlink_urb(u);
+				spin_lock(&ctx->lock);
+				switch (status) {
+				case -EINPROGRESS:
+				case -EBUSY:
+				case -EIDRM:
+					continue;
+				default:
+					ERROR(ctx->dev, "urb unlink --> %d\n",
+							status);
+				}
+			}
+			status = ctx->status;
+		}
+	}
+
+	/* resubmit if we need to, else mark this as done */
+	if ((status == 0) && (ctx->pending < ctx->count)) {
+		status = usb_submit_urb(urb, GFP_ATOMIC);
+		if (status != 0) {
+			ERROR(ctx->dev,
+				"can't resubmit ctrl %02x.%02x, err %d\n",
+				reqp->bRequestType, reqp->bRequest, status);
+			urb->dev = NULL;
+		} else
+			ctx->pending++;
+	} else
+		urb->dev = NULL;
+
+	/* signal completion when nothing's queued */
+	if (ctx->pending == 0)
+		complete(&ctx->complete);
+	spin_unlock(&ctx->lock);
+}
+
+static int
+test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
+{
+	struct usb_device	*udev = testdev_to_usbdev(dev);
+	struct urb		**urb;
+	struct ctrl_ctx		context;
+	int			i;
+
+	if (param->sglen == 0 || param->iterations > UINT_MAX / param->sglen)
+		return -EOPNOTSUPP;
+
+	spin_lock_init(&context.lock);
+	context.dev = dev;
+	init_completion(&context.complete);
+	context.count = param->sglen * param->iterations;
+	context.pending = 0;
+	context.status = -ENOMEM;
+	context.param = param;
+	context.last = -1;
+
+	/* allocate and init the urbs we'll queue.
+	 * as with bulk/intr sglists, sglen is the queue depth; it also
+	 * controls which subtests run (more tests than sglen) or rerun.
+	 */
+	urb = kcalloc(param->sglen, sizeof(struct urb *), GFP_KERNEL);
+	if (!urb)
+		return -ENOMEM;
+	for (i = 0; i < param->sglen; i++) {
+		int			pipe = usb_rcvctrlpipe(udev, 0);
+		unsigned		len;
+		struct urb		*u;
+		struct usb_ctrlrequest	req;
+		struct subcase		*reqp;
+
+		/* sign of this variable means:
+		 *  -: tested code must return this (negative) error code
+		 *  +: tested code may return this (negative too) error code
+		 */
+		int			expected = 0;
+
+		/* requests here are mostly expected to succeed on any
+		 * device, but some are chosen to trigger protocol stalls
+		 * or short reads.
+		 */
+		memset(&req, 0, sizeof(req));
+		req.bRequest = USB_REQ_GET_DESCRIPTOR;
+		req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;
+
+		switch (i % NUM_SUBCASES) {
+		case 0:		/* get device descriptor */
+			req.wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+			len = sizeof(struct usb_device_descriptor);
+			break;
+		case 1:		/* get first config descriptor (only) */
+			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
+			len = sizeof(struct usb_config_descriptor);
+			break;
+		case 2:		/* get altsetting (OFTEN STALLS) */
+			req.bRequest = USB_REQ_GET_INTERFACE;
+			req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE;
+			/* index = 0 means first interface */
+			len = 1;
+			expected = EPIPE;
+			break;
+		case 3:		/* get interface status */
+			req.bRequest = USB_REQ_GET_STATUS;
+			req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE;
+			/* interface 0 */
+			len = 2;
+			break;
+		case 4:		/* get device status */
+			req.bRequest = USB_REQ_GET_STATUS;
+			req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE;
+			len = 2;
+			break;
+		case 5:		/* get device qualifier (MAY STALL) */
+			req.wValue = cpu_to_le16 (USB_DT_DEVICE_QUALIFIER << 8);
+			len = sizeof(struct usb_qualifier_descriptor);
+			if (udev->speed != USB_SPEED_HIGH)
+				expected = EPIPE;
+			break;
+		case 6:		/* get first config descriptor, plus interface */
+			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
+			len = sizeof(struct usb_config_descriptor);
+			len += sizeof(struct usb_interface_descriptor);
+			break;
+		case 7:		/* get interface descriptor (ALWAYS STALLS) */
+			req.wValue = cpu_to_le16 (USB_DT_INTERFACE << 8);
+			/* interface == 0 */
+			len = sizeof(struct usb_interface_descriptor);
+			expected = -EPIPE;
+			break;
+		/* NOTE: two consecutive stalls in the queue here.
+		 *  that tests fault recovery a bit more aggressively. */
+		case 8:		/* clear endpoint halt (MAY STALL) */
+			req.bRequest = USB_REQ_CLEAR_FEATURE;
+			req.bRequestType = USB_RECIP_ENDPOINT;
+			/* wValue 0 == ep halt */
+			/* wIndex 0 == ep0 (shouldn't halt!) */
+			len = 0;
+			pipe = usb_sndctrlpipe(udev, 0);
+			expected = EPIPE;
+			break;
+		case 9:		/* get endpoint status */
+			req.bRequest = USB_REQ_GET_STATUS;
+			req.bRequestType = USB_DIR_IN|USB_RECIP_ENDPOINT;
+			/* endpoint 0 */
+			len = 2;
+			break;
+		case 10:	/* trigger short read (EREMOTEIO) */
+			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
+			len = 1024;
+			expected = -EREMOTEIO;
+			break;
+		/* NOTE: two consecutive _different_ faults in the queue. */
+		case 11:	/* get endpoint descriptor (ALWAYS STALLS) */
+			req.wValue = cpu_to_le16(USB_DT_ENDPOINT << 8);
+			/* endpoint == 0 */
+			len = sizeof(struct usb_interface_descriptor);
+			expected = EPIPE;
+			break;
+		/* NOTE: sometimes even a third fault in the queue! */
+		case 12:	/* get string 0 descriptor (MAY STALL) */
+			req.wValue = cpu_to_le16(USB_DT_STRING << 8);
+			/* string == 0, for language IDs */
+			len = sizeof(struct usb_interface_descriptor);
+			/* may succeed when > 4 languages */
+			expected = EREMOTEIO;	/* or EPIPE, if no strings */
+			break;
+		case 13:	/* short read, resembling case 10 */
+			req.wValue = cpu_to_le16((USB_DT_CONFIG << 8) | 0);
+			/* last data packet "should" be DATA1, not DATA0 */
+			if (udev->speed == USB_SPEED_SUPER)
+				len = 1024 - 512;
+			else
+				len = 1024 - udev->descriptor.bMaxPacketSize0;
+			expected = -EREMOTEIO;
+			break;
+		case 14:	/* short read; try to fill the last packet */
+			req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0);
+			/* device descriptor size == 18 bytes */
+			len = udev->descriptor.bMaxPacketSize0;
+			if (udev->speed == USB_SPEED_SUPER)
+				len = 512;
+			switch (len) {
+			case 8:
+				len = 24;
+				break;
+			case 16:
+				len = 32;
+				break;
+			}
+			expected = -EREMOTEIO;
+			break;
+		case 15:
+			req.wValue = cpu_to_le16(USB_DT_BOS << 8);
+			if (udev->bos)
+				len = le16_to_cpu(udev->bos->desc->wTotalLength);
+			else
+				len = sizeof(struct usb_bos_descriptor);
+			if (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0201)
+				expected = -EPIPE;
+			break;
+		default:
+			ERROR(dev, "bogus number of ctrl queue testcases!\n");
+			context.status = -EINVAL;
+			goto cleanup;
+		}
+		req.wLength = cpu_to_le16(len);
+		urb[i] = u = simple_alloc_urb(udev, pipe, len, 0);
+		if (!u)
+			goto cleanup;
+
+		reqp = kmalloc(sizeof(*reqp), GFP_KERNEL);
+		if (!reqp)
+			goto cleanup;
+		reqp->setup = req;
+		reqp->number = i % NUM_SUBCASES;
+		reqp->expected = expected;
+		u->setup_packet = (char *) &reqp->setup;
+
+		u->context = &context;
+		u->complete = ctrl_complete;
+	}
+
+	/* queue the urbs */
+	context.urb = urb;
+	spin_lock_irq(&context.lock);
+	for (i = 0; i < param->sglen; i++) {
+		context.status = usb_submit_urb(urb[i], GFP_ATOMIC);
+		if (context.status != 0) {
+			ERROR(dev, "can't submit urb[%d], status %d\n",
+					i, context.status);
+			context.count = context.pending;
+			break;
+		}
+		context.pending++;
+	}
+	spin_unlock_irq(&context.lock);
+
+	/* FIXME  set timer and time out; provide a disconnect hook */
+
+	/* wait for the last one to complete */
+	if (context.pending > 0)
+		wait_for_completion(&context.complete);
+
+cleanup:
+	for (i = 0; i < param->sglen; i++) {
+		if (!urb[i])
+			continue;
+		urb[i]->dev = udev;
+		kfree(urb[i]->setup_packet);
+		simple_free_urb(urb[i]);
+	}
+	kfree(urb);
+	return context.status;
+}
+#undef NUM_SUBCASES
+
+
+/*-------------------------------------------------------------------------*/
+
+static void unlink1_callback(struct urb *urb)
+{
+	int	status = urb->status;
+
+	/* we "know" -EPIPE (stall) never happens */
+	if (!status)
+		status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		urb->status = status;
+		complete(urb->context);
+	}
+}
+
+static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async)
+{
+	struct urb		*urb;
+	struct completion	completion;
+	int			retval = 0;
+
+	init_completion(&completion);
+	urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size, 0);
+	if (!urb)
+		return -ENOMEM;
+	urb->context = &completion;
+	urb->complete = unlink1_callback;
+
+	if (usb_pipeout(urb->pipe)) {
+		simple_fill_buf(urb);
+		urb->transfer_flags |= URB_ZERO_PACKET;
+	}
+
+	/* keep the endpoint busy.  there are lots of hc/hcd-internal
+	 * states, and testing should get to all of them over time.
+	 *
+	 * FIXME want additional tests for when endpoint is STALLing
+	 * due to errors, or is just NAKing requests.
+	 */
+	retval = usb_submit_urb(urb, GFP_KERNEL);
+	if (retval != 0) {
+		dev_err(&dev->intf->dev, "submit fail %d\n", retval);
+		return retval;
+	}
+
+	/* unlinking that should always work.  variable delay tests more
+	 * hcd states and code paths, even with little other system load.
+	 */
+	msleep(jiffies % (2 * INTERRUPT_RATE));
+	if (async) {
+		while (!completion_done(&completion)) {
+			retval = usb_unlink_urb(urb);
+
+			if (retval == 0 && usb_pipein(urb->pipe))
+				retval = simple_check_buf(dev, urb);
+
+			switch (retval) {
+			case -EBUSY:
+			case -EIDRM:
+				/* we can't unlink urbs while they're completing
+				 * or if they've completed, and we haven't
+				 * resubmitted. "normal" drivers would prevent
+				 * resubmission, but since we're testing unlink
+				 * paths, we can't.
+				 */
+				ERROR(dev, "unlink retry\n");
+				continue;
+			case 0:
+			case -EINPROGRESS:
+				break;
+
+			default:
+				dev_err(&dev->intf->dev,
+					"unlink fail %d\n", retval);
+				return retval;
+			}
+
+			break;
+		}
+	} else
+		usb_kill_urb(urb);
+
+	wait_for_completion(&completion);
+	retval = urb->status;
+	simple_free_urb(urb);
+
+	if (async)
+		return (retval == -ECONNRESET) ? 0 : retval - 1000;
+	else
+		return (retval == -ENOENT || retval == -EPERM) ?
+				0 : retval - 2000;
+}
+
+static int unlink_simple(struct usbtest_dev *dev, int pipe, int len)
+{
+	int			retval = 0;
+
+	/* test sync and async paths */
+	retval = unlink1(dev, pipe, len, 1);
+	if (!retval)
+		retval = unlink1(dev, pipe, len, 0);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct queued_ctx {
+	struct completion	complete;
+	atomic_t		pending;
+	unsigned		num;
+	int			status;
+	struct urb		**urbs;
+};
+
+static void unlink_queued_callback(struct urb *urb)
+{
+	int			status = urb->status;
+	struct queued_ctx	*ctx = urb->context;
+
+	if (ctx->status)
+		goto done;
+	if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) {
+		if (status == -ECONNRESET)
+			goto done;
+		/* What error should we report if the URB completed normally? */
+	}
+	if (status != 0)
+		ctx->status = status;
+
+ done:
+	if (atomic_dec_and_test(&ctx->pending))
+		complete(&ctx->complete);
+}
+
+static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num,
+		unsigned size)
+{
+	struct queued_ctx	ctx;
+	struct usb_device	*udev = testdev_to_usbdev(dev);
+	void			*buf;
+	dma_addr_t		buf_dma;
+	int			i;
+	int			retval = -ENOMEM;
+
+	init_completion(&ctx.complete);
+	atomic_set(&ctx.pending, 1);	/* One more than the actual value */
+	ctx.num = num;
+	ctx.status = 0;
+
+	buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma);
+	if (!buf)
+		return retval;
+	memset(buf, 0, size);
+
+	/* Allocate and init the urbs we'll queue */
+	ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL);
+	if (!ctx.urbs)
+		goto free_buf;
+	for (i = 0; i < num; i++) {
+		ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (!ctx.urbs[i])
+			goto free_urbs;
+		usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size,
+				unlink_queued_callback, &ctx);
+		ctx.urbs[i]->transfer_dma = buf_dma;
+		ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+
+		if (usb_pipeout(ctx.urbs[i]->pipe)) {
+			simple_fill_buf(ctx.urbs[i]);
+			ctx.urbs[i]->transfer_flags |= URB_ZERO_PACKET;
+		}
+	}
+
+	/* Submit all the URBs and then unlink URBs num - 4 and num - 2. */
+	for (i = 0; i < num; i++) {
+		atomic_inc(&ctx.pending);
+		retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL);
+		if (retval != 0) {
+			dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n",
+					i, retval);
+			atomic_dec(&ctx.pending);
+			ctx.status = retval;
+			break;
+		}
+	}
+	if (i == num) {
+		usb_unlink_urb(ctx.urbs[num - 4]);
+		usb_unlink_urb(ctx.urbs[num - 2]);
+	} else {
+		while (--i >= 0)
+			usb_unlink_urb(ctx.urbs[i]);
+	}
+
+	if (atomic_dec_and_test(&ctx.pending))		/* The extra count */
+		complete(&ctx.complete);
+	wait_for_completion(&ctx.complete);
+	retval = ctx.status;
+
+ free_urbs:
+	for (i = 0; i < num; i++)
+		usb_free_urb(ctx.urbs[i]);
+	kfree(ctx.urbs);
+ free_buf:
+	usb_free_coherent(udev, size, buf, buf_dma);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
+{
+	int	retval;
+	u16	status;
+
+	/* shouldn't look or act halted */
+	retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
+	if (retval < 0) {
+		ERROR(tdev, "ep %02x couldn't get no-halt status, %d\n",
+				ep, retval);
+		return retval;
+	}
+	if (status != 0) {
+		ERROR(tdev, "ep %02x bogus status: %04x != 0\n", ep, status);
+		return -EINVAL;
+	}
+	retval = simple_io(tdev, urb, 1, 0, 0, __func__);
+	if (retval != 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
+{
+	int	retval;
+	u16	status;
+
+	/* should look and act halted */
+	retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
+	if (retval < 0) {
+		ERROR(tdev, "ep %02x couldn't get halt status, %d\n",
+				ep, retval);
+		return retval;
+	}
+	if (status != 1) {
+		ERROR(tdev, "ep %02x bogus status: %04x != 1\n", ep, status);
+		return -EINVAL;
+	}
+	retval = simple_io(tdev, urb, 1, 0, -EPIPE, __func__);
+	if (retval != -EPIPE)
+		return -EINVAL;
+	retval = simple_io(tdev, urb, 1, 0, -EPIPE, "verify_still_halted");
+	if (retval != -EPIPE)
+		return -EINVAL;
+	return 0;
+}
+
+static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb)
+{
+	int	retval;
+
+	/* shouldn't look or act halted now */
+	retval = verify_not_halted(tdev, ep, urb);
+	if (retval < 0)
+		return retval;
+
+	/* set halt (protocol test only), verify it worked */
+	retval = usb_control_msg(urb->dev, usb_sndctrlpipe(urb->dev, 0),
+			USB_REQ_SET_FEATURE, USB_RECIP_ENDPOINT,
+			USB_ENDPOINT_HALT, ep,
+			NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (retval < 0) {
+		ERROR(tdev, "ep %02x couldn't set halt, %d\n", ep, retval);
+		return retval;
+	}
+	retval = verify_halted(tdev, ep, urb);
+	if (retval < 0) {
+		int ret;
+
+		/* clear halt anyways, else further tests will fail */
+		ret = usb_clear_halt(urb->dev, urb->pipe);
+		if (ret)
+			ERROR(tdev, "ep %02x couldn't clear halt, %d\n",
+			      ep, ret);
+
+		return retval;
+	}
+
+	/* clear halt (tests API + protocol), verify it worked */
+	retval = usb_clear_halt(urb->dev, urb->pipe);
+	if (retval < 0) {
+		ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval);
+		return retval;
+	}
+	retval = verify_not_halted(tdev, ep, urb);
+	if (retval < 0)
+		return retval;
+
+	/* NOTE:  could also verify SET_INTERFACE clear halts ... */
+
+	return 0;
+}
+
+static int halt_simple(struct usbtest_dev *dev)
+{
+	int			ep;
+	int			retval = 0;
+	struct urb		*urb;
+	struct usb_device	*udev = testdev_to_usbdev(dev);
+
+	if (udev->speed == USB_SPEED_SUPER)
+		urb = simple_alloc_urb(udev, 0, 1024, 0);
+	else
+		urb = simple_alloc_urb(udev, 0, 512, 0);
+	if (urb == NULL)
+		return -ENOMEM;
+
+	if (dev->in_pipe) {
+		ep = usb_pipeendpoint(dev->in_pipe) | USB_DIR_IN;
+		urb->pipe = dev->in_pipe;
+		retval = test_halt(dev, ep, urb);
+		if (retval < 0)
+			goto done;
+	}
+
+	if (dev->out_pipe) {
+		ep = usb_pipeendpoint(dev->out_pipe);
+		urb->pipe = dev->out_pipe;
+		retval = test_halt(dev, ep, urb);
+	}
+done:
+	simple_free_urb(urb);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Control OUT tests use the vendor control requests from Intel's
+ * USB 2.0 compliance test device:  write a buffer, read it back.
+ *
+ * Intel's spec only _requires_ that it work for one packet, which
+ * is pretty weak.   Some HCDs place limits here; most devices will
+ * need to be able to handle more than one OUT data packet.  We'll
+ * try whatever we're told to try.
+ */
+static int ctrl_out(struct usbtest_dev *dev,
+		unsigned count, unsigned length, unsigned vary, unsigned offset)
+{
+	unsigned		i, j, len;
+	int			retval;
+	u8			*buf;
+	char			*what = "?";
+	struct usb_device	*udev;
+
+	if (length < 1 || length > 0xffff || vary >= length)
+		return -EINVAL;
+
+	buf = kmalloc(length + offset, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf += offset;
+	udev = testdev_to_usbdev(dev);
+	len = length;
+	retval = 0;
+
+	/* NOTE:  hardware might well act differently if we pushed it
+	 * with lots back-to-back queued requests.
+	 */
+	for (i = 0; i < count; i++) {
+		/* write patterned data */
+		for (j = 0; j < len; j++)
+			buf[j] = (u8)(i + j);
+		retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				0x5b, USB_DIR_OUT|USB_TYPE_VENDOR,
+				0, 0, buf, len, USB_CTRL_SET_TIMEOUT);
+		if (retval != len) {
+			what = "write";
+			if (retval >= 0) {
+				ERROR(dev, "ctrl_out, wlen %d (expected %d)\n",
+						retval, len);
+				retval = -EBADMSG;
+			}
+			break;
+		}
+
+		/* read it back -- assuming nothing intervened!!  */
+		retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				0x5c, USB_DIR_IN|USB_TYPE_VENDOR,
+				0, 0, buf, len, USB_CTRL_GET_TIMEOUT);
+		if (retval != len) {
+			what = "read";
+			if (retval >= 0) {
+				ERROR(dev, "ctrl_out, rlen %d (expected %d)\n",
+						retval, len);
+				retval = -EBADMSG;
+			}
+			break;
+		}
+
+		/* fail if we can't verify */
+		for (j = 0; j < len; j++) {
+			if (buf[j] != (u8)(i + j)) {
+				ERROR(dev, "ctrl_out, byte %d is %d not %d\n",
+					j, buf[j], (u8)(i + j));
+				retval = -EBADMSG;
+				break;
+			}
+		}
+		if (retval < 0) {
+			what = "verify";
+			break;
+		}
+
+		len += vary;
+
+		/* [real world] the "zero bytes IN" case isn't really used.
+		 * hardware can easily trip up in this weird case, since its
+		 * status stage is IN, not OUT like other ep0in transfers.
+		 */
+		if (len > length)
+			len = realworld ? 1 : 0;
+	}
+
+	if (retval < 0)
+		ERROR(dev, "ctrl_out %s failed, code %d, count %d\n",
+			what, retval, i);
+
+	kfree(buf - offset);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ISO/BULK tests ... mimics common usage
+ *  - buffer length is split into N packets (mostly maxpacket sized)
+ *  - multi-buffers according to sglen
+ */
+
+struct transfer_context {
+	unsigned		count;
+	unsigned		pending;
+	spinlock_t		lock;
+	struct completion	done;
+	int			submit_error;
+	unsigned long		errors;
+	unsigned long		packet_count;
+	struct usbtest_dev	*dev;
+	bool			is_iso;
+};
+
+static void complicated_callback(struct urb *urb)
+{
+	struct transfer_context	*ctx = urb->context;
+
+	spin_lock(&ctx->lock);
+	ctx->count--;
+
+	ctx->packet_count += urb->number_of_packets;
+	if (urb->error_count > 0)
+		ctx->errors += urb->error_count;
+	else if (urb->status != 0)
+		ctx->errors += (ctx->is_iso ? urb->number_of_packets : 1);
+	else if (urb->actual_length != urb->transfer_buffer_length)
+		ctx->errors++;
+	else if (check_guard_bytes(ctx->dev, urb) != 0)
+		ctx->errors++;
+
+	if (urb->status == 0 && ctx->count > (ctx->pending - 1)
+			&& !ctx->submit_error) {
+		int status = usb_submit_urb(urb, GFP_ATOMIC);
+		switch (status) {
+		case 0:
+			goto done;
+		default:
+			dev_err(&ctx->dev->intf->dev,
+					"iso resubmit err %d\n",
+					status);
+			/* FALLTHROUGH */
+		case -ENODEV:			/* disconnected */
+		case -ESHUTDOWN:		/* endpoint disabled */
+			ctx->submit_error = 1;
+			break;
+		}
+	}
+
+	ctx->pending--;
+	if (ctx->pending == 0) {
+		if (ctx->errors)
+			dev_err(&ctx->dev->intf->dev,
+				"iso test, %lu errors out of %lu\n",
+				ctx->errors, ctx->packet_count);
+		complete(&ctx->done);
+	}
+done:
+	spin_unlock(&ctx->lock);
+}
+
+static struct urb *iso_alloc_urb(
+	struct usb_device	*udev,
+	int			pipe,
+	struct usb_endpoint_descriptor	*desc,
+	long			bytes,
+	unsigned offset
+)
+{
+	struct urb		*urb;
+	unsigned		i, maxp, packets;
+
+	if (bytes < 0 || !desc)
+		return NULL;
+	maxp = 0x7ff & usb_endpoint_maxp(desc);
+	maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11));
+	packets = DIV_ROUND_UP(bytes, maxp);
+
+	urb = usb_alloc_urb(packets, GFP_KERNEL);
+	if (!urb)
+		return urb;
+	urb->dev = udev;
+	urb->pipe = pipe;
+
+	urb->number_of_packets = packets;
+	urb->transfer_buffer_length = bytes;
+	urb->transfer_buffer = usb_alloc_coherent(udev, bytes + offset,
+							GFP_KERNEL,
+							&urb->transfer_dma);
+	if (!urb->transfer_buffer) {
+		usb_free_urb(urb);
+		return NULL;
+	}
+	if (offset) {
+		memset(urb->transfer_buffer, GUARD_BYTE, offset);
+		urb->transfer_buffer += offset;
+		urb->transfer_dma += offset;
+	}
+	/* For inbound transfers use guard byte so that test fails if
+		data not correctly copied */
+	memset(urb->transfer_buffer,
+			usb_pipein(urb->pipe) ? GUARD_BYTE : 0,
+			bytes);
+
+	for (i = 0; i < packets; i++) {
+		/* here, only the last packet will be short */
+		urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp);
+		bytes -= urb->iso_frame_desc[i].length;
+
+		urb->iso_frame_desc[i].offset = maxp * i;
+	}
+
+	urb->complete = complicated_callback;
+	/* urb->context = SET BY CALLER */
+	urb->interval = 1 << (desc->bInterval - 1);
+	urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+	return urb;
+}
+
+static int
+test_queue(struct usbtest_dev *dev, struct usbtest_param *param,
+		int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
+{
+	struct transfer_context	context;
+	struct usb_device	*udev;
+	unsigned		i;
+	unsigned long		packets = 0;
+	int			status = 0;
+	struct urb		*urbs[param->sglen];
+
+	memset(&context, 0, sizeof(context));
+	context.count = param->iterations * param->sglen;
+	context.dev = dev;
+	context.is_iso = !!desc;
+	init_completion(&context.done);
+	spin_lock_init(&context.lock);
+
+	udev = testdev_to_usbdev(dev);
+
+	for (i = 0; i < param->sglen; i++) {
+		if (context.is_iso)
+			urbs[i] = iso_alloc_urb(udev, pipe, desc,
+					param->length, offset);
+		else
+			urbs[i] = complicated_alloc_urb(udev, pipe,
+					param->length, 0);
+
+		if (!urbs[i]) {
+			status = -ENOMEM;
+			goto fail;
+		}
+		packets += urbs[i]->number_of_packets;
+		urbs[i]->context = &context;
+	}
+	packets *= param->iterations;
+
+	if (context.is_iso) {
+		dev_info(&dev->intf->dev,
+			"iso period %d %sframes, wMaxPacket %d, transactions: %d\n",
+			1 << (desc->bInterval - 1),
+			(udev->speed == USB_SPEED_HIGH) ? "micro" : "",
+			usb_endpoint_maxp(desc) & 0x7ff,
+			1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)));
+
+		dev_info(&dev->intf->dev,
+			"total %lu msec (%lu packets)\n",
+			(packets * (1 << (desc->bInterval - 1)))
+				/ ((udev->speed == USB_SPEED_HIGH) ? 8 : 1),
+			packets);
+	}
+
+	spin_lock_irq(&context.lock);
+	for (i = 0; i < param->sglen; i++) {
+		++context.pending;
+		status = usb_submit_urb(urbs[i], GFP_ATOMIC);
+		if (status < 0) {
+			ERROR(dev, "submit iso[%d], error %d\n", i, status);
+			if (i == 0) {
+				spin_unlock_irq(&context.lock);
+				goto fail;
+			}
+
+			simple_free_urb(urbs[i]);
+			urbs[i] = NULL;
+			context.pending--;
+			context.submit_error = 1;
+			break;
+		}
+	}
+	spin_unlock_irq(&context.lock);
+
+	wait_for_completion(&context.done);
+
+	for (i = 0; i < param->sglen; i++) {
+		if (urbs[i])
+			simple_free_urb(urbs[i]);
+	}
+	/*
+	 * Isochronous transfers are expected to fail sometimes.  As an
+	 * arbitrary limit, we will report an error if any submissions
+	 * fail or if the transfer failure rate is > 10%.
+	 */
+	if (status != 0)
+		;
+	else if (context.submit_error)
+		status = -EACCES;
+	else if (context.errors >
+			(context.is_iso ? context.packet_count / 10 : 0))
+		status = -EIO;
+	return status;
+
+fail:
+	for (i = 0; i < param->sglen; i++) {
+		if (urbs[i])
+			simple_free_urb(urbs[i]);
+	}
+	return status;
+}
+
+static int test_unaligned_bulk(
+	struct usbtest_dev *tdev,
+	int pipe,
+	unsigned length,
+	int iterations,
+	unsigned transfer_flags,
+	const char *label)
+{
+	int retval;
+	struct urb *urb = usbtest_alloc_urb(testdev_to_usbdev(tdev),
+			pipe, length, transfer_flags, 1, 0, simple_callback);
+
+	if (!urb)
+		return -ENOMEM;
+
+	retval = simple_io(tdev, urb, iterations, 0, 0, label);
+	simple_free_urb(urb);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We only have this one interface to user space, through usbfs.
+ * User mode code can scan usbfs to find N different devices (maybe on
+ * different busses) to use when testing, and allocate one thread per
+ * test.  So discovery is simplified, and we have no device naming issues.
+ *
+ * Don't use these only as stress/load tests.  Use them along with with
+ * other USB bus activity:  plugging, unplugging, mousing, mp3 playback,
+ * video capture, and so on.  Run different tests at different times, in
+ * different sequences.  Nothing here should interact with other devices,
+ * except indirectly by consuming USB bandwidth and CPU resources for test
+ * threads and request completion.  But the only way to know that for sure
+ * is to test when HC queues are in use by many devices.
+ *
+ * WARNING:  Because usbfs grabs udev->dev.sem before calling this ioctl(),
+ * it locks out usbcore in certain code paths.  Notably, if you disconnect
+ * the device-under-test, hub_wq will wait block forever waiting for the
+ * ioctl to complete ... so that usb_disconnect() can abort the pending
+ * urbs and then call usbtest_disconnect().  To abort a test, you're best
+ * off just killing the userspace task and waiting for it to exit.
+ */
+
+static int
+usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
+{
+	struct usbtest_dev	*dev = usb_get_intfdata(intf);
+	struct usb_device	*udev = testdev_to_usbdev(dev);
+	struct usbtest_param	*param = buf;
+	int			retval = -EOPNOTSUPP;
+	struct urb		*urb;
+	struct scatterlist	*sg;
+	struct usb_sg_request	req;
+	struct timeval		start;
+	unsigned		i;
+
+	/* FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. */
+
+	pattern = mod_pattern;
+
+	if (code != USBTEST_REQUEST)
+		return -EOPNOTSUPP;
+
+	if (param->iterations <= 0)
+		return -EINVAL;
+
+	if (param->sglen > MAX_SGLEN)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&dev->lock))
+		return -ERESTARTSYS;
+
+	/* FIXME: What if a system sleep starts while a test is running? */
+
+	/* some devices, like ez-usb default devices, need a non-default
+	 * altsetting to have any active endpoints.  some tests change
+	 * altsettings; force a default so most tests don't need to check.
+	 */
+	if (dev->info->alt >= 0) {
+		int	res;
+
+		if (intf->altsetting->desc.bInterfaceNumber) {
+			mutex_unlock(&dev->lock);
+			return -ENODEV;
+		}
+		res = set_altsetting(dev, dev->info->alt);
+		if (res) {
+			dev_err(&intf->dev,
+					"set altsetting to %d failed, %d\n",
+					dev->info->alt, res);
+			mutex_unlock(&dev->lock);
+			return res;
+		}
+	}
+
+	/*
+	 * Just a bunch of test cases that every HCD is expected to handle.
+	 *
+	 * Some may need specific firmware, though it'd be good to have
+	 * one firmware image to handle all the test cases.
+	 *
+	 * FIXME add more tests!  cancel requests, verify the data, control
+	 * queueing, concurrent read+write threads, and so on.
+	 */
+	do_gettimeofday(&start);
+	switch (param->test_num) {
+
+	case 0:
+		dev_info(&intf->dev, "TEST 0:  NOP\n");
+		retval = 0;
+		break;
+
+	/* Simple non-queued bulk I/O tests */
+	case 1:
+		if (dev->out_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+				"TEST 1:  write %d bytes %u times\n",
+				param->length, param->iterations);
+		urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
+		retval = simple_io(dev, urb, param->iterations, 0, 0, "test1");
+		simple_free_urb(urb);
+		break;
+	case 2:
+		if (dev->in_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+				"TEST 2:  read %d bytes %u times\n",
+				param->length, param->iterations);
+		urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk source (maybe generates short writes) */
+		retval = simple_io(dev, urb, param->iterations, 0, 0, "test2");
+		simple_free_urb(urb);
+		break;
+	case 3:
+		if (dev->out_pipe == 0 || param->vary == 0)
+			break;
+		dev_info(&intf->dev,
+				"TEST 3:  write/%d 0..%d bytes %u times\n",
+				param->vary, param->length, param->iterations);
+		urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
+		retval = simple_io(dev, urb, param->iterations, param->vary,
+					0, "test3");
+		simple_free_urb(urb);
+		break;
+	case 4:
+		if (dev->in_pipe == 0 || param->vary == 0)
+			break;
+		dev_info(&intf->dev,
+				"TEST 4:  read/%d 0..%d bytes %u times\n",
+				param->vary, param->length, param->iterations);
+		urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk source (maybe generates short writes) */
+		retval = simple_io(dev, urb, param->iterations, param->vary,
+					0, "test4");
+		simple_free_urb(urb);
+		break;
+
+	/* Queued bulk I/O tests */
+	case 5:
+		if (dev->out_pipe == 0 || param->sglen == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 5:  write %d sglists %d entries of %d bytes\n",
+				param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist(param->sglen, param->length,
+				0, dev, dev->out_pipe);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
+		retval = perform_sglist(dev, param->iterations, dev->out_pipe,
+				&req, sg, param->sglen);
+		free_sglist(sg, param->sglen);
+		break;
+
+	case 6:
+		if (dev->in_pipe == 0 || param->sglen == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 6:  read %d sglists %d entries of %d bytes\n",
+				param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist(param->sglen, param->length,
+				0, dev, dev->in_pipe);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk source (maybe generates short writes) */
+		retval = perform_sglist(dev, param->iterations, dev->in_pipe,
+				&req, sg, param->sglen);
+		free_sglist(sg, param->sglen);
+		break;
+	case 7:
+		if (dev->out_pipe == 0 || param->sglen == 0 || param->vary == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 7:  write/%d %d sglists %d entries 0..%d bytes\n",
+				param->vary, param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist(param->sglen, param->length,
+				param->vary, dev, dev->out_pipe);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk sink (maybe accepts short writes) */
+		retval = perform_sglist(dev, param->iterations, dev->out_pipe,
+				&req, sg, param->sglen);
+		free_sglist(sg, param->sglen);
+		break;
+	case 8:
+		if (dev->in_pipe == 0 || param->sglen == 0 || param->vary == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 8:  read/%d %d sglists %d entries 0..%d bytes\n",
+				param->vary, param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist(param->sglen, param->length,
+				param->vary, dev, dev->in_pipe);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE:  bulk source (maybe generates short writes) */
+		retval = perform_sglist(dev, param->iterations, dev->in_pipe,
+				&req, sg, param->sglen);
+		free_sglist(sg, param->sglen);
+		break;
+
+	/* non-queued sanity tests for control (chapter 9 subset) */
+	case 9:
+		retval = 0;
+		dev_info(&intf->dev,
+			"TEST 9:  ch9 (subset) control tests, %d times\n",
+				param->iterations);
+		for (i = param->iterations; retval == 0 && i--; /* NOP */)
+			retval = ch9_postconfig(dev);
+		if (retval)
+			dev_err(&intf->dev, "ch9 subset failed, "
+					"iterations left %d\n", i);
+		break;
+
+	/* queued control messaging */
+	case 10:
+		retval = 0;
+		dev_info(&intf->dev,
+				"TEST 10:  queue %d control calls, %d times\n",
+				param->sglen,
+				param->iterations);
+		retval = test_ctrl_queue(dev, param);
+		break;
+
+	/* simple non-queued unlinks (ring with one urb) */
+	case 11:
+		if (dev->in_pipe == 0 || !param->length)
+			break;
+		retval = 0;
+		dev_info(&intf->dev, "TEST 11:  unlink %d reads of %d\n",
+				param->iterations, param->length);
+		for (i = param->iterations; retval == 0 && i--; /* NOP */)
+			retval = unlink_simple(dev, dev->in_pipe,
+						param->length);
+		if (retval)
+			dev_err(&intf->dev, "unlink reads failed %d, "
+				"iterations left %d\n", retval, i);
+		break;
+	case 12:
+		if (dev->out_pipe == 0 || !param->length)
+			break;
+		retval = 0;
+		dev_info(&intf->dev, "TEST 12:  unlink %d writes of %d\n",
+				param->iterations, param->length);
+		for (i = param->iterations; retval == 0 && i--; /* NOP */)
+			retval = unlink_simple(dev, dev->out_pipe,
+						param->length);
+		if (retval)
+			dev_err(&intf->dev, "unlink writes failed %d, "
+				"iterations left %d\n", retval, i);
+		break;
+
+	/* ep halt tests */
+	case 13:
+		if (dev->out_pipe == 0 && dev->in_pipe == 0)
+			break;
+		retval = 0;
+		dev_info(&intf->dev, "TEST 13:  set/clear %d halts\n",
+				param->iterations);
+		for (i = param->iterations; retval == 0 && i--; /* NOP */)
+			retval = halt_simple(dev);
+
+		if (retval)
+			ERROR(dev, "halts failed, iterations left %d\n", i);
+		break;
+
+	/* control write tests */
+	case 14:
+		if (!dev->info->ctrl_out)
+			break;
+		dev_info(&intf->dev, "TEST 14:  %d ep0out, %d..%d vary %d\n",
+				param->iterations,
+				realworld ? 1 : 0, param->length,
+				param->vary);
+		retval = ctrl_out(dev, param->iterations,
+				param->length, param->vary, 0);
+		break;
+
+	/* iso write tests */
+	case 15:
+		if (dev->out_iso_pipe == 0 || param->sglen == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 15:  write %d iso, %d entries of %d bytes\n",
+				param->iterations,
+				param->sglen, param->length);
+		/* FIRMWARE:  iso sink */
+		retval = test_queue(dev, param,
+				dev->out_iso_pipe, dev->iso_out, 0);
+		break;
+
+	/* iso read tests */
+	case 16:
+		if (dev->in_iso_pipe == 0 || param->sglen == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 16:  read %d iso, %d entries of %d bytes\n",
+				param->iterations,
+				param->sglen, param->length);
+		/* FIRMWARE:  iso source */
+		retval = test_queue(dev, param,
+				dev->in_iso_pipe, dev->iso_in, 0);
+		break;
+
+	/* FIXME scatterlist cancel (needs helper thread) */
+
+	/* Tests for bulk I/O using DMA mapping by core and odd address */
+	case 17:
+		if (dev->out_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 17:  write odd addr %d bytes %u times core map\n",
+			param->length, param->iterations);
+
+		retval = test_unaligned_bulk(
+				dev, dev->out_pipe,
+				param->length, param->iterations,
+				0, "test17");
+		break;
+
+	case 18:
+		if (dev->in_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 18:  read odd addr %d bytes %u times core map\n",
+			param->length, param->iterations);
+
+		retval = test_unaligned_bulk(
+				dev, dev->in_pipe,
+				param->length, param->iterations,
+				0, "test18");
+		break;
+
+	/* Tests for bulk I/O using premapped coherent buffer and odd address */
+	case 19:
+		if (dev->out_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 19:  write odd addr %d bytes %u times premapped\n",
+			param->length, param->iterations);
+
+		retval = test_unaligned_bulk(
+				dev, dev->out_pipe,
+				param->length, param->iterations,
+				URB_NO_TRANSFER_DMA_MAP, "test19");
+		break;
+
+	case 20:
+		if (dev->in_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 20:  read odd addr %d bytes %u times premapped\n",
+			param->length, param->iterations);
+
+		retval = test_unaligned_bulk(
+				dev, dev->in_pipe,
+				param->length, param->iterations,
+				URB_NO_TRANSFER_DMA_MAP, "test20");
+		break;
+
+	/* control write tests with unaligned buffer */
+	case 21:
+		if (!dev->info->ctrl_out)
+			break;
+		dev_info(&intf->dev,
+				"TEST 21:  %d ep0out odd addr, %d..%d vary %d\n",
+				param->iterations,
+				realworld ? 1 : 0, param->length,
+				param->vary);
+		retval = ctrl_out(dev, param->iterations,
+				param->length, param->vary, 1);
+		break;
+
+	/* unaligned iso tests */
+	case 22:
+		if (dev->out_iso_pipe == 0 || param->sglen == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 22:  write %d iso odd, %d entries of %d bytes\n",
+				param->iterations,
+				param->sglen, param->length);
+		retval = test_queue(dev, param,
+				dev->out_iso_pipe, dev->iso_out, 1);
+		break;
+
+	case 23:
+		if (dev->in_iso_pipe == 0 || param->sglen == 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 23:  read %d iso odd, %d entries of %d bytes\n",
+				param->iterations,
+				param->sglen, param->length);
+		retval = test_queue(dev, param,
+				dev->in_iso_pipe, dev->iso_in, 1);
+		break;
+
+	/* unlink URBs from a bulk-OUT queue */
+	case 24:
+		if (dev->out_pipe == 0 || !param->length || param->sglen < 4)
+			break;
+		retval = 0;
+		dev_info(&intf->dev, "TEST 24:  unlink from %d queues of "
+				"%d %d-byte writes\n",
+				param->iterations, param->sglen, param->length);
+		for (i = param->iterations; retval == 0 && i > 0; --i) {
+			retval = unlink_queued(dev, dev->out_pipe,
+						param->sglen, param->length);
+			if (retval) {
+				dev_err(&intf->dev,
+					"unlink queued writes failed %d, "
+					"iterations left %d\n", retval, i);
+				break;
+			}
+		}
+		break;
+
+	/* Simple non-queued interrupt I/O tests */
+	case 25:
+		if (dev->out_int_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+				"TEST 25: write %d bytes %u times\n",
+				param->length, param->iterations);
+		urb = simple_alloc_urb(udev, dev->out_int_pipe, param->length,
+				dev->int_out->bInterval);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE: interrupt sink (maybe accepts short writes) */
+		retval = simple_io(dev, urb, param->iterations, 0, 0, "test25");
+		simple_free_urb(urb);
+		break;
+	case 26:
+		if (dev->in_int_pipe == 0)
+			break;
+		dev_info(&intf->dev,
+				"TEST 26: read %d bytes %u times\n",
+				param->length, param->iterations);
+		urb = simple_alloc_urb(udev, dev->in_int_pipe, param->length,
+				dev->int_in->bInterval);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		/* FIRMWARE: interrupt source (maybe generates short writes) */
+		retval = simple_io(dev, urb, param->iterations, 0, 0, "test26");
+		simple_free_urb(urb);
+		break;
+	case 27:
+		/* We do performance test, so ignore data compare */
+		if (dev->out_pipe == 0 || param->sglen == 0 || pattern != 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 27: bulk write %dMbytes\n", (param->iterations *
+			param->sglen * param->length) / (1024 * 1024));
+		retval = test_queue(dev, param,
+				dev->out_pipe, NULL, 0);
+		break;
+	case 28:
+		if (dev->in_pipe == 0 || param->sglen == 0 || pattern != 0)
+			break;
+		dev_info(&intf->dev,
+			"TEST 28: bulk read %dMbytes\n", (param->iterations *
+			param->sglen * param->length) / (1024 * 1024));
+		retval = test_queue(dev, param,
+				dev->in_pipe, NULL, 0);
+		break;
+	}
+	do_gettimeofday(&param->duration);
+	param->duration.tv_sec -= start.tv_sec;
+	param->duration.tv_usec -= start.tv_usec;
+	if (param->duration.tv_usec < 0) {
+		param->duration.tv_usec += 1000 * 1000;
+		param->duration.tv_sec -= 1;
+	}
+	mutex_unlock(&dev->lock);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static unsigned force_interrupt;
+module_param(force_interrupt, uint, 0);
+MODULE_PARM_DESC(force_interrupt, "0 = test default; else interrupt");
+
+#ifdef	GENERIC
+static unsigned short vendor;
+module_param(vendor, ushort, 0);
+MODULE_PARM_DESC(vendor, "vendor code (from usb-if)");
+
+static unsigned short product;
+module_param(product, ushort, 0);
+MODULE_PARM_DESC(product, "product code (from vendor)");
+#endif
+
+static int
+usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device	*udev;
+	struct usbtest_dev	*dev;
+	struct usbtest_info	*info;
+	char			*rtest, *wtest;
+	char			*irtest, *iwtest;
+	char			*intrtest, *intwtest;
+
+	udev = interface_to_usbdev(intf);
+
+#ifdef	GENERIC
+	/* specify devices by module parameters? */
+	if (id->match_flags == 0) {
+		/* vendor match required, product match optional */
+		if (!vendor || le16_to_cpu(udev->descriptor.idVendor) != (u16)vendor)
+			return -ENODEV;
+		if (product && le16_to_cpu(udev->descriptor.idProduct) != (u16)product)
+			return -ENODEV;
+		dev_info(&intf->dev, "matched module params, "
+					"vend=0x%04x prod=0x%04x\n",
+				le16_to_cpu(udev->descriptor.idVendor),
+				le16_to_cpu(udev->descriptor.idProduct));
+	}
+#endif
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	info = (struct usbtest_info *) id->driver_info;
+	dev->info = info;
+	mutex_init(&dev->lock);
+
+	dev->intf = intf;
+
+	/* cacheline-aligned scratch for i/o */
+	dev->buf = kmalloc(TBUF_SIZE, GFP_KERNEL);
+	if (dev->buf == NULL) {
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	/* NOTE this doesn't yet test the handful of difference that are
+	 * visible with high speed interrupts:  bigger maxpacket (1K) and
+	 * "high bandwidth" modes (up to 3 packets/uframe).
+	 */
+	rtest = wtest = "";
+	irtest = iwtest = "";
+	intrtest = intwtest = "";
+	if (force_interrupt || udev->speed == USB_SPEED_LOW) {
+		if (info->ep_in) {
+			dev->in_pipe = usb_rcvintpipe(udev, info->ep_in);
+			rtest = " intr-in";
+		}
+		if (info->ep_out) {
+			dev->out_pipe = usb_sndintpipe(udev, info->ep_out);
+			wtest = " intr-out";
+		}
+	} else {
+		if (override_alt >= 0 || info->autoconf) {
+			int status;
+
+			status = get_endpoints(dev, intf);
+			if (status < 0) {
+				WARNING(dev, "couldn't get endpoints, %d\n",
+						status);
+				kfree(dev->buf);
+				kfree(dev);
+				return status;
+			}
+			/* may find bulk or ISO pipes */
+		} else {
+			if (info->ep_in)
+				dev->in_pipe = usb_rcvbulkpipe(udev,
+							info->ep_in);
+			if (info->ep_out)
+				dev->out_pipe = usb_sndbulkpipe(udev,
+							info->ep_out);
+		}
+		if (dev->in_pipe)
+			rtest = " bulk-in";
+		if (dev->out_pipe)
+			wtest = " bulk-out";
+		if (dev->in_iso_pipe)
+			irtest = " iso-in";
+		if (dev->out_iso_pipe)
+			iwtest = " iso-out";
+		if (dev->in_int_pipe)
+			intrtest = " int-in";
+		if (dev->out_int_pipe)
+			intwtest = " int-out";
+	}
+
+	usb_set_intfdata(intf, dev);
+	dev_info(&intf->dev, "%s\n", info->name);
+	dev_info(&intf->dev, "%s {control%s%s%s%s%s%s%s} tests%s\n",
+			usb_speed_string(udev->speed),
+			info->ctrl_out ? " in/out" : "",
+			rtest, wtest,
+			irtest, iwtest,
+			intrtest, intwtest,
+			info->alt >= 0 ? " (+alt)" : "");
+	return 0;
+}
+
+static int usbtest_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	return 0;
+}
+
+static int usbtest_resume(struct usb_interface *intf)
+{
+	return 0;
+}
+
+
+static void usbtest_disconnect(struct usb_interface *intf)
+{
+	struct usbtest_dev	*dev = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+	dev_dbg(&intf->dev, "disconnect\n");
+	kfree(dev);
+}
+
+/* Basic testing only needs a device that can source or sink bulk traffic.
+ * Any device can test control transfers (default with GENERIC binding).
+ *
+ * Several entries work with the default EP0 implementation that's built
+ * into EZ-USB chips.  There's a default vendor ID which can be overridden
+ * by (very) small config EEPROMS, but otherwise all these devices act
+ * identically until firmware is loaded:  only EP0 works.  It turns out
+ * to be easy to make other endpoints work, without modifying that EP0
+ * behavior.  For now, we expect that kind of firmware.
+ */
+
+/* an21xx or fx versions of ez-usb */
+static struct usbtest_info ez1_info = {
+	.name		= "EZ-USB device",
+	.ep_in		= 2,
+	.ep_out		= 2,
+	.alt		= 1,
+};
+
+/* fx2 version of ez-usb */
+static struct usbtest_info ez2_info = {
+	.name		= "FX2 device",
+	.ep_in		= 6,
+	.ep_out		= 2,
+	.alt		= 1,
+};
+
+/* ezusb family device with dedicated usb test firmware,
+ */
+static struct usbtest_info fw_info = {
+	.name		= "usb test device",
+	.ep_in		= 2,
+	.ep_out		= 2,
+	.alt		= 1,
+	.autoconf	= 1,		/* iso and ctrl_out need autoconf */
+	.ctrl_out	= 1,
+	.iso		= 1,		/* iso_ep's are #8 in/out */
+};
+
+/* peripheral running Linux and 'zero.c' test firmware, or
+ * its user-mode cousin. different versions of this use
+ * different hardware with the same vendor/product codes.
+ * host side MUST rely on the endpoint descriptors.
+ */
+static struct usbtest_info gz_info = {
+	.name		= "Linux gadget zero",
+	.autoconf	= 1,
+	.ctrl_out	= 1,
+	.iso		= 1,
+	.intr		= 1,
+	.alt		= 0,
+};
+
+static struct usbtest_info um_info = {
+	.name		= "Linux user mode test driver",
+	.autoconf	= 1,
+	.alt		= -1,
+};
+
+static struct usbtest_info um2_info = {
+	.name		= "Linux user mode ISO test driver",
+	.autoconf	= 1,
+	.iso		= 1,
+	.alt		= -1,
+};
+
+#ifdef IBOT2
+/* this is a nice source of high speed bulk data;
+ * uses an FX2, with firmware provided in the device
+ */
+static struct usbtest_info ibot2_info = {
+	.name		= "iBOT2 webcam",
+	.ep_in		= 2,
+	.alt		= -1,
+};
+#endif
+
+#ifdef GENERIC
+/* we can use any device to test control traffic */
+static struct usbtest_info generic_info = {
+	.name		= "Generic USB device",
+	.alt		= -1,
+};
+#endif
+
+
+static const struct usb_device_id id_table[] = {
+
+	/*-------------------------------------------------------------*/
+
+	/* EZ-USB devices which download firmware to replace (or in our
+	 * case augment) the default device implementation.
+	 */
+
+	/* generic EZ-USB FX controller */
+	{ USB_DEVICE(0x0547, 0x2235),
+		.driver_info = (unsigned long) &ez1_info,
+	},
+
+	/* CY3671 development board with EZ-USB FX */
+	{ USB_DEVICE(0x0547, 0x0080),
+		.driver_info = (unsigned long) &ez1_info,
+	},
+
+	/* generic EZ-USB FX2 controller (or development board) */
+	{ USB_DEVICE(0x04b4, 0x8613),
+		.driver_info = (unsigned long) &ez2_info,
+	},
+
+	/* re-enumerated usb test device firmware */
+	{ USB_DEVICE(0xfff0, 0xfff0),
+		.driver_info = (unsigned long) &fw_info,
+	},
+
+	/* "Gadget Zero" firmware runs under Linux */
+	{ USB_DEVICE(0x0525, 0xa4a0),
+		.driver_info = (unsigned long) &gz_info,
+	},
+
+	/* so does a user-mode variant */
+	{ USB_DEVICE(0x0525, 0xa4a4),
+		.driver_info = (unsigned long) &um_info,
+	},
+
+	/* ... and a user-mode variant that talks iso */
+	{ USB_DEVICE(0x0525, 0xa4a3),
+		.driver_info = (unsigned long) &um2_info,
+	},
+
+#ifdef KEYSPAN_19Qi
+	/* Keyspan 19qi uses an21xx (original EZ-USB) */
+	/* this does not coexist with the real Keyspan 19qi driver! */
+	{ USB_DEVICE(0x06cd, 0x010b),
+		.driver_info = (unsigned long) &ez1_info,
+	},
+#endif
+
+	/*-------------------------------------------------------------*/
+
+#ifdef IBOT2
+	/* iBOT2 makes a nice source of high speed bulk-in data */
+	/* this does not coexist with a real iBOT2 driver! */
+	{ USB_DEVICE(0x0b62, 0x0059),
+		.driver_info = (unsigned long) &ibot2_info,
+	},
+#endif
+
+	/*-------------------------------------------------------------*/
+
+#ifdef GENERIC
+	/* module params can specify devices to use for control tests */
+	{ .driver_info = (unsigned long) &generic_info, },
+#endif
+
+	/*-------------------------------------------------------------*/
+
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver usbtest_driver = {
+	.name =		"usbtest",
+	.id_table =	id_table,
+	.probe =	usbtest_probe,
+	.unlocked_ioctl = usbtest_ioctl,
+	.disconnect =	usbtest_disconnect,
+	.suspend =	usbtest_suspend,
+	.resume =	usbtest_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init usbtest_init(void)
+{
+#ifdef GENERIC
+	if (vendor)
+		pr_debug("params: vend=0x%04x prod=0x%04x\n", vendor, product);
+#endif
+	return usb_register(&usbtest_driver);
+}
+module_init(usbtest_init);
+
+static void __exit usbtest_exit(void)
+{
+	usb_deregister(&usbtest_driver);
+}
+module_exit(usbtest_exit);
+
+MODULE_DESCRIPTION("USB Core/HCD Testing Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
new file mode 100644
index 0000000..442b663
--- /dev/null
+++ b/drivers/usb/misc/uss720.c
@@ -0,0 +1,839 @@
+/*****************************************************************************/
+
+/*
+ *	uss720.c  --  USS720 USB Parport Cable.
+ *
+ *	Copyright (C) 1999, 2005, 2010
+ *	    Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *
+ *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Based on parport_pc.c
+ *
+ *  History:
+ *   0.1  04.08.1999  Created
+ *   0.2  07.08.1999  Some fixes mainly suggested by Tim Waugh
+ *		      Interrupt handling currently disabled because
+ *		      usb_request_irq crashes somewhere within ohci.c
+ *		      for no apparent reason (that is for me, anyway)
+ *		      ECP currently untested
+ *   0.3  10.08.1999  fixing merge errors
+ *   0.4  13.08.1999  Added Vendor/Product ID of Brad Hard's cable
+ *   0.5  20.09.1999  usb_control_msg wrapper used
+ *        Nov01.2000  usb_device_table support by Adam J. Richter
+ *        08.04.2001  Identify version on module load.  gb
+ *   0.6  02.09.2005  Fix "scheduling in interrupt" problem by making save/restore
+ *                    context asynchronous
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/parport.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/kref.h>
+#include <linux/slab.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.6"
+#define DRIVER_AUTHOR "Thomas M. Sailer, t.sailer@alumni.ethz.ch"
+#define DRIVER_DESC "USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip"
+
+/* --------------------------------------------------------------------- */
+
+struct parport_uss720_private {
+	struct usb_device *usbdev;
+	struct parport *pp;
+	struct kref ref_count;
+	__u8 reg[7];  /* USB registers */
+	struct list_head asynclist;
+	spinlock_t asynclock;
+};
+
+struct uss720_async_request {
+	struct parport_uss720_private *priv;
+	struct kref ref_count;
+	struct list_head asynclist;
+	struct completion compl;
+	struct urb *urb;
+	struct usb_ctrlrequest *dr;
+	__u8 reg[7];
+};
+
+/* --------------------------------------------------------------------- */
+
+static void destroy_priv(struct kref *kref)
+{
+	struct parport_uss720_private *priv = container_of(kref, struct parport_uss720_private, ref_count);
+
+	dev_dbg(&priv->usbdev->dev, "destroying priv datastructure\n");
+	usb_put_dev(priv->usbdev);
+	kfree(priv);
+}
+
+static void destroy_async(struct kref *kref)
+{
+	struct uss720_async_request *rq = container_of(kref, struct uss720_async_request, ref_count);
+	struct parport_uss720_private *priv = rq->priv;
+	unsigned long flags;
+
+	if (likely(rq->urb))
+		usb_free_urb(rq->urb);
+	kfree(rq->dr);
+	spin_lock_irqsave(&priv->asynclock, flags);
+	list_del_init(&rq->asynclist);
+	spin_unlock_irqrestore(&priv->asynclock, flags);
+	kfree(rq);
+	kref_put(&priv->ref_count, destroy_priv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void async_complete(struct urb *urb)
+{
+	struct uss720_async_request *rq;
+	struct parport *pp;
+	struct parport_uss720_private *priv;
+	int status = urb->status;
+
+	rq = urb->context;
+	priv = rq->priv;
+	pp = priv->pp;
+	if (status) {
+		dev_err(&urb->dev->dev, "async_complete: urb error %d\n",
+			status);
+	} else if (rq->dr->bRequest == 3) {
+		memcpy(priv->reg, rq->reg, sizeof(priv->reg));
+#if 0
+		dev_dbg(&priv->usbdev->dev, "async_complete regs %7ph\n",
+			priv->reg);
+#endif
+		/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
+		if (rq->reg[2] & rq->reg[1] & 0x10 && pp)
+			parport_generic_irq(pp);
+	}
+	complete(&rq->compl);
+	kref_put(&rq->ref_count, destroy_async);
+}
+
+static struct uss720_async_request *submit_async_request(struct parport_uss720_private *priv,
+							 __u8 request, __u8 requesttype, __u16 value, __u16 index,
+							 gfp_t mem_flags)
+{
+	struct usb_device *usbdev;
+	struct uss720_async_request *rq;
+	unsigned long flags;
+	int ret;
+
+	if (!priv)
+		return NULL;
+	usbdev = priv->usbdev;
+	if (!usbdev)
+		return NULL;
+	rq = kzalloc(sizeof(struct uss720_async_request), mem_flags);
+	if (!rq) {
+		dev_err(&usbdev->dev, "submit_async_request out of memory\n");
+		return NULL;
+	}
+	kref_init(&rq->ref_count);
+	INIT_LIST_HEAD(&rq->asynclist);
+	init_completion(&rq->compl);
+	kref_get(&priv->ref_count);
+	rq->priv = priv;
+	rq->urb = usb_alloc_urb(0, mem_flags);
+	if (!rq->urb) {
+		kref_put(&rq->ref_count, destroy_async);
+		dev_err(&usbdev->dev, "submit_async_request out of memory\n");
+		return NULL;
+	}
+	rq->dr = kmalloc(sizeof(*rq->dr), mem_flags);
+	if (!rq->dr) {
+		kref_put(&rq->ref_count, destroy_async);
+		return NULL;
+	}
+	rq->dr->bRequestType = requesttype;
+	rq->dr->bRequest = request;
+	rq->dr->wValue = cpu_to_le16(value);
+	rq->dr->wIndex = cpu_to_le16(index);
+	rq->dr->wLength = cpu_to_le16((request == 3) ? sizeof(rq->reg) : 0);
+	usb_fill_control_urb(rq->urb, usbdev, (requesttype & 0x80) ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0),
+			     (unsigned char *)rq->dr,
+			     (request == 3) ? rq->reg : NULL, (request == 3) ? sizeof(rq->reg) : 0, async_complete, rq);
+	/* rq->urb->transfer_flags |= URB_ASYNC_UNLINK; */
+	spin_lock_irqsave(&priv->asynclock, flags);
+	list_add_tail(&rq->asynclist, &priv->asynclist);
+	spin_unlock_irqrestore(&priv->asynclock, flags);
+	kref_get(&rq->ref_count);
+	ret = usb_submit_urb(rq->urb, mem_flags);
+	if (!ret)
+		return rq;
+	destroy_async(&rq->ref_count);
+	dev_err(&usbdev->dev, "submit_async_request submit_urb failed with %d\n", ret);
+	return NULL;
+}
+
+static unsigned int kill_all_async_requests_priv(struct parport_uss720_private *priv)
+{
+	struct uss720_async_request *rq;
+	unsigned long flags;
+	unsigned int ret = 0;
+
+	spin_lock_irqsave(&priv->asynclock, flags);
+	list_for_each_entry(rq, &priv->asynclist, asynclist) {
+		usb_unlink_urb(rq->urb);
+		ret++;
+	}
+	spin_unlock_irqrestore(&priv->asynclock, flags);
+	return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val, gfp_t mem_flags)
+{
+	struct parport_uss720_private *priv;
+	struct uss720_async_request *rq;
+	static const unsigned char regindex[9] = {
+		4, 0, 1, 5, 5, 0, 2, 3, 6
+	};
+	int ret;
+
+	if (!pp)
+		return -EIO;
+	priv = pp->private_data;
+	rq = submit_async_request(priv, 3, 0xc0, ((unsigned int)reg) << 8, 0, mem_flags);
+	if (!rq) {
+		dev_err(&priv->usbdev->dev, "get_1284_register(%u) failed",
+			(unsigned int)reg);
+		return -EIO;
+	}
+	if (!val) {
+		kref_put(&rq->ref_count, destroy_async);
+		return 0;
+	}
+	if (wait_for_completion_timeout(&rq->compl, HZ)) {
+		ret = rq->urb->status;
+		*val = priv->reg[(reg >= 9) ? 0 : regindex[reg]];
+		if (ret)
+			printk(KERN_WARNING "get_1284_register: "
+			       "usb error %d\n", ret);
+		kref_put(&rq->ref_count, destroy_async);
+		return ret;
+	}
+	printk(KERN_WARNING "get_1284_register timeout\n");
+	kill_all_async_requests_priv(priv);
+	return -EIO;
+}
+
+static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val, gfp_t mem_flags)
+{
+	struct parport_uss720_private *priv;
+	struct uss720_async_request *rq;
+
+	if (!pp)
+		return -EIO;
+	priv = pp->private_data;
+	rq = submit_async_request(priv, 4, 0x40, (((unsigned int)reg) << 8) | val, 0, mem_flags);
+	if (!rq) {
+		dev_err(&priv->usbdev->dev, "set_1284_register(%u,%u) failed",
+			(unsigned int)reg, (unsigned int)val);
+		return -EIO;
+	}
+	kref_put(&rq->ref_count, destroy_async);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/* ECR modes */
+#define ECR_SPP 00
+#define ECR_PS2 01
+#define ECR_PPF 02
+#define ECR_ECP 03
+#define ECR_EPP 04
+
+/* Safely change the mode bits in the ECR */
+static int change_mode(struct parport *pp, int m)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	int mode;
+	__u8 reg;
+
+	if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
+		return -EIO;
+	/* Bits <7:5> contain the mode. */
+	mode = (priv->reg[2] >> 5) & 0x7;
+	if (mode == m)
+		return 0;
+	/* We have to go through mode 000 or 001 */
+	if (mode > ECR_PS2 && m > ECR_PS2)
+		if (change_mode(pp, ECR_PS2))
+			return -EIO;
+
+	if (m <= ECR_PS2 && !(priv->reg[1] & 0x20)) {
+		/* This mode resets the FIFO, so we may
+		 * have to wait for it to drain first. */
+		unsigned long expire = jiffies + pp->physport->cad->timeout;
+		switch (mode) {
+		case ECR_PPF: /* Parallel Port FIFO mode */
+		case ECR_ECP: /* ECP Parallel Port mode */
+			/* Poll slowly. */
+			for (;;) {
+				if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
+					return -EIO;
+				if (priv->reg[2] & 0x01)
+					break;
+				if (time_after_eq (jiffies, expire))
+					/* The FIFO is stuck. */
+					return -EBUSY;
+				msleep_interruptible(10);
+				if (signal_pending (current))
+					break;
+			}
+		}
+	}
+	/* Set the mode. */
+	if (set_1284_register(pp, 6, m << 5, GFP_KERNEL))
+		return -EIO;
+	if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
+		return -EIO;
+	return 0;
+}
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int clear_epp_timeout(struct parport *pp)
+{
+	unsigned char stat;
+
+	if (get_1284_register(pp, 1, &stat, GFP_KERNEL))
+		return 1;
+	return stat & 1;
+}
+
+/*
+ * Access functions.
+ */
+#if 0
+static int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id)
+{
+	struct parport *pp = (struct parport *)dev_id;
+	struct parport_uss720_private *priv = pp->private_data;	
+
+	if (usbstatus != 0 || len < 4 || !buffer)
+		return 1;
+	memcpy(priv->reg, buffer, 4);
+	/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
+	if (priv->reg[2] & priv->reg[1] & 0x10)
+		parport_generic_irq(pp);
+	return 1;
+}
+#endif
+
+static void parport_uss720_write_data(struct parport *pp, unsigned char d)
+{
+	set_1284_register(pp, 0, d, GFP_KERNEL);
+}
+
+static unsigned char parport_uss720_read_data(struct parport *pp)
+{
+	unsigned char ret;
+
+	if (get_1284_register(pp, 0, &ret, GFP_KERNEL))
+		return 0;
+	return ret;
+}
+
+static void parport_uss720_write_control(struct parport *pp, unsigned char d)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+
+	d = (d & 0xf) | (priv->reg[1] & 0xf0);
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
+		return;
+	priv->reg[1] = d;
+}
+
+static unsigned char parport_uss720_read_control(struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	return priv->reg[1] & 0xf; /* Use soft copy */
+}
+
+static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned char mask, unsigned char val)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	mask &= 0x0f;
+	val &= 0x0f;
+	d = (priv->reg[1] & (~mask)) ^ val;
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
+		return 0;
+	priv->reg[1] = d;
+	return d & 0xf;
+}
+
+static unsigned char parport_uss720_read_status(struct parport *pp)
+{
+	unsigned char ret;
+
+	if (get_1284_register(pp, 1, &ret, GFP_KERNEL))
+		return 0;
+	return ret & 0xf8;
+}
+
+static void parport_uss720_disable_irq(struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] & ~0x10;
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_enable_irq(struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] | 0x10;
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_data_forward (struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] & ~0x20;
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_data_reverse (struct parport *pp)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	unsigned char d;
+
+	d = priv->reg[1] | 0x20;
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
+		return;
+	priv->reg[1] = d;
+}
+
+static void parport_uss720_init_state(struct pardevice *dev, struct parport_state *s)
+{
+	s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
+	s->u.pc.ecr = 0x24;
+}
+
+static void parport_uss720_save_state(struct parport *pp, struct parport_state *s)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+
+#if 0
+	if (get_1284_register(pp, 2, NULL, GFP_ATOMIC))
+		return;
+#endif
+	s->u.pc.ctr = priv->reg[1];
+	s->u.pc.ecr = priv->reg[2];
+}
+
+static void parport_uss720_restore_state(struct parport *pp, struct parport_state *s)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+
+	set_1284_register(pp, 2, s->u.pc.ctr, GFP_ATOMIC);
+	set_1284_register(pp, 6, s->u.pc.ecr, GFP_ATOMIC);
+	get_1284_register(pp, 2, NULL, GFP_ATOMIC);
+	priv->reg[1] = s->u.pc.ctr;
+	priv->reg[2] = s->u.pc.ecr;
+}
+
+static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t length, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t got = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; got < length; got++) {
+		if (get_1284_register(pp, 4, (char *)buf, GFP_KERNEL))
+			break;
+		buf++;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return got;
+}
+
+static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, size_t length, int flags)
+{
+#if 0
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t written = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; written < length; written++) {
+		if (set_1284_register(pp, 4, (char *)buf, GFP_KERNEL))
+			break;
+		((char*)buf)++;
+		if (get_1284_register(pp, 1, NULL, GFP_KERNEL))
+			break;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return written;
+#else
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buf, length, &rlen, 20000);
+	if (i)
+		printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buf, length, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+#endif
+}
+
+static size_t parport_uss720_epp_read_addr(struct parport *pp, void *buf, size_t length, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t got = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; got < length; got++) {
+		if (get_1284_register(pp, 3, (char *)buf, GFP_KERNEL))
+			break;
+		buf++;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return got;
+}
+
+static size_t parport_uss720_epp_write_addr(struct parport *pp, const void *buf, size_t length, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;	
+	size_t written = 0;
+
+	if (change_mode(pp, ECR_EPP))
+		return 0;
+	for (; written < length; written++) {
+		if (set_1284_register(pp, 3, *(char *)buf, GFP_KERNEL))
+			break;
+		buf++;
+		if (get_1284_register(pp, 1, NULL, GFP_KERNEL))
+			break;
+		if (priv->reg[0] & 0x01) {
+			clear_epp_timeout(pp);
+			break;
+		}
+	}
+	change_mode(pp, ECR_PS2);
+	return written;
+}
+
+static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buffer, size_t len, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_ECP))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000);
+	if (i)
+		printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+}
+
+static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, size_t len, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_ECP))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, 20000);
+	if (i)
+		printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %Zu rlen %u\n", buffer, len, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+}
+
+static size_t parport_uss720_ecp_write_addr(struct parport *pp, const void *buffer, size_t len, int flags)
+{
+	size_t written = 0;
+
+	if (change_mode(pp, ECR_ECP))
+		return 0;
+	for (; written < len; written++) {
+		if (set_1284_register(pp, 5, *(char *)buffer, GFP_KERNEL))
+			break;
+		buffer++;
+	}
+	change_mode(pp, ECR_PS2);
+	return written;
+}
+
+static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer, size_t len, int flags)
+{
+	struct parport_uss720_private *priv = pp->private_data;
+	struct usb_device *usbdev = priv->usbdev;
+	int rlen;
+	int i;
+
+	if (!usbdev)
+		return 0;
+	if (change_mode(pp, ECR_PPF))
+		return 0;
+	i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, 20000);
+	if (i)
+		printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %Zu rlen %u\n", buffer, len, rlen);
+	change_mode(pp, ECR_PS2);
+	return rlen;
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct parport_operations parport_uss720_ops = 
+{
+	.owner =		THIS_MODULE,
+	.write_data =		parport_uss720_write_data,
+	.read_data =		parport_uss720_read_data,
+
+	.write_control =	parport_uss720_write_control,
+	.read_control =		parport_uss720_read_control,
+	.frob_control =		parport_uss720_frob_control,
+
+	.read_status =		parport_uss720_read_status,
+
+	.enable_irq =		parport_uss720_enable_irq,
+	.disable_irq =		parport_uss720_disable_irq,
+
+	.data_forward =		parport_uss720_data_forward,
+	.data_reverse =		parport_uss720_data_reverse,
+
+	.init_state =		parport_uss720_init_state,
+	.save_state =		parport_uss720_save_state,
+	.restore_state =	parport_uss720_restore_state,
+
+	.epp_write_data =	parport_uss720_epp_write_data,
+	.epp_read_data =	parport_uss720_epp_read_data,
+	.epp_write_addr =	parport_uss720_epp_write_addr,
+	.epp_read_addr =	parport_uss720_epp_read_addr,
+
+	.ecp_write_data =	parport_uss720_ecp_write_data,
+	.ecp_read_data =	parport_uss720_ecp_read_data,
+	.ecp_write_addr =	parport_uss720_ecp_write_addr,
+
+	.compat_write_data =	parport_uss720_write_compat,
+	.nibble_read_data =	parport_ieee1284_read_nibble,
+	.byte_read_data =	parport_ieee1284_read_byte,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int uss720_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = usb_get_dev(interface_to_usbdev(intf));
+	struct usb_host_interface *interface;
+	struct usb_host_endpoint *endpoint;
+	struct parport_uss720_private *priv;
+	struct parport *pp;
+	unsigned char reg;
+	int i;
+
+	dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n",
+		le16_to_cpu(usbdev->descriptor.idVendor),
+		le16_to_cpu(usbdev->descriptor.idProduct));
+
+	/* our known interfaces have 3 alternate settings */
+	if (intf->num_altsetting != 3) {
+		usb_put_dev(usbdev);
+		return -ENODEV;
+	}
+	i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
+	dev_dbg(&intf->dev, "set interface result %d\n", i);
+
+	interface = intf->cur_altsetting;
+
+	if (interface->desc.bNumEndpoints < 3) {
+		usb_put_dev(usbdev);
+		return -ENODEV;
+	}
+
+	/*
+	 * Allocate parport interface 
+	 */
+	priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL);
+	if (!priv) {
+		usb_put_dev(usbdev);
+		return -ENOMEM;
+	}
+	priv->pp = NULL;
+	priv->usbdev = usbdev;
+	kref_init(&priv->ref_count);
+	spin_lock_init(&priv->asynclock);
+	INIT_LIST_HEAD(&priv->asynclist);
+	pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops);
+	if (!pp) {
+		printk(KERN_WARNING "uss720: could not register parport\n");
+		goto probe_abort;
+	}
+
+	priv->pp = pp;
+	pp->private_data = priv;
+	pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT;
+
+	/* set the USS720 control register to manual mode, no ECP compression, enable all ints */
+	set_1284_register(pp, 7, 0x00, GFP_KERNEL);
+	set_1284_register(pp, 6, 0x30, GFP_KERNEL);  /* PS/2 mode */
+	set_1284_register(pp, 2, 0x0c, GFP_KERNEL);
+	/* debugging */
+	get_1284_register(pp, 0, &reg, GFP_KERNEL);
+	dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
+
+	endpoint = &interface->endpoint[2];
+	dev_dbg(&intf->dev, "epaddr %d interval %d\n",
+		endpoint->desc.bEndpointAddress, endpoint->desc.bInterval);
+	parport_announce_port(pp);
+
+	usb_set_intfdata(intf, pp);
+	return 0;
+
+probe_abort:
+	kill_all_async_requests_priv(priv);
+	kref_put(&priv->ref_count, destroy_priv);
+	return -ENODEV;
+}
+
+static void uss720_disconnect(struct usb_interface *intf)
+{
+	struct parport *pp = usb_get_intfdata(intf);
+	struct parport_uss720_private *priv;
+	struct usb_device *usbdev;
+
+	dev_dbg(&intf->dev, "disconnect\n");
+	usb_set_intfdata(intf, NULL);
+	if (pp) {
+		priv = pp->private_data;
+		usbdev = priv->usbdev;
+		priv->usbdev = NULL;
+		priv->pp = NULL;
+		dev_dbg(&intf->dev, "parport_remove_port\n");
+		parport_remove_port(pp);
+		parport_put_port(pp);
+		kill_all_async_requests_priv(priv);
+		kref_put(&priv->ref_count, destroy_priv);
+	}
+	dev_dbg(&intf->dev, "disconnect done\n");
+}
+
+/* table of cables that work through this driver */
+static const struct usb_device_id uss720_table[] = {
+	{ USB_DEVICE(0x047e, 0x1001) },
+	{ USB_DEVICE(0x0557, 0x2001) },
+	{ USB_DEVICE(0x0729, 0x1284) },
+	{ USB_DEVICE(0x1293, 0x0002) },
+	{ USB_DEVICE(0x050d, 0x0002) },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, uss720_table);
+
+
+static struct usb_driver uss720_driver = {
+	.name =		"uss720",
+	.probe =	uss720_probe,
+	.disconnect =	uss720_disconnect,
+	.id_table =	uss720_table,
+};
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int __init uss720_init(void)
+{
+	int retval;
+	retval = usb_register(&uss720_driver);
+	if (retval)
+		goto out;
+
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+	       DRIVER_DESC "\n");
+	printk(KERN_INFO KBUILD_MODNAME ": NOTE: this is a special purpose "
+	       "driver to allow nonstandard\n");
+	printk(KERN_INFO KBUILD_MODNAME ": protocols (eg. bitbang) over "
+	       "USS720 usb to parallel cables\n");
+	printk(KERN_INFO KBUILD_MODNAME ": If you just want to connect to a "
+	       "printer, use usblp instead\n");
+out:
+	return retval;
+}
+
+static void __exit uss720_cleanup(void)
+{
+	usb_deregister(&uss720_driver);
+}
+
+module_init(uss720_init);
+module_exit(uss720_cleanup);
+
+/* --------------------------------------------------------------------- */
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
new file mode 100644
index 0000000..343fa6f
--- /dev/null
+++ b/drivers/usb/misc/yurex.c
@@ -0,0 +1,548 @@
+/*
+ * Driver for Meywa-Denki & KAYAC YUREX
+ *
+ * Copyright (C) 2010 Tomoki Sekiyama (tomoki.sekiyama@gmail.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#define DRIVER_AUTHOR "Tomoki Sekiyama"
+#define DRIVER_DESC "Driver for Meywa-Denki & KAYAC YUREX"
+
+#define YUREX_VENDOR_ID		0x0c45
+#define YUREX_PRODUCT_ID	0x1010
+
+#define CMD_ACK		'!'
+#define CMD_ANIMATE	'A'
+#define CMD_COUNT	'C'
+#define CMD_LED		'L'
+#define CMD_READ	'R'
+#define CMD_SET		'S'
+#define CMD_VERSION	'V'
+#define CMD_EOF		0x0d
+#define CMD_PADDING	0xff
+
+#define YUREX_BUF_SIZE		8
+#define YUREX_WRITE_TIMEOUT	(HZ*2)
+
+/* table of devices that work with this driver */
+static struct usb_device_id yurex_table[] = {
+	{ USB_DEVICE(YUREX_VENDOR_ID, YUREX_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, yurex_table);
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define YUREX_MINOR_BASE	0
+#else
+#define YUREX_MINOR_BASE	192
+#endif
+
+/* Structure to hold all of our device specific stuff */
+struct usb_yurex {
+	struct usb_device	*udev;
+	struct usb_interface	*interface;
+	__u8			int_in_endpointAddr;
+	struct urb		*urb;		/* URB for interrupt in */
+	unsigned char           *int_buffer;	/* buffer for intterupt in */
+	struct urb		*cntl_urb;	/* URB for control msg */
+	struct usb_ctrlrequest	*cntl_req;	/* req for control msg */
+	unsigned char		*cntl_buffer;	/* buffer for control msg */
+
+	struct kref		kref;
+	struct mutex		io_mutex;
+	struct fasync_struct	*async_queue;
+	wait_queue_head_t	waitq;
+
+	spinlock_t		lock;
+	__s64			bbu;		/* BBU from device */
+};
+#define to_yurex_dev(d) container_of(d, struct usb_yurex, kref)
+
+static struct usb_driver yurex_driver;
+static const struct file_operations yurex_fops;
+
+
+static void yurex_control_callback(struct urb *urb)
+{
+	struct usb_yurex *dev = urb->context;
+	int status = urb->status;
+
+	if (status) {
+		dev_err(&urb->dev->dev, "%s - control failed: %d\n",
+			__func__, status);
+		wake_up_interruptible(&dev->waitq);
+		return;
+	}
+	/* on success, sender woken up by CMD_ACK int in, or timeout */
+}
+
+static void yurex_delete(struct kref *kref)
+{
+	struct usb_yurex *dev = to_yurex_dev(kref);
+
+	dev_dbg(&dev->interface->dev, "%s\n", __func__);
+
+	usb_put_dev(dev->udev);
+	if (dev->cntl_urb) {
+		usb_kill_urb(dev->cntl_urb);
+		kfree(dev->cntl_req);
+		if (dev->cntl_buffer)
+			usb_free_coherent(dev->udev, YUREX_BUF_SIZE,
+				dev->cntl_buffer, dev->cntl_urb->transfer_dma);
+		usb_free_urb(dev->cntl_urb);
+	}
+	if (dev->urb) {
+		usb_kill_urb(dev->urb);
+		if (dev->int_buffer)
+			usb_free_coherent(dev->udev, YUREX_BUF_SIZE,
+				dev->int_buffer, dev->urb->transfer_dma);
+		usb_free_urb(dev->urb);
+	}
+	kfree(dev);
+}
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver yurex_class = {
+	.name =		"yurex%d",
+	.fops =		&yurex_fops,
+	.minor_base =	YUREX_MINOR_BASE,
+};
+
+static void yurex_interrupt(struct urb *urb)
+{
+	struct usb_yurex *dev = urb->context;
+	unsigned char *buf = dev->int_buffer;
+	int status = urb->status;
+	unsigned long flags;
+	int retval, i;
+
+	switch (status) {
+	case 0: /*success*/
+		break;
+	case -EOVERFLOW:
+		dev_err(&dev->interface->dev,
+			"%s - overflow with length %d, actual length is %d\n",
+			__func__, YUREX_BUF_SIZE, dev->urb->actual_length);
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+	case -EILSEQ:
+		/* The device is terminated, clean up */
+		return;
+	default:
+		dev_err(&dev->interface->dev,
+			"%s - unknown status received: %d\n", __func__, status);
+		goto exit;
+	}
+
+	/* handle received message */
+	switch (buf[0]) {
+	case CMD_COUNT:
+	case CMD_READ:
+		if (buf[6] == CMD_EOF) {
+			spin_lock_irqsave(&dev->lock, flags);
+			dev->bbu = 0;
+			for (i = 1; i < 6; i++) {
+				dev->bbu += buf[i];
+				if (i != 5)
+					dev->bbu <<= 8;
+			}
+			dev_dbg(&dev->interface->dev, "%s count: %lld\n",
+				__func__, dev->bbu);
+			spin_unlock_irqrestore(&dev->lock, flags);
+
+			kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+		}
+		else
+			dev_dbg(&dev->interface->dev,
+				"data format error - no EOF\n");
+		break;
+	case CMD_ACK:
+		dev_dbg(&dev->interface->dev, "%s ack: %c\n",
+			__func__, buf[1]);
+		wake_up_interruptible(&dev->waitq);
+		break;
+	}
+
+exit:
+	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+	if (retval) {
+		dev_err(&dev->interface->dev, "%s - usb_submit_urb failed: %d\n",
+			__func__, retval);
+	}
+}
+
+static int yurex_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_yurex *dev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int retval = -ENOMEM;
+	int i;
+	DEFINE_WAIT(wait);
+
+	/* allocate memory for our device state and initialize it */
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		dev_err(&interface->dev, "Out of memory\n");
+		goto error;
+	}
+	kref_init(&dev->kref);
+	mutex_init(&dev->io_mutex);
+	spin_lock_init(&dev->lock);
+	init_waitqueue_head(&dev->waitq);
+
+	dev->udev = usb_get_dev(interface_to_usbdev(interface));
+	dev->interface = interface;
+
+	/* set up the endpoint information */
+	iface_desc = interface->cur_altsetting;
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(endpoint)) {
+			dev->int_in_endpointAddr = endpoint->bEndpointAddress;
+			break;
+		}
+	}
+	if (!dev->int_in_endpointAddr) {
+		retval = -ENODEV;
+		dev_err(&interface->dev, "Could not find endpoints\n");
+		goto error;
+	}
+
+
+	/* allocate control URB */
+	dev->cntl_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->cntl_urb) {
+		dev_err(&interface->dev, "Could not allocate control URB\n");
+		goto error;
+	}
+
+	/* allocate buffer for control req */
+	dev->cntl_req = kmalloc(YUREX_BUF_SIZE, GFP_KERNEL);
+	if (!dev->cntl_req) {
+		dev_err(&interface->dev, "Could not allocate cntl_req\n");
+		goto error;
+	}
+
+	/* allocate buffer for control msg */
+	dev->cntl_buffer = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE,
+					      GFP_KERNEL,
+					      &dev->cntl_urb->transfer_dma);
+	if (!dev->cntl_buffer) {
+		dev_err(&interface->dev, "Could not allocate cntl_buffer\n");
+		goto error;
+	}
+
+	/* configure control URB */
+	dev->cntl_req->bRequestType = USB_DIR_OUT | USB_TYPE_CLASS |
+				      USB_RECIP_INTERFACE;
+	dev->cntl_req->bRequest	= HID_REQ_SET_REPORT;
+	dev->cntl_req->wValue	= cpu_to_le16((HID_OUTPUT_REPORT + 1) << 8);
+	dev->cntl_req->wIndex	= cpu_to_le16(iface_desc->desc.bInterfaceNumber);
+	dev->cntl_req->wLength	= cpu_to_le16(YUREX_BUF_SIZE);
+
+	usb_fill_control_urb(dev->cntl_urb, dev->udev,
+			     usb_sndctrlpipe(dev->udev, 0),
+			     (void *)dev->cntl_req, dev->cntl_buffer,
+			     YUREX_BUF_SIZE, yurex_control_callback, dev);
+	dev->cntl_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+
+	/* allocate interrupt URB */
+	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb) {
+		dev_err(&interface->dev, "Could not allocate URB\n");
+		goto error;
+	}
+
+	/* allocate buffer for interrupt in */
+	dev->int_buffer = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE,
+					GFP_KERNEL, &dev->urb->transfer_dma);
+	if (!dev->int_buffer) {
+		dev_err(&interface->dev, "Could not allocate int_buffer\n");
+		goto error;
+	}
+
+	/* configure interrupt URB */
+	usb_fill_int_urb(dev->urb, dev->udev,
+			 usb_rcvintpipe(dev->udev, dev->int_in_endpointAddr),
+			 dev->int_buffer, YUREX_BUF_SIZE, yurex_interrupt,
+			 dev, 1);
+	dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	if (usb_submit_urb(dev->urb, GFP_KERNEL)) {
+		retval = -EIO;
+		dev_err(&interface->dev, "Could not submitting URB\n");
+		goto error;
+	}
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(interface, dev);
+	dev->bbu = -1;
+
+	/* we can register the device now, as it is ready */
+	retval = usb_register_dev(interface, &yurex_class);
+	if (retval) {
+		dev_err(&interface->dev,
+			"Not able to get a minor for this device.\n");
+		usb_set_intfdata(interface, NULL);
+		goto error;
+	}
+
+	dev_info(&interface->dev,
+		 "USB YUREX device now attached to Yurex #%d\n",
+		 interface->minor);
+
+	return 0;
+
+error:
+	if (dev)
+		/* this frees allocated memory */
+		kref_put(&dev->kref, yurex_delete);
+	return retval;
+}
+
+static void yurex_disconnect(struct usb_interface *interface)
+{
+	struct usb_yurex *dev;
+	int minor = interface->minor;
+
+	dev = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+
+	/* give back our minor */
+	usb_deregister_dev(interface, &yurex_class);
+
+	/* prevent more I/O from starting */
+	mutex_lock(&dev->io_mutex);
+	dev->interface = NULL;
+	mutex_unlock(&dev->io_mutex);
+
+	/* wakeup waiters */
+	kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+	wake_up_interruptible(&dev->waitq);
+
+	/* decrement our usage count */
+	kref_put(&dev->kref, yurex_delete);
+
+	dev_info(&interface->dev, "USB YUREX #%d now disconnected\n", minor);
+}
+
+static struct usb_driver yurex_driver = {
+	.name =		"yurex",
+	.probe =	yurex_probe,
+	.disconnect =	yurex_disconnect,
+	.id_table =	yurex_table,
+};
+
+
+static int yurex_fasync(int fd, struct file *file, int on)
+{
+	struct usb_yurex *dev;
+
+	dev = file->private_data;
+	return fasync_helper(fd, file, on, &dev->async_queue);
+}
+
+static int yurex_open(struct inode *inode, struct file *file)
+{
+	struct usb_yurex *dev;
+	struct usb_interface *interface;
+	int subminor;
+	int retval = 0;
+
+	subminor = iminor(inode);
+
+	interface = usb_find_interface(&yurex_driver, subminor);
+	if (!interface) {
+		printk(KERN_ERR "%s - error, can't find device for minor %d",
+		       __func__, subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	dev = usb_get_intfdata(interface);
+	if (!dev) {
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	/* increment our usage count for the device */
+	kref_get(&dev->kref);
+
+	/* save our object in the file's private structure */
+	mutex_lock(&dev->io_mutex);
+	file->private_data = dev;
+	mutex_unlock(&dev->io_mutex);
+
+exit:
+	return retval;
+}
+
+static int yurex_release(struct inode *inode, struct file *file)
+{
+	struct usb_yurex *dev;
+
+	dev = file->private_data;
+	if (dev == NULL)
+		return -ENODEV;
+
+	/* decrement the count on our device */
+	kref_put(&dev->kref, yurex_delete);
+	return 0;
+}
+
+static ssize_t yurex_read(struct file *file, char __user *buffer, size_t count,
+			  loff_t *ppos)
+{
+	struct usb_yurex *dev;
+	int retval = 0;
+	int bytes_read = 0;
+	char in_buffer[20];
+	unsigned long flags;
+
+	dev = file->private_data;
+
+	mutex_lock(&dev->io_mutex);
+	if (!dev->interface) {		/* already disconnected */
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	bytes_read = snprintf(in_buffer, 20, "%lld\n", dev->bbu);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (*ppos < bytes_read) {
+		if (copy_to_user(buffer, in_buffer + *ppos, bytes_read - *ppos))
+			retval = -EFAULT;
+		else {
+			retval = bytes_read - *ppos;
+			*ppos += bytes_read;
+		}
+	}
+
+exit:
+	mutex_unlock(&dev->io_mutex);
+	return retval;
+}
+
+static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
+			   size_t count, loff_t *ppos)
+{
+	struct usb_yurex *dev;
+	int i, set = 0, retval = 0;
+	char buffer[16];
+	char *data = buffer;
+	unsigned long long c, c2 = 0;
+	signed long timeout = 0;
+	DEFINE_WAIT(wait);
+
+	count = min(sizeof(buffer), count);
+	dev = file->private_data;
+
+	/* verify that we actually have some data to write */
+	if (count == 0)
+		goto error;
+
+	mutex_lock(&dev->io_mutex);
+	if (!dev->interface) {		/* already disconnected */
+		mutex_unlock(&dev->io_mutex);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if (copy_from_user(buffer, user_buffer, count)) {
+		mutex_unlock(&dev->io_mutex);
+		retval = -EFAULT;
+		goto error;
+	}
+	memset(dev->cntl_buffer, CMD_PADDING, YUREX_BUF_SIZE);
+
+	switch (buffer[0]) {
+	case CMD_ANIMATE:
+	case CMD_LED:
+		dev->cntl_buffer[0] = buffer[0];
+		dev->cntl_buffer[1] = buffer[1];
+		dev->cntl_buffer[2] = CMD_EOF;
+		break;
+	case CMD_READ:
+	case CMD_VERSION:
+		dev->cntl_buffer[0] = buffer[0];
+		dev->cntl_buffer[1] = 0x00;
+		dev->cntl_buffer[2] = CMD_EOF;
+		break;
+	case CMD_SET:
+		data++;
+		/* FALL THROUGH */
+	case '0' ... '9':
+		set = 1;
+		c = c2 = simple_strtoull(data, NULL, 0);
+		dev->cntl_buffer[0] = CMD_SET;
+		for (i = 1; i < 6; i++) {
+			dev->cntl_buffer[i] = (c>>32) & 0xff;
+			c <<= 8;
+		}
+		buffer[6] = CMD_EOF;
+		break;
+	default:
+		mutex_unlock(&dev->io_mutex);
+		return -EINVAL;
+	}
+
+	/* send the data as the control msg */
+	prepare_to_wait(&dev->waitq, &wait, TASK_INTERRUPTIBLE);
+	dev_dbg(&dev->interface->dev, "%s - submit %c\n", __func__,
+		dev->cntl_buffer[0]);
+	retval = usb_submit_urb(dev->cntl_urb, GFP_KERNEL);
+	if (retval >= 0)
+		timeout = schedule_timeout(YUREX_WRITE_TIMEOUT);
+	finish_wait(&dev->waitq, &wait);
+
+	mutex_unlock(&dev->io_mutex);
+
+	if (retval < 0) {
+		dev_err(&dev->interface->dev,
+			"%s - failed to send bulk msg, error %d\n",
+			__func__, retval);
+		goto error;
+	}
+	if (set && timeout)
+		dev->bbu = c2;
+	return timeout ? count : -EIO;
+
+error:
+	return retval;
+}
+
+static const struct file_operations yurex_fops = {
+	.owner =	THIS_MODULE,
+	.read =		yurex_read,
+	.write =	yurex_write,
+	.open =		yurex_open,
+	.release =	yurex_release,
+	.fasync	=	yurex_fasync,
+	.llseek =	default_llseek,
+};
+
+module_usb_driver(yurex_driver);
+
+MODULE_LICENSE("GPL");