Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6 
diff --git a/Documentation/input/appletouch.txt b/Documentation/input/appletouch.txt
new file mode 100644
index 0000000..b48d11d
--- /dev/null
+++ b/Documentation/input/appletouch.txt
@@ -0,0 +1,84 @@
+Apple Touchpad Driver (appletouch)
+----------------------------------
+	Copyright (C) 2005 Stelian Pop <stelian@popies.net>
+
+appletouch is a Linux kernel driver for the USB touchpad found on post
+February 2005 Apple Alu Powerbooks.
+
+This driver is derived from Johannes Berg's appletrackpad driver[1], but it has
+been improved in some areas:
+	* appletouch is a full kernel driver, no userspace program is necessary
+	* appletouch can be interfaced with the synaptics X11 driver, in order
+	  to have touchpad acceleration, scrolling, etc.
+
+Credits go to Johannes Berg for reverse-engineering the touchpad protocol,
+Frank Arnold for further improvements, and Alex Harper for some additional
+information about the inner workings of the touchpad sensors.
+
+Usage:
+------
+
+In order to use the touchpad in the basic mode, compile the driver and load
+the module. A new input device will be detected and you will be able to read
+the mouse data from /dev/input/mice (using gpm, or X11).
+
+In X11, you can configure the touchpad to use the synaptics X11 driver, which
+will give additional functionalities, like acceleration, scrolling, 2 finger
+tap for middle button mouse emulation, 3 finger tap for right button mouse
+emulation, etc. In order to do this, make sure you're using a recent version of
+the synaptics driver (tested with 0.14.2, available from [2]), and configure a
+new input device in your X11 configuration file (take a look below for an
+example). For additional configuration, see the synaptics driver documentation.
+
+	Section "InputDevice"
+        	Identifier      "Synaptics Touchpad"
+	        Driver          "synaptics"
+		Option          "SendCoreEvents"        "true"
+		Option          "Device"                "/dev/input/mice"
+		Option          "Protocol"              "auto-dev"
+		Option		"LeftEdge"		"0"
+		Option		"RightEdge"		"850"
+		Option		"TopEdge"		"0"
+		Option		"BottomEdge"		"645"
+		Option		"MinSpeed"		"0.4"
+		Option		"MaxSpeed"		"1"
+		Option		"AccelFactor"		"0.02"
+		Option		"FingerLow"		"0"
+		Option		"FingerHigh"		"30"
+		Option		"MaxTapMove"		"20"
+		Option		"MaxTapTime"		"100"
+		Option		"HorizScrollDelta"	"0"
+		Option		"VertScrollDelta"	"30"
+		Option		"SHMConfig"		"on"
+	EndSection
+
+	Section "ServerLayout"
+		...
+		InputDevice	"Mouse"
+		InputDevice	"Synaptics Touchpad"
+	...
+	EndSection
+
+Fuzz problems:
+--------------
+
+The touchpad sensors are very sensitive to heat, and will generate a lot of
+noise when the temperature changes. This is especially true when you power-on
+the laptop for the first time.
+
+The appletouch driver tries to handle this noise and auto adapt itself, but it
+is not perfect. If finger movements are not recognized anymore, try reloading
+the driver.
+
+You can activate debugging using the 'debug' module parameter. A value of 0
+deactivates any debugging, 1 activates tracing of invalid samples, 2 activates
+full tracing (each sample is being traced):
+	modprobe appletouch debug=1
+		or
+	echo "1" > /sys/module/appletouch/parameters/debug
+
+Links:
+------
+
+[1]: http://johannes.sipsolutions.net/PowerBook/touchpad/
+[2]: http://web.telia.com/~u89404340/touchpad/index.html
diff --git a/Documentation/usb/proc_usb_info.txt b/Documentation/usb/proc_usb_info.txt
index 729c72d..f86550f 100644
--- a/Documentation/usb/proc_usb_info.txt
+++ b/Documentation/usb/proc_usb_info.txt
@@ -20,7 +20,7 @@
 
 	  to /etc/fstab.  This will mount usbfs at each reboot.
 	  You can then issue `cat /proc/bus/usb/devices` to extract
-	  USB device information, and user mode drivers can use usbfs 
+	  USB device information, and user mode drivers can use usbfs
 	  to interact with USB devices.
 
 	  There are a number of mount options supported by usbfs.
@@ -32,7 +32,7 @@
 	  still see references to the older "usbdevfs" name.
 
 For more information on mounting the usbfs file system, see the
-"USB Device Filesystem" section of the USB Guide. The latest copy 
+"USB Device Filesystem" section of the USB Guide. The latest copy
 of the USB Guide can be found at http://www.linux-usb.org/
 
 
@@ -133,7 +133,7 @@
     are the only transfers that reserve bandwidth.  Control and bulk
     transfers use all other bandwidth, including reserved bandwidth that
     is not used for transfers (such as for short packets).
-    
+
     The percentage is how much of the "reserved" bandwidth is scheduled by
     those transfers.  For a low or full speed bus (loosely, "USB 1.1"),
     90% of the bus bandwidth is reserved.  For a high speed bus (loosely,
@@ -197,7 +197,7 @@
 | | |__NumberOfInterfaces
 | |__ "*" indicates the active configuration (others are " ")
 |__Config info tag
-    
+
     USB devices may have multiple configurations, each of which act
     rather differently.  For example, a bus-powered configuration
     might be much less capable than one that is self-powered.  Only
@@ -228,7 +228,7 @@
     For example, default settings may not use more than a small
     amount of periodic bandwidth.  To use significant fractions
     of bus bandwidth, drivers must select a non-default altsetting.
-    
+
     Only one setting for an interface may be active at a time, and
     only one driver may bind to an interface at a time.  Most devices
     have only one alternate setting per interface.
@@ -297,18 +297,21 @@
 C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
 I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
 E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=255ms
+
 T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 4
 D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
 P:  Vendor=0451 ProdID=1446 Rev= 1.00
 C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
 I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
 E:  Ad=81(I) Atr=03(Int.) MxPS=   1 Ivl=255ms
+
 T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5 MxCh= 0
 D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
 P:  Vendor=04b4 ProdID=0001 Rev= 0.00
 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
 I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
 E:  Ad=81(I) Atr=03(Int.) MxPS=   3 Ivl= 10ms
+
 T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12  MxCh= 0
 D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
 P:  Vendor=0565 ProdID=0001 Rev= 1.08
diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c
index f8f2156..5085827 100644
--- a/drivers/usb/class/audio.c
+++ b/drivers/usb/class/audio.c
@@ -631,8 +631,10 @@
 	i = u->flags;
 	spin_unlock_irqrestore(&as->lock, flags);
 	while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
-		set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
+		if (notkilled)
+			schedule_timeout_interruptible(1);
+		else
+			schedule_timeout_uninterruptible(1);
 		spin_lock_irqsave(&as->lock, flags);
 		i = u->flags;
 		spin_unlock_irqrestore(&as->lock, flags);
@@ -1102,8 +1104,10 @@
 	i = u->flags;
 	spin_unlock_irqrestore(&as->lock, flags);
 	while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) {
-		set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
+		if (notkilled)
+			schedule_timeout_interruptible(1);
+		else
+			schedule_timeout_uninterruptible(1);
 		spin_lock_irqsave(&as->lock, flags);
 		i = u->flags;
 		spin_unlock_irqrestore(&as->lock, flags);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 12ecdb0..1017a97 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1606,7 +1606,7 @@
 		return IRQ_NONE;
 
 	hcd->saw_irq = 1;
-	if (hcd->state != start && hcd->state == HC_STATE_HALT)
+	if (hcd->state == HC_STATE_HALT)
 		usb_hc_died (hcd);
 	return IRQ_HANDLED;
 }
@@ -1630,7 +1630,6 @@
 	spin_lock_irqsave (&hcd_root_hub_lock, flags);
 	if (hcd->rh_registered) {
 		hcd->poll_rh = 0;
-		del_timer(&hcd->rh_timer);
 
 		/* make khubd clean up old urbs and devices */
 		usb_set_device_state (hcd->self.root_hub,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 758c7f0..a12cab5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -435,6 +435,7 @@
 static void hub_power_on(struct usb_hub *hub)
 {
 	int port1;
+	unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
 
 	/* if hub supports power switching, enable power on each port */
 	if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
@@ -444,8 +445,8 @@
 					USB_PORT_FEAT_POWER);
 	}
 
-	/* Wait for power to be enabled */
-	msleep(hub->descriptor->bPwrOn2PwrGood * 2);
+	/* Wait at least 100 msec for power to become stable */
+	msleep(max(pgood_delay, (unsigned) 100));
 }
 
 static void hub_quiesce(struct usb_hub *hub)
@@ -1460,7 +1461,7 @@
 					port1, status);
 		else {
 			status = hub_port_wait_reset(hub, port1, udev, delay);
-			if (status)
+			if (status && status != -ENOTCONN)
 				dev_dbg(hub->intfdev,
 						"port_wait_reset: err = %d\n",
 						status);
@@ -1469,8 +1470,8 @@
 		/* return on disconnect or reset */
 		switch (status) {
 		case 0:
-			/* TRSTRCY = 10 ms */
-			msleep(10);
+			/* TRSTRCY = 10 ms; plus some extra */
+			msleep(10 + 40);
 			/* FALL THROUGH */
 		case -ENOTCONN:
 		case -ENODEV:
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 0208153..5c40980 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -483,6 +483,7 @@
 		data->state = STATE_EP_DISABLED;
 		data->desc.bDescriptorType = 0;
 		data->hs_desc.bDescriptorType = 0;
+		usb_ep_disable(data->ep);
 	}
 	put_ep (data);
 	return 0;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 2507e89..b948ffd 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -400,6 +400,23 @@
 				return -EIO;
 			}
 			break;
+		case PCI_VENDOR_ID_NVIDIA:
+			/* NVidia reports that certain chips don't handle
+			 * QH, ITD, or SITD addresses above 2GB.  (But TD,
+			 * data buffer, and periodic schedule are normal.)
+			 */
+			switch (pdev->device) {
+			case 0x003c:	/* MCP04 */
+			case 0x005b:	/* CK804 */
+			case 0x00d8:	/* CK8 */
+			case 0x00e8:	/* CK8S */
+				if (pci_set_consistent_dma_mask(pdev,
+							DMA_31BIT_MASK) < 0)
+					ehci_warn (ehci, "can't enable NVidia "
+						"workaround for >2GB RAM\n");
+				break;
+			}
+			break;
 		}
 
 		/* optional debug port, normally in the first BAR */
@@ -759,12 +776,16 @@
 	if (time_before (jiffies, ehci->next_statechange))
 		msleep (100);
 
-	/* If any port is suspended, we know we can/must resume the HC. */
+	/* If any port is suspended (or owned by the companion),
+	 * we know we can/must resume the HC (and mustn't reset it).
+	 */
 	for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) {
 		u32	status;
 		port--;
 		status = readl (&ehci->regs->port_status [port]);
-		if (status & PORT_SUSPEND) {
+		if (!(status & PORT_POWER))
+			continue;
+		if (status & (PORT_SUSPEND | PORT_OWNER)) {
 			down (&hcd->self.root_hub->serialize);
 			retval = ehci_hub_resume (hcd);
 			up (&hcd->self.root_hub->serialize);
@@ -1126,8 +1147,7 @@
 	case QH_STATE_UNLINK:		/* wait for hw to finish? */
 idle_timeout:
 		spin_unlock_irqrestore (&ehci->lock, flags);
-		set_current_state (TASK_UNINTERRUPTIBLE);
-		schedule_timeout (1);
+		schedule_timeout_uninterruptible(1);
 		goto rescan;
 	case QH_STATE_IDLE:		/* fully unlinked */
 		if (list_empty (&qh->qtd_list)) {
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 36cc1f2..18d3f22 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -54,7 +54,7 @@
 	/* suspend any active/unsuspended ports, maybe allow wakeup */
 	while (port--) {
 		u32 __iomem	*reg = &ehci->regs->port_status [port];
-		u32		t1 = readl (reg);
+		u32		t1 = readl (reg) & ~PORT_RWC_BITS;
 		u32		t2 = t1;
 
 		if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
@@ -115,7 +115,8 @@
 	i = HCS_N_PORTS (ehci->hcs_params);
 	while (i--) {
 		temp = readl (&ehci->regs->port_status [i]);
-		temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
+		temp &= ~(PORT_RWC_BITS
+			| PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E);
 		if (temp & PORT_SUSPEND) {
 			ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
 			temp |= PORT_RESUME;
@@ -128,7 +129,7 @@
 		temp = readl (&ehci->regs->port_status [i]);
 		if ((temp & PORT_SUSPEND) == 0)
 			continue;
-		temp &= ~PORT_RESUME;
+		temp &= ~(PORT_RWC_BITS | PORT_RESUME);
 		writel (temp, &ehci->regs->port_status [i]);
 		ehci_vdbg (ehci, "resumed port %d\n", i + 1);
 	}
@@ -191,6 +192,7 @@
 
 		// what happens if HCS_N_CC(params) == 0 ?
 		port_status |= PORT_OWNER;
+		port_status &= ~PORT_RWC_BITS;
 		writel (port_status, &ehci->regs->port_status [index]);
 
 	} else
@@ -233,7 +235,8 @@
 		if (temp & PORT_OWNER) {
 			/* don't report this in GetPortStatus */
 			if (temp & PORT_CSC) {
-				temp &= ~PORT_CSC;
+				temp &= ~PORT_RWC_BITS;
+				temp |= PORT_CSC;
 				writel (temp, &ehci->regs->port_status [i]);
 			}
 			continue;
@@ -343,7 +346,7 @@
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_ENABLE:
-			writel (temp | PORT_PEC,
+			writel((temp & ~PORT_RWC_BITS) | PORT_PEC,
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_SUSPEND:
@@ -353,7 +356,8 @@
 				if ((temp & PORT_PE) == 0)
 					goto error;
 				/* resume signaling for 20 msec */
-				writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
+				temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+				writel (temp | PORT_RESUME,
 					&ehci->regs->port_status [wIndex]);
 				ehci->reset_done [wIndex] = jiffies
 						+ msecs_to_jiffies (20);
@@ -364,15 +368,15 @@
 			break;
 		case USB_PORT_FEAT_POWER:
 			if (HCS_PPC (ehci->hcs_params))
-				writel (temp & ~PORT_POWER,
+				writel (temp & ~(PORT_RWC_BITS | PORT_POWER),
 					&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_CONNECTION:
-			writel (temp | PORT_CSC,
+			writel((temp & ~PORT_RWC_BITS) | PORT_CSC,
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_OVER_CURRENT:
-			writel (temp | PORT_OCC,
+			writel((temp & ~PORT_RWC_BITS) | PORT_OCC,
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_RESET:
@@ -416,7 +420,7 @@
 
 			/* stop resume signaling */
 			temp = readl (&ehci->regs->port_status [wIndex]);
-			writel (temp & ~PORT_RESUME,
+			writel (temp & ~(PORT_RWC_BITS | PORT_RESUME),
 				&ehci->regs->port_status [wIndex]);
 			retval = handshake (
 					&ehci->regs->port_status [wIndex],
@@ -437,7 +441,7 @@
 			ehci->reset_done [wIndex] = 0;
 
 			/* force reset to complete */
-			writel (temp & ~PORT_RESET,
+			writel (temp & ~(PORT_RWC_BITS | PORT_RESET),
 					&ehci->regs->port_status [wIndex]);
 			/* REVISIT:  some hardware needs 550+ usec to clear
 			 * this bit; seems too long to spin routinely...
@@ -500,6 +504,7 @@
 		if (temp & PORT_OWNER)
 			break;
 
+		temp &= ~PORT_RWC_BITS;
 		switch (wValue) {
 		case USB_PORT_FEAT_SUSPEND:
 			if ((temp & PORT_PE) == 0
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 20c9b55..f34a051 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -263,6 +263,7 @@
 #define PORT_PE		(1<<2)		/* port enable */
 #define PORT_CSC	(1<<1)		/* connect status change */
 #define PORT_CONNECT	(1<<0)		/* device connected */
+#define PORT_RWC_BITS   (PORT_CSC | PORT_PEC | PORT_OCC)
 } __attribute__ ((packed));
 
 /* Appendix C, Debug port ... intended for use with special "debug devices"
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 447f488..7924c74 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -228,23 +228,22 @@
 	char **next,
 	unsigned *size)
 {
-	u32			temp, ndp, i;
+	u32			temp, i;
 
 	temp = roothub_a (controller);
 	if (temp == ~(u32)0)
 		return;
-	ndp = (temp & RH_A_NDP);
 
 	if (verbose) {
 		ohci_dbg_sw (controller, next, size,
-			"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
+			"roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d(%d)\n", temp,
 			((temp & RH_A_POTPGT) >> 24) & 0xff,
 			(temp & RH_A_NOCP) ? " NOCP" : "",
 			(temp & RH_A_OCPM) ? " OCPM" : "",
 			(temp & RH_A_DT) ? " DT" : "",
 			(temp & RH_A_NPS) ? " NPS" : "",
 			(temp & RH_A_PSM) ? " PSM" : "",
-			ndp
+			(temp & RH_A_NDP), controller->num_ports
 			);
 		temp = roothub_b (controller);
 		ohci_dbg_sw (controller, next, size,
@@ -266,7 +265,7 @@
 			);
 	}
 
-	for (i = 0; i < ndp; i++) {
+	for (i = 0; i < controller->num_ports; i++) {
 		temp = roothub_portstatus (controller, i);
 		dbg_port_sw (controller, i, temp, next, size);
 	}
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 56b43f2..67c1aa5 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -382,8 +382,7 @@
 			goto sanitize;
 		}
 		spin_unlock_irqrestore (&ohci->lock, flags);
-		set_current_state (TASK_UNINTERRUPTIBLE);
-		schedule_timeout (1);
+		schedule_timeout_uninterruptible(1);
 		goto rescan;
 	case ED_IDLE:		/* fully unlinked */
 		if (list_empty (&ed->td_list)) {
@@ -485,6 +484,10 @@
 	// flush the writes
 	(void) ohci_readl (ohci, &ohci->regs->control);
 
+	/* Read the number of ports unless overridden */
+	if (ohci->num_ports == 0)
+		ohci->num_ports = roothub_a(ohci) & RH_A_NDP;
+
 	if (ohci->hcca)
 		return 0;
 
@@ -561,10 +564,8 @@
 	msleep(temp);
 	temp = roothub_a (ohci);
 	if (!(temp & RH_A_NPS)) {
-		unsigned ports = temp & RH_A_NDP; 
-
 		/* power down each port */
-		for (temp = 0; temp < ports; temp++)
+		for (temp = 0; temp < ohci->num_ports; temp++)
 			ohci_writel (ohci, RH_PS_LSDA,
 				&ohci->regs->roothub.portstatus [temp]);
 	}
@@ -720,6 +721,7 @@
 
 	if (ints & OHCI_INTR_RD) {
 		ohci_vdbg (ohci, "resume detect\n");
+		ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
 		if (hcd->state != HC_STATE_QUIESCING)
 			schedule_work(&ohci->rh_resume);
 	}
@@ -861,7 +863,7 @@
 		 * and that if we try to turn them back on the root hub
 		 * will respond to CSC processing.
 		 */
-		i = roothub_a (ohci) & RH_A_NDP;
+		i = ohci->num_ports;
 		while (i--)
 			ohci_writel (ohci, RH_PS_PSS,
 				&ohci->regs->roothub.portstatus [temp]);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 83ca454..ce7b28d 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -184,7 +184,7 @@
 	if (status != -EINPROGRESS)
 		return status;
 
-	temp = roothub_a (ohci) & RH_A_NDP;
+	temp = ohci->num_ports;
 	enables = 0;
 	while (temp--) {
 		u32 stat = ohci_readl (ohci,
@@ -304,7 +304,7 @@
 ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
-	int		ports, i, changed = 0, length = 1;
+	int		i, changed = 0, length = 1;
 	int		can_suspend = hcd->can_wakeup;
 	unsigned long	flags;
 
@@ -319,9 +319,10 @@
 		goto done;
 	}
 
-	ports = roothub_a (ohci) & RH_A_NDP; 
-	if (ports > MAX_ROOT_PORTS) {
-		ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports,
+	/* undocumented erratum seen on at least rev D */
+	if ((ohci->flags & OHCI_QUIRK_AMD756)
+			&& (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) {
+		ohci_warn (ohci, "bogus NDP, rereads as NDP=%d\n",
 			  ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
 		/* retry later; "should not happen" */
 		goto done;
@@ -332,13 +333,13 @@
 		buf [0] = changed = 1;
 	else
 		buf [0] = 0;
-	if (ports > 7) {
+	if (ohci->num_ports > 7) {
 		buf [1] = 0;
 		length++;
 	}
 
 	/* look at each port */
-	for (i = 0; i < ports; i++) {
+	for (i = 0; i < ohci->num_ports; i++) {
 		u32	status = roothub_portstatus (ohci, i);
 
 		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
@@ -395,15 +396,14 @@
 	struct usb_hub_descriptor	*desc
 ) {
 	u32		rh = roothub_a (ohci);
-	int		ports = rh & RH_A_NDP; 
 	u16		temp;
 
 	desc->bDescriptorType = 0x29;
 	desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
 	desc->bHubContrCurrent = 0;
 
-	desc->bNbrPorts = ports;
-	temp = 1 + (ports / 8);
+	desc->bNbrPorts = ohci->num_ports;
+	temp = 1 + (ohci->num_ports / 8);
 	desc->bDescLength = 7 + 2 * temp;
 
 	temp = 0;
@@ -421,7 +421,7 @@
 	rh = roothub_b (ohci);
 	memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
 	desc->bitmap [0] = rh & RH_B_DR;
-	if (ports > 7) {
+	if (ohci->num_ports > 7) {
 		desc->bitmap [1] = (rh & RH_B_DR) >> 8;
 		desc->bitmap [2] = 0xff;
 	} else
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index e5bc178..2fdb262 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -75,33 +75,6 @@
 	return 0;
 }
 
-/*
-  If you select PMM_PERPORT_MODE, you should set the port power
- */
-static int pxa27x_ohci_set_port_power( int port )
-{
-	if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
-	     && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
-		UHCRHPS(port) |= 0x100;
-		return 0;
-	}
-	return -1;
-}
-
-/*
-  If you select PMM_PERPORT_MODE, you should set the port power
- */
-static int pxa27x_ohci_clear_port_power( int port )
-{
-	if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) 
-	     && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
-		UHCRHPS(port) |= 0x200;
-		return 0;
-	}
-	 
-	return -1;
-}
-
 extern int usb_disabled(void);
 
 /*-------------------------------------------------------------------------*/
@@ -130,11 +103,17 @@
 		   Polarity Low to active low. Supply power to USB ports. */
 		UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
 			~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+
+		pxa27x_ohci_pmm_state = PMM_PERPORT_MODE;
 	}
 
 	UHCHR &= ~UHCHR_SSE;
 
 	UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
+
+	/* Clear any OTG Pin Hold */
+	if (PSSR & PSSR_OTGPH)
+		PSSR |= PSSR_OTGPH;
 }
 
 static void pxa27x_stop_hc(struct platform_device *dev)
@@ -198,17 +177,7 @@
 	pxa27x_start_hc(dev);
 
 	/* Select Power Management Mode */
-	pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
-
-	/* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
-	if (pxa27x_ohci_set_port_power(1) < 0)
-		printk(KERN_ERR "Setting port 1 power failed.\n");
-
-	if (pxa27x_ohci_clear_port_power(2) < 0)
-		printk(KERN_ERR "Setting port 2 power failed.\n");
-
-	if (pxa27x_ohci_clear_port_power(3) < 0)
-		printk(KERN_ERR "Setting port 3 power failed.\n");
+	pxa27x_ohci_select_pmm(pxa27x_ohci_pmm_state);
 
 	ohci_hcd_init(hcd_to_ohci(hcd));
 
@@ -258,6 +227,9 @@
 
 	ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
 
+	/* The value of NDP in roothub_a is incorrect on this hardware */
+	ohci->num_ports = 3;
+
 	if ((ret = ohci_init(ohci)) < 0)
 		return ret;
 
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 71cdd22..8a9b9d9 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -383,6 +383,7 @@
 	/*
 	 * driver state
 	 */
+	int			num_ports;
 	int			load [NUM_INTS];
 	u32 			hc_control;	/* copy of hc control reg */
 	unsigned long		next_statechange;	/* suspend/resume */
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 0d5d254..0c02489 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -97,14 +97,9 @@
 /* to make sure it doesn't hog all of the bandwidth */
 #define DEPTH_INTERVAL 5
 
-static inline void restart_timer(struct uhci_hcd *uhci)
-{
-	mod_timer(&uhci->stall_timer, jiffies + msecs_to_jiffies(100));
-}
-
-#include "uhci-hub.c"
 #include "uhci-debug.c"
 #include "uhci-q.c"
+#include "uhci-hub.c"
 
 /*
  * Make sure the controller is completely inactive, unable to
@@ -160,7 +155,6 @@
 {
 	reset_hc(uhci);
 	uhci->hc_inaccessible = 1;
-	del_timer(&uhci->stall_timer);
 }
 
 /*
@@ -287,8 +281,11 @@
 	/* Enable resume-detect interrupts if they work.
 	 * Then enter Global Suspend mode, still configured.
 	 */
-	int_enable = (resume_detect_interrupts_are_broken(uhci) ?
-			0 : USBINTR_RESUME);
+	uhci->working_RD = 1;
+	int_enable = USBINTR_RESUME;
+	if (resume_detect_interrupts_are_broken(uhci)) {
+		uhci->working_RD = int_enable = 0;
+	}
 	outw(int_enable, uhci->io_addr + USBINTR);
 	outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD);
 	mb();
@@ -315,7 +312,6 @@
 
 	uhci->rh_state = new_state;
 	uhci->is_stopped = UHCI_IS_STOPPED;
-	del_timer(&uhci->stall_timer);
 	uhci_to_hcd(uhci)->poll_rh = !int_enable;
 
 	uhci_scan_schedule(uhci, NULL);
@@ -335,7 +331,6 @@
 	mb();
 	uhci->rh_state = UHCI_RH_RUNNING;
 	uhci_to_hcd(uhci)->poll_rh = 1;
-	restart_timer(uhci);
 }
 
 static void wakeup_rh(struct uhci_hcd *uhci)
@@ -374,20 +369,6 @@
 	mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
 }
 
-static void stall_callback(unsigned long _uhci)
-{
-	struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->lock, flags);
-	uhci_scan_schedule(uhci, NULL);
-	check_fsbr(uhci);
-
-	if (!uhci->is_stopped)
-		restart_timer(uhci);
-	spin_unlock_irqrestore(&uhci->lock, flags);
-}
-
 static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
@@ -418,8 +399,10 @@
 					"host controller halted, "
 					"very bad!\n");
 				hc_died(uhci);
-				spin_unlock_irqrestore(&uhci->lock, flags);
-				return IRQ_HANDLED;
+
+				/* Force a callback in case there are
+				 * pending unlinks */
+				mod_timer(&hcd->rh_timer, jiffies);
 			}
 			spin_unlock_irqrestore(&uhci->lock, flags);
 		}
@@ -427,10 +410,11 @@
 
 	if (status & USBSTS_RD)
 		usb_hcd_poll_rh_status(hcd);
-
-	spin_lock_irqsave(&uhci->lock, flags);
-	uhci_scan_schedule(uhci, regs);
-	spin_unlock_irqrestore(&uhci->lock, flags);
+	else {
+		spin_lock_irqsave(&uhci->lock, flags);
+		uhci_scan_schedule(uhci, regs);
+		spin_unlock_irqrestore(&uhci->lock, flags);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -595,10 +579,6 @@
 
 	init_waitqueue_head(&uhci->waitqh);
 
-	init_timer(&uhci->stall_timer);
-	uhci->stall_timer.function = stall_callback;
-	uhci->stall_timer.data = (unsigned long) uhci;
-
 	uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
 			&dma_handle, 0);
 	if (!uhci->fl) {
@@ -745,11 +725,11 @@
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
 	spin_lock_irq(&uhci->lock);
-	reset_hc(uhci);
+	if (!uhci->hc_inaccessible)
+		reset_hc(uhci);
 	uhci_scan_schedule(uhci, NULL);
 	spin_unlock_irq(&uhci->lock);
 
-	del_timer_sync(&uhci->stall_timer);
 	release_uhci(uhci);
 }
 
@@ -811,13 +791,12 @@
 	 */
 	pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
 	uhci->hc_inaccessible = 1;
+	hcd->poll_rh = 0;
 
 	/* FIXME: Enable non-PME# remote wakeup? */
 
 done:
 	spin_unlock_irq(&uhci->lock);
-	if (rc == 0)
-		del_timer_sync(&hcd->rh_timer);
 	return rc;
 }
 
@@ -850,8 +829,11 @@
 
 	spin_unlock_irq(&uhci->lock);
 
-	if (hcd->poll_rh)
+	if (!uhci->working_RD) {
+		/* Suspended root hub needs to be polled */
+		hcd->poll_rh = 1;
 		usb_hcd_poll_rh_status(hcd);
+	}
 	return 0;
 }
 #endif
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index bf9c5f9..282f40b 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -345,9 +345,6 @@
 
 /*
  * This describes the full uhci information.
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs.
  */
 struct uhci_hcd {
 
@@ -360,8 +357,6 @@
 	struct dma_pool *qh_pool;
 	struct dma_pool *td_pool;
 
-	struct usb_bus *bus;
-
 	struct uhci_td *term_td;	/* Terminating TD, see UHCI bug */
 	struct uhci_qh *skelqh[UHCI_NUM_SKELQH];	/* Skeleton QH's */
 
@@ -380,6 +375,8 @@
 	unsigned int scan_in_progress:1;	/* Schedule scan is running */
 	unsigned int need_rescan:1;		/* Redo the schedule scan */
 	unsigned int hc_inaccessible:1;		/* HC is suspended or dead */
+	unsigned int working_RD:1;		/* Suspended root hub doesn't
+						   need to be polled */
 
 	/* Support for port suspend/resume/reset */
 	unsigned long port_c_suspend;		/* Bit-arrays of ports */
@@ -405,9 +402,7 @@
 	/* List of URB's awaiting completion callback */
 	struct list_head complete_list;		/* P: uhci->lock */
 
-	int rh_numports;
-
-	struct timer_list stall_timer;
+	int rh_numports;			/* Number of root-hub ports */
 
 	wait_queue_head_t waitqh;		/* endpoint_disable waiters */
 };
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 4eace2b..a71e48a 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -145,15 +145,16 @@
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 	unsigned long flags;
-	int status;
+	int status = 0;
 
 	spin_lock_irqsave(&uhci->lock, flags);
-	if (uhci->hc_inaccessible) {
-		status = 0;
-		goto done;
-	}
 
+	uhci_scan_schedule(uhci, NULL);
+	if (uhci->hc_inaccessible)
+		goto done;
+	check_fsbr(uhci);
 	uhci_check_ports(uhci);
+
 	status = get_hub_status_data(uhci, buf);
 
 	switch (uhci->rh_state) {
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index bbb36cd..ea0d168 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -33,7 +33,7 @@
 static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
 {
 	if (uhci->is_stopped)
-		mod_timer(&uhci->stall_timer, jiffies);
+		mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
 	uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
 }
 
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 482c4be..1e53934 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -286,3 +286,23 @@
 
 	  To compile this driver as a module, choose M here: the module will
 	  be called keyspan_remote.
+
+config USB_APPLETOUCH
+	tristate "Apple USB Touchpad support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use an Apple USB Touchpad.
+
+	  These are the touchpads that can be found on post-February 2005
+	  Apple Powerbooks (prior models have a Synaptics touchpad connected
+	  to the ADB bus).
+
+	  This driver provides a basic mouse driver but can be interfaced
+	  with the synaptics X11 driver to provide acceleration and
+	  scrolling in X11.
+
+	  For further information, see
+	  <file:Documentation/input/appletouch.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called appletouch.
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index 43b2f99..5e03b93 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -41,3 +41,4 @@
 obj-$(CONFIG_USB_ACECAD)	+= acecad.o
 obj-$(CONFIG_USB_YEALINK)	+= yealink.o
 obj-$(CONFIG_USB_XPAD)		+= xpad.o
+obj-$(CONFIG_USB_APPLETOUCH)	+= appletouch.o
diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c
new file mode 100644
index 0000000..e03c1c5
--- /dev/null
+++ b/drivers/usb/input/appletouch.c
@@ -0,0 +1,469 @@
+/*
+ * Apple USB Touchpad (for post-February 2005 PowerBooks) driver
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005      Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
+ *
+ * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <linux/usb_input.h>
+
+/* Apple has powerbooks which have the keyboard with different Product IDs */
+#define APPLE_VENDOR_ID		0x05AC
+
+#define ATP_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 = 0x03,				\
+	.bInterfaceProtocol = 0x02
+
+/* table of devices that work with this driver */
+static struct usb_device_id atp_table [] = {
+	{ ATP_DEVICE(0x020E) },
+	{ ATP_DEVICE(0x020F) },
+	{ ATP_DEVICE(0x030A) },
+	{ ATP_DEVICE(0x030B) },
+	{ }					/* Terminating entry */
+};
+MODULE_DEVICE_TABLE (usb, atp_table);
+
+/* size of a USB urb transfer */
+#define ATP_DATASIZE	81
+
+/*
+ * number of sensors. Note that only 16 instead of 26 X (horizontal)
+ * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
+ * (vertical) sensors.
+ */
+#define ATP_XSENSORS	26
+#define ATP_YSENSORS	16
+
+/* amount of fuzz this touchpad generates */
+#define ATP_FUZZ	16
+
+/* maximum pressure this driver will report */
+#define ATP_PRESSURE	300
+/*
+ * multiplication factor for the X and Y coordinates.
+ * We try to keep the touchpad aspect ratio while still doing only simple
+ * arithmetics.
+ * The factors below give coordinates like:
+ * 	0 <= x <  960 on 12" and 15" Powerbooks
+ * 	0 <= x < 1600 on 17" Powerbooks
+ * 	0 <= y <  646
+ */
+#define ATP_XFACT	64
+#define ATP_YFACT	43
+
+/*
+ * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
+ * ignored.
+ */
+#define ATP_THRESHOLD	 5
+
+/* Structure to hold all of our device specific stuff */
+struct atp {
+	struct usb_device *	udev;		/* usb device */
+	struct urb *		urb;		/* usb request block */
+	signed char *		data;		/* transferred data */
+	int			open;		/* non-zero if opened */
+	struct input_dev	input;		/* input dev */
+	int			valid;		/* are the sensors valid ? */
+	int			x_old;		/* last reported x/y, */
+	int			y_old;		/* used for smoothing */
+						/* current value of the sensors */
+	signed char		xy_cur[ATP_XSENSORS + ATP_YSENSORS];
+						/* last value of the sensors */
+	signed char		xy_old[ATP_XSENSORS + ATP_YSENSORS];
+						/* accumulated sensors */
+	int			xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+};
+
+#define dbg_dump(msg, tab) \
+	if (debug > 1) {						\
+		int i;							\
+		printk("appletouch: %s %lld", msg, (long long)jiffies); \
+		for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++)	\
+			printk(" %02x", tab[i]); 			\
+		printk("\n"); 						\
+	}
+
+#define dprintk(format, a...) 						\
+	do {								\
+		if (debug) printk(format, ##a);				\
+	} while (0)
+
+MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold");
+MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
+MODULE_LICENSE("GPL");
+
+static int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
+			     int *z, int *fingers)
+{
+	int i;
+	/* values to calculate mean */
+	int pcum = 0, psum = 0;
+
+	*fingers = 0;
+
+	for (i = 0; i < nb_sensors; i++) {
+		if (xy_sensors[i] < ATP_THRESHOLD)
+			continue;
+		if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD))
+			(*fingers)++;
+		pcum += xy_sensors[i] * i;
+		psum += xy_sensors[i];
+	}
+
+	if (psum > 0) {
+		*z = psum;
+		return pcum * fact / psum;
+	}
+
+	return 0;
+}
+
+static inline void atp_report_fingers(struct input_dev *input, int fingers)
+{
+	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
+	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
+	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
+}
+
+static void atp_complete(struct urb* urb, struct pt_regs* regs)
+{
+	int x, y, x_z, y_z, x_f, y_f;
+	int retval, i;
+	struct atp *dev = urb->context;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* This urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	/* drop incomplete datasets */
+	if (dev->urb->actual_length != ATP_DATASIZE) {
+		dprintk("appletouch: incomplete data package.\n");
+		goto exit;
+	}
+
+	/* reorder the sensors values */
+	for (i = 0; i < 8; i++) {
+		/* X values */
+		dev->xy_cur[i     ] = dev->data[5 * i +  2];
+		dev->xy_cur[i +  8] = dev->data[5 * i +  4];
+		dev->xy_cur[i + 16] = dev->data[5 * i + 42];
+		if (i < 2)
+			dev->xy_cur[i + 24] = dev->data[5 * i + 44];
+
+		/* Y values */
+		dev->xy_cur[i + 26] = dev->data[5 * i +  1];
+		dev->xy_cur[i + 34] = dev->data[5 * i +  3];
+	}
+
+	dbg_dump("sample", dev->xy_cur);
+
+	if (!dev->valid) {
+		/* first sample */
+		dev->valid = 1;
+		dev->x_old = dev->y_old = -1;
+		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+		/* 17" Powerbooks have 10 extra X sensors */
+		for (i = 16; i < ATP_XSENSORS; i++)
+			if (dev->xy_cur[i]) {
+				printk("appletouch: 17\" model detected.\n");
+				input_set_abs_params(&dev->input, ABS_X, 0,
+				     		     (ATP_XSENSORS - 1) *
+						     ATP_XFACT - 1,
+						     ATP_FUZZ, 0);
+				break;
+			}
+
+		goto exit;
+	}
+
+	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
+		/* accumulate the change */
+		signed char change = dev->xy_old[i] - dev->xy_cur[i];
+		dev->xy_acc[i] -= change;
+
+		/* prevent down drifting */
+		if (dev->xy_acc[i] < 0)
+			dev->xy_acc[i] = 0;
+	}
+
+	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+	dbg_dump("accumulator", dev->xy_acc);
+
+	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
+			      ATP_XFACT, &x_z, &x_f);
+	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
+			      ATP_YFACT, &y_z, &y_f);
+
+	if (x && y) {
+		if (dev->x_old != -1) {
+			x = (dev->x_old * 3 + x) >> 2;
+			y = (dev->y_old * 3 + y) >> 2;
+			dev->x_old = x;
+			dev->y_old = y;
+
+			if (debug > 1)
+				printk("appletouch: X: %3d Y: %3d "
+				       "Xz: %3d Yz: %3d\n",
+				       x, y, x_z, y_z);
+
+			input_report_key(&dev->input, BTN_TOUCH, 1);
+			input_report_abs(&dev->input, ABS_X, x);
+			input_report_abs(&dev->input, ABS_Y, y);
+			input_report_abs(&dev->input, ABS_PRESSURE,
+					 min(ATP_PRESSURE, x_z + y_z));
+			atp_report_fingers(&dev->input, max(x_f, y_f));
+		}
+		dev->x_old = x;
+		dev->y_old = y;
+	}
+	else if (!x && !y) {
+
+		dev->x_old = dev->y_old = -1;
+		input_report_key(&dev->input, BTN_TOUCH, 0);
+		input_report_abs(&dev->input, ABS_PRESSURE, 0);
+		atp_report_fingers(&dev->input, 0);
+
+		/* reset the accumulator on release */
+		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+	}
+
+	input_report_key(&dev->input, BTN_LEFT, !!dev->data[80]);
+
+	input_sync(&dev->input);
+
+exit:
+	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s - usb_submit_urb failed with result %d",
+		    __FUNCTION__, retval);
+	}
+}
+
+static int atp_open(struct input_dev *input)
+{
+	struct atp *dev = input->private;
+
+	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
+		return -EIO;
+
+	dev->open = 1;
+	return 0;
+}
+
+static void atp_close(struct input_dev *input)
+{
+	struct atp *dev = input->private;
+
+	usb_kill_urb(dev->urb);
+	dev->open = 0;
+}
+
+static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+	struct atp *dev = NULL;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int int_in_endpointAddr = 0;
+	int i, retval = -ENOMEM;
+
+	/* allocate memory for our device state and initialize it */
+	dev = kmalloc(sizeof(struct atp), GFP_KERNEL);
+	if (dev == NULL) {
+		err("Out of memory");
+		goto err_kmalloc;
+	}
+	memset(dev, 0, sizeof(struct atp));
+
+	dev->udev = interface_to_usbdev(iface);
+
+	/* 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 &&
+		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
+		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+					== USB_ENDPOINT_XFER_INT)) {
+			/* we found an interrupt in endpoint */
+			int_in_endpointAddr = endpoint->bEndpointAddress;
+			break;
+		}
+	}
+	if (!int_in_endpointAddr) {
+		retval = -EIO;
+		err("Could not find int-in endpoint");
+		goto err_endpoint;
+	}
+
+	/* save our data pointer in this interface device */
+	usb_set_intfdata(iface, dev);
+
+	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->urb) {
+		retval = -ENOMEM;
+		goto err_usballoc;
+	}
+	dev->data = usb_buffer_alloc(dev->udev, ATP_DATASIZE, GFP_KERNEL,
+				     &dev->urb->transfer_dma);
+	if (!dev->data) {
+		retval = -ENOMEM;
+		goto err_usbbufalloc;
+	}
+	usb_fill_int_urb(dev->urb, dev->udev,
+			 usb_rcvintpipe(dev->udev, int_in_endpointAddr),
+			 dev->data, ATP_DATASIZE, atp_complete, dev, 1);
+
+	init_input_dev(&dev->input);
+	dev->input.name = "appletouch";
+	dev->input.dev = &iface->dev;
+	dev->input.private = dev;
+	dev->input.open = atp_open;
+	dev->input.close = atp_close;
+
+	usb_to_input_id(dev->udev, &dev->input.id);
+
+	set_bit(EV_ABS, dev->input.evbit);
+
+	/*
+	 * 12" and 15" Powerbooks only have 16 x sensors,
+	 * 17" models are detected later.
+	 */
+	input_set_abs_params(&dev->input, ABS_X, 0,
+			     (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
+	input_set_abs_params(&dev->input, ABS_Y, 0,
+			     (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
+	input_set_abs_params(&dev->input, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
+
+	set_bit(EV_KEY, dev->input.evbit);
+	set_bit(BTN_TOUCH, dev->input.keybit);
+	set_bit(BTN_TOOL_FINGER, dev->input.keybit);
+	set_bit(BTN_TOOL_DOUBLETAP, dev->input.keybit);
+	set_bit(BTN_TOOL_TRIPLETAP, dev->input.keybit);
+	set_bit(BTN_LEFT, dev->input.keybit);
+
+	input_register_device(&dev->input);
+
+	printk(KERN_INFO "input: appletouch connected\n");
+
+	return 0;
+
+err_usbbufalloc:
+	usb_free_urb(dev->urb);
+err_usballoc:
+	usb_set_intfdata(iface, NULL);
+err_endpoint:
+	kfree(dev);
+err_kmalloc:
+	return retval;
+}
+
+static void atp_disconnect(struct usb_interface *iface)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+
+	usb_set_intfdata(iface, NULL);
+	if (dev) {
+		usb_kill_urb(dev->urb);
+		input_unregister_device(&dev->input);
+		usb_free_urb(dev->urb);
+		usb_buffer_free(dev->udev, ATP_DATASIZE,
+				dev->data, dev->urb->transfer_dma);
+		kfree(dev);
+	}
+	printk(KERN_INFO "input: appletouch disconnected\n");
+}
+
+static int atp_suspend(struct usb_interface *iface, pm_message_t message)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+	usb_kill_urb(dev->urb);
+	dev->valid = 0;
+	return 0;
+}
+
+static int atp_resume(struct usb_interface *iface)
+{
+	struct atp *dev = usb_get_intfdata(iface);
+	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
+		return -EIO;
+
+	return 0;
+}
+
+static struct usb_driver atp_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "appletouch",
+	.probe		= atp_probe,
+	.disconnect	= atp_disconnect,
+	.suspend	= atp_suspend,
+	.resume		= atp_resume,
+	.id_table	= atp_table,
+};
+
+static int __init atp_init(void)
+{
+	return usb_register(&atp_driver);
+}
+
+static void __exit atp_exit(void)
+{
+	usb_deregister(&atp_driver);
+}
+
+module_init(atp_init);
+module_exit(atp_exit);
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index e108e0a..a99865c 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1446,7 +1446,6 @@
 
 #define USB_VENDOR_ID_APPLE		0x05ac
 #define USB_DEVICE_ID_APPLE_POWERMOUSE	0x0304
-#define USB_DEVICE_ID_APPLE_BLUETOOTH	0x1000
 
 /*
  * Alphabetically sorted blacklist by quirk type.
@@ -1465,7 +1464,6 @@
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_BLUETOOTH, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
 	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig
index 3957e14..7603cbe 100644
--- a/drivers/usb/misc/sisusbvga/Kconfig
+++ b/drivers/usb/misc/sisusbvga/Kconfig
@@ -4,11 +4,43 @@
 	depends on USB && USB_EHCI_HCD
         ---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 
+	  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 sisusb.  If unsure, say N.
+	  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
index 76f1643..7f934cf 100644
--- a/drivers/usb/misc/sisusbvga/Makefile
+++ b/drivers/usb/misc/sisusbvga/Makefile
@@ -2,5 +2,7 @@
 # Makefile for the sisusb driver (if driver is inside kernel tree).
 #
 
-obj-$(CONFIG_USB_SISUSBVGA) += sisusb.o
+obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga.o
+
+sisusbvga-objs := sisusb.o sisusb_init.o sisusb_con.o
 
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index d63ce6c..39db315 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -1,6 +1,8 @@
 /*
  * 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
@@ -48,16 +50,60 @@
 #include <linux/kref.h>
 #include <linux/usb.h>
 #include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
 
 #include "sisusb.h"
 
+#ifdef INCL_SISUSB_CON
+#include <linux/font.h>
+#endif
+
 #define SISUSB_DONTSYNC
 
 /* Forward declarations / clean-up routines */
 
+#ifdef INCL_SISUSB_CON
+int	sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
+int	sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data);
+int	sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data);
+int	sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data);
+int	sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,	u8 myand, u8 myor);
+int	sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor);
+int	sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand);
+
+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_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);
+int	sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *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);
+
+extern int  SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
+extern int  SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
+
+extern void sisusb_init_concode(void);
+extern int  sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last);
+extern void sisusb_console_exit(struct sisusb_usb_data *sisusb);
+
+extern void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location);
+
+extern 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);
+
+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 DECLARE_MUTEX(disconnect_sem);
+DECLARE_MUTEX(disconnect_sem);
 
 static void
 sisusb_free_buffers(struct sisusb_usb_data *sisusb)
@@ -639,7 +685,10 @@
 
 /* The following routines assume being used to transfer byte, word,
  * long etc.
- * This means that they assume "data" in machine endianness format.
+ * 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,
@@ -839,7 +888,7 @@
 				if (get_user(swap16, (u16 __user *)userbuffer))
 					return -EFAULT;
 			} else
-				swap16 = (kernbuffer[0] << 8) | kernbuffer[1];
+				swap16 = *((u16 *)kernbuffer);
 
 			ret = sisusb_write_memio_word(sisusb,
 							SISUSB_TYPE_MEM,
@@ -855,14 +904,25 @@
 			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,
@@ -879,10 +939,7 @@
 				if (get_user(swap32, (u32 __user *)userbuffer))
 					return -EFAULT;
 			} else
-				swap32 = (kernbuffer[0] << 24) |
-					 (kernbuffer[1] << 16) |
-					 (kernbuffer[2] <<  8) |
-					 kernbuffer[3];
+				swap32 = *((u32 *)kernbuffer);
 
 			ret = sisusb_write_memio_long(sisusb,
 							SISUSB_TYPE_MEM,
@@ -1005,6 +1062,10 @@
 	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)
 {
@@ -1191,8 +1252,7 @@
 						(u16 __user *)userbuffer))
 						return -EFAULT;
 				} else {
-					kernbuffer[0] = swap16 >> 8;
-					kernbuffer[1] = swap16 & 0xff;
+					*((u16 *)kernbuffer) = swap16;
 				}
 			}
 			return ret;
@@ -1202,9 +1262,15 @@
 								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;
@@ -1228,10 +1294,7 @@
 
 					userbuffer += 4;
 				} else {
-					kernbuffer[0] = (swap32 >> 24) & 0xff;
-					kernbuffer[1] = (swap32 >> 16) & 0xff;
-					kernbuffer[2] = (swap32 >> 8) & 0xff;
-					kernbuffer[3] = swap32 & 0xff;
+					*((u32 *)kernbuffer) = swap32;
 					kernbuffer += 4;
 				}
 				addr += 4;
@@ -1289,7 +1352,24 @@
 
 /* High level: Gfx (indexed) register access */
 
-static int
+#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
+
+#ifndef INCL_SISUSB_CON
+static
+#endif
+int
 sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
 {
 	int ret;
@@ -1298,7 +1378,10 @@
 	return ret;
 }
 
-static int
+#ifndef INCL_SISUSB_CON
+static
+#endif
+int
 sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
 {
 	int ret;
@@ -1307,7 +1390,10 @@
 	return ret;
 }
 
-static int
+#ifndef INCL_SISUSB_CON
+static
+#endif
+int
 sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
 							u8 myand, u8 myor)
 {
@@ -1336,18 +1422,89 @@
 	return ret;
 }
 
-static int
+#ifndef INCL_SISUSB_CON
+static
+#endif
+int
 sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
 {
 	return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
 }
 
-static int
+#ifndef INCL_SISUSB_CON
+static
+#endif
+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_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data)
+{
+	return(sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, adr, data));
+}
+
+int
+sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data)
+{
+	return(sisusb_read_memio_word(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++) {
+        printk(KERN_DEBUG "sisusb: rwtest %d bytes\n", i);
+	sisusb_read_memory(sisusb, destbuffer, sisusb->vrambase, i, &dummy);
+	for(j = 0; j < i; j++) {
+	     printk(KERN_DEBUG "sisusb: rwtest read[%d] = %x\n", j, destbuffer[j]);
+	}
+    }
+}
+#endif
+
 /* access pci config registers (reg numbers 0, 4, 8, etc) */
 
 static int
@@ -2270,6 +2427,129 @@
 	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;
+	struct font_desc *myfont;
+	u8 *tempbuf;
+	u16 *tempbufb;
+	size_t written;
+	static char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer.";
+	static 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);
+
+	if (!(myfont = find_font("VGA8x16")))
+		return 1;
+
+	if (!(tempbuf = vmalloc(8192)))
+		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) {
+
+		if ((tempbuf = vmalloc(8192))) {
+
+			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
@@ -2329,7 +2609,7 @@
 		}
 	}
 
-	/* increment usage count for the device */
+	/* Increment usage count for our sisusb */
 	kref_get(&sisusb->kref);
 
 	sisusb->isopen = 1;
@@ -2340,12 +2620,10 @@
 
 	up(&disconnect_sem);
 
-	printk(KERN_DEBUG "sisusbvga[%d]: opened", sisusb->minor);
-
 	return 0;
 }
 
-static void
+void
 sisusb_delete(struct kref *kref)
 {
 	struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
@@ -2359,6 +2637,9 @@
 	sisusb->sisusb_dev = NULL;
 	sisusb_free_buffers(sisusb);
 	sisusb_free_urbs(sisusb);
+#ifdef INCL_SISUSB_CON
+	kfree(sisusb->SiS_Pr);
+#endif
 	kfree(sisusb);
 }
 
@@ -2395,8 +2676,6 @@
 
 	up(&disconnect_sem);
 
-	printk(KERN_DEBUG "sisusbvga[%d]: released", myminor);
-
 	return 0;
 }
 
@@ -2733,6 +3012,12 @@
 	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;
@@ -2774,6 +3059,10 @@
 			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 +
@@ -2781,11 +3070,61 @@
 			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)
+	if (retval > 0)
 		retval = -EIO;
 
 	return retval;
@@ -2835,6 +3174,11 @@
 			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
 
 			if (copy_to_user((void __user *)arg, &x, sizeof(x)))
 				retval = -EFAULT;
@@ -2895,9 +3239,13 @@
 };
 
 static struct usb_class_driver usb_sisusb_class = {
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
 	.name =		"usb/sisusbvga%d",
-	.fops =		&usb_sisusb_fops,
 	.mode =		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+#else
+	.name =		"sisusbvga%d",
+#endif
+	.fops =		&usb_sisusb_fops,
 	.minor_base =	SISUSB_MINOR
 };
 
@@ -2994,12 +3342,25 @@
 	printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n",
 					sisusb->minor, sisusb->numobufs);
 
+#ifdef INCL_SISUSB_CON
+	/* Allocate our SiS_Pr */
+	if (!(sisusb->SiS_Pr = kmalloc(sizeof(struct SiS_Private), GFP_KERNEL))) {
+		printk(KERN_ERR
+			"sisusbvga[%d]: Failed to allocate SiS_Pr\n",
+			sisusb->minor);
+	}
+#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;
+
 #ifdef SISUSB_OLD_CONFIG_COMPAT
 	{
 	int ret;
@@ -3014,14 +3375,19 @@
 			sisusb->minor);
 	else
 		sisusb->ioctl32registered = 1;
-
 	}
 #endif
 
-	sisusb->present = 1;
-
 	if (dev->speed == USB_SPEED_HIGH) {
-		if (sisusb_init_gfxdevice(sisusb, 1))
+		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))
 			printk(KERN_ERR
 				"sisusbvga[%d]: Failed to early "
 				"initialize device\n",
@@ -3035,6 +3401,16 @@
 
 	sisusb->ready = 1;
 
+#ifdef SISUSBENDIANTEST
+	printk(KERN_DEBUG "sisusb: *** RWTEST ***\n");
+	sisusb_testreadwrite(sisusb);
+	printk(KERN_DEBUG "sisusb: *** RWTEST END ***\n");
+#endif
+
+#ifdef INCL_SISUSB_CON
+	sisusb_console_init(sisusb, sisusb_first_vc, sisusb_last_vc);
+#endif
+
 	return 0;
 
 error_4:
@@ -3053,13 +3429,20 @@
 	struct sisusb_usb_data *sisusb;
 	int minor;
 
-	down(&disconnect_sem);
-
 	/* This should *not* happen */
-	if (!(sisusb = usb_get_intfdata(intf))) {
-		up(&disconnect_sem);
+	if (!(sisusb = usb_get_intfdata(intf)))
 		return;
-	}
+
+#ifdef INCL_SISUSB_CON
+	sisusb_console_exit(sisusb);
+#endif
+
+	/* The above code doesn't need the disconnect
+	 * semaphore to be down; its meaning is to
+	 * protect all other routines from the disconnect
+	 * case, not the other way round.
+	 */
+	down(&disconnect_sem);
 
 	down(&sisusb->lock);
 
@@ -3123,11 +3506,17 @@
 {
 	int retval;
 
+#ifdef INCL_SISUSB_CON
+	sisusb_init_concode();
+#endif
+
 	if (!(retval = usb_register(&sisusb_driver))) {
+
 		printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n",
 			SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL);
 		printk(KERN_INFO
 			"sisusb: Copyright (C) 2005 Thomas Winischhofer\n");
+
 	}
 
 	return retval;
@@ -3142,6 +3531,6 @@
 module_exit(usb_sisusb_exit);
 
 MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
-MODULE_DESCRIPTION("sisusb - Driver for Net2280/SiS315-based USB2VGA dongles");
+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
index 1306d00..401ff21 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.h
+++ b/drivers/usb/misc/sisusbvga/sisusb.h
@@ -46,15 +46,36 @@
 #endif
 #endif
 
+/* For older kernels, support for text consoles is by default
+ * off. To ensable text console support, change the following:
+ */
+#if 0
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13)
+#define CONFIG_USB_SISUSBVGA_CON
+#endif
+#endif
+
 /* Version Information */
 
 #define SISUSB_VERSION		0
 #define SISUSB_REVISION 	0
-#define SISUSB_PATCHLEVEL	7
+#define SISUSB_PATCHLEVEL	8
+
+/* Include console and mode switching code? */
+
+#ifdef CONFIG_USB_SISUSBVGA_CON
+#define INCL_SISUSB_CON		1
+#endif
+
+#ifdef INCL_SISUSB_CON
+#include <linux/console.h>
+#include <linux/vt_kern.h>
+#include "sisusb_struct.h"
+#endif
 
 /* USB related */
 
-#define SISUSB_MINOR	133		/* FIXME */
+#define SISUSB_MINOR		133	/* official */
 
 /* Size of the sisusb input/output buffers */
 #define SISUSB_IBUF_SIZE  0x01000
@@ -131,6 +152,26 @@
 	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)
@@ -249,7 +290,9 @@
 
 	__u32   sisusb_fbdevactive;	/* != 0 if framebuffer device active */
 
-	__u8	sisusb_reserved[32];	/* for future use */
+	__u32   sisusb_conactive;	/* != 0 if console driver active */
+
+	__u8	sisusb_reserved[28];	/* for future use */
 };
 
 struct sisusb_command {
@@ -261,18 +304,24 @@
 	__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_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_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)
+#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..2458446
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -0,0 +1,1658 @@
+/*
+ * 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/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/kd.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/smp_lock.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+
+#include "sisusb.h"
+
+#ifdef INCL_SISUSB_CON
+extern int sisusb_setreg(struct sisusb_usb_data *, int, u8);
+extern int sisusb_getreg(struct sisusb_usb_data *, int, u8 *);
+extern int sisusb_setidxreg(struct sisusb_usb_data *, int, u8, u8);
+extern int sisusb_getidxreg(struct sisusb_usb_data *, int, u8, u8 *);
+extern int sisusb_setidxregor(struct sisusb_usb_data *, int, u8, u8);
+extern int sisusb_setidxregand(struct sisusb_usb_data *, int, u8, u8);
+extern int sisusb_setidxregandor(struct sisusb_usb_data *, int, u8, u8, u8);
+
+extern int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
+extern int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);
+extern int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);
+extern int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data);
+extern int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
+			u32 dest, int length, size_t *bytes_written);
+
+extern void sisusb_delete(struct kref *kref);
+extern int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
+
+extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
+
+#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;
+
+extern struct semaphore disconnect_sem;
+
+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;
+
+	if (!(sisusb = sisusb_get_sisusb(console)))
+		return NULL;
+
+	down(&sisusb->lock);
+
+	if (!sisusb_sisusb_valid(sisusb) ||
+	    !sisusb->havethisconsole[console]) {
+		up(&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 take_over_console(),
+	 * ie by us/under our control. It is
+	 * only called after text mode and fonts
+	 * are set up/restored.
+	 */
+
+	down(&disconnect_sem);
+
+	if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
+		up(&disconnect_sem);
+		return;
+	}
+
+	down(&sisusb->lock);
+
+	if (!sisusb_sisusb_valid(sisusb)) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		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);
+
+	up(&sisusb->lock);
+
+	up(&disconnect_sem);
+
+	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 take_over_console()
+	 * and others, ie not under our control.
+	 */
+
+	down(&disconnect_sem);
+
+	if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
+		up(&disconnect_sem);
+		return;
+	}
+
+	down(&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;
+		}
+	}
+
+	up(&sisusb->lock);
+
+	/* decrement the usage count on our sisusb */
+	kref_put(&sisusb->kref, sisusb_delete);
+
+	up(&disconnect_sem);
+}
+
+/* interface routine */
+static u8
+sisusbcon_build_attr(struct vc_data *c, u8 color, u8 intensity,
+			    u8 blink, u8 underline, u8 reverse)
+{
+	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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return;
+
+	/* sisusb->lock is down */
+
+	/* Don't need to put the character into buffer ourselves,
+	 * because the vt does this BEFORE calling us.
+	 */
+#if 0
+	sisusbcon_writew(ch, SISUSB_VADDR(x, y));
+#endif
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&sisusb->lock);
+		return;
+	}
+
+
+	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
+				(u32)SISUSB_HADDR(x, y), 2, &written);
+
+	up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		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)) {
+		up(&sisusb->lock);
+		return;
+	}
+
+	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
+				(u32)SISUSB_HADDR(x, y), count * 2, &written);
+
+	up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		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)) {
+		up(&sisusb->lock);
+		return;
+	}
+
+	length = ((height * cols) - x - (cols - width - x)) * 2;
+
+
+	sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y),
+				(u32)SISUSB_HADDR(x, y), length, &written);
+
+	up(&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 0
+	u16 *src, *dest;
+	int i;
+#endif
+
+	if (width <= 0 || height <= 0)
+		return;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return;
+
+	/* sisusb->lock is down */
+
+	cols = sisusb->sisusb_num_columns;
+
+	/* Don't need to move data outselves, because
+	 * vt does this BEFORE calling us.
+	 * This is only used by vt's insert/deletechar.
+	 */
+#if 0
+	if (sx == 0 && dx == 0 && width >= c->vc_cols && width <= cols) {
+
+		sisusbcon_memmovew(SISUSB_VADDR(0, dy), SISUSB_VADDR(0, sy),
+					height * width * 2);
+
+	} else if (dy < sy || (dy == sy && dx < sx)) {
+
+		src  = SISUSB_VADDR(sx, sy);
+		dest = SISUSB_VADDR(dx, dy);
+
+		for (i = height; i > 0; i--) {
+			sisusbcon_memmovew(dest, src, width * 2);
+			src  += cols;
+			dest += cols;
+		}
+
+	} else {
+
+		src  = SISUSB_VADDR(sx, sy + height - 1);
+		dest = SISUSB_VADDR(dx, dy + height - 1);
+
+		for (i = height; i > 0; i--) {
+			sisusbcon_memmovew(dest, src, width * 2);
+			src  -= cols;
+			dest -= cols;
+		}
+
+	}
+#endif
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&sisusb->lock);
+		return;
+	}
+
+	length = ((height * cols) - dx - (cols - width - dx)) * 2;
+
+
+	sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy),
+				(u32)SISUSB_HADDR(dx, dy), length, &written);
+
+	up(&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.
+	 */
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return 0;
+
+	/* sisusb->lock is down */
+
+	/* Don't write to screen if in gfx mode */
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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) {
+		up(&sisusb->lock);
+		printk(KERN_DEBUG "sisusb: 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,
+				(u32)SISUSB_HADDR(0, 0),
+				length, &written);
+
+	up(&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.
+	 */
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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);
+
+	up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return -EINVAL;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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;
+	}
+
+	up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (mode_switch)
+		sisusb->is_gfx = blank ? 1 : 0;
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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:
+			up(&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);
+
+	}
+
+	up(&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 */
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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);
+
+	up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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;
+		up(&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;
+	}
+
+	up(&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),
+				(u32)SISUSB_HADDR(0, t), length, &written);
+
+	up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb)) {
+		up(&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;
+
+	up(&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.
+	 */
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return 0;
+
+	/* sisusb->lock is down */
+
+	if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) {
+		up(&sisusb->lock);
+		return 0;
+	}
+
+	c->vc_origin = c->vc_visible_origin = sisusb->scrbuf;
+
+	sisusbcon_set_start_address(sisusb, c);
+
+	sisusb->con_rolled_over = 0;
+
+	up(&sisusb->lock);
+
+	return 1;
+}
+
+/* Interface routine */
+static int
+sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows)
+{
+	struct sisusb_usb_data *sisusb;
+	int fh;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return -ENODEV;
+
+	fh = sisusb->current_font_height;
+
+	up(&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)
+			up(&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 *c = vc_cons[i].d;
+			if (c && c->vc_sw == &sisusb_con)
+				c->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)
+		up(&sisusb->lock);
+
+	if (dorecalc && c) {
+		int i, 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)
+		up(&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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		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;
+
+	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
+		return -ENODEV;
+
+	/* sisusb->lock is down */
+
+	font->width = 8;
+	font->height = c->vc_font.height;
+	font->charcount = 256;
+
+	if (!font->data) {
+		up(&sisusb->lock);
+		return 0;
+	}
+
+	if (!sisusb->font_backup) {
+		up(&sisusb->lock);
+		return -ENODEV;
+	}
+
+	/* Copy 256 chars only, like vgacon */
+	memcpy(font->data, sisusb->font_backup, 256 * 32);
+
+	up(&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
+
+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, minor = sisusb->minor;
+
+	down(&disconnect_sem);
+
+	down(&sisusb->lock);
+
+	/* Erm.. that should not happen */
+	if (sisusb->haveconsole || !sisusb->SiS_Pr) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		return 1;
+	}
+
+	sisusb->con_first = first;
+	sisusb->con_last  = last;
+
+	if (first > last ||
+	    first > MAX_NR_CONSOLES ||
+	    last > MAX_NR_CONSOLES) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		return 1;
+	}
+
+	/* If gfxcore not initialized or no consoles given, quit graciously */
+	if (!sisusb->gfxinit || first < 1 || last < 1) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		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)) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		printk(KERN_ERR
+			"sisusbvga[%d]: Failed to set up text mode\n",
+			minor);
+		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))) {
+		up(&sisusb->lock);
+		up(&disconnect_sem);
+		printk(KERN_ERR
+			"sisusbvga[%d]: Failed to allocate screen buffer\n",
+			minor);
+		return 1;
+	}
+
+	up(&sisusb->lock);
+	up(&disconnect_sem);
+
+	/* Now grab the desired console(s) */
+	ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
+
+	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])
+				take_over_console(&sisusb_dummy_con, i, i, 0);
+				/* At this point, con_deinit for all our
+				 * consoles is executed by 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..f28bc24
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.c
@@ -0,0 +1,1047 @@
+/*
+ * 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/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.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 "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: Get ModeID             */
+/*********************************************/
+
+unsigned short
+SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth)
+{
+	unsigned short ModeIndex = 0;
+
+	switch (HDisplay)
+	{
+		case 320:
+			if (VDisplay == 200)
+				ModeIndex = ModeIndex_320x200[Depth];
+			else if (VDisplay == 240)
+				ModeIndex = ModeIndex_320x240[Depth];
+			break;
+		case 400:
+			if (VDisplay == 300)
+				ModeIndex = ModeIndex_400x300[Depth];
+			break;
+		case 512:
+			if (VDisplay == 384)
+				ModeIndex = ModeIndex_512x384[Depth];
+			break;
+		case 640:
+			if (VDisplay == 480)
+				ModeIndex = ModeIndex_640x480[Depth];
+			else if (VDisplay == 400)
+				ModeIndex = ModeIndex_640x400[Depth];
+			break;
+		case 720:
+			if (VDisplay == 480)
+				ModeIndex = ModeIndex_720x480[Depth];
+			else if (VDisplay == 576)
+				ModeIndex = ModeIndex_720x576[Depth];
+			break;
+		case 768:
+			if (VDisplay == 576)
+				ModeIndex = ModeIndex_768x576[Depth];
+			break;
+		case 800:
+			if (VDisplay == 600)
+				ModeIndex = ModeIndex_800x600[Depth];
+			else if (VDisplay == 480)
+				ModeIndex = ModeIndex_800x480[Depth];
+			break;
+		case 848:
+			if (VDisplay == 480)
+				ModeIndex = ModeIndex_848x480[Depth];
+			break;
+		case 856:
+			if (VDisplay == 480)
+				ModeIndex = ModeIndex_856x480[Depth];
+			break;
+		case 960:
+			if (VDisplay == 540)
+				ModeIndex = ModeIndex_960x540[Depth];
+			else if (VDisplay == 600)
+				ModeIndex = ModeIndex_960x600[Depth];
+			break;
+		case 1024:
+			if (VDisplay == 576)
+				ModeIndex = ModeIndex_1024x576[Depth];
+			else if (VDisplay == 768)
+				ModeIndex = ModeIndex_1024x768[Depth];
+			break;
+		case 1152:
+			if (VDisplay == 864)
+				ModeIndex = ModeIndex_1152x864[Depth];
+			break;
+		case 1280:
+			switch (VDisplay) {
+				case 720:
+					ModeIndex = ModeIndex_1280x720[Depth];
+					break;
+				case 768:
+					ModeIndex = ModeIndex_1280x768[Depth];
+					break;
+				case 1024:
+					ModeIndex = ModeIndex_1280x1024[Depth];
+					break;
+			}
+	}
+
+	return ModeIndex;
+}
+
+/*********************************************/
+/*          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        */
+/*********************************************/
+
+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..5b11577
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.h
@@ -0,0 +1,830 @@
+/* $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 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 */
+};
+
+void		SiSUSBRegInit(struct SiS_Private *SiS_Pr, unsigned long BaseAddr);
+unsigned short	SiSUSB_GetModeID(int HDisplay, int VDisplay, int Depth);
+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);
+
+#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..94edd47
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_struct.h
@@ -0,0 +1,169 @@
+/*
+ * 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;
+	struct SiS_VCLKData		*SiS_VCLKData;
+	const struct SiS_ModeResInfo	*SiS_ModeResInfo;
+};
+
+#endif
+
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index faa7443..03fb70e 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -3,8 +3,8 @@
 /*
  *	uss720.c  --  USS720 USB Parport Cable.
  *
- *	Copyright (C) 1999
- *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *	Copyright (C) 1999, 2005
+ *	    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
@@ -23,99 +23,236 @@
  *  Based on parport_pc.c
  *
  *  History:
- *   0.1  04.08.99  Created
- *   0.2  07.08.99  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.99  fixing merge errors
- *   0.4  13.08.99  Added Vendor/Product ID of Brad Hard's cable
- *   0.5  20.09.99  usb_control_msg wrapper used
- *        Nov01.00  usb_device_table support by Adam J. Richter
- *        08.04.01  Identify version on module load.  gb
+ *   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
  *
  */
 
 /*****************************************************************************/
 
+#define DEBUG
+
 #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>
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.5"
-#define DRIVER_AUTHOR "Thomas M. Sailer, sailer@ife.ee.ethz.ch"
+#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;
-	void *irqhandle;
-	unsigned int irqpipe;
-	unsigned char reg[7];  /* USB registers */
+	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 int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val)
+static void destroy_priv(struct kref *kref)
 {
-	struct parport_uss720_private *priv = pp->private_data;
-	struct usb_device *usbdev = priv->usbdev;
+	struct parport_uss720_private *priv = container_of(kref, struct parport_uss720_private, ref_count);
+
+	usb_put_dev(priv->usbdev);
+	kfree(priv);
+	dbg("destroying priv datastructure");
+}
+
+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);
+	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 pt_regs *ptregs)
+{
+	struct uss720_async_request *rq;
+	struct parport *pp;
+	struct parport_uss720_private *priv;
+
+	rq = urb->context;
+	priv = rq->priv;
+	pp = priv->pp;
+	if (urb->status) {
+		err("async_complete: urb error %d", urb->status);
+	} else if (rq->dr.bRequest == 3) {
+		memcpy(priv->reg, rq->reg, sizeof(priv->reg));
+#if 0
+		dbg("async_complete regs %02x %02x %02x %02x %02x %02x %02x",
+		    (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], (unsigned int)priv->reg[2],
+		    (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], (unsigned int)priv->reg[5],
+		    (unsigned int)priv->reg[6]);
+#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(0, pp, NULL);
+	}
+	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,
+							 unsigned int 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 = kmalloc(sizeof(struct uss720_async_request), mem_flags);
+	if (!rq) {
+		err("submit_async_request out of memory");
+		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);
+		err("submit_async_request out of memory");
+		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);
+	ret = usb_submit_urb(rq->urb, mem_flags);
+	if (!ret) {
+		kref_get(&rq->ref_count);
+		return rq;
+	}
+	kref_put(&rq->ref_count, destroy_async);
+	err("submit_async_request submit_urb failed with %d", 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, unsigned int 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 (!usbdev)
-		return -1;
-	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, 1000);
-	if (ret != 7) {
-		printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x expected 7\n",
-		       (unsigned int)reg, ret);
-		ret = -1;
-	} else {
-#if 0
-		printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n",
-		       (unsigned int)reg, (unsigned int)priv->reg[0], (unsigned int)priv->reg[1],
-		       (unsigned int)priv->reg[2], (unsigned int)priv->reg[3], (unsigned int)priv->reg[4],
-		       (unsigned int)priv->reg[5], (unsigned int)priv->reg[6]);
-#endif
-		/* 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(0, pp, NULL);
-		ret = 0;
+	if (!pp)
+		return -EIO;
+	priv = pp->private_data;
+	rq = submit_async_request(priv, 3, 0xc0, ((unsigned int)reg) << 8, 0, mem_flags);
+	if (!rq) {
+		err("get_1284_register(%u) failed", (unsigned int)reg);
+		return -EIO;
 	}
-	if (val)
+	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]];
-	return ret;
+		if (ret)
+			warn("get_1284_register: usb error %d", ret);
+		kref_put(&rq->ref_count, destroy_async);
+		return ret;
+	}
+	warn("get_1284_register timeout");
+	kill_all_async_requests_priv(priv);
+	return -EIO;
 }
 
-static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val)
+static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val, unsigned int mem_flags)
 {
-	struct parport_uss720_private *priv = pp->private_data;
-	struct usb_device *usbdev = priv->usbdev;
-	int ret;
+	struct parport_uss720_private *priv;
+	struct uss720_async_request *rq;
 
-	if (!usbdev)
-		return -1;
-	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, 1000);
-	if (ret) {
-		printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", 
-		       (unsigned int)reg, (unsigned int)val, ret);
-	} else {
-#if 0
-		printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", 
-		       (unsigned int)reg, (unsigned int)val);
-#endif
+	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) {
+		err("set_1284_register(%u,%u) failed", (unsigned int)reg, (unsigned int)val);
+		return -EIO;
 	}
-	return ret;
+	kref_put(&rq->ref_count, destroy_async);
+	return 0;
 }
 
 /* --------------------------------------------------------------------- */
@@ -132,8 +269,9 @@
 {
 	struct parport_uss720_private *priv = pp->private_data;
 	int mode;
+	__u8 reg;
 
-	if (get_1284_register(pp, 6, NULL))
+	if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
 		return -EIO;
 	/* Bits <7:5> contain the mode. */
 	mode = (priv->reg[2] >> 5) & 0x7;
@@ -153,7 +291,7 @@
 		case ECR_ECP: /* ECP Parallel Port mode */
 			/* Poll slowly. */
 			for (;;) {
-				if (get_1284_register(pp, 6, NULL))
+				if (get_1284_register(pp, 6, &reg, GFP_KERNEL))
 					return -EIO;
 				if (priv->reg[2] & 0x01)
 					break;
@@ -167,7 +305,9 @@
 		}
 	}
 	/* Set the mode. */
-	if (set_1284_register(pp, 6, m << 5))
+	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;
 }
@@ -179,7 +319,7 @@
 {
 	unsigned char stat;
 
-	if (get_1284_register(pp, 1, &stat))
+	if (get_1284_register(pp, 1, &stat, GFP_KERNEL))
 		return 1;
 	return stat & 1;
 }
@@ -205,14 +345,14 @@
 
 static void parport_uss720_write_data(struct parport *pp, unsigned char d)
 {
-	set_1284_register(pp, 0, 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))
+	if (get_1284_register(pp, 0, &ret, GFP_KERNEL))
 		return 0;
 	return ret;
 }
@@ -222,7 +362,7 @@
 	struct parport_uss720_private *priv = pp->private_data;	
 
 	d = (d & 0xf) | (priv->reg[1] & 0xf0);
-	if (set_1284_register(pp, 2, d))
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
 		return;
 	priv->reg[1] = d;
 }
@@ -241,7 +381,7 @@
 	mask &= 0x0f;
 	val &= 0x0f;
 	d = (priv->reg[1] & (~mask)) ^ val;
-	if (set_1284_register(pp, 2, d))
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
 		return 0;
 	priv->reg[1] = d;
 	return d & 0xf;
@@ -251,7 +391,7 @@
 {
 	unsigned char ret;
 
-	if (get_1284_register(pp, 1, &ret))
+	if (get_1284_register(pp, 1, &ret, GFP_KERNEL))
 		return 0;
 	return ret & 0xf8;
 }
@@ -262,7 +402,7 @@
 	unsigned char d;
 
 	d = priv->reg[1] & ~0x10;
-	if (set_1284_register(pp, 2, d))
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
 		return;
 	priv->reg[1] = d;
 }
@@ -273,7 +413,7 @@
 	unsigned char d;
 
 	d = priv->reg[1] | 0x10;
-	if (set_1284_register(pp, 2, d))
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
 		return;
 	priv->reg[1] = d;
 }
@@ -284,7 +424,7 @@
 	unsigned char d;
 
 	d = priv->reg[1] & ~0x20;
-	if (set_1284_register(pp, 2, d))
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
 		return;
 	priv->reg[1] = d;
 }
@@ -295,7 +435,7 @@
 	unsigned char d;
 
 	d = priv->reg[1] | 0x20;
-	if (set_1284_register(pp, 2, d))
+	if (set_1284_register(pp, 2, d, GFP_KERNEL))
 		return;
 	priv->reg[1] = d;
 }
@@ -310,17 +450,23 @@
 {
 	struct parport_uss720_private *priv = pp->private_data;	
 
-	if (get_1284_register(pp, 2, NULL))
+#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)
 {
-	set_1284_register(pp, 2, s->u.pc.ctr);
-	set_1284_register(pp, 6, s->u.pc.ecr);
-	get_1284_register(pp, 2, NULL);
+	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)
@@ -331,7 +477,7 @@
 	if (change_mode(pp, ECR_EPP))
 		return 0;
 	for (; got < length; got++) {
-		if (get_1284_register(pp, 4, (char *)buf))
+		if (get_1284_register(pp, 4, (char *)buf, GFP_KERNEL))
 			break;
 		buf++;
 		if (priv->reg[0] & 0x01) {
@@ -352,10 +498,10 @@
 	if (change_mode(pp, ECR_EPP))
 		return 0;
 	for (; written < length; written++) {
-		if (set_1284_register(pp, 4, (char *)buf))
+		if (set_1284_register(pp, 4, (char *)buf, GFP_KERNEL))
 			break;
 		((char*)buf)++;
-		if (get_1284_register(pp, 1, NULL))
+		if (get_1284_register(pp, 1, NULL, GFP_KERNEL))
 			break;
 		if (priv->reg[0] & 0x01) {
 			clear_epp_timeout(pp);
@@ -390,7 +536,7 @@
 	if (change_mode(pp, ECR_EPP))
 		return 0;
 	for (; got < length; got++) {
-		if (get_1284_register(pp, 3, (char *)buf))
+		if (get_1284_register(pp, 3, (char *)buf, GFP_KERNEL))
 			break;
 		buf++;
 		if (priv->reg[0] & 0x01) {
@@ -410,10 +556,10 @@
 	if (change_mode(pp, ECR_EPP))
 		return 0;
 	for (; written < length; written++) {
-		if (set_1284_register(pp, 3, *(char *)buf))
+		if (set_1284_register(pp, 3, *(char *)buf, GFP_KERNEL))
 			break;
 		buf++;
-		if (get_1284_register(pp, 1, NULL))
+		if (get_1284_register(pp, 1, NULL, GFP_KERNEL))
 			break;
 		if (priv->reg[0] & 0x01) {
 			clear_epp_timeout(pp);
@@ -467,7 +613,7 @@
 	if (change_mode(pp, ECR_ECP))
 		return 0;
 	for (; written < len; written++) {
-		if (set_1284_register(pp, 5, *(char *)buffer))
+		if (set_1284_register(pp, 5, *(char *)buffer, GFP_KERNEL))
 			break;
 		buffer++;
 	}
@@ -536,93 +682,91 @@
 static int uss720_probe(struct usb_interface *intf,
 			const struct usb_device_id *id)
 {
-	struct usb_device *usbdev = interface_to_usbdev(intf);
+	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;
 
-	printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n",
-	       le16_to_cpu(usbdev->descriptor.idVendor),
-	       le16_to_cpu(usbdev->descriptor.idProduct));
+	dbg("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)
+	if (intf->num_altsetting != 3) {
+		usb_put_dev(usbdev);
 		return -ENODEV;
-
+	}
 	i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2);
-	printk(KERN_DEBUG "uss720: set inteface result %d\n", i);
+	dbg("set inteface result %d", i);
 
 	interface = intf->cur_altsetting;
 
 	/*
 	 * Allocate parport interface 
 	 */
-	printk(KERN_INFO "uss720: (C) 1999 by Thomas Sailer, <sailer@ife.ee.ethz.ch>\n");
-
-	if (!(priv = kmalloc(sizeof(struct parport_uss720_private), GFP_KERNEL)))
+	if (!(priv = kcalloc(sizeof(struct parport_uss720_private), 1, GFP_KERNEL))) {
+		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);
 	if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) {
-		printk(KERN_WARNING "usb-uss720: could not register parport\n");
+		warn("could not register parport");
 		goto probe_abort;
 	}
 
+	priv->pp = pp;
 	pp->private_data = priv;
-	priv->usbdev = usbdev;
 	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);
-	set_1284_register(pp, 6, 0x30);  /* PS/2 mode */
-	set_1284_register(pp, 2, 0x0c);
+	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, NULL);
-	printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n",
-	       priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]);
+	get_1284_register(pp, 0, &reg, GFP_KERNEL);
+	dbg("reg: %02x %02x %02x %02x %02x %02x %02x",
+	    priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]);
 
 	endpoint = &interface->endpoint[2];
-	printk(KERN_DEBUG "uss720: epaddr %d interval %d\n", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval);
-#if 0
-	priv->irqpipe = usb_rcvctrlpipe(usbdev, endpoint->bEndpointAddress);
-	i = usb_request_irq(usbdev, priv->irqpipe,
-				  uss720_irq, endpoint->bInterval,
-				  pp, &priv->irqhandle);
-	if (i) {
-		printk (KERN_WARNING "usb-uss720: usb_request_irq failed (0x%x)\n", i);
-		goto probe_abort_port;
-	}
-#endif
+	dbg("epaddr %d interval %d", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval);
 	parport_announce_port(pp);
 
-	usb_set_intfdata (intf, pp);
+	usb_set_intfdata(intf, pp);
 	return 0;
 
-#if 0
-probe_abort_port:
-	parport_put_port(pp);
-#endif
 probe_abort:
-	kfree(priv);
+	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 *pp = usb_get_intfdata(intf);
 	struct parport_uss720_private *priv;
+	struct usb_device *usbdev;
 
-	usb_set_intfdata (intf, NULL);
+	dbg("disconnect");
+	usb_set_intfdata(intf, NULL);
 	if (pp) {
 		priv = pp->private_data;
-		parport_remove_port(pp);
-#if 0
-		usb_release_irq(usbdev, priv->irqhandle, priv->irqpipe);
-#endif
+		usbdev = priv->usbdev;
 		priv->usbdev = NULL;
+		priv->pp = NULL;
+		dbg("parport_remove_port");
+		parport_remove_port(pp);
 		parport_put_port(pp);
-		kfree(priv);
+		kill_all_async_requests_priv(priv);
+		kref_put(&priv->ref_count, destroy_priv);
 	}
+	dbg("disconnect done");
 }
 
 /* table of cables that work through this driver */
@@ -647,8 +791,8 @@
 
 /* --------------------------------------------------------------------- */
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 static int __init uss720_init(void)
@@ -659,6 +803,9 @@
 		goto out;
 
 	info(DRIVER_VERSION ":" DRIVER_DESC);
+	info("NOTE: this is a special purpose driver to allow nonstandard");
+	info("protocols (eg. bitbang) over USS720 usb to parallel cables");
+	info("If you just want to connect to a printer, use usblp instead");
 out:
 	return retval;
 }
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 417464d..17d0190 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -79,7 +79,7 @@
 		return '-';
 
 	if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
-		return 'D';
+		return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
 	if (urb->setup_packet == NULL)
 		return 'Z';	/* '0' would be not as pretty. */
 
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index 4ace996..97c78c2 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -32,7 +32,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.04"
+#define DRIVER_VERSION "v0.05"
 #define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
 
 /*
@@ -54,8 +54,11 @@
 static int debug;
 
 static struct usb_device_id id_table [] = {
+	{ USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
 	{ USB_DEVICE(0x10C4, 0xEA60) },	/* Silicon Labs factory default */
 	{ USB_DEVICE(0x10C4, 0x80CA) },	/* Degree Controls Inc */
+	{ USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
+	{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
 	{ USB_DEVICE(0x10AB, 0x10C5) },	/* Siemens MC60 Cable */
 	{ } /* Terminating Entry */
 };
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 05c44ae..9ee1aaf 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -610,8 +610,7 @@
 		timeout = max((HZ*2560)/bps,HZ/10);
 	else
 		timeout = 2*HZ;
-	set_current_state(TASK_INTERRUPTIBLE);
-	schedule_timeout(timeout);
+	schedule_timeout_interruptible(timeout);
 
 	dbg("%s - stopping urbs", __FUNCTION__);
 	usb_kill_urb (port->interrupt_in_urb);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 0a6e8b4..4e434cb 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -914,7 +914,7 @@
 	unsigned interfaces;
 
 	/* Assume it is not the original SIO device for now. */
-	priv->baud_base = 48000000 / 16;
+	priv->baud_base = 48000000 / 2;
 	priv->write_offset = 0;
 
 	version = le16_to_cpu(udev->descriptor.bcdDevice);
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 4614741..3cf245b 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -95,6 +95,7 @@
 	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) },
 	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) },
 	{ USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
+	{ USB_DEVICE( NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID ) },
 	{ }					/* Terminating entry */
 };
 
@@ -652,8 +653,7 @@
 		timeout = max((HZ*2560)/bps,HZ/10);
 	else
 		timeout = 2*HZ;
-	set_current_state(TASK_INTERRUPTIBLE);
-	schedule_timeout(timeout);
+	schedule_timeout_interruptible(timeout);
 
 	/* shutdown our urbs */
 	dbg("%s - shutting down urbs", __FUNCTION__);
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index b734c40..7be9644 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -58,3 +58,7 @@
 
 #define SYNTECH_VENDOR_ID	0x0745
 #define SYNTECH_PRODUCT_ID	0x0001
+
+/* Nokia CA-42 Cable */
+#define NOKIA_CA42_VENDOR_ID	0x078b
+#define NOKIA_CA42_PRODUCT_ID	0x1234
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index d34dc9f..4837524 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -227,42 +227,42 @@
  ***********************************************************************/
 
 /* Command timeout and abort */
-/* This is always called with scsi_lock(host) held */
 static int command_abort(struct scsi_cmnd *srb)
 {
 	struct us_data *us = host_to_us(srb->device->host);
 
 	US_DEBUGP("%s called\n", __FUNCTION__);
 
+	/* us->srb together with the TIMED_OUT, RESETTING, and ABORTING
+	 * bits are protected by the host lock. */
+	scsi_lock(us_to_host(us));
+
 	/* Is this command still active? */
 	if (us->srb != srb) {
+		scsi_unlock(us_to_host(us));
 		US_DEBUGP ("-- nothing to abort\n");
 		return FAILED;
 	}
 
 	/* Set the TIMED_OUT bit.  Also set the ABORTING bit, but only if
 	 * a device reset isn't already in progress (to avoid interfering
-	 * with the reset).  To prevent races with auto-reset, we must
-	 * stop any ongoing USB transfers while still holding the host
-	 * lock. */
+	 * with the reset).  Note that we must retain the host lock while
+	 * calling usb_stor_stop_transport(); otherwise it might interfere
+	 * with an auto-reset that begins as soon as we release the lock. */
 	set_bit(US_FLIDX_TIMED_OUT, &us->flags);
 	if (!test_bit(US_FLIDX_RESETTING, &us->flags)) {
 		set_bit(US_FLIDX_ABORTING, &us->flags);
 		usb_stor_stop_transport(us);
 	}
+	scsi_unlock(us_to_host(us));
 
 	/* Wait for the aborted command to finish */
 	wait_for_completion(&us->notify);
-
-	/* Reacquire the lock and allow USB transfers to resume */
-	clear_bit(US_FLIDX_ABORTING, &us->flags);
-	clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
 	return SUCCESS;
 }
 
 /* This invokes the transport reset mechanism to reset the state of the
  * device */
-/* This is always called with scsi_lock(host) held */
 static int device_reset(struct scsi_cmnd *srb)
 {
 	struct us_data *us = host_to_us(srb->device->host);
@@ -279,7 +279,6 @@
 }
 
 /* Simulate a SCSI bus reset by resetting the device's USB port. */
-/* This is always called with scsi_lock(host) held */
 static int bus_reset(struct scsi_cmnd *srb)
 {
 	struct us_data *us = host_to_us(srb->device->host);
@@ -291,7 +290,6 @@
 	result = usb_stor_port_reset(us);
 	up(&(us->dev_semaphore));
 
-	/* lock the host for the return */
 	return result < 0 ? FAILED : SUCCESS;
 }
 
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index ad0cfd7..b79dad1 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -86,6 +86,16 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_IGNORE_RESIDUE ),
 
+/* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net>
+ * and Olaf Hering <olh@suse.de> (different bcd's, same vendor/product)
+ * for USB floppies that need the SINGLE_LUN enforcement.
+ */
+UNUSUAL_DEV(  0x0409, 0x0040, 0x0000, 0x9999,
+		"NEC",
+		"NEC USB UF000x",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_SINGLE_LUN ),
+
 /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
  * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
  * always fails and confuses drive.
@@ -96,6 +106,13 @@
 		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_INQUIRY ),
 
+/* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */
+UNUSUAL_DEV(  0x0424, 0x0fdc, 0x0210, 0x0210,
+		"SMSC",
+		"FDC GOLD-2.30",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_SINGLE_LUN ),
+
 #ifdef CONFIG_USB_STORAGE_DPCM
 UNUSUAL_DEV(  0x0436, 0x0005, 0x0100, 0x0100,
 		"Microtech",
@@ -103,6 +120,24 @@
  		US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
 #endif
 
+/*
+ * Pete Zaitcev <zaitcev@yahoo.com>, from Patrick C. F. Ernzer, bz#162559.
+ * The key does not actually break, but it returns zero sense which
+ * makes our SCSI stack to print confusing messages.
+ */
+UNUSUAL_DEV(  0x0457, 0x0150, 0x0100, 0x0100,
+		"USBest Technology",	/* sold by Transcend */
+		"USB Mass Storage Device",
+		US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
+
+/* Patch submitted by Daniel Drake <dsd@gentoo.org>
+ * Device reports nonsense bInterfaceProtocol 6 when connected over USB2 */
+UNUSUAL_DEV(  0x0451, 0x5416, 0x0100, 0x0100,
+		"Neuros Audio",
+		"USB 2.0 HD 2.5",
+		US_SC_DEVICE, US_PR_BULK, NULL,
+		US_FL_NEED_OVERRIDE ),
+
 /* Patch submitted by Philipp Friedrich <philipp@void.at> */
 UNUSUAL_DEV(  0x0482, 0x0100, 0x0100, 0x0100,
 		"Kyocera",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index cb4c770..f9a9bfa 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -392,11 +392,16 @@
 		/* If an abort request was received we need to signal that
 		 * the abort has finished.  The proper test for this is
 		 * the TIMED_OUT flag, not srb->result == DID_ABORT, because
-		 * a timeout/abort request might be received after all the
-		 * USB processing was complete. */
-		if (test_bit(US_FLIDX_TIMED_OUT, &us->flags))
+		 * the timeout might have occurred after the command had
+		 * already completed with a different result code. */
+		if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
 			complete(&(us->notify));
 
+			/* Allow USB transfers to resume */
+			clear_bit(US_FLIDX_ABORTING, &us->flags);
+			clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
+		}
+
 		/* finished working on this command */
 		us->srb = NULL;
 		scsi_unlock(host);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 5fe182d..eb83a78 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -137,7 +137,7 @@
 
 config FONT_8x16
 	bool "VGA 8x16 font" if FONTS
-	depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE=y
+	depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE=y || USB_SISUSBVGA_CON
 	default y if !SPARC32 && !SPARC64 && !FONTS
 	help
 	  This is the "high resolution" font for the VGA frame buffer (the one
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index b562f6b..42c7b8d 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -33,6 +33,10 @@
 
 obj-$(CONFIG_FB_STI)              += sticore.o font.o
 
+ifeq ($(CONFIG_USB_SISUSBVGA_CON),y)
+obj-$(CONFIG_USB_SISUSBVGA)           += font.o
+endif
+
 # Targets that kbuild needs to know about
 targets := promcon_tbl.c
 
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index fb57c22..9facf73 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -32,7 +32,6 @@
 #define _LINUX_USBDEVICE_FS_H
 
 #include <linux/types.h>
-#include <linux/compat.h>
 
 /* --------------------------------------------------------------------- */
 
@@ -125,6 +124,7 @@
 };
 
 #ifdef CONFIG_COMPAT
+#include <linux/compat.h>
 struct usbdevfs_urb32 {
 	unsigned char type;
 	unsigned char endpoint;