[PATCH] USB: sisusb[vga] update

here is a new and extended version of the sisusbvga (previously: sisusb)
driver. The patch is against 2.6.13 and updates the driver to version 0.0.8.

Additions include complete VGA/EGA text console support and a build-in
display mode infrastructure for userland applications that don't know
 about the graphics internals.

Fixes include some BE/LE issues and a get/put_dev bug in the previous
version.

Other changes include a change of the module name from "sisusb" to
"sisusbvga". The previous one was too generic IMHO.

Please note that the patch also affects the Makefile in
drivers/video/console as the driver requires the VGA 8x16 font in case
the text console part is selected.

Heavily tested, as usual. Please apply.

One thing though: I already prepared for removal of the "mode" field and
the changed "name" field in the usb_class_driver structure. This will
perhaps need some refinement depending on whether you/Linus merge the
respective core changes before or after 2.6.14.

Signed-off-by: Thomas Winischhofer <thomas@winischhofer.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
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");