From 80908309ce44677a07763e24e6ec9371cfa3ab5f Mon Sep 17 00:00:00 2001 From: Greg KH Date: Mon, 12 Sep 2005 11:58:07 -0700 Subject: Revert "[PATCH] USB: Prevent hid-core claiming Apple Bluetooth device on new G4 powerbooks" This reverts 22af8878d2d641c6b15fe39fe4de3c05b2c477f0 commit. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/input/hid-core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index e108e0a36b7..a99865c689c 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1446,7 +1446,6 @@ void hid_init_reports(struct hid_device *hid) #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 @@ static struct hid_blacklist { { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_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 }, -- cgit v1.2.3 From 1bbb4f2035d94d86e52e9b5341c142dcb39bb879 Mon Sep 17 00:00:00 2001 From: Thomas Winischhofer Date: Mon, 29 Aug 2005 17:01:16 +0200 Subject: [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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/sisusbvga/Kconfig | 42 +- drivers/usb/misc/sisusbvga/Makefile | 4 +- drivers/usb/misc/sisusbvga/sisusb.c | 463 +++++++- drivers/usb/misc/sisusbvga/sisusb.h | 73 +- drivers/usb/misc/sisusbvga/sisusb_con.c | 1658 ++++++++++++++++++++++++++++ drivers/usb/misc/sisusbvga/sisusb_init.c | 1047 ++++++++++++++++++ drivers/usb/misc/sisusbvga/sisusb_init.h | 830 ++++++++++++++ drivers/usb/misc/sisusbvga/sisusb_struct.h | 169 +++ drivers/video/console/Kconfig | 2 +- drivers/video/console/Makefile | 4 + 10 files changed, 4236 insertions(+), 56 deletions(-) create mode 100644 drivers/usb/misc/sisusbvga/sisusb_con.c create mode 100644 drivers/usb/misc/sisusbvga/sisusb_init.c create mode 100644 drivers/usb/misc/sisusbvga/sisusb_init.h create mode 100644 drivers/usb/misc/sisusbvga/sisusb_struct.h diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 3957e144caf..7603cbe0865 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -4,11 +4,43 @@ config USB_SISUSBVGA 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 76f1643ceaf..7f934cfc906 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 d63ce6c030f..39db3155723 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 #include #include +#include #include "sisusb.h" +#ifdef INCL_SISUSB_CON +#include +#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 @@ static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, /* 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 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, 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 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, 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 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, 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 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, 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 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, (u16 __user *)userbuffer)) return -EFAULT; } else { - kernbuffer[0] = swap16 >> 8; - kernbuffer[1] = swap16 & 0xff; + *((u16 *)kernbuffer) = swap16; } } return ret; @@ -1202,9 +1262,15 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, 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 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, 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 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, /* 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 @@ sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data) 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 @@ sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data) 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 @@ sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx, 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 @@ sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) return ret; } + +#ifdef INCL_SISUSB_CON + +/* Set up default text mode: + - Set text mode (0x03) + - Upload default font + - Upload user font (if available) +*/ + +int +sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) +{ + int ret = 0, slot = sisusb->font_slot, i; + 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 @@ sisusb_open(struct inode *inode, struct file *file) } } - /* increment usage count for the device */ + /* Increment usage count for our sisusb */ kref_get(&sisusb->kref); sisusb->isopen = 1; @@ -2340,12 +2620,10 @@ sisusb_open(struct inode *inode, struct file *file) 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_delete(struct kref *kref) 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 @@ sisusb_release(struct inode *inode, struct file *file) up(&disconnect_sem); - printk(KERN_DEBUG "sisusbvga[%d]: released", myminor); - return 0; } @@ -2733,6 +3012,12 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, 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 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, 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 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, 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 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, 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 file_operations usb_sisusb_fops = { }; 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 @@ static int sisusb_probe(struct usb_interface *intf, 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 @@ static int sisusb_probe(struct usb_interface *intf, 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 @@ static int sisusb_probe(struct usb_interface *intf, 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 @@ static void sisusb_disconnect(struct usb_interface *intf) 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 @@ static int __init usb_sisusb_init(void) { 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_init(usb_sisusb_init); module_exit(usb_sisusb_exit); MODULE_AUTHOR("Thomas Winischhofer "); -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 1306d006a25..401ff21d788 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 +#include +#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 @@ struct sisusb_usb_data { 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 @@ struct sisusb_info { __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 @@ struct sisusb_command { __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 00000000000..24584463553 --- /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 + * + * Portions based on vgacon.c which are + * Created 28 Sep 1997 by Geert Uytterhoeven + * Rewritten by Martin Mares , 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..f28bc240f9b --- /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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 00000000000..5b11577835c --- /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 + * + */ + +#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 00000000000..94edd4726c4 --- /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 + * + */ + +#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/video/console/Kconfig b/drivers/video/console/Kconfig index 5fe182d6e4a..eb83a7874c7 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -137,7 +137,7 @@ config FONT_8x8 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 b562f6bb9d3..42c7b8dcd22 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -33,6 +33,10 @@ endif 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 -- cgit v1.2.3 From f7214ff4e8248513ec626212b2c1a3ca0b2a0888 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Thu, 8 Sep 2005 10:19:48 +0200 Subject: [PATCH] USB: add apple usb touchpad driver This is a driver for the USB touchpad which can be found on post-February 2005 Apple 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[2], in order to have touchpad acceleration, scrolling, two/three finger tap, etc. This driver has been tested by the readers of the 'debian-powerpc' mailing list for a few weeks now and I believe it is now ready for inclusion into the mainline kernel. 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. Signed-off-by: Stelian Pop Signed-off-by: Greg Kroah-Hartman --- Documentation/input/appletouch.txt | 84 +++++++ drivers/usb/input/Kconfig | 20 ++ drivers/usb/input/Makefile | 1 + drivers/usb/input/appletouch.c | 469 +++++++++++++++++++++++++++++++++++++ 4 files changed, 574 insertions(+) create mode 100644 Documentation/input/appletouch.txt create mode 100644 drivers/usb/input/appletouch.c diff --git a/Documentation/input/appletouch.txt b/Documentation/input/appletouch.txt new file mode 100644 index 00000000000..b48d11d0326 --- /dev/null +++ b/Documentation/input/appletouch.txt @@ -0,0 +1,84 @@ +Apple Touchpad Driver (appletouch) +---------------------------------- + Copyright (C) 2005 Stelian Pop + +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/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 482c4be521f..1e53934907c 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -286,3 +286,23 @@ config USB_KEYSPAN_REMOTE 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 + . + + 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 43b2f999edf..5e03b93f29f 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_USB_WACOM) += wacom.o 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 00000000000..e03c1c567a1 --- /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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); -- cgit v1.2.3 From f1a15606d5be8490a122f1c94c554bd0f07d8d26 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 5 Sep 2005 13:55:23 -0400 Subject: [PATCH] usbcore: small changes to HCD glue layer This patch (as549) introduces two small changes in the HCD glue layer. The first simply removes a redundant test. The second allows root-hub polling to continue for a single iteration after a host controller dies; this is needed for the patch that follows. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 12ecdb03ee5..1017a97a418 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1606,7 +1606,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) 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 @@ void usb_hc_died (struct usb_hcd *hcd) 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, -- cgit v1.2.3 From 1f09df8bfe358994fcb5f3dc4f890694c4069621 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 5 Sep 2005 13:59:51 -0400 Subject: [PATCH] USB UHCI: remove the FSBR kernel timer This patch (as558) removes from the UHCI driver a kernel timer used for checking Full Speed Bandwidth Reclamation (FSBR). The checking can be done during normal root-hub polling; it doesn't need a separate timer. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 62 ++++++++++++++++----------------------------- drivers/usb/host/uhci-hcd.h | 11 +++----- drivers/usb/host/uhci-hub.c | 11 ++++---- drivers/usb/host/uhci-q.c | 2 +- 4 files changed, 32 insertions(+), 54 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 0d5d2545bf0..0c024898cbe 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -97,14 +97,9 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); /* 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 @@ static void hc_died(struct uhci_hcd *uhci) { reset_hc(uhci); uhci->hc_inaccessible = 1; - del_timer(&uhci->stall_timer); } /* @@ -287,8 +281,11 @@ __acquires(uhci->lock) /* 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 @@ __acquires(uhci->lock) 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 @@ static void start_rh(struct uhci_hcd *uhci) 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 @@ __acquires(uhci->lock) 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 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) "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 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) 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 @@ static int uhci_start(struct usb_hcd *hcd) 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 @@ static void uhci_stop(struct usb_hcd *hcd) 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 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) */ 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 @@ static int uhci_resume(struct usb_hcd *hcd) 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 bf9c5f9b508..282f40b7588 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -345,9 +345,6 @@ enum uhci_rh_state { /* * 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 uhci_hcd { 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 @@ struct uhci_hcd { 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 @@ struct uhci_hcd { /* 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 4eace2b19dd..a71e48a6680 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -145,15 +145,16 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { 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 bbb36cd6ed6..ea0d168a8c6 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -33,7 +33,7 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci); 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); } -- cgit v1.2.3 From 198b95170f2c7ad56b4ba92fe3d4d896f5be5c7e Mon Sep 17 00:00:00 2001 From: Craig Shelley Date: Sun, 28 Aug 2005 09:51:15 +0100 Subject: [PATCH] USB: CP2101 New Device IDs Three new device IDs for CP2101 USB to UART Bridge Signed-off-by: Craig Shelley Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp2101.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 4ace9964fc6..97c78c21e8d 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 void cp2101_shutdown(struct usb_serial*); 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 */ }; -- cgit v1.2.3 From 10f6524a8ef1413a8cbd952673997013183fe2a9 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 31 Aug 2005 10:55:38 -0700 Subject: [PATCH] USB: EHCI port tweaks One change may improve some S1 or S3 resume cases, and the other seems mostly to explain some strange state "lsusb" would show. Two fixes: - On resume, don't think about resuming any unpowered port, or resetting any port with OWNER set to the OHCI/UHCI companion. This will make some S1 and S3 resume scenarios work better. - PORT_CSC was not being cleared correctly in ehci_hub_status_data. This was visible at least through current versions of "lsusb", and might have caused some other hub related strangeness. The fix addresses all three write-to-clear bits, using the same approach that UHCI happens to use: a mask of bits that are cleared in most writes to that port status register. Original patch seems to have been from from William.Morrow@amd.com and this version (from David) finishes the write-to-clear changes. Signed-off-by: Jordan Crouse Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 ++++++-- drivers/usb/host/ehci-hub.c | 27 ++++++++++++++++----------- drivers/usb/host/ehci.h | 1 + 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2507e898af0..2f7037c62e8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -759,12 +759,16 @@ static int ehci_resume (struct usb_hcd *hcd) 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); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 36cc1f2218d..18d3f227031 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) /* 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 @@ static int ehci_hub_resume (struct usb_hcd *hcd) 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 @@ static int ehci_hub_resume (struct usb_hcd *hcd) 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 @@ static int check_reset_complete ( // 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 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) 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 @@ static int ehci_hub_control ( &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 @@ static int ehci_hub_control ( 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 @@ static int ehci_hub_control ( 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 @@ static int ehci_hub_control ( /* 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 @@ static int ehci_hub_control ( 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 @@ static int ehci_hub_control ( 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 20c9b550097..f34a0516d35 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -263,6 +263,7 @@ struct ehci_regs { #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" -- cgit v1.2.3 From f7201c3dcd7799f2aa3d6ec427b194225360ecee Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 2 Sep 2005 18:58:09 -0700 Subject: [PATCH] USB: EHCI workaround for NForce and mem > 2GB NVidia reports (via Mark Overby) that some of their EHCI controllers don't like certain data structure addresses beyond the 2GB mark. He provided an earlier version of this patch. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2f7037c62e8..ae5ba4ddfb4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -400,6 +400,23 @@ static int ehci_hc_reset (struct usb_hcd *hcd) 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 */ -- cgit v1.2.3 From dd16525b698528172899f10c14a3eb6ddb888a53 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 31 Aug 2005 10:45:25 -0700 Subject: [PATCH] USB: get rid of minor log spamming Routine cases like handoff-to-companion shouldn't trigger diagnostics. This gets rid of some recently added log spamming. It's routine for hub_port_wait_reset() to return -ENOTCONN to indicate handoff from highspeed hubs to companions, so an error message is incorrect. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 758c7f0ed15..c55208299cf 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1460,7 +1460,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, 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); -- cgit v1.2.3 From 22c438632850c0d6257b45c90afed0cea6953afc Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Mon, 15 Aug 2005 11:30:11 -0700 Subject: [PATCH] drivers/usb: fix-up schedule_timeout() usage Description: Use schedule_timeout_{,un}interruptible() instead of set_current_state()/schedule_timeout() to reduce kernel size. Signed-off-by: Nishanth Aravamudan Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/audio.c | 12 ++++++++---- drivers/usb/host/ehci-hcd.c | 3 +-- drivers/usb/host/ohci-hcd.c | 3 +-- drivers/usb/serial/cypress_m8.c | 3 +-- drivers/usb/serial/pl2303.c | 3 +-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index f8f21567cc2..50858273f8d 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -631,8 +631,10 @@ static void usbin_stop(struct usb_audiodev *as) 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 @@ static void usbout_stop(struct usb_audiodev *as) 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/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ae5ba4ddfb4..b948ffd94f4 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1147,8 +1147,7 @@ rescan: 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/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 56b43f2a0e5..ddaa9c82c58 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -382,8 +382,7 @@ sanitize: 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)) { diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 05c44ae3ed3..9ee1aaff2fc 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -610,8 +610,7 @@ static void cypress_close(struct usb_serial_port *port, struct file * filp) 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/pl2303.c b/drivers/usb/serial/pl2303.c index 461474176cf..ea8c6e74be4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -652,8 +652,7 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp) 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__); -- cgit v1.2.3 From 4809ecc29935893d954ab9244899777ffaca40ac Mon Sep 17 00:00:00 2001 From: Pavol Kurina Date: Wed, 7 Sep 2005 09:49:34 -0700 Subject: [PATCH] USB gadgetfs: fixes an error on writing to endpoint file this patch fixes an "Invalid argument" error returned by a write to an endpoint-file after reopening it in the gadgetfs module in the kernel 2.6.12. This was testet only with dummy_hcd module! Signed-off-by: Pavol Kurina Signed-off-by: David Brownell --- drivers/usb/gadget/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 020815397a4..5c40980a5bd 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -483,6 +483,7 @@ ep_release (struct inode *inode, struct file *fd) data->state = STATE_EP_DISABLED; data->desc.bDescriptorType = 0; data->hs_desc.bDescriptorType = 0; + usb_ep_disable(data->ep); } put_ep (data); return 0; -- cgit v1.2.3 From e0fd3cbc50a8c925e8e7d8448df689015362c458 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 31 Aug 2005 10:47:20 -0700 Subject: [PATCH] USB: OHCI irq tweak Evidently there are some boards which care a lot about this, but as a rule it's been hard to notice. OHCI_INTR_RD wasn't always cleared in the ohci irq handler. On some systems this means certain remote wakeup scenarios could seem to hang (in an interrupt storm, RD never clearing). From: "William Morrow" Signed-off-by: Jordan Crouse Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index ddaa9c82c58..6efb69f7c07 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -719,6 +719,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) if (ints & OHCI_INTR_RD) { ohci_vdbg (ohci, "resume detect\n"); + ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus); if (hcd->state != HC_STATE_QUIESCING) schedule_work(&ohci->rh_resume); } -- cgit v1.2.3 From fdd13b36c4a501d8787a27e54635fbd943f2685d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 31 Aug 2005 11:52:57 -0700 Subject: [PATCH] USB: OHCI relies less on NDP register Some OHCI implementations have differences in the way the NDP register (in roothub_a) reports the number of ports present. This patch allows the platform specific code to optionally supply the number of ports. The driver just reads the value at init (if not supplied) instead of reading it every time its needed (except for an AMD756 bug workaround). It also sets the value correctly for the ARM pxa27x architecture. Signed-Off-By: Richard Purdie Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-dbg.c | 9 ++++----- drivers/usb/host/ohci-hcd.c | 10 ++++++---- drivers/usb/host/ohci-hub.c | 22 +++++++++++----------- drivers/usb/host/ohci-pxa27x.c | 3 +++ drivers/usb/host/ohci.h | 1 + 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 447f488f5d9..7924c74f958 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -228,23 +228,22 @@ ohci_dump_roothub ( 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 @@ ohci_dump_roothub ( ); } - 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 6efb69f7c07..67c1aa5eb1c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -484,6 +484,10 @@ static int ohci_init (struct ohci_hcd *ohci) // 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; @@ -560,10 +564,8 @@ static int ohci_run (struct ohci_hcd *ohci) 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]); } @@ -861,7 +863,7 @@ static int ohci_restart (struct ohci_hcd *ohci) * 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 83ca4549a50..ce7b28da7a1 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -184,7 +184,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) 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 @@ static int 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 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) 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 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) 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 @@ ohci_hub_descriptor ( 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 @@ ohci_hub_descriptor ( 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 e5bc1789d18..5dd20dbe852 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -258,6 +258,9 @@ ohci_pxa27x_start (struct usb_hcd *hcd) 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 71cdd226286..8a9b9d9209e 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -383,6 +383,7 @@ struct ohci_hcd { /* * driver state */ + int num_ports; int load [NUM_INTS]; u32 hc_control; /* copy of hc control reg */ unsigned long next_statechange; /* suspend/resume */ -- cgit v1.2.3 From 155faf5e1e36ca3a6127bdfb5c624d58e520c411 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 31 Aug 2005 11:54:09 -0700 Subject: [PATCH] USB: OHCI, pxa27x OHCI port power tweaks Now that it's in use on other boards, a bug in the original code needs fixing. There is no need for the PXA27x OHCI to set usb power during init, since the hub driver in usbcore handles that. Those platform-specific power control functions are also incorrect, and should therefore be removed. Add a check to clear the OTG pin hold bit until such times OTG is properly implemented. Signed-Off-By: Richard Purdie Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pxa27x.c | 45 +++++++----------------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 5dd20dbe852..2fdb262d472 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -75,33 +75,6 @@ static int pxa27x_ohci_select_pmm( int mode ) 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) && (port0) && (port Date: Tue, 30 Aug 2005 23:38:23 +0100 Subject: [PATCH] USB: PL2303: CA-42 Phone cable This patch adds the product ID and vendor ID for a Nokia CA-42 USB cable to the list of devices handled by the pl2303 driver. The patch is against 2.6.13. Signed-off-by: Robert Spanton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ea8c6e74be4..3cf245bdda5 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -95,6 +95,7 @@ static struct usb_device_id id_table [] = { { 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 */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index b734c4003c5..7be9644f5a0 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 -- cgit v1.2.3 From 5f9809487bf035b49ff470709a65fbce3b345f51 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 8 Sep 2005 21:56:56 -0700 Subject: [PATCH] USB: proc_usb_info.txt: add blank lines Update Documentation/usb/proc_usb_info.txt: - remove some trailing whitespace - add a blank line before each T: line to match current kernel and to make the text more readable. Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/proc_usb_info.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/usb/proc_usb_info.txt b/Documentation/usb/proc_usb_info.txt index 729c72d34c8..f86550fe38e 100644 --- a/Documentation/usb/proc_usb_info.txt +++ b/Documentation/usb/proc_usb_info.txt @@ -20,7 +20,7 @@ the /proc/bus/usb/BBB/DDD files. 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 @@ the /proc/bus/usb/BBB/DDD files. 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 @@ B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd 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 @@ C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA | | |__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 @@ I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss 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 @@ S: SerialNumber=dce0 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 -- cgit v1.2.3 From b789696af8b4102b7cc26dec30c2c51ce51ee18b Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 31 Aug 2005 10:41:44 -0700 Subject: [PATCH] USB: relax usbcore reset timings This appears to help some folk, please merge. This patch relaxes reset timings. There are some reports that it helps make enumeration work better on some high speed devices. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c55208299cf..a12cab5314e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -435,6 +435,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) 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 @@ static void hub_power_on(struct usb_hub *hub) 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) @@ -1469,8 +1470,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* 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: -- cgit v1.2.3 From 226173edae1c49c68ebb723771a02302c85e3475 Mon Sep 17 00:00:00 2001 From: Matthew Dharm Date: Thu, 25 Aug 2005 20:03:50 -0700 Subject: [PATCH] USB: storage: Fix messed-up locking This is patch as550 from Alan Stern. Apparently someone changed the SCSI core so that it no longer holds the host lock when doing a device or bus reset. usb-storage was updated at the time, but the change was done carelessly. Some of the code depends on that lock being held. This patch reintroduces the host lock where needed and tries to clarify the comments explaining why the lock is necessary. It also moves the code that clears the TIMED_OUT and ABORTING bitflags so that it executes as soon as the timed-out command has completed (and while the host lock is held). Signed-off-by: Alan Stern Signed-off-by: Matthew Dharm Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/scsiglue.c | 20 +++++++++----------- drivers/usb/storage/usb.c | 11 ++++++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index d34dc9f417f..4837524eada 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -227,42 +227,42 @@ static int queuecommand(struct scsi_cmnd *srb, ***********************************************************************/ /* 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 @@ static int device_reset(struct scsi_cmnd *srb) } /* 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 @@ static int bus_reset(struct scsi_cmnd *srb) 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/usb.c b/drivers/usb/storage/usb.c index cb4c770baf3..f9a9bfa1aef 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -392,11 +392,16 @@ SkipForAbort: /* 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); -- cgit v1.2.3 From 490dce15ce7b36026e5430d10ee28197a593c711 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Tue, 23 Aug 2005 07:46:13 -0700 Subject: [PATCH] USB Storage: unusual_devs.h request for Transcend The stick replies to the door lock commands with a check condition (e.g. FAIL status in a normal bulk CSW), but the subsequent REQUEST SENSE returns all-zero sense. The situation is documented in our Bugzilla, including usbmon traces. https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=162559 The error is purely cosmetic, data integrity is not in danger. But I thought we might as well do it. It looks nicer that way. I discussed this with Phil and he told me to submit directly. Signed-off-by: Pete Zaitcev Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ad0cfd7a782..e60bfd8d8b6 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -103,6 +103,16 @@ UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif +/* + * Pete Zaitcev , 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 Philipp Friedrich */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", -- cgit v1.2.3 From 1ea640ce1118b0742a6e8675bddc07fc3df326b7 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Mon, 29 Aug 2005 22:38:28 -0700 Subject: [PATCH] USB: storage: Add unusual_dev SINGLE_LUN entries This patch adds entries for several USB floppies that need the US_FL_SINGLE_LUN flag. These were reported by Sebastian Kapfer and Olaf Hering , with rediffing and cleaning from me. Reported-by: Sebastian Kapfer Reported-by: Olaf Hering Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index e60bfd8d8b6..5e24bc921a6 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -86,6 +86,16 @@ UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Sebastian Kapfer + * and Olaf Hering (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 * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. @@ -96,6 +106,13 @@ UNUSUAL_DEV( 0x0411, 0x001c, 0x0113, 0x0113, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Reported by Olaf Hering 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", -- cgit v1.2.3 From e1c37b8d83fb588cc1142938fb1a1476046c8d67 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 31 Aug 2005 16:38:41 +0100 Subject: [PATCH] USB: usb-storage: Add unusual_devs entry for Neuros Audio MP3 player Alan Stern wrote: > If the device sometimes reports the correct values, then you should > include NEED_OVERRIDE flag to prevent messages about unnecessary > overrides showing up in the system log. Also, if bInterfaceSubclass > is correct and only bInterfaceProtocol is wrong, then the entry should > say US_SC_DEVICE instead of US_SC_SCSI. Fair points, thanks. When connected over USB2, this device reports a nonsense bInterfaceProtocol value 6 and doesn't work with usb-storage. When connected over USB1, the device reports the correct bInterfaceProtocol value 0x50 (bulk) and works with no problems. Signed-off-by: Daniel Drake Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 5e24bc921a6..b79dad1b598 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -130,6 +130,14 @@ UNUSUAL_DEV( 0x0457, 0x0150, 0x0100, 0x0100, "USB Mass Storage Device", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ), +/* Patch submitted by Daniel Drake + * 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 */ UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100, "Kyocera", -- cgit v1.2.3 From ce441594e965e32965432404cfaba73e8fbc6ff7 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 3 Sep 2005 11:27:08 +0200 Subject: [PATCH] USB: fix usbdevice_fs header breakage [USBDEVFS] fix inclusion of to avoud header mess Without moving the include of compat.h down, userspace programs that use usbdevice_fs.h end up including half the kernel includes (and eventually fail to compile). Signed-off-by: Harald Welte Signed-off-by: Greg Kroah-Hartman --- include/linux/usbdevice_fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index fb57c221746..9facf733800 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -32,7 +32,6 @@ #define _LINUX_USBDEVICE_FS_H #include -#include /* --------------------------------------------------------------------- */ @@ -125,6 +124,7 @@ struct usbdevfs_hub_portinfo { }; #ifdef CONFIG_COMPAT +#include struct usbdevfs_urb32 { unsigned char type; unsigned char endpoint; -- cgit v1.2.3 From 0f36163d3abefbda1b21a330b3fdf3c2dc076d94 Mon Sep 17 00:00:00 2001 From: Thomas Sailer Date: Fri, 9 Sep 2005 10:43:50 +0200 Subject: [PATCH] usb: fix uss720 schedule with interrupts off This patch fixes the long standing schedule with interrupts off problem of the uss720 driver. The problem is caused by the parport layer calling the save and restore methods within a write_lock_irqsave guarded region. The fix is to issue the control transaction requests required by save and restore asynchronously. Signed-off-by: Thomas Sailer, Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 393 +++++++++++++++++++++++++++++++--------------- 1 file changed, 270 insertions(+), 123 deletions(-) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index faa74436de5..03fb70ef2eb 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,103 +23,240 @@ * 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 #include #include #include #include #include +#include +#include /* * 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; - static const unsigned char regindex[9] = { - 4, 0, 1, 5, 5, 0, 2, 3, 6 - }; - int ret; + struct parport_uss720_private *priv = container_of(kref, struct parport_uss720_private, ref_count); - 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 { + 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 - 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]); + 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 (priv->reg[2] & priv->reg[1] & 0x10) + if (rq->reg[2] & rq->reg[1] & 0x10 && pp) parport_generic_irq(0, pp, NULL); - ret = 0; } - if (val) - *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; - return ret; + complete(&rq->compl); + kref_put(&rq->ref_count, destroy_async); } -static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val) +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 parport_uss720_private *priv = pp->private_data; - struct usb_device *usbdev = priv->usbdev; + struct usb_device *usbdev; + struct uss720_async_request *rq; + unsigned long flags; int ret; + if (!priv) + return NULL; + usbdev = priv->usbdev; 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 + 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 (!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) { + kref_put(&rq->ref_count, destroy_async); + return 0; + } + if (wait_for_completion_timeout(&rq->compl, HZ)) { + ret = rq->urb->status; + *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; + if (ret) + 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, unsigned int mem_flags) +{ + struct parport_uss720_private *priv; + struct uss720_async_request *rq; + + if (!pp) + return -EIO; + priv = pp->private_data; + rq = submit_async_request(priv, 4, 0x40, (((unsigned int)reg) << 8) | val, 0, mem_flags); + if (!rq) { + err("set_1284_register(%u,%u) failed", (unsigned int)reg, (unsigned int)val); + return -EIO; + } + kref_put(&rq->ref_count, destroy_async); + return 0; +} + +/* --------------------------------------------------------------------- */ + /* ECR modes */ #define ECR_SPP 00 #define ECR_PS2 01 @@ -132,8 +269,9 @@ static int change_mode(struct parport *pp, int m) { struct parport_uss720_private *priv = pp->private_data; int mode; + __u8 reg; - if (get_1284_register(pp, 6, NULL)) + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; /* Bits <7:5> contain the mode. */ mode = (priv->reg[2] >> 5) & 0x7; @@ -153,7 +291,7 @@ static int change_mode(struct parport *pp, int m) case ECR_ECP: /* ECP Parallel Port mode */ /* Poll slowly. */ for (;;) { - if (get_1284_register(pp, 6, NULL)) + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; if (priv->reg[2] & 0x01) break; @@ -167,7 +305,9 @@ static int change_mode(struct parport *pp, int m) } } /* 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, ®, GFP_KERNEL)) return -EIO; return 0; } @@ -179,7 +319,7 @@ static int clear_epp_timeout(struct parport *pp) { 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 int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id) 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 @@ static void parport_uss720_write_control(struct parport *pp, unsigned char d) struct parport_uss720_private *priv = pp->private_data; d = (d & 0xf) | (priv->reg[1] & 0xf0); - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -241,7 +381,7 @@ static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned ch 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 @@ static unsigned char parport_uss720_read_status(struct parport *pp) { 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 @@ static void parport_uss720_disable_irq(struct parport *pp) 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 @@ static void parport_uss720_enable_irq(struct parport *pp) 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 @@ static void parport_uss720_data_forward (struct parport *pp) 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 @@ static void parport_uss720_data_reverse (struct parport *pp) 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 @@ static void parport_uss720_save_state(struct parport *pp, struct parport_state * { 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 @@ static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t 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 @@ static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, 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 @@ static size_t parport_uss720_epp_read_addr(struct parport *pp, void *buf, size_t 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 @@ static size_t parport_uss720_epp_write_addr(struct parport *pp, const void *buf, 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 @@ static size_t parport_uss720_ecp_write_addr(struct parport *pp, const void *buff 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 struct parport_operations parport_uss720_ops = 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, \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, ®, 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 @@ static struct usb_driver uss720_driver = { /* --------------------------------------------------------------------- */ -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 @@ static int __init uss720_init(void) 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; } -- cgit v1.2.3 From bc506517ec5dcc638eaa55672d5a924c1e2a2d0b Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Thu, 1 Sep 2005 14:35:05 -0700 Subject: [PATCH] USB: Usbmon setup DMA patch Alan Stern sent me this patch. It goes on top of the patch the adds mon_dmapeek: http://ftp.kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/gregkh-04-usb/usb-usbmon-dma-areas.patch Please be warned about ordering requirements or the build may fail. Actually, mon_dmapeek is generic enough to support SETUP packets too. Signed-off-by: Pete Zaitcev Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_text.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 417464dea9f..17d0190ef64 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -79,7 +79,7 @@ static inline char mon_text_get_setup(struct mon_event_text *ep, 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. */ -- cgit v1.2.3 From f5e09b7cac4a2705f55830db64d448c062e84e8e Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Mon, 12 Sep 2005 12:23:25 +0100 Subject: [PATCH] USB: ftdi_sio: custom baud rate fix ftdi_sio: I messed up the baud_base for custom baud rate support in 2.6.13. The attached one-liner patch fixes it. Signed-off-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0a6e8b474b1..4e434cb10bb 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -914,7 +914,7 @@ static void ftdi_determine_type(struct usb_serial_port *port) 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); -- cgit v1.2.3