From 6423f883d873b001640748756b5a6f5f45863a9e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:40:41 -0500 Subject: [PATCH] - Stephen Rothwell: APM updates - Johannes Erdfelt: USB updates - Linus: call_usermodehelper(/sbin/hotplug) cleanup and deadlock fix - Leonard Zubkoff: DAC960 Driver Update - Martin Diehl: fix PCI PM callback ordering - Andrew Morton: call_usermodehelper() fixes - Urban Widmark: clean up and enable shared mmap on smbfs. - Trond Myklebust: fix NFS path revalidation. --- Documentation/Configure.help | 10 + Documentation/README.DAC960 | 10 +- Documentation/usb/error-codes.txt | 6 +- Documentation/usb/usb-serial.txt | 12 + arch/i386/kernel/apm.c | 188 +-- arch/i386/kernel/i386_ksyms.c | 2 +- arch/i386/kernel/setup.c | 4 +- arch/mips64/Makefile | 3 +- drivers/acpi/driver.c | 4 +- drivers/block/DAC960.c | 22 +- drivers/block/ll_rw_blk.c | 44 +- drivers/char/tty_io.c | 2 +- drivers/pci/pci.c | 57 +- drivers/sound/ac97_codec.c | 2 +- drivers/sound/cs4281.c | 2 +- drivers/sound/cs46xx.c | 2 +- drivers/usb/hub.h | 4 +- drivers/usb/scanner.c | 3 +- drivers/usb/serial/Config.in | 1 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/keyspan.c | 1699 ++++++++++++++++++------ drivers/usb/serial/keyspan.h | 291 +++- drivers/usb/serial/keyspan_usa26msg.h | 28 +- drivers/usb/serial/keyspan_usa28msg.h | 28 +- drivers/usb/serial/keyspan_usa49msg.h | 255 ++++ drivers/usb/serial/keyspan_usa49w_fw.h | 457 +++++++ drivers/usb/serial/mct_u232.c | 774 +++++++++++ drivers/usb/serial/mct_u232.h | 362 +++++ drivers/usb/uhci.c | 4 +- drivers/usb/usb-uhci.c | 4 +- drivers/usb/usb.c | 2 +- fs/buffer.c | 250 +--- fs/hpfs/buffer.c | 4 +- fs/nfs/dir.c | 7 +- fs/nfs/inode.c | 15 +- fs/smbfs/dir.c | 6 +- fs/smbfs/file.c | 43 +- fs/smbfs/inode.c | 18 +- fs/smbfs/proc.c | 21 +- include/linux/apm_bios.h | 20 +- include/linux/fs.h | 2 +- include/linux/kernel.h | 2 + include/linux/nfs_fs.h | 3 + include/linux/sched.h | 3 +- include/linux/smb_fs.h | 4 +- include/linux/smb_fs_sb.h | 5 +- include/linux/tqueue.h | 47 +- include/linux/usb.h | 2 +- init/main.c | 8 - kernel/context.c | 101 +- kernel/exit.c | 2 +- kernel/kmod.c | 144 +- kernel/ksyms.c | 1 - kernel/timer.c | 2 +- net/core/dev.c | 58 +- net/ipv4/devinet.c | 3 + 56 files changed, 4019 insertions(+), 1035 deletions(-) create mode 100644 drivers/usb/serial/keyspan_usa49msg.h create mode 100644 drivers/usb/serial/keyspan_usa49w_fw.h create mode 100644 drivers/usb/serial/mct_u232.c create mode 100644 drivers/usb/serial/mct_u232.h diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 7e813107d15a..135426b104f3 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -10392,6 +10392,16 @@ CONFIG_USB_SERIAL_EMPEG The module will be called empeg.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +USB MCT Single Port Serial Driver +CONFIG_USB_SERIAL_MCT_U232 + Say Y here if you want to use a USB Serial single port adapter from + Magic Control Technology Corp. (U232 is one of the model numbers). + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called mct_u232.o. If you want to compile it as + a module, say M here and read Documentation/modules.txt. + USB Serial Converter verbose debug CONFIG_USB_SERIAL_DEBUG Say Y here if you want verbose debug messages from the USB Serial diff --git a/Documentation/README.DAC960 b/Documentation/README.DAC960 index 88f52364b647..4c0832e79e15 100644 --- a/Documentation/README.DAC960 +++ b/Documentation/README.DAC960 @@ -1,11 +1,11 @@ Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers - Version 2.2.8 for Linux 2.2.16 - Version 2.4.8 for Linux 2.4.0 + Version 2.2.9 for Linux 2.2.17 + Version 2.4.9 for Linux 2.4.0 PRODUCTION RELEASE - 19 August 2000 + 7 September 2000 Leonard N. Zubkoff Dandelion Digital @@ -203,13 +203,13 @@ ftp://ftp.mylex.com/pub/dac960/diskcomp.html. DRIVER INSTALLATION -This distribution was prepared for Linux kernel version 2.2.16 or 2.4.0. +This distribution was prepared for Linux kernel version 2.2.17 or 2.4.0. To install the DAC960 RAID driver, you may use the following commands, replacing "/usr/src" with wherever you keep your Linux kernel source tree: cd /usr/src - tar -xvzf DAC960-2.2.8.tar.gz (or DAC960-2.4.8.tar.gz) + tar -xvzf DAC960-2.2.9.tar.gz (or DAC960-2.4.9.tar.gz) mv README.DAC960 linux/Documentation mv DAC960.[ch] linux/drivers/block patch -p0 < DAC960.patch (if DAC960.patch is included) diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt index e42ab36e07c6..964af3033dec 100644 --- a/Documentation/usb/error-codes.txt +++ b/Documentation/usb/error-codes.txt @@ -26,9 +26,9 @@ USB-specific: -ENODEV specified USB-device or bus doesn't exist USB_ST_REQUEST_ERROR --ENXIO a) specified endpoint doesn't exist on the device - b) an URB is already queued to this endpoint and - USB_QUEUE_BULK wasn't used (UHCI HCDs only) +-ENXIO a control or interrupt URB is already queued to this endpoint; or + a bulk URB is already queued to this endpoint and + USB_QUEUE_BULK wasn't used (UHCI HCDs only) USB_ST_URB_INVALID_ERROR -EINVAL a) Invalid transfer type specified (or not supported) diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt index d4740c4048e4..2fc5ac8eb941 100644 --- a/Documentation/usb/usb-serial.txt +++ b/Documentation/usb/usb-serial.txt @@ -200,6 +200,18 @@ Empeg empeg-car Mark I/II Driver (empeg.c) helpful. :) +MCT USB Single Port Serial Adapter U232 + + This driver is for the MCT USB-RS232 Converter (25 pin, Model No. + U232-P25) from Magic Control Technology Corp. (there is also a 9 pin + Model No. U232-P9). More information about this device can be found + at the manufacture's web-site: http://www.mct.com.tw. + + The driver is generally working, though it still needs some more + testing. It is derived from the Belkin USB Serial Adapter F5U103 + driver and its TODO list is valid for this driver as well. + + Generic Serial driver If your device is not one of the above listed devices, compatible with diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index dc47528cd1c7..9703e33044c4 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -37,6 +37,7 @@ * Nov 1999, Version 1.11 * Jan 2000, Version 1.12 * Feb 2000, Version 1.13 + * Nov 2000, Version 1.14 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -144,6 +145,9 @@ * and ) modified by sfr. * Remove CONFIG_APM_SUSPEND_BOUNCE. The bounce ignore * interval is now configurable. + * 1.14: Make connection version persist across module unload/load. + * Enable and engage power management earlier. + * Disengage power management on module unload. * * APM 1.1 Reference: * @@ -344,9 +348,9 @@ static int kapmd_running; static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); -static struct apm_user * user_list = NULL; +static struct apm_user * user_list; -static char driver_version[] = "1.13"; /* no spaces */ +static char driver_version[] = "1.14"; /* no spaces */ static char * apm_event_name[] = { "system standby", @@ -527,7 +531,7 @@ static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info) &dummy, &dummy)) return (eax >> 8) & 0xff; *event = ebx; - if (apm_bios_info.version < 0x0102) + if (apm_info.connection_version < 0x0102) *info = ~0; /* indicate info not valid */ else *info = ecx; @@ -559,7 +563,7 @@ static int apm_do_idle(void) #ifdef ALWAYS_CALL_BUSY clock_slowed = 1; #else - clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0; + clock_slowed = (apm_info.bios.flags & APM_IDLE_SLOWS_CLOCK) != 0; #endif return 1; } @@ -670,15 +674,15 @@ static int apm_enable_power_management(int enable) { u32 eax; - if ((enable == 0) && (apm_bios_info.flags & APM_BIOS_DISENGAGED)) + if ((enable == 0) && (apm_info.bios.flags & APM_BIOS_DISENGAGED)) return APM_NOT_ENGAGED; if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL, enable, &eax)) return (eax >> 8) & 0xff; if (enable) - apm_bios_info.flags &= ~APM_BIOS_DISABLED; + apm_info.bios.flags &= ~APM_BIOS_DISABLED; else - apm_bios_info.flags |= APM_BIOS_DISABLED; + apm_info.bios.flags |= APM_BIOS_DISABLED; return APM_SUCCESS; } #endif @@ -691,6 +695,8 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) u32 edx; u32 dummy; + if (apm_info.get_power_status_broken) + return APM_32_UNSUPPORTED; if (apm_bios_call(APM_FUNC_GET_STATUS, APM_DEVICE_ALL, 0, &eax, &ebx, &ecx, &edx, &dummy)) return (eax >> 8) & 0xff; @@ -710,7 +716,7 @@ static int apm_get_battery_status(u_short which, u_short *status, u32 edx; u32 esi; - if (apm_bios_info.version < 0x0102) { + if (apm_info.connection_version < 0x0102) { /* pretend we only have one battery. */ if (which != 1) return APM_BAD_DEVICE; @@ -734,15 +740,15 @@ static int apm_engage_power_management(u_short device, int enable) u32 eax; if ((enable == 0) && (device == APM_DEVICE_ALL) - && (apm_bios_info.flags & APM_BIOS_DISABLED)) + && (apm_info.bios.flags & APM_BIOS_DISABLED)) return APM_DISABLED; if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax)) return (eax >> 8) & 0xff; if (device == APM_DEVICE_ALL) { if (enable) - apm_bios_info.flags &= ~APM_BIOS_DISENGAGED; + apm_info.bios.flags &= ~APM_BIOS_DISENGAGED; else - apm_bios_info.flags |= APM_BIOS_DISENGAGED; + apm_info.bios.flags |= APM_BIOS_DISENGAGED; } return APM_SUCCESS; } @@ -890,7 +896,7 @@ static int send_event(apm_event_t event) printk(KERN_CRIT "apm: Critical suspend was vetoed, expect armageddon\n" ); return 0; } - if (apm_bios_info.version > 0x100) + if (apm_info.connection_version > 0x100) apm_set_power_state(APM_STATE_REJECT); return 0; } @@ -993,13 +999,13 @@ static void check_events(void) case APM_USER_SUSPEND: #ifdef CONFIG_APM_IGNORE_USER_SUSPEND - if (apm_bios_info.version > 0x100) + if (apm_info.connection_version > 0x100) apm_set_power_state(APM_STATE_REJECT); break; #endif case APM_SYS_SUSPEND: if (ignore_bounce) { - if (apm_bios_info.version > 0x100) + if (apm_info.connection_version > 0x100) apm_set_power_state(APM_STATE_REJECT); break; } @@ -1064,7 +1070,7 @@ static void apm_event_handler(void) int err; if ((standbys_pending > 0) || (suspends_pending > 0)) { - if ((apm_bios_info.version > 0x100) && (pending_count-- <= 0)) { + if ((apm_info.connection_version > 0x100) && (pending_count-- <= 0)) { pending_count = 4; if (debug) printk(KERN_DEBUG "apm: setting state busy\n"); @@ -1334,7 +1340,7 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length) unsigned short bx; unsigned short cx; unsigned short dx; - unsigned short error; + int error; unsigned short ac_line_status = 0xff; unsigned short battery_status = 0xff; unsigned short battery_flag = 0xff; @@ -1351,7 +1357,7 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length) if ((cx & 0xff) != 0xff) percentage = cx & 0xff; - if (apm_bios_info.version > 0x100) { + if (apm_info.connection_version > 0x100) { battery_flag = (cx >> 8) & 0xff; if (dx != 0xffff) { units = (dx & 0x8000) ? "min" : "sec"; @@ -1399,9 +1405,9 @@ static int apm_get_info(char *buf, char **start, off_t fpos, int length) p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", driver_version, - (apm_bios_info.version >> 8) & 0xff, - apm_bios_info.version & 0xff, - apm_bios_info.flags, + (apm_info.bios.version >> 8) & 0xff, + apm_info.bios.version & 0xff, + apm_info.bios.flags, ac_line_status, battery_status, battery_flag, @@ -1417,7 +1423,7 @@ static int apm(void *unused) unsigned short bx; unsigned short cx; unsigned short dx; - unsigned short error; + int error; char * power_stat; char * bat_stat; @@ -1429,22 +1435,53 @@ static int apm(void *unused) sigfillset(¤t->blocked); current->tty = NULL; /* get rid of controlling tty */ - if (apm_bios_info.version > 0x100) { + if (apm_info.connection_version == 0) { + apm_info.connection_version = apm_info.bios.version; + if (apm_info.connection_version > 0x100) { + /* + * We only support BIOSs up to version 1.2 + */ + if (apm_info.connection_version > 0x0102) + apm_info.connection_version = 0x0102; + error = apm_driver_version(&apm_info.connection_version); + if (error != APM_SUCCESS) { + apm_error("driver version", error); + /* Fall back to an APM 1.0 connection. */ + apm_info.connection_version = 0x100; + } + } + } + + if (debug) + printk(KERN_INFO "apm: Connection version %d.%d\n", + (apm_info.connection_version >> 8) & 0xff, + apm_info.connection_version & 0xff); + +#ifdef CONFIG_APM_DO_ENABLE + if (apm_info.bios.flags & APM_BIOS_DISABLED) { /* - * We only support BIOSs up to version 1.2 + * This call causes my NEC UltraLite Versa 33/C to hang if it + * is booted with PM disabled but not in the docking station. + * Unfortunate ... */ - if (apm_bios_info.version > 0x0102) - apm_bios_info.version = 0x0102; - if (apm_driver_version(&apm_bios_info.version) != APM_SUCCESS) { - /* Fall back to an APM 1.0 connection. */ - apm_bios_info.version = 0x100; + error = apm_enable_power_management(1); + if (error) { + apm_error("enable power management", error); + return -1; + } + } +#endif + + if ((apm_info.bios.flags & APM_BIOS_DISENGAGED) + && (apm_info.connection_version > 0x0100)) { + error = apm_engage_power_management(APM_DEVICE_ALL, 1); + if (error) { + apm_error("engage power management", error); + return -1; } } - if (debug && (smp_num_cpus == 1)) { - printk(KERN_INFO "apm: Connection version %d.%d\n", - (apm_bios_info.version >> 8) & 0xff, - apm_bios_info.version & 0xff); + if (debug && (smp_num_cpus == 1)) { error = apm_get_power_status(&bx, &cx, &dx); if (error) printk(KERN_INFO "apm: power status not available\n"); @@ -1469,7 +1506,7 @@ static int apm(void *unused) printk("unknown\n"); else printk("%d%%\n", cx & 0xff); - if (apm_bios_info.version > 0x100) { + if (apm_info.connection_version > 0x100) { printk(KERN_INFO "apm: battery flag 0x%02x, battery life ", (cx >> 8) & 0xff); @@ -1483,29 +1520,6 @@ static int apm(void *unused) } } -#ifdef CONFIG_APM_DO_ENABLE - if (apm_bios_info.flags & APM_BIOS_DISABLED) { - /* - * This call causes my NEC UltraLite Versa 33/C to hang if it - * is booted with PM disabled but not in the docking station. - * Unfortunate ... - */ - error = apm_enable_power_management(1); - if (error) { - apm_error("enable power management", error); - return -1; - } - } -#endif - if ((apm_bios_info.flags & APM_BIOS_DISENGAGED) - && (apm_bios_info.version > 0x0100)) { - error = apm_engage_power_management(APM_DEVICE_ALL, 1); - if (error) { - apm_error("engage power management", error); - return -1; - } - } - /* Install our power off handler.. */ if (power_off) pm_power_off = apm_power_off; @@ -1584,17 +1598,19 @@ static struct miscdevice apm_device = { */ static int __init apm_init(void) { - if (apm_bios_info.version == 0) { + struct proc_dir_entry *apm_proc; + + if (apm_info.bios.version == 0) { printk(KERN_INFO "apm: BIOS not found.\n"); return -ENODEV; } printk(KERN_INFO "apm: BIOS version %d.%d Flags 0x%02x (Driver version %s)\n", - ((apm_bios_info.version >> 8) & 0xff), - (apm_bios_info.version & 0xff), - apm_bios_info.flags, + ((apm_info.bios.version >> 8) & 0xff), + (apm_info.bios.version & 0xff), + apm_info.bios.flags, driver_version); - if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) { + if ((apm_info.bios.flags & APM_32_BIT_SUPPORT) == 0) { printk(KERN_INFO "apm: no 32 bit BIOS support\n"); return -ENODEV; } @@ -1603,23 +1619,23 @@ static int __init apm_init(void) * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1 * but is reportedly a 1.0 BIOS. */ - if (apm_bios_info.version == 0x001) - apm_bios_info.version = 0x100; + if (apm_info.bios.version == 0x001) + apm_info.bios.version = 0x100; /* BIOS < 1.2 doesn't set cseg_16_len */ - if (apm_bios_info.version < 0x102) - apm_bios_info.cseg_16_len = 0; /* 64k */ + if (apm_info.bios.version < 0x102) + apm_info.bios.cseg_16_len = 0; /* 64k */ if (debug) { printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x", - apm_bios_info.cseg, apm_bios_info.offset, - apm_bios_info.cseg_16, apm_bios_info.dseg); - if (apm_bios_info.version > 0x100) + apm_info.bios.cseg, apm_info.bios.offset, + apm_info.bios.cseg_16, apm_info.bios.dseg); + if (apm_info.bios.version > 0x100) printk(" cseg len %x, dseg len %x", - apm_bios_info.cseg_len, - apm_bios_info.dseg_len); - if (apm_bios_info.version > 0x101) - printk(" cseg16 len %x", apm_bios_info.cseg_16_len); + apm_info.bios.cseg_len, + apm_info.bios.dseg_len); + if (apm_info.bios.version > 0x101) + printk(" cseg16 len %x", apm_info.bios.cseg_16_len); printk("\n"); } @@ -1647,16 +1663,16 @@ static int __init apm_init(void) __va((unsigned long)0x40 << 4)); _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); - apm_bios_entry.offset = apm_bios_info.offset; + apm_bios_entry.offset = apm_info.bios.offset; apm_bios_entry.segment = APM_CS; set_base(gdt[APM_CS >> 3], - __va((unsigned long)apm_bios_info.cseg << 4)); + __va((unsigned long)apm_info.bios.cseg << 4)); set_base(gdt[APM_CS_16 >> 3], - __va((unsigned long)apm_bios_info.cseg_16 << 4)); + __va((unsigned long)apm_info.bios.cseg_16 << 4)); set_base(gdt[APM_DS >> 3], - __va((unsigned long)apm_bios_info.dseg << 4)); + __va((unsigned long)apm_info.bios.dseg << 4)); #ifndef APM_RELAX_SEGMENTS - if (apm_bios_info.version == 0x100) { + if (apm_info.bios.version == 0x100) { #endif /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); @@ -1667,15 +1683,17 @@ static int __init apm_init(void) #ifndef APM_RELAX_SEGMENTS } else { _set_limit((char *)&gdt[APM_CS >> 3], - (apm_bios_info.cseg_len - 1) & 0xffff); + (apm_info.bios.cseg_len - 1) & 0xffff); _set_limit((char *)&gdt[APM_CS_16 >> 3], - (apm_bios_info.cseg_16_len - 1) & 0xffff); + (apm_info.bios.cseg_16_len - 1) & 0xffff); _set_limit((char *)&gdt[APM_DS >> 3], - (apm_bios_info.dseg_len - 1) & 0xffff); + (apm_info.bios.dseg_len - 1) & 0xffff); } #endif - create_proc_info_entry("apm", 0, NULL, apm_get_info); + apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info); + if (apm_proc) + SET_MODULE_OWNER(apm_proc); kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); @@ -1692,6 +1710,14 @@ static int __init apm_init(void) static void __exit apm_exit(void) { + int error; + + if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0) + && (apm_info.connection_version > 0x0100)) { + error = apm_engage_power_management(APM_DEVICE_ALL, 0); + if (error) + apm_error("disengage power management", error); + } misc_deregister(&apm_device); remove_proc_entry("apm", NULL); #ifdef CONFIG_MAGIC_SYSRQ diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 3de4a76d1b98..071b3991a988 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -68,7 +68,7 @@ EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(pm_idle); EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL(get_cmos_time); -EXPORT_SYMBOL(apm_bios_info); +EXPORT_SYMBOL(apm_info); EXPORT_SYMBOL(gdt); EXPORT_SYMBOL_NOVERS(__down_failed); diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index b0bbdb759c85..fffe11398481 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -126,7 +126,7 @@ unsigned int mca_pentium_flag; */ struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info; -struct apm_bios_info apm_bios_info; +struct apm_info apm_info; struct sys_desc_table_struct { unsigned short length; unsigned char table[0]; @@ -608,7 +608,7 @@ void __init setup_arch(char **cmdline_p) ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; - apm_bios_info = APM_BIOS_INFO; + apm_info.bios = APM_BIOS_INFO; if( SYS_DESC_TABLE.length != 0 ) { MCA_bus = SYS_DESC_TABLE.table[3] &0x2; machine_id = SYS_DESC_TABLE.table[0]; diff --git a/arch/mips64/Makefile b/arch/mips64/Makefile index f52468efe5c5..917229bfde30 100644 --- a/arch/mips64/Makefile +++ b/arch/mips64/Makefile @@ -33,6 +33,7 @@ endif # machines may also. Since BFD is incredibly buggy with respect to # crossformat linking we rely on the elf2ecoff tool for format conversion. # +CFLAGS += -I $(TOPDIR)/include/asm $(CFLAGS) CFLAGS += -mabi=64 -G 0 -mno-abicalls -fno-pic -Wa,--trap -pipe LINKFLAGS += -G 0 -static # -N MODFLAGS += -mlong-calls @@ -154,7 +155,7 @@ archclean: @$(MAKEBOOT) clean $(MAKE) -C arch/$(ARCH)/kernel clean $(MAKE) -C arch/$(ARCH)/tools clean - rm -f vmlinux.64 arch/($ARCH)/ld.script.elf32 + rm -f vmlinux.64 arch/$(ARCH)/ld.script.elf32 archmrproper: @$(MAKEBOOT) mrproper diff --git a/drivers/acpi/driver.c b/drivers/acpi/driver.c index 411e07fcbbba..5015551eb594 100644 --- a/drivers/acpi/driver.c +++ b/drivers/acpi/driver.c @@ -336,9 +336,7 @@ acpi_thread(void *context) interruptible_sleep_on(&acpi_thread_wait); if (signal_pending(current)) break; - do { - run_task_queue(&acpi_thread_run); - } while (acpi_thread_run); + run_task_queue(&acpi_thread_run); } /* diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index dba6b5adfdb4..5e8dc19f9fee 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -19,8 +19,8 @@ */ -#define DAC960_DriverVersion "2.4.8" -#define DAC960_DriverDate "19 August 2000" +#define DAC960_DriverVersion "2.4.9" +#define DAC960_DriverDate "7 September 2000" #include @@ -300,13 +300,8 @@ static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command) static void DAC960_WaitForCommand(DAC960_Controller_T *Controller) { - DECLARE_WAITQUEUE(WaitQueueEntry, current); - add_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry); - current->state = TASK_UNINTERRUPTIBLE; - spin_unlock(&io_request_lock); - schedule(); - current->state = TASK_RUNNING; - remove_wait_queue(&Controller->CommandWaitQueue, &WaitQueueEntry); + spin_unlock_irq(&io_request_lock); + __wait_event(Controller->CommandWaitQueue, Controller->FreeCommands); spin_lock_irq(&io_request_lock); } @@ -4957,7 +4952,8 @@ static int DAC960_IOCTL(Inode_T *Inode, File_T *File, } Geometry.start = Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].start_sect; - return copy_to_user(UserGeometry, &Geometry, sizeof(DiskGeometry_T)); + return (copy_to_user(UserGeometry, &Geometry, + sizeof(DiskGeometry_T)) ? -EFAULT : 0); case BLKGETSIZE: /* Get Device Size. */ if ((long *) Argument == NULL) return -EINVAL; @@ -5078,8 +5074,8 @@ static int DAC960_UserIOCTL(Inode_T *Inode, File_T *File, ControllerInfo.PCI_Address = Controller->PCI_Address; strcpy(ControllerInfo.ModelName, Controller->ModelName); strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion); - return copy_to_user(UserSpaceControllerInfo, &ControllerInfo, - sizeof(DAC960_ControllerInfo_T)); + return (copy_to_user(UserSpaceControllerInfo, &ControllerInfo, + sizeof(DAC960_ControllerInfo_T)) ? -EFAULT : 0); } case DAC960_IOCTL_V1_EXECUTE_COMMAND: { @@ -6401,7 +6397,7 @@ static int DAC960_ProcWriteUserCommand(File_T *File, const char *Buffer, unsigned char CommandBuffer[80]; int Length; if (Count > sizeof(CommandBuffer)-1) return -EINVAL; - copy_from_user(CommandBuffer, Buffer, Count); + if (copy_from_user(CommandBuffer, Buffer, Count)) return -EFAULT; CommandBuffer[Count] = '\0'; Length = strlen(CommandBuffer); if (CommandBuffer[Length-1] == '\n') diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 96c5b2b3961b..a228034de4d7 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -885,6 +885,36 @@ void generic_make_request (int rw, struct buffer_head * bh) while (q->make_request_fn(q, rw, bh)); } + +/* + * Submit a buffer head for IO. + */ +void submit_bh(int rw, struct buffer_head * bh) +{ + if (!test_bit(BH_Lock, &bh->b_state)) + BUG(); + + set_bit(BH_Req, &bh->b_state); + + /* + * First step, 'identity mapping' - RAID or LVM might + * further remap this. + */ + bh->b_rdev = bh->b_dev; + bh->b_rsector = bh->b_blocknr * (bh->b_size>>9); + + generic_make_request(rw, bh); +} + +/* + * Default IO end handler, used by "ll_rw_block()". + */ +static void end_buffer_io_sync(struct buffer_head *bh, int uptodate) +{ + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); +} + /* This function can be used to request a number of buffers from a block device. Currently the only restriction is that all buffers must belong to the same device */ @@ -931,7 +961,8 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]) if (test_and_set_bit(BH_Lock, &bh->b_state)) continue; - set_bit(BH_Req, &bh->b_state); + /* We have the buffer lock */ + bh->b_end_io = end_buffer_io_sync; switch(rw) { case WRITE: @@ -954,17 +985,9 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]) end_io: bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); continue; - } - /* - * First step, 'identity mapping' - RAID or LVM might - * further remap this. - */ - bh->b_rdev = bh->b_dev; - bh->b_rsector = bh->b_blocknr * (bh->b_size>>9); - - generic_make_request(rw, bh); + submit_bh(rw, bh); } return; @@ -973,7 +996,6 @@ sorry: buffer_IO_error(bhs[i]); } - #ifdef CONFIG_STRAM_SWAP extern int stram_device_init (void); #endif diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 33f5bc879af2..92569a777ac9 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1262,7 +1262,7 @@ static void release_dev(struct file * filp) * Make sure that the tty's task queue isn't activated. */ run_task_queue(&tq_timer); - run_schedule_tasks(); + flush_scheduled_tasks(); /* * The release_mem function takes care of the details of clearing diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 72a8ec211b2e..afcc00a892c3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -300,18 +300,25 @@ static int pci_announce_device(struct pci_driver *drv, struct pci_dev *dev) { const struct pci_device_id *id; + int ret = 0; if (drv->id_table) { id = pci_match_device(drv->id_table, dev); - if (!id) - return 0; + if (!id) { + ret = 0; + goto out; + } } else id = NULL; + + dev_probe_lock(); if (drv->probe(dev, id) >= 0) { dev->driver = drv; - return 1; + ret = 1; } - return 0; + dev_probe_unlock(); +out: + return ret; } int @@ -360,9 +367,9 @@ run_sbin_hotplug(struct pci_dev *pdev, int insert) if (!hotplug_path[0]) return; - sprintf(class_id, "PCI_CLASS=%X", pdev->class); - sprintf(id, "PCI_ID=%X/%X", pdev->vendor, pdev->device); - sprintf(sub_id, "PCI_SUBSYS_ID=%X/%X", pdev->subsystem_vendor, pdev->subsystem_device); + sprintf(class_id, "PCI_CLASS=%04X", pdev->class); + sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device); + sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device); sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name); i = 0; @@ -704,6 +711,7 @@ static struct pci_bus * __init pci_alloc_bus(void) static struct pci_bus * __init pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) { struct pci_bus *child; + int i; /* * Allocate a new bus, and inherit stuff from the parent.. @@ -725,6 +733,10 @@ static struct pci_bus * __init pci_add_new_bus(struct pci_bus *parent, struct pc child->primary = parent->secondary; child->subordinate = 0xff; + /* Set up default resource pointers.. */ + for (i = 0; i < 4; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + return child; } @@ -765,10 +777,7 @@ static int __init pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int unsigned int cmax = pci_do_scan_bus(child); if (cmax > max) max = cmax; } else { - int i; unsigned int cmax = child->subordinate; - for (i = 0; i < 4; i++) - child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; if (cmax > max) max = cmax; } } else { @@ -782,6 +791,7 @@ static int __init pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int pci_read_config_word(dev, PCI_COMMAND, &cr); pci_write_config_word(dev, PCI_COMMAND, 0x0000); pci_write_config_word(dev, PCI_STATUS, 0xffff); + child = pci_add_new_bus(bus, dev, ++max); buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) @@ -795,15 +805,12 @@ static int __init pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int /* Now we can scan all subordinate buses... */ max = pci_do_scan_bus(child); } else { - int i; /* * For CardBus bridges, we leave 4 bus numbers * as cards with a PCI-to-PCI bridge can be * inserted later. */ max += 3; - for (i = 0; i < 4; i++) - child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; } /* * Set the subordinate bus number to its real value. @@ -1082,6 +1089,9 @@ static int pci_pm_resume_device(struct pci_dev *dev) return 0; } + +/* take care to suspend/resume bridges only once */ + static int pci_pm_suspend_bus(struct pci_bus *bus) { struct list_head *list; @@ -1093,9 +1103,6 @@ static int pci_pm_suspend_bus(struct pci_bus *bus) /* Walk the device children list */ list_for_each(list, &bus->devices) pci_pm_suspend_device(pci_dev_b(list)); - - /* Suspend the bus controller.. */ - pci_pm_suspend_device(bus->self); return 0; } @@ -1103,8 +1110,6 @@ static int pci_pm_resume_bus(struct pci_bus *bus) { struct list_head *list; - pci_pm_resume_device(bus->self); - /* Walk the device children list */ list_for_each(list, &bus->devices) pci_pm_resume_device(pci_dev_b(list)); @@ -1118,18 +1123,26 @@ static int pci_pm_resume_bus(struct pci_bus *bus) static int pci_pm_suspend(void) { struct list_head *list; + struct pci_bus *bus; - list_for_each(list, &pci_root_buses) - pci_pm_suspend_bus(pci_bus_b(list)); + list_for_each(list, &pci_root_buses) { + bus = pci_bus_b(list); + pci_pm_suspend_bus(bus); + pci_pm_suspend_device(bus->self); + } return 0; } static int pci_pm_resume(void) { struct list_head *list; + struct pci_bus *bus; - list_for_each(list, &pci_root_buses) - pci_pm_resume_bus(pci_bus_b(list)); + list_for_each(list, &pci_root_buses) { + bus = pci_bus_b(list); + pci_pm_resume_device(bus->self); + pci_pm_resume_bus(bus); + } return 0; } diff --git a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c index 558161d28ea6..e489fbd01872 100644 --- a/drivers/sound/ac97_codec.c +++ b/drivers/sound/ac97_codec.c @@ -134,7 +134,7 @@ static struct mixer_defaults { {SOUND_MIXER_PCM, 0x4343}, {SOUND_MIXER_SPEAKER, 0x4343}, {SOUND_MIXER_LINE, 0x4343}, - {SOUND_MIXER_MIC, 0x4343}, + {SOUND_MIXER_MIC, 0x0000}, {SOUND_MIXER_CD, 0x4343}, {SOUND_MIXER_ALTPCM, 0x4343}, {SOUND_MIXER_IGAIN, 0x4343}, diff --git a/drivers/sound/cs4281.c b/drivers/sound/cs4281.c index 3fcb068d1c31..661caa2144ab 100644 --- a/drivers/sound/cs4281.c +++ b/drivers/sound/cs4281.c @@ -258,7 +258,7 @@ struct cs4281_state { #define SNDCTL_DSP_CS_GETDBGMASK _SIOWR('P', 52, int) #define SNDCTL_DSP_CS_SETDBGMASK _SIOWR('P', 53, int) -void printioctl(unsigned int x) +static void printioctl(unsigned int x) { unsigned int i; unsigned char vidx; diff --git a/drivers/sound/cs46xx.c b/drivers/sound/cs46xx.c index c0bf7c382864..d3ff7d71fe48 100644 --- a/drivers/sound/cs46xx.c +++ b/drivers/sound/cs46xx.c @@ -359,7 +359,7 @@ extern __inline__ unsigned ld2(unsigned int x) #define SNDCTL_DSP_CS_GETDBGMASK _SIOWR('P', 52, int) #define SNDCTL_DSP_CS_SETDBGMASK _SIOWR('P', 53, int) -void printioctl(unsigned int x) +static void printioctl(unsigned int x) { unsigned int i; unsigned char vidx; diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h index 626869736116..422533b444df 100644 --- a/drivers/usb/hub.h +++ b/drivers/usb/hub.h @@ -95,8 +95,8 @@ struct usb_hub { struct urb *urb; /* Interrupt polling pipe */ - char buffer[USB_MAXCHILDREN / 8]; - + char buffer[(USB_MAXCHILDREN + 1 + 7) / 8]; /* add 1 bit for hub status change */ + /* and add 7 bits to round up to byte boundary */ int error; int nerrors; diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c index 2ee4ba127d76..2a7e887d9396 100644 --- a/drivers/usb/scanner.c +++ b/drivers/usb/scanner.c @@ -252,6 +252,7 @@ static struct usb_device_id scanner_device_ids [] = { { idVendor: 0x04a5, idProduct: 0x2022 },/* Vuego Scan Brisa 340U */ /* Agfa */ { idVendor: 0x06bd, idProduct: 0x0001 }, /* SnapScan 1212U */ + { idVendor: 0x06bd, idProduct: 0x0002 }, /* SnapScan 1236U */ { idVendor: 0x06bd, idProduct: 0x2061 }, /* Another SnapScan 1212U (?)*/ { idVendor: 0x06bd, idProduct: 0x0100 }, /* SnapScan Touch */ /* Colorado -- See Primax/Colorado below */ @@ -300,10 +301,10 @@ static struct usb_device_id scanner_device_ids [] = { { idVendor: 0x04b8, idProduct: 0x0101 },/* Perfection 636U and 636Photo */ { idVendor: 0x04b8, idProduct: 0x0103 },/* Perfection 610 */ { idVendor: 0x04b8, idProduct: 0x0104 },/* Perfection 1200U and 1200Photo*/ + { idVendor: 0x04b8, idProduct: 0x0106 },/* Stylus Scan 2500 */ { idVendor: 0x04b8, idProduct: 0x0107 },/* Expression 1600 */ /* Umax */ { idVendor: 0x1606, idProduct: 0x0010 }, /* Astra 1220U */ - { idVendor: 0x1606, idProduct: 0x0002 }, /* Astra 1236U */ { idVendor: 0x1606, idProduct: 0x0030 }, /* Astra 2000U */ { idVendor: 0x1606, idProduct: 0x0230 }, /* Astra 2200U */ /* Visioneer */ diff --git a/drivers/usb/serial/Config.in b/drivers/usb/serial/Config.in index db77957a9782..6d949bc0ba79 100644 --- a/drivers/usb/serial/Config.in +++ b/drivers/usb/serial/Config.in @@ -23,6 +23,7 @@ if [ "$CONFIG_USB_SERIAL" != "n" ]; then bool ' USB Keyspan USA-18X Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA18X bool ' USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W fi + dep_tristate ' USB MCT Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL fi diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 0fc7143045e9..97b5516aab29 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_DIGI_ACCELEPORT) += digi_acceleport.o obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o obj-$(CONFIG_USB_SERIAL_EMPEG) += empeg.o +obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o # Objects that export symbols. export-objs := usbserial.o diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 885108cd0819..b4ee5fdc111d 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -19,24 +19,33 @@ and Keyspan, Inc the manufacturers of the Keyspan USB-serial products. Thanks Guys :) + Thanks to Paulus for miscellaneous tidy ups, some largish chunks + of much nicer and/or completely new code and (perhaps most uniquely) + having the patience to sit down and explain why and where he'd changed + stuff. + Tip 'o the hat to Linuxcare for supporting staff in their work on open source projects. - (11/01/2000) Adam J. Richter + Change History + (11/01/2000) Adam J. Richter usb_device_id table support. + + Tue Oct 10 23:15:33 EST 2000 Hugh + Merged Paul's changes with my USA-49W mods. Work in progress + still... + + Wed Jul 19 14:00:42 EST 2000 gkh + Added module_init and module_exit functions to handle the fact that + this driver is a loadable module now. - (10/05/2000) gkh - Fixed bug with urb->dev not being set properly, now that the usb - core needs it. - - Wed Jul 19 14:00:42 EST 2000 gkh - Added module_init and module_exit functions to handle the fact that this - driver is a loadable module now. - - Tue Jul 18 16:14:52 EST 2000 Hugh - Basic character input/output for USA-19 now mostly works, - fixed at 9600 baud for the moment. + Tue Jul 18 16:14:52 EST 2000 Hugh + Basic character input/output for USA-19 now mostly works, + fixed at 9600 baud for the moment. + Sat Jul 8 11:11:48 EST 2000 Hugh + First public release - nothing works except the firmware upload. + Tested on PPC and x86 architectures, seems to behave... */ @@ -55,53 +64,130 @@ #include #include -#ifdef CONFIG_USB_SERIAL_DEBUG +#define DEBUG +/* #ifdef CONFIG_USB_SERIAL_DEBUG */ #define DEBUG -#else - #undef DEBUG -#endif +/* #endif */ #include #include "usb-serial.h" #include "keyspan.h" +#define INSTAT_BUFLEN 32 +#define GLOCONT_BUFLEN 64 + /* Per device and per port private data */ struct keyspan_serial_private { - struct urb *in_urbs[8]; - struct urb *out_urbs[8]; - char out_buffer[64]; - char in_buffer[64]; + /* number of active ports */ + atomic_t active_count; + + const keyspan_device_details *device_details; + + urb_t *instat_urb; + char instat_buf[INSTAT_BUFLEN]; + + /* XXX this one probably will need a lock */ + urb_t *glocont_urb; + char glocont_buf[GLOCONT_BUFLEN]; }; struct keyspan_port_private { - /* Keep track of which output endpoint to use */ + /* Keep track of which input & output endpoints to use */ + int in_flip; int out_flip; - /* Settings for the port */ + /* Keep duplicate of device details in each port + structure as well - simplifies some of the + callback functions etc. */ + const keyspan_device_details *device_details; + + /* Input endpoints and buffer for this port */ + urb_t *in_urbs[2]; + char in_buffer[2][64]; + /* Output endpoints and buffer for this port */ + urb_t *out_urbs[2]; + char out_buffer[2][64]; + + /* Input ack endpoint */ + urb_t *inack_urb; + char inack_buffer[1]; + + /* Output control endpoint */ + urb_t *outcont_urb; + char outcont_buffer[64]; + + /* Settings for the port */ int baud; int old_baud; - enum {parity_none, parity_odd, parity_even} parity; + unsigned int cflag; enum {flow_none, flow_cts, flow_xon} flow_control; - int rts_state; + int rts_state; /* Handshaking pins (outputs) */ int dtr_state; + int cts_state; /* Handshaking pins (inputs) */ + int dsr_state; + int dcd_state; + int ri_state; + unsigned long tx_start_time[2]; + int resend_cont; /* need to resend control packet */ }; - - /* FIXME this will break if multiple physical interfaces used */ -static wait_queue_head_t out_wait; - /* Include Keyspan message headers (not both yet, need some tweaks - to get clean build) */ -/*#include "keyspan_usa26msg.h"*/ +/* Include Keyspan message headers. All current Keyspan Adapters + make use of one of three message formats which are referred + to as USA-26, USA-28 and USA-49 by Keyspan and within this driver. */ +#include "keyspan_usa26msg.h" #include "keyspan_usa28msg.h" +#include "keyspan_usa49msg.h" - /* If you don't get debugging output, uncomment the following - two lines to enable cheat. */ -#undef dbg -#define dbg printk +/* If you don't get debugging output, uncomment the following + two lines to enable cheat. */ +#if 0 + #undef dbg + #define dbg printk +#endif + +static void keyspan_send_setup(struct usb_serial_port *port); + +/* Functions used by new usb-serial code. */ +int keyspan_init (void) +{ + usb_serial_register (&keyspan_usa18x_pre_device); + usb_serial_register (&keyspan_usa19_pre_device); + usb_serial_register (&keyspan_usa19w_pre_device); + usb_serial_register (&keyspan_usa28_pre_device); + usb_serial_register (&keyspan_usa28x_pre_device); + usb_serial_register (&keyspan_usa49w_pre_device); + + usb_serial_register (&keyspan_usa18x_device); + usb_serial_register (&keyspan_usa19_device); + usb_serial_register (&keyspan_usa19w_device); + usb_serial_register (&keyspan_usa28_device); + usb_serial_register (&keyspan_usa28x_device); + usb_serial_register (&keyspan_usa49w_device); + return 0; +} + +void keyspan_exit (void) +{ + usb_serial_deregister (&keyspan_usa18x_pre_device); + usb_serial_deregister (&keyspan_usa19_pre_device); + usb_serial_deregister (&keyspan_usa19w_pre_device); + usb_serial_deregister (&keyspan_usa28_pre_device); + usb_serial_deregister (&keyspan_usa28x_pre_device); + usb_serial_deregister (&keyspan_usa49w_pre_device); + + usb_serial_deregister (&keyspan_usa18x_device); + usb_serial_deregister (&keyspan_usa19_device); + usb_serial_deregister (&keyspan_usa19w_device); + usb_serial_deregister (&keyspan_usa28_device); + usb_serial_deregister (&keyspan_usa28x_device); + usb_serial_deregister (&keyspan_usa49w_device); +} + +module_init(keyspan_init); +module_exit(keyspan_exit); - /* Functions - mostly stubs for now */ static void keyspan_rx_throttle (struct usb_serial_port *port) { dbg("keyspan_rx_throttle port %d", port->number); @@ -123,194 +209,597 @@ static void keyspan_break_ctl (struct usb_serial_port *port, int break_state) static void keyspan_set_termios (struct usb_serial_port *port, struct termios *old_termios) { - dbg("keyspan_set_termios"); + int baud_rate; + struct keyspan_port_private *p_priv; + const keyspan_device_details *d_details; + unsigned int cflag; + + /* dbg(__FUNCTION__ "."); */ + + p_priv = (struct keyspan_port_private *)(port->private); + d_details = p_priv->device_details; + cflag = port->tty->termios->c_cflag; + + /* Baud rate calculation takes baud rate as an integer + so other rates can be generated if desired. */ + baud_rate = tty_get_baud_rate(port->tty); + /* If no match or invalid, don't change */ + if (baud_rate >= 0 + && d_details->calculate_baud_rate(baud_rate, d_details->baudclk, + NULL, NULL, NULL) == KEYSPAN_BAUD_RATE_OK) { + /* FIXME - more to do here to ensure rate changes cleanly */ + p_priv->baud = baud_rate; + } + + /* set CTS/RTS handshake etc. */ + p_priv->cflag = cflag; + p_priv->flow_control = (cflag & CRTSCTS)? flow_cts: flow_none; + + keyspan_send_setup(port); } static int keyspan_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) { - unsigned int value; + unsigned int value, set; + struct keyspan_port_private *p_priv; - dbg("keyspan_ioctl_info"); + p_priv = (struct keyspan_port_private *)(port->private); switch (cmd) { case TIOCMGET: - value = TIOCM_DTR | TIOCM_RNG; - if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) { + value = ((p_priv->rts_state) ? TIOCM_RTS : 0) | + ((p_priv->dtr_state) ? TIOCM_DTR : 0) | + ((p_priv->cts_state) ? TIOCM_CTS : 0) | + ((p_priv->dsr_state) ? TIOCM_DSR : 0) | + ((p_priv->dcd_state) ? TIOCM_CAR : 0) | + ((p_priv->ri_state) ? TIOCM_RNG : 0); + + if (put_user(value, (unsigned int *) arg)) return -EFAULT; - } - else { - return 0; - } - - default: - return -ENOIOCTLCMD; + return 0; + + case TIOCMSET: + if (get_user(value, (unsigned int *) arg)) + return -EFAULT; + p_priv->rts_state = ((value & TIOCM_RTS) ? 1 : 0); + p_priv->dtr_state = ((value & TIOCM_DTR) ? 1 : 0); + keyspan_send_setup(port); + return 0; + + case TIOCMBIS: + case TIOCMBIC: + if (get_user(value, (unsigned int *) arg)) + return -EFAULT; + set = (cmd == TIOCMBIS); + if (value & TIOCM_RTS) + p_priv->rts_state = set; + if (value & TIOCM_DTR) + p_priv->dtr_state = set; + keyspan_send_setup(port); + return 0; } return -ENOIOCTLCMD; } + /* Write function is generic for the three protocols used + with only a minor change for usa49 required */ static int keyspan_write(struct usb_serial_port *port, int from_user, - const unsigned char *buf, int count) + const unsigned char *buf, int count) { - struct usb_serial *serial = port->serial; - struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; - int current_urb; - int i; + const keyspan_device_details *d_details; + int flip; + int left, todo; + urb_t *this_urb; + int err; - s_priv = (struct keyspan_serial_private *)(serial->private); p_priv = (struct keyspan_port_private *)(port->private); - - if (p_priv->out_flip == 0) { - current_urb = 0; - p_priv->out_flip = 1; - } - else { - current_urb = 1; - p_priv->out_flip = 0; - } + d_details = p_priv->device_details; - dbg("keyspan_write called for port %d (%d) chars {", port->number, count); - for (i = 0; i < count ; i++) { - dbg("%02x ", buf[i]); - } - dbg("}\n"); +#if 0 + dbg(__FUNCTION__ " for port %d (%d chars [%x]), flip=%d", + port->number, count, buf[0], p_priv->out_flip); +#endif - if (count == 0) { - dbg("write request of 0 bytes"); - return (0); - } + for (left = count; left > 0; left -= todo) { + todo = left; + if (todo > 63) + todo = 63; - /* only send data if we have a bulk out endpoint */ - if (s_priv->out_urbs[current_urb]) { - while (s_priv->out_urbs[current_urb]->status == -EINPROGRESS) { - dbg (__FUNCTION__ " INPROGRES\n"); - interruptible_sleep_on(&out_wait); - if (signal_pending(current)) { - dbg (__FUNCTION__ " signal\n"); - return (-ERESTARTSYS); - } + flip = p_priv->out_flip; + + /* Check we have a valid urb/endpoint before we use it... */ + if ((this_urb = p_priv->out_urbs[flip]) == 0) { + /* no bulk out, so return 0 bytes written */ + dbg(__FUNCTION__ " no output urb :("); + return count; + } + + if (this_urb->status == -EINPROGRESS) { + if (this_urb->transfer_flags & USB_ASYNC_UNLINK) + break; + if (jiffies - p_priv->tx_start_time[flip] < 10 * HZ) + break; + this_urb->transfer_flags |= USB_ASYNC_UNLINK; + usb_unlink_urb(this_urb); + break; } - /*if (s_priv->out_urbs[current_urb]->status == -EINPROGRESS) { - dbg ("already writing"); - return (-EAGAIN); - }*/ - /* First byte in buffer is "last flag" - unused so - for now so set to zero */ - memset(s_priv->out_urbs[current_urb]->transfer_buffer, 0, 1); + + /* First byte in buffer is "last flag" - unused so + for now so set to zero */ + ((char *)this_urb->transfer_buffer)[0] = 0; if (from_user) { - copy_from_user(s_priv->out_urbs[current_urb]->transfer_buffer + 1, buf, count); + copy_from_user(this_urb->transfer_buffer + 1, buf, todo); + } else { + memcpy (this_urb->transfer_buffer + 1, buf, todo); } - else { - memcpy (s_priv->out_urbs[current_urb]->transfer_buffer + 1, buf, count); - } + buf += todo; /* send the data out the bulk port */ - s_priv->out_urbs[current_urb]->transfer_buffer_length = count + 1; - s_priv->out_urbs[current_urb]->dev = serial->dev; + this_urb->transfer_buffer_length = todo + 1; - if (usb_submit_urb(s_priv->out_urbs[current_urb])) { - dbg("usb_submit_urb(write bulk) failed"); + this_urb->transfer_flags &= ~USB_ASYNC_UNLINK; + this_urb->dev = port->serial->dev; + if ((err = usb_submit_urb(this_urb)) != 0) { + dbg("usb_submit_urb(write bulk) failed (%d)", err); } + p_priv->tx_start_time[flip] = jiffies; - return (count); + /* Flip for next time if usa26 or usa28 interface + (not used on usa49) */ + p_priv->out_flip = (flip + 1) & d_details->outdat_endp_flip; } - - /* no bulk out, so return 0 bytes written */ - return (0); -} + return count - left; +} -static void keyspan_write_bulk_callback (struct urb *urb) +static void usa26_indat_callback(struct urb *urb) { - int endpoint; - + int i, err; + int endpoint; + struct usb_serial_port *port; + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + + /* dbg (__FUNCTION__); */ + endpoint = usb_pipeendpoint(urb->pipe); - dbg("keyspan_write_bulk_callback for endpoint %d\n", endpoint); + if (urb->status) { + dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.", + urb->status, endpoint); + return; + } + + port = (struct usb_serial_port *) urb->context; + tty = port->tty; + if (urb->actual_length) { + if (data[0] == 0) { + /* no error on any byte */ + for (i = 1; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + } else { + /* some bytes had errors, every byte has status */ + for (i = 0; i + 1 < urb->actual_length; i += 2) { + int stat = data[i], flag = 0; + if (stat & RXERROR_OVERRUN) + flag |= TTY_OVERRUN; + if (stat & RXERROR_FRAMING) + flag |= TTY_FRAME; + if (stat & RXERROR_PARITY) + flag |= TTY_PARITY; + /* XXX should handle break (0x10) */ + tty_insert_flip_char(tty, data[i+1], flag); + } + } + tty_flip_buffer_push(tty); + } + + /* Resubmit urb so we continue receiving */ + urb->dev = port->serial->dev; + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err); + } + return; +} - /* Only do wakeup if this callback is from one of the data - endpoints. */ - if (endpoint == 2 || endpoint == 3) { - wake_up_interruptible(&out_wait); + /* Outdat handling is common for usa26, usa28 and usa49 messages */ +static void usa2x_outdat_callback(struct urb *urb) +{ + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + + port = (struct usb_serial_port *) urb->context; + p_priv = (struct keyspan_port_private *)(port->private); + /* dbg (__FUNCTION__ " urb %d", urb == p_priv->out_urbs[1]); */ + + if (port->active) { + queue_task(&port->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); } +} +static void usa26_inack_callback(struct urb *urb) +{ + dbg (__FUNCTION__); + } +static void usa26_outcont_callback(struct urb *urb) +{ + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; -static void keyspan_read_bulk_callback (struct urb *urb) + port = (struct usb_serial_port *) urb->context; + p_priv = (struct keyspan_port_private *)(port->private); + + if (p_priv->resend_cont) { + /* dbg (__FUNCTION__ " sending setup"); */ + keyspan_usa26_send_setup(port->serial, port); + } +} + +static void usa26_instat_callback(struct urb *urb) { - struct usb_serial *serial = (struct usb_serial *)urb->context; - struct usb_serial_port *port; - int i; - int endpoint; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; + unsigned char *data = urb->transfer_buffer; + keyspan_usa26_portStatusMessage *msg; + struct usb_serial *serial; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + int old_dcd_state, err; - if (serial_paranoia_check (serial, __FUNCTION__)) + serial = (struct usb_serial *) urb->context; + + if (urb->status) { + dbg(__FUNCTION__ " nonzero status: %x", urb->status); return; - port = &serial->port[0]; - if (port_paranoia_check (port, __FUNCTION__)) + } + if (urb->actual_length != 9) { + dbg(__FUNCTION__ " %d byte report??", urb->actual_length); + goto exit; + } + + msg = (keyspan_usa26_portStatusMessage *)data; + +#if 0 + dbg(__FUNCTION__ " port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d", + msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr, msg->ri, msg->_txOff, + msg->_txXoff, msg->rxEnabled, msg->controlResponse); +#endif + + /* Now do something useful with the data */ + + + /* Check port number from message and retrieve private data */ + if (msg->port >= serial->num_ports) { + dbg ("Unexpected port number %d", msg->port); + goto exit; + } + port = &serial->port[msg->port]; + p_priv = (struct keyspan_port_private *)(port->private); + + /* Update handshaking pin state information */ + old_dcd_state = p_priv->dcd_state; + p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); + p_priv->dsr_state = ((msg->dsr) ? 1 : 0); + p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); + p_priv->ri_state = ((msg->ri) ? 1 : 0); + + if (port->tty && !C_CLOCAL(port->tty) + && old_dcd_state != p_priv->dcd_state) { + if (old_dcd_state) + tty_hangup(port->tty); + /* else */ + /* wake_up_interruptible(&p_priv->open_wait); */ + } + +exit: + /* Resubmit urb so we continue receiving */ + urb->dev = serial->dev; + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err); + } +} + +static void usa26_glocont_callback(struct urb *urb) +{ + dbg (__FUNCTION__); + +} + + +static void usa28_indat_callback(struct urb *urb) +{ + int i, err; + struct usb_serial_port *port; + struct tty_struct *tty; + unsigned char *data; + struct keyspan_port_private *p_priv; + + /* dbg (__FUNCTION__); */ + + port = (struct usb_serial_port *) urb->context; + p_priv = (struct keyspan_port_private *)(port->private); + data = urb->transfer_buffer; + + if (urb != p_priv->in_urbs[p_priv->in_flip]) return; + do { + if (urb->status) { + dbg(__FUNCTION__ "nonzero status: %x on endpoint +%d.", + urb->status, usb_pipeendpoint(urb->pipe)); + return; + } + + port = (struct usb_serial_port *) urb->context; + p_priv = (struct keyspan_port_private *)(port->private); + data = urb->transfer_buffer; + + tty = port->tty; + if (urb->actual_length) { + for (i = 0; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + /* Resubmit urb so we continue receiving */ + urb->dev = port->serial->dev; + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ "resubmit read urb failed. (%d)", +err); + } + p_priv->in_flip ^= 1; + + urb = p_priv->in_urbs[p_priv->in_flip]; + } while (urb->status != -EINPROGRESS); +} + +static void usa28_inack_callback(struct urb *urb) +{ + dbg (__FUNCTION__); +} + +static void usa28_outcont_callback(struct urb *urb) +{ + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + + port = (struct usb_serial_port *) urb->context; + p_priv = (struct keyspan_port_private *)(port->private); + + if (p_priv->resend_cont) { + dbg (__FUNCTION__ " sending setup"); + keyspan_usa28_send_setup(port->serial, port); + } +} + +static void usa28_instat_callback(struct urb *urb) +{ + int err; + unsigned char *data = urb->transfer_buffer; + keyspan_usa28_portStatusMessage *msg; + struct usb_serial *serial; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + int old_dcd_state; + + serial = (struct usb_serial *) urb->context; + if (urb->status) { - dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status); + dbg(__FUNCTION__ " nonzero status: %x", urb->status); return; } - usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); + if (urb->actual_length != sizeof(struct keyspan_usa28_portStatusMessage)) { + dbg(__FUNCTION__ " bad length %d", urb->actual_length); + goto exit; + } - endpoint = usb_pipeendpoint(urb->pipe); + /*dbg(__FUNCTION__ " %x %x %x %x %x %x %x %x %x %x %x %x", + data[0], data[1], data[2], data[3], data[4], data[5], + data[6], data[7], data[8], data[9], data[10], data[11]);*/ + + /* Now do something useful with the data */ + msg = (keyspan_usa28_portStatusMessage *)data; + + + /* Check port number from message and retrieve private data */ + if (msg->port >= serial->num_ports) { + dbg ("Unexpected port number %d", msg->port); + goto exit; + } + port = &serial->port[msg->port]; + p_priv = (struct keyspan_port_private *)(port->private); + + /* Update handshaking pin state information */ + old_dcd_state = p_priv->dcd_state; + p_priv->cts_state = ((msg->cts) ? 1 : 0); + p_priv->dsr_state = ((msg->dsr) ? 1 : 0); + p_priv->dcd_state = ((msg->dcd) ? 1 : 0); + p_priv->ri_state = ((msg->ri) ? 1 : 0); + + if (port->tty && !C_CLOCAL(port->tty) + && old_dcd_state != p_priv->dcd_state) { + if (old_dcd_state) + tty_hangup(port->tty); + /* else */ + /* wake_up_interruptible(&p_priv->open_wait); */ + } + +exit: + /* Resubmit urb so we continue receiving */ + urb->dev = serial->dev; + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err); + } +} + +static void usa28_glocont_callback(struct urb *urb) +{ + dbg (__FUNCTION__); +} + + +static void usa49_glocont_callback(struct urb *urb) +{ + struct usb_serial *serial; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + int i; + + /* dbg (__FUNCTION__); */ + serial = (struct usb_serial *) urb->context; + for (i = 0; i < serial->num_ports; ++i) { + port = &serial->port[i]; + p_priv = (struct keyspan_port_private *)(port->private); + + if (p_priv->resend_cont) { + /* dbg (__FUNCTION__ " sending setup"); */ + keyspan_usa49_send_setup(serial, port); + break; + } + } +} + + /* This is actually called glostat in the Keyspan + doco */ +static void usa49_instat_callback(struct urb *urb) +{ + int err; + unsigned char *data = urb->transfer_buffer; + keyspan_usa49_portStatusMessage *msg; + struct usb_serial *serial; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + int old_dcd_state; + + /* dbg (__FUNCTION__); */ + + serial = (struct usb_serial *) urb->context; if (urb->status) { - dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.\n", - urb->status, endpoint); + dbg(__FUNCTION__ " nonzero status: %x", urb->status); return; } - switch (endpoint) { + if (urb->actual_length != sizeof(struct keyspan_usa49_portStatusMessage)) { + dbg(__FUNCTION__ " bad length %d", urb->actual_length); + goto exit; + } - /* If this is one of the data endpoints, stuff it's - contents into the tty flip_buffer. */ - case 1: - case 2: - tty = port->tty; - if (urb->actual_length) { - for (i = 0; i < urb->actual_length ; ++i) { - tty_insert_flip_char(tty, data[i], 0); - } - tty_flip_buffer_push(tty); - } - break; + /*dbg(__FUNCTION__ " %x %x %x %x %x %x %x %x %x %x %x", + data[0], data[1], data[2], data[3], data[4], data[5], + data[6], data[7], data[8], data[9], data[10]);*/ + + /* Now do something useful with the data */ + msg = (keyspan_usa49_portStatusMessage *)data; - /* INACK endpoint */ - case 3: dbg(__FUNCTION__ " callback for INACK endpoint\n"); - break; + /* Check port number from message and retrieve private data */ + if (msg->portNumber >= serial->num_ports) { + dbg ("Unexpected port number %d", msg->portNumber); + goto exit; + } + port = &serial->port[msg->portNumber]; + p_priv = (struct keyspan_port_private *)(port->private); + + /* Update handshaking pin state information */ + old_dcd_state = p_priv->dcd_state; + p_priv->cts_state = ((msg->cts) ? 1 : 0); + p_priv->dsr_state = ((msg->dsr) ? 1 : 0); + p_priv->dcd_state = ((msg->dcd) ? 1 : 0); + p_priv->ri_state = ((msg->ri) ? 1 : 0); + + if (port->tty && !C_CLOCAL(port->tty) + && old_dcd_state != p_priv->dcd_state) { + if (old_dcd_state) + tty_hangup(port->tty); + /* else */ + /* wake_up_interruptible(&p_priv->open_wait); */ + } - /* INSTAT endpoint */ - case 4: dbg(__FUNCTION__ " callback for INSTAT endpoint\n"); - break; - - default: - dbg(__FUNCTION__ " callback for unknown endpoint!\n"); - break; +exit: + /* Resubmit urb so we continue receiving */ + urb->dev = serial->dev; + + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err); + } +} + +static void usa49_inack_callback(struct urb *urb) +{ + dbg (__FUNCTION__); +} + +static void usa49_indat_callback(struct urb *urb) +{ + int i, err; + int endpoint; + struct usb_serial_port *port; + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + + /* dbg (__FUNCTION__); */ + + endpoint = usb_pipeendpoint(urb->pipe); + + if (urb->status) { + dbg(__FUNCTION__ "nonzero status: %x on endpoint %d.", + urb->status, endpoint); + return; + } + + port = (struct usb_serial_port *) urb->context; + tty = port->tty; + if (urb->actual_length) { + if (data[0] == 0) { + /* no error on any byte */ + for (i = 1; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + } else { + /* some bytes had errors, every byte has status */ + for (i = 0; i + 1 < urb->actual_length; i += 2) { + int stat = data[i], flag = 0; + if (stat & RXERROR_OVERRUN) + flag |= TTY_OVERRUN; + if (stat & RXERROR_FRAMING) + flag |= TTY_FRAME; + if (stat & RXERROR_PARITY) + flag |= TTY_PARITY; + /* XXX should handle break (0x10) */ + tty_insert_flip_char(tty, data[i+1], flag); + } + } + tty_flip_buffer_push(tty); } - /* Resubmit urb so we continue receiving */ - urb->dev = serial->dev; - if (usb_submit_urb(urb)) { - dbg(__FUNCTION__ "resubmit read urb failed.\n"); + /* Resubmit urb so we continue receiving */ + urb->dev = port->serial->dev; + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ "resubmit read urb failed. (%d)", err); } - return; - } +/* not used, usa-49 doesn't have per-port control endpoints */ +static void usa49_outcont_callback(struct urb *urb) +{ + dbg (__FUNCTION__); +} + + + static int keyspan_write_room (struct usb_serial_port *port) { -// dbg("keyspan_write_room called\n"); +// dbg("keyspan_write_room called"); return (32); } @@ -327,63 +816,103 @@ static int keyspan_open (struct usb_serial_port *port, struct file *filp) struct keyspan_port_private *p_priv; struct keyspan_serial_private *s_priv; struct usb_serial *serial = port->serial; - int i; - - s_priv = (struct keyspan_serial_private *)(serial->private); - p_priv = (struct keyspan_port_private *)(port->private); - - dbg("keyspan_open called.\n"); - - if (port->active) { - dbg(__FUNCTION__ "port->active already true!\n"); - return (-EINVAL); - } + const keyspan_device_details *d_details; + int i, already_active, err; + unsigned long flags; + urb_t *urb; + s_priv = (struct keyspan_serial_private *)(serial->private); p_priv = (struct keyspan_port_private *)(port->private); + d_details = s_priv->device_details; - p_priv->out_flip = 0; + /* dbg("keyspan_open called."); */ + MOD_INC_USE_COUNT; + + spin_lock_irqsave (&port->port_lock, flags); + ++port->open_count; + already_active = port->active; port->active = 1; + spin_unlock_irqrestore (&port->port_lock, flags); - /* Start reading from port */ - for (i = 0; i < 4; i++) { - if (s_priv->in_urbs[i]) { - s_priv->in_urbs[i]->dev = serial->dev; - if (usb_submit_urb(s_priv->in_urbs[i])) { - dbg(__FUNCTION__ " submit in urb %d failed", i); - } - } + if (already_active) + return 0; + p_priv = (struct keyspan_port_private *)(port->private); + + /* Set some sane defaults */ + p_priv->baud = 9600; + p_priv->cflag = CREAD | CLOCAL; + p_priv->flow_control = flow_none; + p_priv->rts_state = 1; + p_priv->dtr_state = 1; + + /* Start reading from endpoints */ + for (i = 0; i < 2; i++) { + if ((urb = p_priv->in_urbs[i]) == NULL) + continue; + urb->dev = serial->dev; + if ((err = usb_submit_urb(urb)) != 0) { + dbg(__FUNCTION__ " submit urb %d failed (%d)", i, err); + } } - - keyspan_usa19_send_setup(serial, port); +/* Now done in startup routine + if (atomic_inc_return(&s_priv->active_count) == 1) { + s_priv->instat_urb->dev = serial->dev; + if ((err = usb_submit_urb(s_priv->instat_urb)) != 0) { + dbg(__FUNCTION__ " submit instat urb failed %d", err); + } + } +*/ + + keyspan_send_setup(port); return (0); } +static inline void stop_urb(urb_t *urb) +{ + if (urb && urb->status == -EINPROGRESS) { + urb->transfer_flags &= ~USB_ASYNC_UNLINK; + usb_unlink_urb(urb); + } +} static void keyspan_close(struct usb_serial_port *port, struct file *filp) { int i; struct usb_serial *serial = port->serial; /* FIXME should so sanity check */ struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; + unsigned long flags; + /* dbg("keyspan_close called"); */ s_priv = (struct keyspan_serial_private *)(serial->private); - - /* Stop reading/writing urbs */ - for (i = 0; i < 4; i++) { - if (s_priv->in_urbs[i]) { - usb_unlink_urb(s_priv->in_urbs[i]); - } + p_priv = (struct keyspan_port_private *)(port->private); - } - for (i = 0; i < 3; i++) { - if (s_priv->out_urbs[i]) { - usb_unlink_urb(s_priv->out_urbs[i]); - } + spin_lock_irqsave (&port->port_lock, flags); + if (--port->open_count <= 0) { + if (port->active) { + /* Stop reading/writing urbs */ + stop_urb(p_priv->inack_urb); + stop_urb(p_priv->outcont_urb); + for (i = 0; i < 2; i++) { + stop_urb(p_priv->in_urbs[i]); + stop_urb(p_priv->out_urbs[i]); + } + /* Now done in shutdown + if (atomic_dec_return(&s_priv->active_count) <= 0) { + stop_urb(s_priv->instat_urb); + stop_urb(s_priv->glocont_urb); + } */ + } + port->active = 0; + port->open_count = 0; + port->tty = 0; } - port->active = 0; - dbg("keyspan_close called\n"); + spin_unlock_irqrestore (&port->port_lock, flags); + + MOD_DEC_USE_COUNT; } @@ -394,47 +923,59 @@ static int keyspan_fake_startup (struct usb_serial *serial) const struct ezusb_hex_record *record; char *fw_name; - dbg("Keyspan startup version %04x product %04x\n", serial->dev->descriptor.bcdDevice, - serial->dev->descriptor.idProduct); + dbg("Keyspan startup version %04x product %04x", + serial->dev->descriptor.bcdDevice, + serial->dev->descriptor.idProduct); if ((serial->dev->descriptor.bcdDevice & 0x8000) != 0x8000) { - dbg("Firmware already loaded. Quitting.\n"); + dbg("Firmware already loaded. Quitting."); return(1); } /* Select firmware image on the basis of idProduct */ switch (serial->dev->descriptor.idProduct) { - case 0x0101: record = &keyspan_usa28_firmware[0]; - fw_name = "USA28"; - break; + case 0x0101: + record = &keyspan_usa28_firmware[0]; + fw_name = "USA28"; + break; + + case 0x0102: + record = &keyspan_usa28x_firmware[0]; + fw_name = "USA28X"; + break; + + case 0x0103: + record = &keyspan_usa19_firmware[0]; + fw_name = "USA19"; + break; - case 0x0102: record = &keyspan_usa28x_firmware[0]; - fw_name = "USA28X"; - break; - - case 0x0103: record = &keyspan_usa19_firmware[0]; - fw_name = "USA19"; - break; - - case 0x0105: record = &keyspan_usa18x_firmware[0]; - fw_name = "USA18X"; - break; + case 0x0105: + record = &keyspan_usa18x_firmware[0]; + fw_name = "USA18X"; + break; - case 0x0106: record = &keyspan_usa19w_firmware[0]; - fw_name = "USA19W"; - break; + case 0x0106: + record = &keyspan_usa19w_firmware[0]; + fw_name = "USA19W"; + break; - default: record = NULL; - fw_name = "Unknown"; - break; + case 0x0109: + record = &keyspan_usa49w_firmware[0]; + fw_name = "USA49W"; + break; + + default: + record = NULL; + fw_name = "Unknown"; + break; } if (record == NULL) { - err("Required keyspan firmware image (%s) unavailable.\n", fw_name); + err("Required keyspan firmware image (%s) unavailable.", fw_name); return(1); } - dbg("Uploading Keyspan %s firmware.\n", fw_name); + dbg("Uploading Keyspan %s firmware.", fw_name); /* download the firmware image */ response = ezusb_set_reset(serial, 1); @@ -456,72 +997,165 @@ static int keyspan_fake_startup (struct usb_serial *serial) moment and the new device will bind to the real driver */ response = ezusb_set_reset(serial, 0); - /* we don't want this device to have a driver assigned to it. */ + /* we don't want this device to have a driver assigned to it. */ return (1); } - /* USA-19 uses three output endpoints and four input - endpoints. First two output endpoints are for - data (used in an alternating fashion), the third is - output control. First two input endpoints are for - data (again alternating), the third is the ACK - endpoint, the fourth is input status. */ -static void keyspan_usa19_setup_urbs(struct usb_serial *serial) +/* Helper functions used by keyspan_setup_urbs */ +static urb_t *keyspan_setup_urb(struct usb_serial *serial, int endpoint, + int dir, void *ctx, char *buf, int len, + void (*callback)(urb_t *)) { - struct keyspan_serial_private *s_priv; - int i; + urb_t *urb; - s_priv = (struct keyspan_serial_private *)(serial->private); + if (endpoint == -1) + return NULL; /* endpoint not needed */ + + /* dbg (__FUNCTION__ " alloc for endpoint %d.", endpoint); */ + urb = usb_alloc_urb(0); /* No ISO */ + if (urb == NULL) { + dbg (__FUNCTION__ " alloc for endpoint %d failed.", endpoint); + return NULL; + } - /* Output urbs first */ - dbg(__FUNCTION__ "Allocating output urbs.\n"); - for (i = 0; i < 3; i++) { + /* Fill URB using supplied data. */ + FILL_BULK_URB(urb, serial->dev, + usb_sndbulkpipe(serial->dev, endpoint) | dir, + buf, len, callback, ctx); - s_priv->out_urbs[i] = usb_alloc_urb (0); /* No ISO */ - if (!s_priv->out_urbs[i]) { - dbg (__FUNCTION__ "Alloc for %d out urb failed.\n", i); - return; - } + return urb; +} - FILL_BULK_URB(s_priv->out_urbs[i], serial->dev, - usb_sndbulkpipe(serial->dev, i + 1), - &s_priv->out_buffer[i], sizeof(s_priv->out_buffer[i]), - keyspan_write_bulk_callback, - serial); +struct callbacks { + void (*instat_callback)(urb_t *); + void (*glocont_callback)(urb_t *); + void (*indat_callback)(urb_t *); + void (*outdat_callback)(urb_t *); + void (*inack_callback)(urb_t *); + void (*outcont_callback)(urb_t *); +} keyspan_callbacks[] = { + { + /* msg_usa26 callbacks */ + instat_callback: usa26_instat_callback, + glocont_callback: usa26_glocont_callback, + indat_callback: usa26_indat_callback, + outdat_callback: usa2x_outdat_callback, + inack_callback: usa26_inack_callback, + outcont_callback: usa26_outcont_callback, + }, { + /* msg_usa28 callbacks */ + instat_callback: usa28_instat_callback, + glocont_callback: usa28_glocont_callback, + indat_callback: usa28_indat_callback, + outdat_callback: usa2x_outdat_callback, + inack_callback: usa28_inack_callback, + outcont_callback: usa28_outcont_callback, + }, { + /* msg_usa49 callbacks */ + instat_callback: usa49_instat_callback, + glocont_callback: usa49_glocont_callback, + indat_callback: usa49_indat_callback, + outdat_callback: usa2x_outdat_callback, + inack_callback: usa49_inack_callback, + outcont_callback: usa49_outcont_callback, } +}; + + /* Generic setup urbs function that uses + data in device_details */ +static void keyspan_setup_urbs(struct usb_serial *serial) +{ + int i, j; + struct keyspan_serial_private *s_priv; + const keyspan_device_details *d_details; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + struct callbacks *cback; + int endp; - /* Now input urbs */ - dbg(__FUNCTION__ "Allocating input urbs.\n"); - for (i = 0; i < 4; i++) { + /* dbg (__FUNCTION__); */ - s_priv->in_urbs[i] = usb_alloc_urb (0); /* No ISO */ - if (!s_priv->in_urbs[i]) { - dbg (__FUNCTION__ "Alloc for %d in urb failed.\n", i); - return; + s_priv = (struct keyspan_serial_private *)(serial->private); + d_details = s_priv->device_details; + + /* Setup values for the various callback routines */ + cback = &keyspan_callbacks[d_details->msg_format]; + + /* Allocate and set up urbs for each one that is in use, + starting with instat endpoints */ + s_priv->instat_urb = keyspan_setup_urb + (serial, d_details->instat_endpoint, USB_DIR_IN, + serial, s_priv->instat_buf, INSTAT_BUFLEN, + cback->instat_callback); + + s_priv->glocont_urb = keyspan_setup_urb + (serial, d_details->glocont_endpoint, USB_DIR_OUT, + serial, s_priv->glocont_buf, GLOCONT_BUFLEN, + cback->glocont_callback); + + /* Setup endpoints for each port specific thing */ + for (i = 0; i < d_details->num_ports; i ++) { + port = &serial->port[i]; + p_priv = (struct keyspan_port_private *)(port->private); + + /* Do indat endpoints first, once for each flip */ + endp = d_details->indat_endpoints[i]; + for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) { + p_priv->in_urbs[j] = keyspan_setup_urb + (serial, endp, USB_DIR_IN, port, + p_priv->in_buffer[j], 64, + cback->indat_callback); } + for (; j < 2; ++j) + p_priv->in_urbs[j] = NULL; + + /* outdat endpoints also have flip */ + endp = d_details->outdat_endpoints[i]; + for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) { + p_priv->out_urbs[j] = keyspan_setup_urb + (serial, endp, USB_DIR_OUT, port, + p_priv->out_buffer[j], 64, + cback->outdat_callback); + } + for (; j < 2; ++j) + p_priv->out_urbs[j] = NULL; + + /* inack endpoint */ + p_priv->inack_urb = keyspan_setup_urb + (serial, d_details->inack_endpoints[i], USB_DIR_IN, + port, p_priv->inack_buffer, 1, cback->inack_callback); + + /* outcont endpoint */ + p_priv->outcont_urb = keyspan_setup_urb + (serial, d_details->outcont_endpoints[i], USB_DIR_OUT, + port, p_priv->outcont_buffer, 64, + cback->outcont_callback); + } - FILL_BULK_URB(s_priv->in_urbs[i], serial->dev, - usb_rcvbulkpipe(serial->dev, i + 0x81), - &s_priv->in_buffer[i], sizeof(s_priv->in_buffer[i]), - keyspan_read_bulk_callback, - serial); - } - } -static int keyspan_usa19_calc_baud(u32 baud_rate, u8 *rate_hi, u8 *rate_low) +/* usa19 function doesn't require prescaler */ +static int keyspan_usa19_calc_baud(u32 baud_rate, u32 baudclk, + u8 *rate_hi, u8 *rate_low, u8 *prescaler) { - u32 b16, /* baud rate times 16 (actual rate used internally) */ + u32 b16, /* baud rate times 16 (actual rate used internally) */ div, /* divisor */ cnt; /* inverse of divisor (programmed into 8051) */ + /* prevent divide by zero... */ if( (b16 = (baud_rate * 16L)) == 0) { return (KEYSPAN_INVALID_BAUD_RATE); } + /* Any "standard" rate over 57k6 is marginal on the USA-19 + as we run out of divisor resolution. */ + if (baud_rate > 57600) { + return (KEYSPAN_INVALID_BAUD_RATE); + } + /* calculate the divisor and the counter (its inverse) */ - if( (div = (USA19_BAUDCLK / b16)) == 0) { + if( (div = (baudclk / b16)) == 0) { return (KEYSPAN_INVALID_BAUD_RATE); } else { @@ -532,217 +1166,520 @@ static int keyspan_usa19_calc_baud(u32 baud_rate, u8 *rate_hi, u8 *rate_low) return (KEYSPAN_INVALID_BAUD_RATE); } - /* return the counter values */ - *rate_low = (u8) (cnt & 0xff); - *rate_hi = (u8) ((cnt >> 8) & 0xff); + /* return the counter values if non-null */ + if (rate_low) { + *rate_low = (u8) (cnt & 0xff); + } + if (rate_hi) { + *rate_hi = (u8) ((cnt >> 8) & 0xff); + } + if (rate_low && rate_hi) { + dbg (__FUNCTION__ " %d %02x %02x.", baud_rate, *rate_hi, *rate_low); + } - dbg(__FUNCTION__ " Baud rate of %d is %02x %02x.\n", baud_rate, *rate_hi, *rate_low); + return (KEYSPAN_BAUD_RATE_OK); +} + +static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, + u8 *rate_hi, u8 *rate_low, u8 *prescaler) +{ + u32 b16, /* baud rate times 16 (actual rate used internally) */ + clk, /* clock with 13/8 prescaler */ + div, /* divisor using 13/8 prescaler */ + res, /* resulting baud rate using 13/8 prescaler */ + diff, /* error using 13/8 prescaler */ + smallest_diff; + u8 best_prescaler; + int i; + + /* dbg (__FUNCTION__ " %d.", baud_rate); */ + + /* prevent divide by zero */ + if( (b16 = baud_rate * 16L) == 0) { + return (KEYSPAN_INVALID_BAUD_RATE); + } + + /* Calculate prescaler by trying them all and looking + for best fit */ + + /* start with largest possible difference */ + smallest_diff = 0xffffffff; + + /* 0 is an invalid prescaler, used as a flag */ + best_prescaler = 0; + + for(i = 8; i <= 0xff; ++i) + { + clk = (baudclk * 8) / (u32) i; + + if( (div = clk / b16) == 0) { + continue; + } + + res = clk / div; + diff= (res > b16) ? (res-b16) : (b16-res); + + if(diff < smallest_diff) + { + best_prescaler = i; + smallest_diff = diff; + } + } + + if(best_prescaler == 0) { + return (KEYSPAN_INVALID_BAUD_RATE); + } + + clk = (baudclk * 8) / (u32) best_prescaler; + div = clk / b16; + /* return the divisor and prescaler if non-null */ + if (rate_low) { + *rate_low = (u8) (div & 0xff); + } + if (rate_hi) { + *rate_hi = (u8) ((div >> 8) & 0xff); + } + if (prescaler) { + *prescaler = best_prescaler; + /* dbg(__FUNCTION__ " %d %d", *prescaler, div); */ + } return (KEYSPAN_BAUD_RATE_OK); } -static int keyspan_usa19_send_setup(struct usb_serial *serial, struct usb_serial_port *port) +static int keyspan_usa26_send_setup(struct usb_serial *serial, + struct usb_serial_port *port) { - struct portControlMessage msg; - struct keyspan_serial_private *s_priv; - struct keyspan_port_private *p_priv; + struct keyspan_usa26_portControlMessage msg; + struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; + const keyspan_device_details *d_details; + int outcont_urb; + urb_t *this_urb; + int err; + + /* dbg (__FUNCTION__); */ s_priv = (struct keyspan_serial_private *)(serial->private); p_priv = (struct keyspan_port_private *)(port->private); + d_details = s_priv->device_details; + + outcont_urb = d_details->outcont_endpoints[port->number]; + this_urb = p_priv->outcont_urb; + + /* Make sure we have an urb then send the message */ + if (this_urb == NULL) { + dbg(__FUNCTION__ " oops no urb."); + return -1; + } + + p_priv->resend_cont = 1; + if (this_urb->status == -EINPROGRESS) { + /* dbg (__FUNCTION__ " already writing"); */ + return(-1); + } + + memset(&msg, 0, sizeof (struct keyspan_usa26_portControlMessage)); + + /* Only set baud rate if it's changed */ + if (p_priv->old_baud != p_priv->baud) { + p_priv->old_baud = p_priv->baud; + msg.setClocking = 0xff; + if (d_details->calculate_baud_rate + (p_priv->baud, d_details->baudclk, &msg.baudHi, + &msg.baudLo, &msg.prescaler) == KEYSPAN_INVALID_BAUD_RATE ) { + dbg(__FUNCTION__ "Invalid baud rate %d requested, using 9600.", + p_priv->baud); + msg.baudLo = 0; + msg.baudHi = 125; /* Values for 9600 baud */ + msg.prescaler = 10; + } + msg.setPrescaler = 0xff; + } + + msg.lcr = USA_DATABITS_8 | STOPBITS_5678_1; + if (p_priv->cflag & PARENB) { + /* note USA_PARITY_NONE == 0 */ + msg.lcr |= (p_priv->cflag & PARODD)? + USA_PARITY_ODD: USA_PARITY_EVEN; + } + msg.setLcr = 0xff; + + msg.ctsFlowControl = (p_priv->flow_control == flow_cts); + msg.xonFlowControl = 0; + msg.setFlowControl = 0xff; + + msg.forwardingLength = 1; + msg.xonChar = 17; + msg.xoffChar = 19; + + msg._txOn = 1; + msg._txOff = 0; + msg.txFlush = 0; + msg.txBreak = 0; + msg.rxOn = 1; + msg.rxOff = 0; + msg.rxFlush = 0; + msg.rxForward = 0; + /*msg.returnStatus = 1; + msg.resetDataToggle = 0xff;*/ - //memset(msg, 0, sizeof (struct portControlMessage)); + /* Do handshaking outputs */ + msg.setTxTriState_setRts = 0xff; + msg.txTriState_rts = p_priv->rts_state; + + msg.setHskoa_setDtr = 0xff; + msg.hskoa_dtr = p_priv->dtr_state; + p_priv->resend_cont = 0; + memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); + + /* send the data out the device on control endpoint */ + this_urb->transfer_buffer_length = sizeof(msg); + + this_urb->dev = serial->dev; + if ((err = usb_submit_urb(this_urb)) != 0) { + dbg(__FUNCTION__ " usb_submit_urb(setup) failed (%d)", err); + } +#if 0 + else { + dbg(__FUNCTION__ " usb_submit_urb(%d) OK %d bytes (end %d)", + outcont_urb, this_urb->transfer_buffer_length, + usb_pipeendpoint(this_urb->pipe)); + } +#endif + + return (0); +} + +static int keyspan_usa28_send_setup(struct usb_serial *serial, + struct usb_serial_port *port) +{ + struct keyspan_usa28_portControlMessage msg; + struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; + const keyspan_device_details *d_details; + urb_t *this_urb; + int err; + + s_priv = (struct keyspan_serial_private *)(serial->private); + p_priv = (struct keyspan_port_private *)(port->private); + d_details = s_priv->device_details; + + /* only do something if we have a bulk out endpoint */ + if ((this_urb = p_priv->outcont_urb) == NULL) { + dbg(__FUNCTION__ " oops no urb."); + return -1; + } + + p_priv->resend_cont = 1; + if (this_urb->status == -EINPROGRESS) { + dbg (__FUNCTION__ " already writing"); + return(-1); + } + + memset(&msg, 0, sizeof (struct keyspan_usa28_portControlMessage)); + msg.setBaudRate = 1; - if (keyspan_usa19_calc_baud(9600, &msg.baudHi, &msg.baudLo) == - KEYSPAN_INVALID_BAUD_RATE ) { - dbg(__FUNCTION__ "Invalid baud rate requested %d.\n", 9600); + if (keyspan_usa19_calc_baud(p_priv->baud, d_details->baudclk, + &msg.baudHi, &msg.baudLo, NULL) == KEYSPAN_INVALID_BAUD_RATE ) { + dbg(__FUNCTION__ "Invalid baud rate requested %d.", 9600); msg.baudLo = 0xff; msg.baudHi = 0xb2; /* Values for 9600 baud */ } - /* If parity is enabled, we must calculate it ourselves. */ - if (p_priv->parity) { - msg.parity = 1; + /* If parity is enabled, we must calculate it ourselves. */ + msg.parity = 0; /* XXX for now */ + + msg.ctsFlowControl = (p_priv->flow_control == flow_cts); + msg.xonFlowControl = 0; + + /* Do handshaking outputs, DTR is inverted relative to RTS */ + msg.rts = p_priv->rts_state; + msg.dtr = p_priv->dtr_state; + + msg.forwardingLength = 1; + msg.forwardMs = 10; + msg.breakThreshold = 45; + msg.xonChar = 17; + msg.xoffChar = 19; + + msg._txOn = 1; + msg._txOff = 0; + msg.txFlush = 0; + msg.txForceXoff = 0; + msg.txBreak = 0; + msg.rxOn = 1; + msg.rxOff = 0; + msg.rxFlush = 0; + msg.rxForward = 0; + /*msg.returnStatus = 1; + msg.resetDataToggle = 0xff;*/ + + p_priv->resend_cont = 0; + memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); + + /* send the data out the device on control endpoint */ + this_urb->transfer_buffer_length = sizeof(msg); + + this_urb->dev = serial->dev; + if ((err = usb_submit_urb(this_urb)) != 0) { + dbg(__FUNCTION__ " usb_submit_urb(setup) failed"); } +#if 0 else { - msg.parity = 0; + dbg(__FUNCTION__ " usb_submit_urb(setup) OK %d bytes", + this_urb->transfer_buffer_length); + } +#endif + + return (0); +} + +static int keyspan_usa49_send_setup(struct usb_serial *serial, + struct usb_serial_port *port) +{ + struct keyspan_usa49_portControlMessage msg; + struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; + const keyspan_device_details *d_details; + int glocont_urb; + urb_t *this_urb; + int err; + + /* dbg (__FUNCTION__); */ + + s_priv = (struct keyspan_serial_private *)(serial->private); + p_priv = (struct keyspan_port_private *)(port->private); + d_details = s_priv->device_details; + + glocont_urb = d_details->glocont_endpoint; + this_urb = s_priv->glocont_urb; + + /* dbg(__FUNCTION__ " port %d\n", port->number); */ + + /* Make sure we have an urb then send the message */ + if (this_urb == NULL) { + dbg(__FUNCTION__ " oops no urb for port %d.", port->number); + return -1; + } + + p_priv->resend_cont = 1; + if (this_urb->status == -EINPROGRESS) { + /* dbg (__FUNCTION__ " already writing"); */ + return(-1); + } + + memset(&msg, 0, sizeof (struct keyspan_usa49_portControlMessage)); + + msg.portNumber = port->number; + + /* Only set baud rate if it's changed */ + if (p_priv->old_baud != p_priv->baud) { + p_priv->old_baud = p_priv->baud; + msg.setClocking = 0xff; + if (d_details->calculate_baud_rate + (p_priv->baud, d_details->baudclk, &msg.baudHi, + &msg.baudLo, &msg.prescaler) == KEYSPAN_INVALID_BAUD_RATE ) { + dbg(__FUNCTION__ "Invalid baud rate %d requested, using 9600.", + p_priv->baud); + msg.baudLo = 0; + msg.baudHi = 125; /* Values for 9600 baud */ + msg.prescaler = 10; + } + //msg.setPrescaler = 0xff; + } + + msg.lcr = USA_DATABITS_8 | STOPBITS_5678_1; + if (p_priv->cflag & PARENB) { + /* note USA_PARITY_NONE == 0 */ + msg.lcr |= (p_priv->cflag & PARODD)? + USA_PARITY_ODD: USA_PARITY_EVEN; } + msg.setLcr = 0xff; - msg.ctsFlowControl = 0; + msg.ctsFlowControl = (p_priv->flow_control == flow_cts); msg.xonFlowControl = 0; - msg.rts = 1; - msg.dtr = 0; + msg.setFlowControl = 0xff; msg.forwardingLength = 1; - msg.forwardMs = 10; - msg.breakThreshold = 45; msg.xonChar = 17; msg.xoffChar = 19; msg._txOn = 1; msg._txOff = 0; msg.txFlush = 0; - msg.txForceXoff = 0; msg.txBreak = 0; msg.rxOn = 1; msg.rxOff = 0; msg.rxFlush = 0; msg.rxForward = 0; - msg.returnStatus = 1; - msg.resetDataToggle = 1; + msg.enablePort = 0xff; + msg.disablePort = 0; + /* Do handshaking outputs */ + msg.setRts = 0xff; + msg.rts = p_priv->rts_state; + + msg.setDtr = 0xff; + msg.dtr = p_priv->dtr_state; - /* only do something if we have a bulk out endpoint */ - if (s_priv->out_urbs[2]) { - if (s_priv->out_urbs[2]->status == -EINPROGRESS) { - dbg (__FUNCTION__ " already writing"); - return(-1); - } - memcpy (s_priv->out_urbs[2]->transfer_buffer, &msg, sizeof(msg)); + p_priv->resend_cont = 0; + memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); - /* send the data out the device on control endpoint */ - s_priv->out_urbs[2]->transfer_buffer_length = sizeof(msg); - s_priv->out_urbs[2]->dev = serial->dev; + /* send the data out the device on control endpoint */ + this_urb->transfer_buffer_length = sizeof(msg); - if (usb_submit_urb(s_priv->out_urbs[2])) { - dbg(__FUNCTION__ " usb_submit_urb(setup) failed\n"); - } - else { - dbg(__FUNCTION__ " usb_submit_urb(setup) OK %d bytes\n", s_priv->out_urbs[2]->transfer_buffer_length); - } - + this_urb->dev = serial->dev; + if ((err = usb_submit_urb(this_urb)) != 0) { + dbg(__FUNCTION__ " usb_submit_urb(setup) failed (%d)", err); } +#if 0 + else { + dbg(__FUNCTION__ " usb_submit_urb(%d) OK %d bytes (end %d)", + outcont_urb, this_urb->transfer_buffer_length, + usb_pipeendpoint(this_urb->pipe)); + } +#endif + return (0); } +static void keyspan_send_setup(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct keyspan_serial_private *s_priv; + const keyspan_device_details *d_details; + + s_priv = (struct keyspan_serial_private *)(serial->private); + d_details = s_priv->device_details; + + switch (d_details->msg_format) { + case msg_usa26: + keyspan_usa26_send_setup(serial, port); + break; + case msg_usa28: + keyspan_usa28_send_setup(serial, port); + break; + case msg_usa49: + keyspan_usa49_send_setup(serial, port); + break; + } +} - /* Gets called by the "real" driver (ie once firmware is loaded - and renumeration has taken place. */ +/* Gets called by the "real" driver (ie once firmware is loaded + and renumeration has taken place. */ static int keyspan_startup (struct usb_serial *serial) { - int i; - struct usb_serial_port *port; + int i, err; + struct usb_serial_port *port; + struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; + const keyspan_device_details *d_details; - dbg("keyspan_startup called.\n"); + /* dbg("keyspan_startup called."); */ + + for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i) + if (d_details->product_id == serial->dev->descriptor.idProduct) + break; + if (d_details == NULL) { + printk(KERN_ERR __FUNCTION__ ": unknown product id %x\n", + serial->dev->descriptor.idProduct); + return 1; + } - /* Setup private data for serial driver */ - serial->private = kmalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL); + /* Setup private data for serial driver */ + serial->private = kmalloc(sizeof(struct keyspan_serial_private), + GFP_KERNEL); if (!serial->private) { - dbg(__FUNCTION__ "kmalloc for keyspan_serial_private failed!.\n"); + dbg(__FUNCTION__ "kmalloc for keyspan_serial_private failed."); return (1); } memset(serial->private, 0, sizeof(struct keyspan_serial_private)); + + s_priv = (struct keyspan_serial_private *)(serial->private); + s_priv->device_details = d_details; - init_waitqueue_head(&out_wait); - - /* Now setup per port private data */ + /* Now setup per port private data */ for (i = 0; i < serial->num_ports; i++) { port = &serial->port[i]; - port->private = kmalloc(sizeof(struct keyspan_port_private), GFP_KERNEL); + port->private = kmalloc(sizeof(struct keyspan_port_private), + GFP_KERNEL); if (!port->private) { - dbg(__FUNCTION__ "kmalloc for keyspan_port_private (%d) failed!.\n", i); + dbg(__FUNCTION__ "kmalloc for keyspan_port_private (%d) failed!.", i); return (1); } memset(port->private, 0, sizeof(struct keyspan_port_private)); + p_priv = (struct keyspan_port_private *)(port->private); + p_priv->device_details = d_details; } - - - switch (serial->dev->descriptor.idProduct) { - case 0x0107: keyspan_usa19_setup_urbs(serial); - //keyspan_send_usa19_setup(serial); - break; + keyspan_setup_urbs(serial); - default: break; + s_priv->instat_urb->dev = serial->dev; + if ((err = usb_submit_urb(s_priv->instat_urb)) != 0) { + dbg(__FUNCTION__ " submit instat urb failed %d", err); } - return (0); } static void keyspan_shutdown (struct usb_serial *serial) { - int i; + int i, j; struct usb_serial_port *port; struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; - dbg("keyspan_shutdown called freeing "); + /* dbg("keyspan_shutdown called"); */ s_priv = (struct keyspan_serial_private *)(serial->private); - /* Stop reading/writing urbs */ - for (i = 0; i < 4; i++) { - if (s_priv->in_urbs[i]) { - usb_unlink_urb(s_priv->in_urbs[i]); + /* Stop reading/writing urbs */ + stop_urb(s_priv->instat_urb); + stop_urb(s_priv->glocont_urb); + for (i = 0; i < serial->num_ports; ++i) { + port = &serial->port[i]; + p_priv = (struct keyspan_port_private *)(port->private); + stop_urb(p_priv->inack_urb); + stop_urb(p_priv->outcont_urb); + for (j = 0; j < 2; j++) { + stop_urb(p_priv->in_urbs[j]); + stop_urb(p_priv->out_urbs[j]); } - } - for (i = 0; i < 3; i++) { - if (s_priv->out_urbs[i]) { - usb_unlink_urb(s_priv->out_urbs[i]); - } - } - /* Now free them */ - for (i = 0; i < 7; i ++) { - if (s_priv->in_urbs[i] != NULL) { - dbg("in%d ", i); - usb_free_urb(s_priv->in_urbs[i]); - } - - if (s_priv->out_urbs[i] != NULL) { - dbg("out%d ", i); - usb_free_urb(s_priv->out_urbs[i]); + /* Now free them */ + if (s_priv->instat_urb) + usb_free_urb(s_priv->instat_urb); + if (s_priv->glocont_urb) + usb_free_urb(s_priv->glocont_urb); + for (i = 0; i < serial->num_ports; ++i) { + port = &serial->port[i]; + p_priv = (struct keyspan_port_private *)(port->private); + if (p_priv->inack_urb) + usb_free_urb(p_priv->inack_urb); + if (p_priv->outcont_urb) + usb_free_urb(p_priv->outcont_urb); + for (j = 0; j < 2; j++) { + if (p_priv->in_urbs[j]) + usb_free_urb(p_priv->in_urbs[j]); + if (p_priv->out_urbs[j]) + usb_free_urb(p_priv->out_urbs[j]); } } - dbg("urbs.\n"); - dbg("Freeing serial->private.\n"); + /* dbg("Freeing serial->private."); */ kfree(serial->private); - dbg("Freeing port->private.\n"); - /* Now free per port private data */ + /* dbg("Freeing port->private."); */ + /* Now free per port private data */ for (i = 0; i < serial->num_ports; i++) { port = &serial->port[i]; + while (port->open_count > 0) { + --port->open_count; + MOD_DEC_USE_COUNT; + } kfree(port->private); } - -} - - -static int __init keyspan_init (void) -{ - usb_serial_register (&keyspan_usa18x_pre_device); - usb_serial_register (&keyspan_usa19_pre_device); - usb_serial_register (&keyspan_usa19w_pre_device); - usb_serial_register (&keyspan_usa28_pre_device); - usb_serial_register (&keyspan_usa28x_pre_device); - usb_serial_register (&keyspan_usa18x_device); - usb_serial_register (&keyspan_usa19_device); - usb_serial_register (&keyspan_usa19w_device); - usb_serial_register (&keyspan_usa28_device); - usb_serial_register (&keyspan_usa28x_device); - return 0; -} - - -static void __exit keyspan_exit (void) -{ - usb_serial_deregister (&keyspan_usa18x_pre_device); - usb_serial_deregister (&keyspan_usa19_pre_device); - usb_serial_deregister (&keyspan_usa19w_pre_device); - usb_serial_deregister (&keyspan_usa28_pre_device); - usb_serial_deregister (&keyspan_usa28x_pre_device); - usb_serial_deregister (&keyspan_usa18x_device); - usb_serial_deregister (&keyspan_usa19_device); - usb_serial_deregister (&keyspan_usa19w_device); - usb_serial_deregister (&keyspan_usa28_device); - usb_serial_deregister (&keyspan_usa28x_device); } - - -module_init(keyspan_init); -module_exit(keyspan_exit); - -MODULE_AUTHOR("Hugh Blemings "); -MODULE_DESCRIPTION("Keyspan USB to Serial Converter driver"); diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 8d249916c2e0..0d7b4545ae32 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -19,6 +19,11 @@ and Keyspan, Inc the manufacturers of the Keyspan USB-serial products. Thanks Guys :) + Thanks to Paulus for miscellaneous tidy ups, some largish chunks + of much nicer and/or completely new code and (perhaps most uniquely) + having the patience to sit down and explain why and where he'd changed + stuff. + Tip 'o the hat to Linuxcare for supporting staff in their work on open source projects. @@ -29,7 +34,6 @@ #ifndef __LINUX_USB_SERIAL_KEYSPAN_H #define __LINUX_USB_SERIAL_KEYSPAN_H -#include /* Function prototypes for Keyspan serial converter */ static int keyspan_open (struct usb_serial_port *port, @@ -41,12 +45,18 @@ static void keyspan_shutdown (struct usb_serial *serial); static void keyspan_rx_throttle (struct usb_serial_port *port); static void keyspan_rx_unthrottle (struct usb_serial_port *port); static int keyspan_write_room (struct usb_serial_port *port); + static int keyspan_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); + +#if 0 static void keyspan_write_bulk_callback (struct urb *urb); -static void keyspan_read_bulk_callback (struct urb *urb); +#endif + +//static void keyspan_usa26_read_int_callback (struct urb *urb); +//static void keyspan_usa28_read_int_callback (struct urb *urb); static int keyspan_chars_in_buffer (struct usb_serial_port *port); static int keyspan_ioctl (struct usb_serial_port *port, struct file *file, @@ -58,12 +68,20 @@ static void keyspan_break_ctl (struct usb_serial_port *port, int break_state); static int keyspan_fake_startup (struct usb_serial *serial); -static int keyspan_usa19_calc_baud (u32 baud_rate, u8 *rate_hi, - u8 *rate_low); -static void keyspan_usa19_setup_urbs (struct usb_serial *serial); -static int keyspan_usa19_send_setup (struct usb_serial *serial, - struct usb_serial_port *port); +static int keyspan_usa19_calc_baud (u32 baud_rate, u32 baudclk, + u8 *rate_hi, u8 *rate_low, u8 *prescaler); +static int keyspan_usa19w_calc_baud (u32 baud_rate, u32 baudclk, + u8 *rate_hi, u8 *rate_low, u8 *prescaler); + +//static void keyspan_usa19_setup_urbs (struct usb_serial *serial); + +static int keyspan_usa28_send_setup (struct usb_serial *serial, + struct usb_serial_port *port); +static int keyspan_usa26_send_setup (struct usb_serial *serial, + struct usb_serial_port *port); +static int keyspan_usa49_send_setup (struct usb_serial *serial, + struct usb_serial_port *port); /* Functions from usbserial.c for ezusb firmware handling */ extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit); @@ -109,13 +127,162 @@ struct ezusb_hex_record { static const struct ezusb_hex_record *keyspan_usa19w_firmware = NULL; #endif - +#ifdef CONFIG_USB_SERIAL_KEYSPAN_USA49W + #include "keyspan_usa49w_fw.h" +#else + static const struct ezusb_hex_record *keyspan_usa49w_firmware = NULL; +#endif + /* Values used for baud rate calculation - device specific */ #define KEYSPAN_INVALID_BAUD_RATE (-1) #define KEYSPAN_BAUD_RATE_OK (0) -#define USA19_BAUDCLK (12000000L) +#define KEYSPAN_USA18X_BAUDCLK (12000000L) /* a guess */ +#define KEYSPAN_USA19_BAUDCLK (12000000L) +#define KEYSPAN_USA19W_BAUDCLK (24000000L) +#define KEYSPAN_USA28X_BAUDCLK (12000000L) +#define KEYSPAN_USA49W_BAUDCLK (48000000L) + + /* Some constants used to characterise each device. + There is a four port device due later in the year, + we allow for it now in the following */ +#define KEYSPAN_MAX_NUM_PORTS (4) +#define KEYSPAN_MAX_FLIPS (2) + +typedef struct { + /* product ID value */ + int product_id; + + enum {msg_usa26, msg_usa28, msg_usa49} msg_format; + + /* Number of physical ports */ + int num_ports; + + /* 1 if endpoint flipping used on input, 0 if not */ + int indat_endp_flip; + + /* 1 if endpoint flipping used on output, 0 if not */ + int outdat_endp_flip; + + /* Table mapping input data endpoint IDs to physical + port number and flip if used */ + int indat_endpoints[KEYSPAN_MAX_NUM_PORTS]; + + /* Same for output endpoints */ + int outdat_endpoints[KEYSPAN_MAX_NUM_PORTS]; + + /* Input acknowledge endpoints */ + int inack_endpoints[KEYSPAN_MAX_NUM_PORTS]; + + /* Output control endpoints */ + int outcont_endpoints[KEYSPAN_MAX_NUM_PORTS]; + + /* Endpoint used for input status */ + int instat_endpoint; + + /* Endpoint used for global control functions */ + int glocont_endpoint; + + int (*calculate_baud_rate) (u32 baud_rate, u32 baudclk, + u8 *rate_hi, u8 *rate_low, u8 *prescaler); + u32 baudclk; + +} keyspan_device_details; + + /* Now for each device type we setup the device detail + structure with the appropriate information (provided + in Keyspan's documentation) */ + +static const keyspan_device_details usa18x_device_details = { + 0x112, /* product ID */ + msg_usa26, /* msg type*/ + 1, /* num ports */ + 0, /* indat endpoint flip */ + 1, /* outdat endpoint flip */ + {0x81}, /* per port indat */ + {0x01}, /* per port outdat */ + {0x85}, /* per port inack */ + {0x05}, /* per port outcont */ + 0x87, /* instat endpoint */ + 0x07, /* glocont endpoint */ + keyspan_usa19w_calc_baud, /* calc baud rate */ + KEYSPAN_USA18X_BAUDCLK /* base baud clock */ +}; + +static const keyspan_device_details usa19_device_details = { + 0x107, /* product ID */ + msg_usa28, /* msg type*/ + 1, /* num ports */ + 1, /* indat endpoint flip */ + 1, /* outdat endpoint flip */ + {0x81}, /* per port indat */ + {0x01}, /* per port outdat */ + {0x83}, /* per port inack */ + {0x03}, /* per port outcont */ + 0x84, /* instat endpoint */ + -1, /* glocont endpoint */ + keyspan_usa19_calc_baud, /* calc baud rate */ + KEYSPAN_USA19_BAUDCLK /* base baud clock */ +}; + +static const keyspan_device_details usa19w_device_details = { + 0x108, /* product ID */ + msg_usa26, /* msg type*/ + 1, /* num ports */ + 0, /* indat endpoint flip */ + 1, /* outdat endpoint flip */ + {0x81}, /* per port indat */ + {0x01}, /* per port outdat */ + {0x85}, /* per port inack */ + {0x05}, /* per port outcont */ + 0x87, /* instat endpoint */ + 0x07, /* glocont endpoint */ + keyspan_usa19w_calc_baud, /* calc baud rate */ + KEYSPAN_USA19W_BAUDCLK /* base baud clock */ +}; + +static const keyspan_device_details usa28x_device_details = { + 0x110, /* product ID */ + msg_usa26, /* msg type*/ + 2, /* num ports */ + 0, /* indat endpoint flip */ + 1, /* outdat endpoint flip */ + {0x81, 0x83}, /* per port indat */ + {0x01, 0x03}, /* per port outdat */ + {0x85, 0x86}, /* per port inack */ + {0x05, 0x06}, /* per port outcont */ + 0x87, /* instat endpoint */ + 0x07, /* glocont endpoint */ + keyspan_usa19w_calc_baud, /* calc baud rate */ + KEYSPAN_USA28X_BAUDCLK +}; + +static const keyspan_device_details usa49w_device_details = { + 0x010a, /* product ID */ + msg_usa49, /* msg type*/ + 4, /* num ports */ + 0, /* indat endpoint flip */ + 0, /* outdat endpoint flip */ + { 0x81, 0x82, 0x83, 0x84}, /* per port indat */ + { 0x01, 0x02, 0x03, 0x04}, /* per port outdat */ + {-1, -1, -1, -1}, /* per port inack */ + {-1, -1, -1, -1}, /* per port outcont */ + 0x87, /* instat endpoint */ + 0x07, /* glocont endpoint */ + keyspan_usa19w_calc_baud, /* calc baud rate */ + KEYSPAN_USA49W_BAUDCLK +}; - /* Device info for the Keyspan serial converter */ +static const keyspan_device_details *keyspan_devices[] = { + &usa18x_device_details, + &usa19_device_details, + &usa19w_device_details, + &usa28x_device_details, + &usa49w_device_details, + NULL +}; + + /* Device info for the Keyspan serial converter, used + by the overall usb-serial probe function */ #define KEYSPAN_VENDOR_ID (0x06cd) /* Product IDs for the five products supported, pre-renumeration */ @@ -124,6 +291,7 @@ struct ezusb_hex_record { #define keyspan_usa19w_pre_product_id 0x0106 #define keyspan_usa28_pre_product_id 0x0101 #define keyspan_usa28x_pre_product_id 0x0102 +#define keyspan_usa49w_pre_product_id 0x0109 /* Product IDs post-renumeration */ #define keyspan_usa18x_product_id 0x0112 @@ -131,6 +299,7 @@ struct ezusb_hex_record { #define keyspan_usa19w_product_id 0x0108 #define keyspan_usa28_product_id 0x010f #define keyspan_usa28x_product_id 0x0110 +#define keyspan_usa49w_product_id 0x010a static __devinitdata struct usb_device_id keyspan_ids_combined[] = { {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_pre_product_id}, @@ -138,11 +307,13 @@ static __devinitdata struct usb_device_id keyspan_ids_combined[] = { {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19w_pre_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28_pre_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28x_pre_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa49w_pre_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa19w_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28_product_id}, {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa28x_product_id}, + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa49w_product_id}, { } /* Terminating entry */ }; @@ -177,6 +348,11 @@ static __devinitdata struct usb_device_id keyspan_usa28x_pre_ids[] = { { } /* Terminating entry */ }; +static __devinitdata struct usb_device_id keyspan_usa49w_pre_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa49w_pre_product_id}, + { } /* Terminating entry */ +}; + static __devinitdata struct usb_device_id keyspan_usa18x_ids[] = { {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa18x_product_id}, { } /* Terminating entry */ @@ -202,10 +378,15 @@ static __devinitdata struct usb_device_id keyspan_usa28x_ids[] = { { } /* Terminating entry */ }; +static __devinitdata struct usb_device_id keyspan_usa49w_ids[] = { + {idVendor: KEYSPAN_VENDOR_ID, idProduct: keyspan_usa49w_product_id}, + { } /* Terminating entry */ +}; + /* Structs for the devices, pre and post renumeration. These are incomplete at present - HAB 20000708 */ struct usb_serial_device_type keyspan_usa18x_pre_device = { - name: "Keyspan USA18X - (prerenumeration)", + name: "Keyspan USA18X - (without firmware)", id_table: keyspan_usa18x_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, @@ -218,7 +399,7 @@ struct usb_serial_device_type keyspan_usa18x_pre_device = { }; struct usb_serial_device_type keyspan_usa19_pre_device = { - name: "Keyspan USA19 - (prerenumeration)", + name: "Keyspan USA19 - (without firmware)", id_table: keyspan_usa19_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, @@ -232,7 +413,7 @@ struct usb_serial_device_type keyspan_usa19_pre_device = { struct usb_serial_device_type keyspan_usa19w_pre_device = { - name: "Keyspan USA19W - (prerenumeration)", + name: "Keyspan USA19W - (without firmware)", id_table: keyspan_usa19w_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, @@ -246,7 +427,7 @@ struct usb_serial_device_type keyspan_usa19w_pre_device = { struct usb_serial_device_type keyspan_usa28_pre_device = { - name: "Keyspan USA28 - (prerenumeration)", + name: "Keyspan USA28 - (without firmware)", id_table: keyspan_usa28_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, @@ -259,7 +440,7 @@ struct usb_serial_device_type keyspan_usa28_pre_device = { }; struct usb_serial_device_type keyspan_usa28x_pre_device = { - name: "Keyspan USA28X - (prerenumeration)", + name: "Keyspan USA28X - (without firmware)", id_table: keyspan_usa28x_pre_ids, needs_interrupt_in: DONT_CARE, needs_bulk_in: DONT_CARE, @@ -271,22 +452,43 @@ struct usb_serial_device_type keyspan_usa28x_pre_device = { startup: keyspan_fake_startup }; +struct usb_serial_device_type keyspan_usa49w_pre_device = { + name: "Keyspan USA49W - (without firmware)", + id_table: keyspan_usa49w_pre_ids, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: DONT_CARE, + needs_bulk_out: DONT_CARE, + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 4, + startup: keyspan_fake_startup +}; struct usb_serial_device_type keyspan_usa18x_device = { name: "Keyspan USA18X", id_table: keyspan_usa18x_ids, needs_interrupt_in: DONT_CARE, - needs_bulk_in: DONT_CARE, - needs_bulk_out: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, + num_bulk_in: 3, + num_bulk_out: 4, num_ports: 1, open: keyspan_open, close: keyspan_close, + write: keyspan_write, + write_room: keyspan_write_room, + //write_bulk_callback: Not used - we define our own herbs + //read_int_callback: keyspan_usa26_read_int_callback, + chars_in_buffer: keyspan_chars_in_buffer, throttle: keyspan_rx_throttle, unthrottle: keyspan_rx_unthrottle, + ioctl: keyspan_ioctl, set_termios: keyspan_set_termios, + break_ctl: keyspan_break_ctl, + startup: keyspan_startup, + shutdown: keyspan_shutdown, }; struct usb_serial_device_type keyspan_usa19_device = { @@ -303,8 +505,8 @@ struct usb_serial_device_type keyspan_usa19_device = { close: keyspan_close, write: keyspan_write, write_room: keyspan_write_room, - write_bulk_callback: keyspan_write_bulk_callback, - read_int_callback: keyspan_read_bulk_callback, +// write_bulk_callback: keyspan_write_bulk_callback, +// read_int_callback: keyspan_usa28_read_int_callback, chars_in_buffer: keyspan_chars_in_buffer, throttle: keyspan_rx_throttle, unthrottle: keyspan_rx_unthrottle, @@ -320,17 +522,26 @@ struct usb_serial_device_type keyspan_usa19w_device = { name: "Keyspan USA19W", id_table: keyspan_usa19w_ids, needs_interrupt_in: DONT_CARE, - needs_bulk_in: DONT_CARE, - needs_bulk_out: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, num_interrupt_in: NUM_DONT_CARE, - num_bulk_in: NUM_DONT_CARE, - num_bulk_out: NUM_DONT_CARE, + num_bulk_in: 3, + num_bulk_out: 4, num_ports: 1, open: keyspan_open, close: keyspan_close, + write: keyspan_write, + write_room: keyspan_write_room, + //write_bulk_callback: Not used - we define our own herbs + //read_int_callback: keyspan_usa26_read_int_callback, + chars_in_buffer: keyspan_chars_in_buffer, throttle: keyspan_rx_throttle, unthrottle: keyspan_rx_unthrottle, + ioctl: keyspan_ioctl, set_termios: keyspan_set_termios, + break_ctl: keyspan_break_ctl, + startup: keyspan_startup, + shutdown: keyspan_shutdown, }; @@ -366,8 +577,8 @@ struct usb_serial_device_type keyspan_usa28x_device = { close: keyspan_close, write: keyspan_write, write_room: keyspan_write_room, - write_bulk_callback: keyspan_write_bulk_callback, - read_int_callback: keyspan_read_bulk_callback, +// write_bulk_callback: keyspan_write_bulk_callback, +// read_int_callback: keyspan_usa26_read_int_callback, chars_in_buffer: keyspan_chars_in_buffer, throttle: keyspan_rx_throttle, unthrottle: keyspan_rx_unthrottle, @@ -377,10 +588,34 @@ struct usb_serial_device_type keyspan_usa28x_device = { startup: keyspan_startup, shutdown: keyspan_shutdown, +}; +struct usb_serial_device_type keyspan_usa49w_device = { + name: "Keyspan USA49W", + id_table: keyspan_usa49w_ids, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: 5, + num_bulk_out: 5, + num_ports: 4, + open: keyspan_open, + close: keyspan_close, + write: keyspan_write, + write_room: keyspan_write_room, + //write_bulk_callback: Not used - we define our own herbs + //read_int_callback: keyspan_usa26_read_int_callback, + chars_in_buffer: keyspan_chars_in_buffer, + throttle: keyspan_rx_throttle, + unthrottle: keyspan_rx_unthrottle, + ioctl: keyspan_ioctl, + set_termios: keyspan_set_termios, + break_ctl: keyspan_break_ctl, + startup: keyspan_startup, + shutdown: keyspan_shutdown, }; - #endif diff --git a/drivers/usb/serial/keyspan_usa26msg.h b/drivers/usb/serial/keyspan_usa26msg.h index 8f53c96e8158..403fd47adc29 100644 --- a/drivers/usb/serial/keyspan_usa26msg.h +++ b/drivers/usb/serial/keyspan_usa26msg.h @@ -92,16 +92,8 @@ #ifndef __USA26MSG__ #define __USA26MSG__ -#ifndef __stubs__ -#include "datadefs.h" -#endif - -typedef struct txAckMessage -{ - u8 dummy; -} txAckMessage; -typedef struct portControlMessage +typedef struct keyspan_usa26_portControlMessage { /* there are three types of "commands" sent in the control message: @@ -172,7 +164,7 @@ typedef struct portControlMessage returnStatus, // BOTH: return current status (even if it hasn't changed) resetDataToggle;// BOTH: reset data toggle state to DATA0 -} portControlMessage; +} keyspan_usa26_portControlMessage; // defines for bits in lcr #define USA_DATABITS_5 0x00 @@ -190,7 +182,7 @@ typedef struct portControlMessage // all things called "StatusMessage" are sent on the status endpoint -typedef struct portStatusMessage // one for each port +typedef struct keyspan_usa26_portStatusMessage // one for each port { u8 port, // BOTH: 0=first, 1=second, other=see below hskia_cts, // USA26: reports HSKIA pin @@ -203,7 +195,7 @@ typedef struct portStatusMessage // one for each port _txXoff, // port is in XOFF state (either host or RX XOFF) rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off controlResponse;// 1=a control message has been processed -} portStatusMessage; +} keyspan_usa26_portStatusMessage; // bits in RX data message when STAT byte is included #define RXERROR_OVERRUN 0x02 @@ -211,28 +203,28 @@ typedef struct portStatusMessage // one for each port #define RXERROR_FRAMING 0x08 #define RXERROR_BREAK 0x10 -typedef struct globalControlMessage +typedef struct keyspan_usa26_globalControlMessage { u8 sendGlobalStatus, // 2=request for two status responses resetStatusToggle, // 1=reset global status toggle resetStatusCount; // a cycling value -} globalControlMessage; +} keyspan_usa26_globalControlMessage; -typedef struct globalStatusMessage +typedef struct keyspan_usa26_globalStatusMessage { u8 port, // 3 sendGlobalStatus, // from request, decremented resetStatusCount; // as in request -} globalStatusMessage; +} keyspan_usa26_globalStatusMessage; -typedef struct globalDebugMessage +typedef struct keyspan_usa26_globalDebugMessage { u8 port, // 2 a, b, c, d; -} globalDebugMessage; +} keyspan_usa26_globalDebugMessage; // ie: the maximum length of an EZUSB endpoint buffer #define MAX_DATA_LEN 64 diff --git a/drivers/usb/serial/keyspan_usa28msg.h b/drivers/usb/serial/keyspan_usa28msg.h index 6378bc5b03f1..ad03c9c44e62 100644 --- a/drivers/usb/serial/keyspan_usa28msg.h +++ b/drivers/usb/serial/keyspan_usa28msg.h @@ -94,16 +94,8 @@ #ifndef __USA28MSG__ #define __USA28MSG__ -/*#ifndef STUBS -#include "datadefs.h" -#endif*/ -typedef struct txAckMessage -{ - u8 dummy; -} txAckMessage; - -typedef struct portControlMessage +typedef struct keyspan_usa28_portControlMessage { /* there are four types of "commands" sent in the control message: @@ -153,9 +145,9 @@ typedef struct portControlMessage returnStatus, // return current status n times (1 or 2) resetDataToggle;// reset data toggle state to DATA0 -} portControlMessage; +} keyspan_usa28_portControlMessage; -typedef struct portStatusMessage +typedef struct keyspan_usa28_portStatusMessage { u8 port, // 0=first, 1=second, 2=global (see below) cts, @@ -171,32 +163,32 @@ typedef struct portStatusMessage rxBreak, // 1=we're in break state rs232invalid, // 1=no valid signals on rs-232 inputs controlResponse;// 1=a control messages has been processed -} portStatusMessage; +} keyspan_usa28_portStatusMessage; // bit defines in txState #define TX_OFF 0x01 // requested by host txOff command #define TX_XOFF 0x02 // either real, or simulated by host -typedef struct globalControlMessage +typedef struct keyspan_usa28_globalControlMessage { u8 sendGlobalStatus, // 2=request for two status responses resetStatusToggle, // 1=reset global status toggle resetStatusCount; // a cycling value -} globalControlMessage; +} keyspan_usa28_globalControlMessage; -typedef struct globalStatusMessage +typedef struct keyspan_usa28_globalStatusMessage { u8 port, // 3 sendGlobalStatus, // from request, decremented resetStatusCount; // as in request -} globalStatusMessage; +} keyspan_usa28_globalStatusMessage; -typedef struct globalDebugMessage +typedef struct keyspan_usa28_globalDebugMessage { u8 port, // 2 n, // typically a count/status byte b; // typically a data byte -} globalDebugMessage; +} keyspan_usa28_globalDebugMessage; // ie: the maximum length of an EZUSB endpoint buffer #define MAX_DATA_LEN 64 diff --git a/drivers/usb/serial/keyspan_usa49msg.h b/drivers/usb/serial/keyspan_usa49msg.h new file mode 100644 index 000000000000..16ef1057e999 --- /dev/null +++ b/drivers/usb/serial/keyspan_usa49msg.h @@ -0,0 +1,255 @@ +/* + usa49msg.h + + Copyright (c) 1998-2000 InnoSys Incorporated. All Rights Reserved + This file is available under a BSD-style copyright + + Keyspan USB Async Firmware to run on Anchor EZ-USB + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain this licence text + without modification, this list of conditions, and the following + disclaimer. The following copyright notice must appear immediately at + the beginning of all source files: + + Copyright (c) 1998-2000 InnoSys Incorporated. All Rights Reserved + + This file is available under a BSD-style copyright + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of InnoSys Incorprated may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + 4th revision: USA49W version + + See usa26msg.h for description of message formats + + Third revision: USA28X version (aka USA26) + + Buffer formats for RX/TX data messages are not defined by + a structure, but are described here: + + USB OUT (host -> USA26, transmit) messages contain a + REQUEST_ACK indicator (set to 0xff to request an ACK at the + completion of transmit; 0x00 otherwise), followed by data: + + RQSTACK DAT DAT DAT ... + + with a total data length of 63. + + USB IN (USA26 -> host, receive) messages contain either a zero + flag (indicating no error in any data bytes): + + 00 DAT DAT DAT ... + + for a total of 63 data bytes, or a non-zero status flag (indicating + that all data bytes will be preceded by status flag): + + STAT DAT STAT DAT STAT DAT ... + + for a total of 32 data bytes. The valid bits in the STAT bytes are: + + OVERRUN 0x02 + PARITY 0x04 + FRAMING 0x08 + BREAK 0x10 + + Notes: + + 1. a "no status" RX data message (first byte zero) can serve as + a "break off" indicator. + 2. a control message specifying disablePort will be answered + with a status message, but no further status will be sent + until a control messages with enablePort is sent + + revision history: + + 1999feb10 add reportHskiaChanges to allow us to ignore them + 1999feb10 add txAckThreshold for fast+loose throughput enhancement + 1999mar30 beef up support for RX error reporting + 1999apr14 add resetDataToggle to control message + 2000jan04 merge with usa17msg.h + 2000mar08 clone from usa26msg.h -> usa49msg.h + 2000mar09 change to support 4 ports + 2000may03 change external clocking to match USA-49W hardware + 2000jun01 add extended BSD-style copyright text +*/ + +#ifndef __USA49MSG__ +#define __USA49MSG__ + + +/* + Host->device messages sent on the global control endpoint: + + portNumber message + ---------- -------------------- + 0,1,2,3 portControlMessage + 0x80 globalControlMessage +*/ + +typedef struct keyspan_usa49_portControlMessage +{ + /* + 0. 0/1/2/3 port control message follows + 0x80 set non-port control message follows + */ + u8 portNumber, + + /* + there are three types of "commands" sent in the control message: + + 1. configuration changes which must be requested by setting + the corresponding "set" flag (and should only be requested + when necessary, to reduce overhead on the USA26): + */ + setClocking, // host requests baud rate be set + baudLo, // host does baud divisor calculation + baudHi, // baudHi is only used for first port (gives lower rates) + prescaler, // specified as N/8; values 8-ff are valid + // must be set any time internal baud rate is set; + txClocking, // 0=internal, 1=external/DSR + rxClocking, // 0=internal, 1=external/DSR + + setLcr, // host requests lcr be set + lcr, // use PARITY, STOPBITS, DATABITS below + + setFlowControl, // host requests flow control be set + ctsFlowControl, // 1=use CTS flow control, 0=don't + xonFlowControl, // 1=use XON/XOFF flow control, 0=don't + xonChar, // specified in current character format + xoffChar, // specified in current character format + + setRts, // host requests RTS output be set + rts, // 1=active, 0=inactive + + setDtr, // host requests DTR output be set + dtr; // 1=on, 0=off + + + /* + 3. configuration data which is simply used as is (no overhead, + but must be specified correctly in every host message). + */ + u8 forwardingLength, // forward when this number of chars available + dsrFlowControl, // 1=use DSR flow control, 0=don't + txAckThreshold, // 0=not allowed, 1=normal, 2-255 deliver ACK faster + loopbackMode; // 0=no loopback, 1=loopback enabled + + /* + 4. commands which are flags only; these are processed in order + (so that, e.g., if both _txOn and _txOff flags are set, the + port ends in a TX_OFF state); any non-zero value is respected + */ + u8 _txOn, // enable transmitting (and continue if there's data) + _txOff, // stop transmitting + txFlush, // toss outbound data + txBreak, // turn on break (cleared by _txOn) + rxOn, // turn on receiver + rxOff, // turn off receiver + rxFlush, // toss inbound data + rxForward, // forward all inbound data, NOW (as if fwdLen==1) + returnStatus, // return current status (even if it hasn't changed) + resetDataToggle,// reset data toggle state to DATA0 + enablePort, // start servicing port (move data, check status) + disablePort; // stop servicing port (does implicit tx/rx flush/off) + +} keyspan_usa49_portControlMessage; + +// defines for bits in lcr +#define USA_DATABITS_5 0x00 +#define USA_DATABITS_6 0x01 +#define USA_DATABITS_7 0x02 +#define USA_DATABITS_8 0x03 +#define STOPBITS_5678_1 0x00 // 1 stop bit for all byte sizes +#define STOPBITS_5_1p5 0x04 // 1.5 stop bits for 5-bit byte +#define STOPBITS_678_2 0x04 // 2 stop bits for 6/7/8-bit byte +#define USA_PARITY_NONE 0x00 +#define USA_PARITY_ODD 0x08 +#define USA_PARITY_EVEN 0x18 +#define PARITY_1 0x28 +#define PARITY_0 0x38 + +typedef struct keyspan_usa49_globalControlMessage +{ + u8 portNumber, // 0x80 + sendGlobalStatus, // 1/2=number of status responses requested + resetStatusToggle, // 1=reset global status toggle + resetStatusCount, // a cycling value + remoteWakeupEnable; // 0x10=P1, 0x20=P2, 0x40=P3, 0x80=P3 +} keyspan_usa49_globalControlMessage; + +/* + Device->host messages send on the global status endpoint + + portNumber message + ---------- -------------------- + 0x00,0x01,0x02,0x03 portStatusMessage + 0x80 globalStatusMessage + 0x81 globalDebugMessage +*/ + +typedef struct keyspan_usa49_portStatusMessage // one for each port +{ + u8 portNumber, // 0,1,2,3 + cts, // reports CTS pin + dcd, // reports DCD pin + dsr, // reports DSR pin + ri, // reports RI pin + _txOff, // transmit has been disabled (by host) + _txXoff, // transmit is in XOFF state (either host or RX XOFF) + rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off + controlResponse,// 1=a control message has been processed + txAck, // ACK (data TX complete) + rs232valid; // RS-232 signal valid +} keyspan_usa49_portStatusMessage; + +// bits in RX data message when STAT byte is included +#define RXERROR_OVERRUN 0x02 +#define RXERROR_PARITY 0x04 +#define RXERROR_FRAMING 0x08 +#define RXERROR_BREAK 0x10 + +typedef struct keyspan_usa49_globalStatusMessage +{ + u8 portNumber, // 0x80=globalStatusMessage + sendGlobalStatus, // from request, decremented + resetStatusCount; // as in request +} keyspan_usa49_globalStatusMessage; + +typedef struct keyspan_usa49_globalDebugMessage +{ + u8 portNumber, // 0x81=globalDebugMessage + n, // typically a count/status byte + b; // typically a data byte +} keyspan_usa49_globalDebugMessage; + +// ie: the maximum length of an EZUSB endpoint buffer +#define MAX_DATA_LEN 64 + +// update status approx. 60 times a second (16.6666 ms) +#define STATUS_UPDATE_INTERVAL 16 + +// status rationing tuning value (each port gets checked each n ms) +#define STATUS_RATION 10 + +#endif diff --git a/drivers/usb/serial/keyspan_usa49w_fw.h b/drivers/usb/serial/keyspan_usa49w_fw.h new file mode 100644 index 000000000000..4e01712f1931 --- /dev/null +++ b/drivers/usb/serial/keyspan_usa49w_fw.h @@ -0,0 +1,457 @@ +/* keyspan_usa49w_fw.h + + Generated from Keyspan firmware image Thu Sep 28 09:13:26 2000 EST + This firmware is for the Keyspan USA-49W Serial Adaptor + + "The firmware contained herein as keyspan_usa49w_fw.h is + Copyright (C) 1999-2000 Keyspan, A division of InnoSys Incorporated + ("Keyspan"), as an unpublished work. This notice does not imply + unrestricted or public access to this firmware which is a trade secret of + Keyspan, and which may not be reproduced, used, sold or transferred to any + third party without Keyspan's prior written consent. All Rights Reserved. + + This firmware may not be modified and may only be used with the Keyspan + USA-49W Serial Adapter. Distribution and/or Modification of the + keyspan.c driver which includes this firmware, in whole or in part, + requires the inclusion of this statement." + +*/ + +static const struct ezusb_hex_record keyspan_usa49w_firmware[] = { +{ 0x0000, 3, {0x02, 0x10, 0x36} }, +{ 0x0003, 16, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x78, 0x41, 0x74} }, +{ 0x0013, 16, {0x01, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24} }, +{ 0x0023, 16, {0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} }, +{ 0x0033, 3, {0x02, 0x17, 0xf0} }, +{ 0x0036, 12, {0x90, 0x78, 0x41, 0x74, 0x01, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x0043, 3, {0x02, 0x18, 0x00} }, +{ 0x0046, 16, {0xe4, 0xff, 0x74, 0x40, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xe0, 0xfe, 0xe5, 0x27} }, +{ 0x0056, 16, {0x24, 0x04, 0xfd, 0xe4, 0x35, 0x26, 0xfa, 0xa9, 0x05, 0x7b, 0x01, 0xef, 0x7c, 0x00, 0x29, 0xf9} }, +{ 0x0066, 16, {0xec, 0x3a, 0xfa, 0xee, 0x12, 0x11, 0x90, 0x0f, 0xbf, 0x22, 0xd7, 0xe5, 0x27, 0x24, 0x05, 0xf5} }, +{ 0x0076, 16, {0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x70, 0x03, 0x02, 0x01, 0x4f, 0x7f, 0x01, 0xe5, 0x27} }, +{ 0x0086, 16, {0x24, 0x08, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xfd, 0x12, 0x15, 0x3d, 0xe5, 0x27} }, +{ 0x0096, 16, {0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x15, 0x6c, 0xe5, 0x27} }, +{ 0x00a6, 16, {0x24, 0x07, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xff, 0x12, 0x00, 0x03, 0xe5, 0x27} }, +{ 0x00b6, 16, {0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x30, 0xf0, 0x7d, 0x04, 0x44} }, +{ 0x00c6, 16, {0x20, 0xf0, 0xe5, 0x27, 0x24, 0x09, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x12} }, +{ 0x00d6, 16, {0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xdf, 0xf0, 0x43} }, +{ 0x00e6, 16, {0x05, 0xc0, 0xe5, 0x27, 0x24, 0x0a, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x14} }, +{ 0x00f6, 16, {0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xdf, 0xf0, 0x43} }, +{ 0x0106, 16, {0x05, 0x0b, 0x80, 0x03, 0x43, 0x05, 0x02, 0x7f, 0x03, 0x12, 0x15, 0x3d, 0xe5, 0x27, 0x24, 0x39} }, +{ 0x0116, 16, {0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x04} }, +{ 0x0126, 16, {0xf0, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00} }, +{ 0x0136, 16, {0xf0, 0x90, 0x78, 0x41, 0x74, 0x02, 0xf0, 0xe5, 0x27, 0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0146, 16, {0xf5, 0x83, 0xe0, 0x44, 0x06, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x0b, 0xf5, 0x82, 0xe4} }, +{ 0x0156, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x32, 0xe5, 0x27, 0x24, 0x0c, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0166, 16, {0xf5, 0x83, 0xe0, 0x54, 0x3f, 0xff, 0xe5, 0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x0176, 16, {0x83, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4} }, +{ 0x0186, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x0d, 0xf5, 0x82, 0xe4} }, +{ 0x0196, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x70, 0x03, 0x02, 0x02, 0x6a, 0xe5, 0x27, 0x24, 0x17, 0xf5, 0x82} }, +{ 0x01a6, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x01b6, 16, {0x26, 0xf5, 0x83, 0xe0, 0x44, 0x04, 0xf0, 0x80, 0x0f, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4} }, +{ 0x01c6, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xfb, 0xf0, 0xe4, 0xff, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82} }, +{ 0x01d6, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xfd, 0x12, 0x15, 0x3d, 0xe5, 0x27, 0x24, 0x0e, 0xf5, 0x82} }, +{ 0x01e6, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x27, 0x24, 0x33, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x01f6, 16, {0x26, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x0f, 0xe5, 0x27, 0x24, 0x33, 0xf5, 0x82, 0xe4} }, +{ 0x0206, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0xf0, 0xe5, 0x27, 0x24, 0x33, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0216, 16, {0x26, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0xf0, 0xe5, 0x27, 0x24, 0x0f, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0226, 16, {0xf5, 0x83, 0xe0, 0x60, 0x2f, 0xe5, 0x27, 0x24, 0x33, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x0236, 16, {0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x27, 0x24, 0x10, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0246, 16, {0xff, 0x12, 0x14, 0xdd, 0xe5, 0x27, 0x24, 0x11, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0256, 16, {0xff, 0x12, 0x15, 0x0d, 0xe5, 0x27, 0x24, 0x33, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0266, 16, {0xff, 0x12, 0x14, 0xad, 0xe5, 0x27, 0x24, 0x14, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0276, 16, {0x60, 0x44, 0xe5, 0x27, 0x24, 0x15, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x11} }, +{ 0x0286, 16, {0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x01, 0xf0, 0x80} }, +{ 0x0296, 16, {0x0f, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xfe, 0xf0} }, +{ 0x02a6, 16, {0x90, 0x78, 0x41, 0x74, 0x04, 0xf0, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x02b6, 16, {0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x12, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x02c6, 16, {0x83, 0xe0, 0x60, 0x44, 0xe5, 0x27, 0x24, 0x13, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x02d6, 16, {0x60, 0x11, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x02} }, +{ 0x02e6, 16, {0xf0, 0x80, 0x0f, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54} }, +{ 0x02f6, 16, {0xfd, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x04, 0xf0, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0306, 16, {0x26, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x16, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0316, 16, {0x26, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x27, 0x24, 0x35, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x0326, 16, {0xef, 0xf0, 0xe5, 0x27, 0x24, 0x17, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x30, 0xe0} }, +{ 0x0336, 16, {0x11, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x40, 0xf0} }, +{ 0x0346, 16, {0x80, 0x0f, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xbf} }, +{ 0x0356, 16, {0xf0, 0xe5, 0x27, 0x24, 0x18, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x27} }, +{ 0x0366, 16, {0x24, 0x3b, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x27, 0x24, 0x19, 0xf5} }, +{ 0x0376, 16, {0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x11, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4} }, +{ 0x0386, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x0f, 0xe5, 0x27, 0x24, 0x39, 0xf5, 0x82} }, +{ 0x0396, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x04, 0xf0, 0xe5} }, +{ 0x03a6, 16, {0x27, 0x24, 0x39, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x90, 0xc0, 0x00, 0xf0, 0xe5} }, +{ 0x03b6, 16, {0x27, 0x24, 0x1a, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x6b, 0xe5, 0x27, 0x24} }, +{ 0x03c6, 16, {0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x78, 0x41, 0x74} }, +{ 0x03d6, 16, {0x03, 0xf0, 0xe5, 0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f} }, +{ 0x03e6, 16, {0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x01, 0xf0, 0x12, 0x00, 0x36, 0xef, 0x54, 0xfe} }, +{ 0x03f6, 16, {0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0406, 16, {0x54, 0xfd, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x15, 0x3d, 0xe5, 0x27, 0x24, 0x2c, 0xf5, 0x82} }, +{ 0x0416, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x27, 0x24, 0x2b, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0426, 16, {0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x28, 0x42, 0x25, 0xe5, 0x27, 0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0436, 16, {0x26, 0xf5, 0x83, 0xe0, 0x70, 0x0e, 0xe5, 0x27, 0x24, 0x25, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x0446, 16, {0x83, 0xe0, 0x60, 0x28, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0456, 16, {0x44, 0x02, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x15, 0x3d, 0xe5, 0x27, 0x24, 0x2b, 0xf5, 0x82} }, +{ 0x0466, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5, 0x28, 0x42, 0x25, 0xe5, 0x27, 0x24, 0x1c} }, +{ 0x0476, 16, {0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x70, 0x0e, 0xe5, 0x27, 0x24, 0x25, 0xf5, 0x82} }, +{ 0x0486, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x2c, 0x90, 0x78, 0x41, 0x74, 0x02, 0xf0, 0xe5, 0x27} }, +{ 0x0496, 16, {0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x04, 0x90, 0xc0, 0x00, 0xf0} }, +{ 0x04a6, 16, {0xe4, 0xff, 0x12, 0x14, 0x0c, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x04b6, 16, {0xe0, 0x54, 0x7f, 0xf0, 0xe5, 0x27, 0x24, 0x1d, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x04c6, 16, {0x60, 0x27, 0xe5, 0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x40} }, +{ 0x04d6, 16, {0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x04e6, 16, {0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x1e, 0xf5, 0x82, 0xe4} }, +{ 0x04f6, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x28, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0506, 16, {0xf5, 0x83, 0xe0, 0x54, 0xfe, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x15, 0x3d, 0xe5, 0x27, 0x24} }, +{ 0x0516, 16, {0x2d, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5, 0x28, 0x42, 0x25, 0xe5} }, +{ 0x0526, 16, {0x27, 0x24, 0x1f, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x70, 0x0e, 0xe5, 0x27, 0x24} }, +{ 0x0536, 16, {0x25, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x27, 0xe5, 0x27, 0x24, 0x32, 0xf5} }, +{ 0x0546, 16, {0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x01, 0xff, 0xf0, 0xfd, 0xe4, 0xff, 0x12, 0x15} }, +{ 0x0556, 16, {0x3d, 0xe5, 0x27, 0x24, 0x2d, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x28} }, +{ 0x0566, 16, {0x42, 0x25, 0xe5, 0x27, 0x24, 0x20, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x70, 0x0e} }, +{ 0x0576, 16, {0xe5, 0x27, 0x24, 0x25, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x18, 0x90, 0x78} }, +{ 0x0586, 16, {0x41, 0x74, 0x02, 0xf0, 0xe5, 0x27, 0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x0596, 16, {0x44, 0x02, 0x90, 0xc0, 0x00, 0xf0, 0xe5, 0x27, 0x24, 0x21, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x05a6, 16, {0x83, 0xe0, 0x60, 0x0f, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x05b6, 16, {0x44, 0x02, 0xf0, 0xe5, 0x27, 0x24, 0x22, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60} }, +{ 0x05c6, 16, {0x1f, 0xe5, 0x27, 0x24, 0x2e, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5} }, +{ 0x05d6, 16, {0x27, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x28, 0x42, 0x25} }, +{ 0x05e6, 16, {0xe5, 0x27, 0x24, 0x23, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x03, 0x12, 0x17} }, +{ 0x05f6, 16, {0x4e, 0xe5, 0x27, 0x24, 0x24, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x1b, 0xe5} }, +{ 0x0606, 16, {0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0x90, 0x7f} }, +{ 0x0616, 16, {0x98, 0xe0, 0xff, 0xe5, 0x28, 0xf4, 0xfe, 0xef, 0x5e, 0xf0, 0xe5, 0x27, 0x24, 0x25, 0xf5, 0x82} }, +{ 0x0626, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x60, 0x16, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0636, 14, {0x26, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x90, 0x7f, 0x98, 0xe0, 0x45, 0x28, 0xf0} }, +{ 0x0644, 1, {0x22} }, +{ 0x0645, 16, {0x90, 0x7f, 0xe9, 0xe0, 0x12, 0x11, 0xa2, 0x07, 0x42, 0x00, 0x07, 0xb6, 0x01, 0x08, 0x22, 0x03} }, +{ 0x0655, 16, {0x06, 0x68, 0x06, 0x07, 0x35, 0x08, 0x07, 0x2f, 0x09, 0x07, 0x17, 0x0a, 0x07, 0x26, 0x0b, 0x00} }, +{ 0x0665, 16, {0x00, 0x08, 0x71, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x19, 0x14, 0x60, 0x77, 0x24, 0x02} }, +{ 0x0675, 16, {0x60, 0x03, 0x02, 0x07, 0x0d, 0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5} }, +{ 0x0685, 16, {0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xea, 0xe0, 0x04, 0x75, 0x82, 0x17, 0x75, 0x83, 0x19, 0xf0} }, +{ 0x0695, 16, {0x90, 0x7f, 0xea, 0xe0, 0x30, 0xe0, 0x04, 0x7f, 0x03, 0x80, 0x02, 0x7f, 0x02, 0x75, 0x82, 0x82} }, +{ 0x06a5, 16, {0x75, 0x83, 0x19, 0xef, 0xf0, 0x75, 0x82, 0x6d, 0x75, 0x83, 0x19, 0xf0, 0x75, 0x82, 0x66, 0x75} }, +{ 0x06b5, 16, {0x83, 0x19, 0xf0, 0x75, 0x82, 0x5f, 0x75, 0x83, 0x19, 0xf0, 0x75, 0x82, 0x58, 0x75, 0x83, 0x19} }, +{ 0x06c5, 16, {0xf0, 0x90, 0x7f, 0xea, 0xe0, 0x30, 0xe1, 0x04, 0x7f, 0x64, 0x80, 0x02, 0x7f, 0x32, 0x75, 0x82} }, +{ 0x06d5, 16, {0x1a, 0x75, 0x83, 0x19, 0xef, 0xf0, 0x74, 0x19, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x12, 0x90, 0x7f} }, +{ 0x06e5, 16, {0xd5, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x13, 0x54, 0xea, 0x49, 0x60} }, +{ 0x06f5, 16, {0x0d, 0xea, 0x90, 0x7f, 0xd4, 0xf0, 0xe9, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f} }, +{ 0x0705, 16, {0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02} }, +{ 0x0715, 16, {0x08, 0x78, 0x90, 0x7f, 0x00, 0xe5, 0x0a, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x02, 0x08} }, +{ 0x0725, 16, {0x78, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0a, 0x02, 0x08, 0x78, 0x12, 0x0a, 0x6a, 0x02, 0x08, 0x78} }, +{ 0x0735, 16, {0x90, 0x7f, 0x00, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xe8} }, +{ 0x0745, 16, {0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2, 0x00, 0xe4, 0x33} }, +{ 0x0755, 16, {0xff, 0x25, 0xe0, 0xff, 0xa2, 0x06, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0} }, +{ 0x0765, 16, {0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x08, 0x78, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0xa3, 0xf0} }, +{ 0x0775, 16, {0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80} }, +{ 0x0785, 16, {0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4} }, +{ 0x0795, 16, {0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f} }, +{ 0x07a5, 16, {0xb5, 0x74, 0x02, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x08} }, +{ 0x07b5, 16, {0x78, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03, 0x02, 0x08, 0x78} }, +{ 0x07c5, 16, {0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x00, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xb4, 0xe0} }, +{ 0x07d5, 16, {0x44, 0x01, 0xf0, 0x02, 0x08, 0x78, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90, 0x7f, 0xec, 0xe0} }, +{ 0x07e5, 16, {0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} }, +{ 0x07f5, 16, {0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff} }, +{ 0x0805, 16, {0x13, 0x13, 0x13, 0x54, 0x1f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44} }, +{ 0x0815, 16, {0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x56, 0x90, 0x7f, 0xe8} }, +{ 0x0825, 16, {0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4a, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04} }, +{ 0x0835, 16, {0xd2, 0x00, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x36, 0x90, 0x7f, 0xea} }, +{ 0x0845, 16, {0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0} }, +{ 0x0855, 16, {0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01} }, +{ 0x0865, 16, {0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0xb4, 0xe0} }, +{ 0x0875, 10, {0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} }, +{ 0x087f, 1, {0x22} }, +{ 0x0880, 16, {0xe5, 0x23, 0x54, 0x0f, 0x70, 0x03, 0x02, 0x09, 0x5f, 0x12, 0x15, 0x9b, 0xef, 0x20, 0xe1, 0x63} }, +{ 0x0890, 16, {0x12, 0x15, 0xf9, 0xef, 0x14, 0xf5, 0x1a, 0x12, 0x17, 0xc1, 0xef, 0x25, 0x1a, 0xff, 0xe4, 0x33} }, +{ 0x08a0, 16, {0xfe, 0xc3, 0xef, 0x94, 0x80, 0xee, 0x64, 0x80, 0x94, 0x80, 0x50, 0x47, 0x85, 0x27, 0x82, 0x85} }, +{ 0x08b0, 16, {0x26, 0x83, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xf5, 0x82, 0x8e, 0x83, 0xe0, 0x30, 0xe0, 0x11, 0xe5} }, +{ 0x08c0, 16, {0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x0f} }, +{ 0x08d0, 16, {0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0xf0, 0x85} }, +{ 0x08e0, 16, {0x1a, 0x08, 0xef, 0x24, 0x01, 0xf5, 0x10, 0xe4, 0x3e, 0xf5, 0x0f, 0x12, 0x13, 0xd6, 0xe4, 0xff} }, +{ 0x08f0, 16, {0x12, 0x14, 0x0c, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x30} }, +{ 0x0900, 16, {0xe7, 0x5d, 0x12, 0x17, 0xc1, 0xe5, 0x27, 0x24, 0x3b, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x0910, 16, {0xe0, 0xfe, 0xef, 0xc3, 0x9e, 0x50, 0x48, 0xe5, 0x27, 0x24, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0920, 16, {0xf5, 0x83, 0x74, 0x01, 0xf0, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x0930, 16, {0xe0, 0x54, 0x7f, 0xf0, 0xe5, 0x27, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe4} }, +{ 0x0940, 16, {0xf0, 0xe5, 0x28, 0x42, 0x25, 0x90, 0x7f, 0xc2, 0xe0, 0x30, 0xe1, 0x10, 0xe5, 0x27, 0x24, 0x26} }, +{ 0x0950, 16, {0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xf5, 0x09, 0x80, 0x03, 0x12, 0x12, 0x3d, 0x12} }, +{ 0x0960, 16, {0x15, 0xca, 0xef, 0x30, 0xe1, 0x03, 0x02, 0x0a, 0x69, 0xe5, 0x27, 0x24, 0x38, 0xf5, 0x82, 0xe4} }, +{ 0x0970, 16, {0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x80, 0xf0, 0xe5, 0x27, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0980, 16, {0x26, 0xf5, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfe, 0x12, 0x17, 0xcd, 0xee, 0x4f, 0xd0, 0x82} }, +{ 0x0990, 16, {0xd0, 0x83, 0xf0, 0x12, 0x16, 0xd1, 0x8f, 0x1a, 0xe5, 0x27, 0x24, 0x35, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x09a0, 16, {0x26, 0xf5, 0x83, 0xe0, 0xfe, 0xef, 0xc3, 0x9e, 0x50, 0x28, 0x12, 0x17, 0xa9, 0xef, 0x30, 0xe0} }, +{ 0x09b0, 16, {0x21, 0xe5, 0x27, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x20, 0xe7, 0x12} }, +{ 0x09c0, 16, {0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x20, 0xe1, 0x03, 0x02} }, +{ 0x09d0, 16, {0x0a, 0x69, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0xfd} }, +{ 0x09e0, 16, {0xf0, 0xe5, 0x1a, 0x70, 0x0e, 0xe5, 0x27, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x09f0, 16, {0xe4, 0xf0, 0x22, 0xe5, 0x27, 0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xff} }, +{ 0x0a00, 16, {0x30, 0xe7, 0x29, 0xe5, 0x1a, 0xd3, 0x94, 0x20, 0x40, 0x03, 0x75, 0x1a, 0x20, 0x85, 0x1a, 0x08} }, +{ 0x0a10, 16, {0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0x8c, 0x0f, 0xf5, 0x10} }, +{ 0x0a20, 16, {0x12, 0x13, 0x06, 0xe5, 0x1a, 0x25, 0xe0, 0xff, 0x12, 0x14, 0x42, 0x22, 0xe5, 0x1a, 0xd3, 0x94} }, +{ 0x0a30, 16, {0x3f, 0x40, 0x03, 0x75, 0x1a, 0x3f, 0x85, 0x1a, 0x08, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0xa3} }, +{ 0x0a40, 16, {0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xf5, 0x82, 0x8e, 0x83, 0xe4, 0xf0, 0x85, 0x27, 0x82, 0x85, 0x26} }, +{ 0x0a50, 16, {0x83, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0x24, 0x01, 0xf5, 0x10, 0xe4, 0x3e, 0xf5, 0x0f, 0x12} }, +{ 0x0a60, 9, {0x13, 0x95, 0xe5, 0x1a, 0x04, 0xff, 0x12, 0x14, 0x42} }, +{ 0x0a69, 1, {0x22} }, +{ 0x0a6a, 16, {0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xf0, 0xf0, 0x90, 0x7f, 0x96, 0xf0, 0xe4} }, +{ 0x0a7a, 16, {0x90, 0x7f, 0x94, 0xf0, 0x90, 0x78, 0x4a, 0x04, 0xf0, 0xf5, 0x8e, 0x90, 0x7f, 0x95, 0x74, 0xc0} }, +{ 0x0a8a, 16, {0xf0, 0x90, 0x7f, 0x9e, 0x74, 0x3f, 0xf0, 0x90, 0x7f, 0x98, 0x74, 0x1f, 0xf0, 0x90, 0x78, 0x43} }, +{ 0x0a9a, 16, {0x74, 0xff, 0xf0, 0xe4, 0x90, 0x78, 0x41, 0xf0, 0x90, 0x7f, 0xdf, 0x74, 0x9f, 0xf0, 0x90, 0x7f} }, +{ 0x0aaa, 16, {0xde, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0x44, 0x02, 0xf0, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x26, 0x7b} }, +{ 0x0aba, 16, {0x75, 0x27, 0xc0, 0x90, 0x7f, 0x96, 0x74, 0xef, 0xf0, 0x75, 0x28, 0x01, 0x12, 0x0e, 0xb3, 0x7e} }, +{ 0x0aca, 16, {0x7b, 0x7f, 0xc0, 0x75, 0x26, 0x7b, 0x75, 0x27, 0xc0, 0x90, 0x7f, 0x96, 0x74, 0xef, 0xf0, 0x75} }, +{ 0x0ada, 16, {0x28, 0x01, 0xe5, 0x27, 0x24, 0x26, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe4, 0xf0, 0x7e} }, +{ 0x0aea, 16, {0x7e, 0x7f, 0x40, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0x74, 0x7e, 0xf0, 0xa3, 0x74, 0x40, 0xf0} }, +{ 0x0afa, 16, {0x7e, 0x7e, 0x7f, 0x80, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0xa3, 0xa3, 0x74, 0x7e, 0xf0, 0xa3} }, +{ 0x0b0a, 16, {0x74, 0x80, 0xf0, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x00, 0x90, 0x7f, 0x96} }, +{ 0x0b1a, 16, {0x74, 0xdf, 0xf0, 0x75, 0x28, 0x02, 0x12, 0x0e, 0xb3, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x26, 0x7c} }, +{ 0x0b2a, 16, {0x75, 0x27, 0x00, 0x90, 0x7f, 0x96, 0x74, 0xdf, 0xf0, 0x75, 0x28, 0x02, 0xe5, 0x27, 0x24, 0x26} }, +{ 0x0b3a, 16, {0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x7e, 0x7d, 0x7f, 0xc0, 0x85, 0x27} }, +{ 0x0b4a, 16, {0x82, 0x85, 0x26, 0x83, 0x74, 0x7d, 0xf0, 0xa3, 0x74, 0xc0, 0xf0, 0x7e, 0x7e, 0x7f, 0x00, 0x85} }, +{ 0x0b5a, 16, {0x27, 0x82, 0x85, 0x26, 0x83, 0xa3, 0xa3, 0x74, 0x7e, 0xf0, 0xa3, 0x74, 0x00, 0xf0, 0x7e, 0x7c} }, +{ 0x0b6a, 16, {0x7f, 0x40, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x40, 0x90, 0x7f, 0x96, 0x74, 0xbf, 0xf0, 0x75, 0x28} }, +{ 0x0b7a, 16, {0x04, 0x12, 0x0e, 0xb3, 0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x40, 0x90, 0x7f} }, +{ 0x0b8a, 16, {0x96, 0x74, 0xbf, 0xf0, 0x75, 0x28, 0x04, 0xe5, 0x27, 0x24, 0x26, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0b9a, 16, {0xf5, 0x83, 0x74, 0x02, 0xf0, 0x7e, 0x7d, 0x7f, 0x40, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0x74} }, +{ 0x0baa, 16, {0x7d, 0xf0, 0xa3, 0x74, 0x40, 0xf0, 0x7e, 0x7d, 0x7f, 0x80, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83} }, +{ 0x0bba, 16, {0xa3, 0xa3, 0x74, 0x7d, 0xf0, 0xa3, 0x74, 0x80, 0xf0, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x26, 0x7c} }, +{ 0x0bca, 16, {0x75, 0x27, 0x80, 0x90, 0x7f, 0x96, 0x74, 0x7f, 0xf0, 0x75, 0x28, 0x08, 0x12, 0x0e, 0xb3, 0x7e} }, +{ 0x0bda, 16, {0x7c, 0x7f, 0x80, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x80, 0x90, 0x7f, 0x96, 0x74, 0x7f, 0xf0, 0x75} }, +{ 0x0bea, 16, {0x28, 0x08, 0xe5, 0x27, 0x24, 0x26, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x03, 0xf0} }, +{ 0x0bfa, 16, {0x7e, 0x7c, 0x7f, 0xc0, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0x74, 0x7c, 0xf0, 0xa3, 0x74, 0xc0} }, +{ 0x0c0a, 16, {0xf0, 0x7e, 0x7d, 0x7f, 0x00, 0x85, 0x27, 0x82, 0x85, 0x26, 0x83, 0xa3, 0xa3, 0x74, 0x7d, 0xf0} }, +{ 0x0c1a, 7, {0xa3, 0x74, 0x00, 0xf0, 0xd2, 0x02, 0x22} }, +{ 0x0c21, 16, {0xe5, 0x22, 0x04, 0x54, 0x03, 0xf5, 0x22, 0x14, 0x60, 0x1f, 0x14, 0x60, 0x31, 0x14, 0x60, 0x43} }, +{ 0x0c31, 16, {0x24, 0x03, 0x70, 0x52, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x26, 0x7b, 0x75, 0x27, 0xc0, 0x90, 0x7f} }, +{ 0x0c41, 16, {0x96, 0x74, 0xef, 0xf0, 0x75, 0x28, 0x01, 0x80, 0x3d, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x26, 0x7c} }, +{ 0x0c51, 16, {0x75, 0x27, 0x00, 0x90, 0x7f, 0x96, 0x74, 0xdf, 0xf0, 0x75, 0x28, 0x02, 0x80, 0x28, 0x7e, 0x7c} }, +{ 0x0c61, 16, {0x7f, 0x40, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x40, 0x90, 0x7f, 0x96, 0x74, 0xbf, 0xf0, 0x75, 0x28} }, +{ 0x0c71, 16, {0x04, 0x80, 0x13, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x80, 0x90, 0x7f, 0x96} }, +{ 0x0c81, 16, {0x74, 0x7f, 0xf0, 0x75, 0x28, 0x08, 0xe5, 0x15, 0x55, 0x28, 0x70, 0x03, 0x02, 0x0d, 0xbf, 0xe5} }, +{ 0x0c91, 16, {0x28, 0xf4, 0xff, 0x52, 0x15, 0xe5, 0x0b, 0x54, 0x7f, 0xfe, 0x70, 0x0f, 0xe5, 0x0d, 0x55, 0x28} }, +{ 0x0ca1, 16, {0x60, 0x24, 0x90, 0x7f, 0x98, 0xe0, 0x45, 0x28, 0xf0, 0x80, 0x1b, 0xbe, 0x20, 0x18, 0xe5, 0x27} }, +{ 0x0cb1, 16, {0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x30, 0xe3, 0x09, 0xe4, 0xf5, 0x0d} }, +{ 0x0cc1, 16, {0x90, 0x7f, 0x98, 0xe0, 0x5f, 0xf0, 0xe5, 0x27, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x0cd1, 16, {0x83, 0xe0, 0x60, 0x03, 0xe0, 0x14, 0xf0, 0xe5, 0x27, 0x24, 0x34, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0ce1, 16, {0xf5, 0x83, 0xe0, 0x60, 0x03, 0xe0, 0x14, 0xf0, 0xe0, 0x60, 0x03, 0x02, 0x0d, 0xbf, 0x74, 0x0a} }, +{ 0x0cf1, 16, {0xf0, 0x12, 0x00, 0x36, 0xef, 0x54, 0x01, 0xff, 0xf5, 0x1a, 0xe5, 0x27, 0x24, 0x2c, 0xf5, 0x82} }, +{ 0x0d01, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x6f, 0x60, 0x07, 0xe5, 0x1a, 0xf0, 0xe5, 0x28, 0x42, 0x25} }, +{ 0x0d11, 16, {0x12, 0x17, 0xd9, 0x8f, 0x1a, 0xe5, 0x27, 0x24, 0x27, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83} }, +{ 0x0d21, 16, {0xe0, 0xff, 0xe5, 0x1a, 0x54, 0x10, 0xfe, 0x6f, 0x60, 0x06, 0xee, 0xf0, 0xe5, 0x28, 0x42, 0x25} }, +{ 0x0d31, 16, {0xe5, 0x27, 0x24, 0x28, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x1a, 0x54} }, +{ 0x0d41, 16, {0x80, 0xfe, 0x6f, 0x60, 0x06, 0xee, 0xf0, 0xe5, 0x28, 0x42, 0x25, 0xe5, 0x27, 0x24, 0x29, 0xf5} }, +{ 0x0d51, 16, {0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x1a, 0x54, 0x20, 0xfe, 0x6f, 0x60, 0x15} }, +{ 0x0d61, 16, {0xee, 0xf0, 0xe5, 0x27, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x30, 0xe4} }, +{ 0x0d71, 16, {0x04, 0xe5, 0x28, 0x42, 0x25, 0xe5, 0x24, 0x55, 0x28, 0xff, 0xf5, 0x1a, 0xe5, 0x27, 0x24, 0x2a} }, +{ 0x0d81, 16, {0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x6f, 0x60, 0x16, 0xe5, 0x1a, 0xf0, 0xe5, 0x27} }, +{ 0x0d91, 16, {0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x30, 0xe5, 0x04, 0xe5, 0x28, 0x42} }, +{ 0x0da1, 16, {0x25, 0xe5, 0x29, 0x55, 0x28, 0xff, 0xf5, 0x1a, 0xe5, 0x27, 0x24, 0x30, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x0db1, 14, {0x26, 0xf5, 0x83, 0xe0, 0x6f, 0x60, 0x07, 0xe5, 0x1a, 0xf0, 0xe5, 0x28, 0x42, 0x25} }, +{ 0x0dbf, 1, {0x22} }, +{ 0x0dc0, 16, {0xe5, 0x09, 0x14, 0x60, 0x2a, 0x14, 0x60, 0x41, 0x14, 0x60, 0x58, 0x14, 0x60, 0x6f, 0x24, 0x04} }, +{ 0x0dd0, 16, {0x60, 0x03, 0x02, 0x0e, 0x77, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x26, 0x7b, 0x75, 0x27, 0xc0, 0x90} }, +{ 0x0de0, 16, {0x7f, 0x96, 0x74, 0xef, 0xf0, 0x75, 0x28, 0x01, 0x12, 0x12, 0x3d, 0x75, 0x09, 0x01, 0x22, 0x7e} }, +{ 0x0df0, 16, {0x7c, 0x7f, 0x00, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x00, 0x90, 0x7f, 0x96, 0x74, 0xdf, 0xf0, 0x75} }, +{ 0x0e00, 16, {0x28, 0x02, 0x12, 0x12, 0x3d, 0x75, 0x09, 0x02, 0x22, 0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x26, 0x7c} }, +{ 0x0e10, 16, {0x75, 0x27, 0x40, 0x90, 0x7f, 0x96, 0x74, 0xbf, 0xf0, 0x75, 0x28, 0x04, 0x12, 0x12, 0x3d, 0x75} }, +{ 0x0e20, 16, {0x09, 0x03, 0x22, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x80, 0x90, 0x7f, 0x96} }, +{ 0x0e30, 16, {0x74, 0x7f, 0xf0, 0x75, 0x28, 0x08, 0x12, 0x12, 0x3d, 0x75, 0x09, 0x04, 0x22, 0x30, 0x04, 0x33} }, +{ 0x0e40, 16, {0xc2, 0x04, 0x53, 0x25, 0xdf, 0xe4, 0xf5, 0x1a, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x11, 0x25, 0x1a} }, +{ 0x0e50, 16, {0xf9, 0xee, 0x34, 0x00, 0xfa, 0x12, 0x11, 0x4a, 0xff, 0x74, 0x80, 0x25, 0x1a, 0xf5, 0x82, 0xe4} }, +{ 0x0e60, 16, {0x34, 0x7b, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x1a, 0xe5, 0x1a, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3} }, +{ 0x0e70, 16, {0x74, 0x03, 0xf0, 0x75, 0x09, 0x05, 0x22, 0xe5, 0x17, 0x60, 0x34, 0xd5, 0x17, 0x03, 0x53, 0x25} }, +{ 0x0e80, 16, {0xef, 0xe4, 0xf5, 0x1a, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x16, 0x25, 0x1a, 0xf9, 0xee, 0x34, 0x00} }, +{ 0x0e90, 16, {0xfa, 0x12, 0x11, 0x4a, 0xff, 0x74, 0x80, 0x25, 0x1a, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83} }, +{ 0x0ea0, 16, {0xef, 0xf0, 0x05, 0x1a, 0xe5, 0x1a, 0xb4, 0x03, 0xdb, 0x90, 0x7f, 0xc3, 0x74, 0x03, 0xf0, 0xe4} }, +{ 0x0eb0, 2, {0xf5, 0x09} }, +{ 0x0eb2, 1, {0x22} }, +{ 0x0eb3, 16, {0xe4, 0xf5, 0x19, 0x7e, 0x00, 0x7b, 0x01, 0xe5, 0x27, 0x25, 0x19, 0xf9, 0xee, 0x35, 0x26, 0xfa} }, +{ 0x0ec3, 16, {0xe4, 0x12, 0x11, 0x90, 0x05, 0x19, 0xe5, 0x19, 0xb4, 0x3c, 0xe8, 0xe5, 0x27, 0x24, 0x35, 0xf5} }, +{ 0x0ed3, 16, {0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5} }, +{ 0x0ee3, 16, {0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00} }, +{ 0x0ef3, 16, {0xf0, 0x7f, 0x0c, 0xe4, 0xfd, 0x12, 0x15, 0x3d, 0x7f, 0x10, 0xe5, 0x27, 0x24, 0x33, 0xf5, 0x82} }, +{ 0x0f03, 16, {0xe4, 0x35, 0x26, 0xf5, 0x83, 0xef, 0xf0, 0x12, 0x14, 0xad, 0x90, 0x78, 0x41, 0x74, 0x02, 0xf0} }, +{ 0x0f13, 16, {0x7f, 0x01, 0xe5, 0x27, 0x24, 0x36, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xef, 0xf0, 0x44} }, +{ 0x0f23, 16, {0x06, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x04, 0xf0, 0xe5, 0x27, 0x24, 0x39, 0xf5} }, +{ 0x0f33, 16, {0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x90, 0xc0, 0x00, 0xf0, 0x0f, 0xe4, 0xfd} }, +{ 0x0f43, 16, {0x12, 0x15, 0x3d, 0xe4, 0xff, 0x7e, 0xa3, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x26} }, +{ 0x0f53, 16, {0xf5, 0x83, 0xee, 0xf0, 0xfd, 0x12, 0x15, 0x3d, 0x90, 0x78, 0x41, 0x74, 0x01, 0xf0, 0x90, 0xc0} }, +{ 0x0f63, 16, {0x00, 0xe4, 0xf0, 0x7f, 0x05, 0x7d, 0x7f, 0x12, 0x15, 0x3d, 0x7f, 0x01, 0x12, 0x14, 0x78, 0x7f} }, +{ 0x0f73, 6, {0x03, 0x7d, 0x07, 0x12, 0x15, 0x3d} }, +{ 0x0f79, 1, {0x22} }, +{ 0x0f7a, 16, {0x53, 0x25, 0x3f, 0x90, 0x7b, 0xf1, 0xe0, 0x30, 0xe3, 0x16, 0x7e, 0x7b, 0x7f, 0xc0, 0x75, 0x26} }, +{ 0x0f8a, 16, {0x7b, 0x75, 0x27, 0xc0, 0x90, 0x7f, 0x96, 0x74, 0xef, 0xf0, 0x75, 0x28, 0x01, 0x12, 0x08, 0x80} }, +{ 0x0f9a, 16, {0x90, 0x7c, 0x31, 0xe0, 0x30, 0xe3, 0x16, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x26, 0x7c, 0x75, 0x27} }, +{ 0x0faa, 16, {0x00, 0x90, 0x7f, 0x96, 0x74, 0xdf, 0xf0, 0x75, 0x28, 0x02, 0x12, 0x08, 0x80, 0x90, 0x7c, 0x71} }, +{ 0x0fba, 16, {0xe0, 0x30, 0xe3, 0x16, 0x7e, 0x7c, 0x7f, 0x40, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x40, 0x90, 0x7f} }, +{ 0x0fca, 16, {0x96, 0x74, 0xbf, 0xf0, 0x75, 0x28, 0x04, 0x12, 0x08, 0x80, 0x90, 0x7c, 0xb1, 0xe0, 0x30, 0xe3} }, +{ 0x0fda, 16, {0x16, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x80, 0x90, 0x7f, 0x96, 0x74, 0x7f} }, +{ 0x0fea, 16, {0xf0, 0x75, 0x28, 0x08, 0x12, 0x08, 0x80, 0x05, 0x23, 0xe5, 0x23, 0x54, 0x0f, 0xf5, 0x19, 0x70} }, +{ 0x0ffa, 16, {0x1f, 0x90, 0x78, 0x41, 0xe0, 0x54, 0xf7, 0xf0, 0x90, 0x7f, 0x99, 0xe0, 0xf5, 0x29, 0x90, 0x78} }, +{ 0x100a, 16, {0x41, 0xe0, 0x44, 0x08, 0xf0, 0x90, 0x7f, 0x99, 0xe0, 0xf4, 0xf5, 0x24, 0x12, 0x10, 0xc2, 0x22} }, +{ 0x101a, 16, {0xe5, 0x19, 0xb4, 0x01, 0x04, 0x12, 0x0c, 0x21, 0x22, 0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0x08} }, +{ 0x102a, 11, {0xe5, 0x25, 0x60, 0x04, 0x12, 0x0d, 0xc0, 0x22, 0x12, 0x0c, 0x21} }, +{ 0x1035, 1, {0x22} }, +{ 0x1036, 12, {0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x29, 0x02, 0x10, 0x7d} }, +{ 0x1042, 16, {0x02, 0x11, 0xc8, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} }, +{ 0x1052, 16, {0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} }, +{ 0x1062, 16, {0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} }, +{ 0x1072, 16, {0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x17, 0x67, 0xe4, 0x7e} }, +{ 0x1082, 16, {0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} }, +{ 0x1092, 16, {0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} }, +{ 0x10a2, 16, {0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} }, +{ 0x10b2, 16, {0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} }, +{ 0x10c2, 16, {0x90, 0x7f, 0xd2, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x11, 0x49, 0x90, 0x7b, 0x40, 0xe0, 0x14, 0x60} }, +{ 0x10d2, 16, {0x26, 0x14, 0x60, 0x3b, 0x14, 0x60, 0x50, 0x24, 0x83, 0x60, 0x64, 0x24, 0x80, 0x70, 0x63, 0x7e} }, +{ 0x10e2, 16, {0x7b, 0x7f, 0xc0, 0x75, 0x26, 0x7b, 0x75, 0x27, 0xc0, 0x90, 0x7f, 0x96, 0x74, 0xef, 0xf0, 0x75} }, +{ 0x10f2, 16, {0x28, 0x01, 0x12, 0x00, 0x46, 0x80, 0x4b, 0x7e, 0x7c, 0x7f, 0x00, 0x75, 0x26, 0x7c, 0x75, 0x27} }, +{ 0x1102, 16, {0x00, 0x90, 0x7f, 0x96, 0x74, 0xdf, 0xf0, 0x75, 0x28, 0x02, 0x12, 0x00, 0x46, 0x80, 0x33, 0x7e} }, +{ 0x1112, 16, {0x7c, 0x7f, 0x40, 0x75, 0x26, 0x7c, 0x75, 0x27, 0x40, 0x90, 0x7f, 0x96, 0x74, 0xbf, 0xf0, 0x75} }, +{ 0x1122, 16, {0x28, 0x04, 0x12, 0x00, 0x46, 0x80, 0x1b, 0x7e, 0x7c, 0x7f, 0x80, 0x75, 0x26, 0x7c, 0x75, 0x27} }, +{ 0x1132, 16, {0x80, 0x90, 0x7f, 0x96, 0x74, 0x7f, 0xf0, 0x75, 0x28, 0x08, 0x12, 0x00, 0x46, 0x80, 0x03, 0x12} }, +{ 0x1142, 8, {0x16, 0x56, 0xe4, 0x90, 0x7f, 0xd3, 0xf0, 0x22} }, +{ 0x114a, 16, {0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} }, +{ 0x115a, 9, {0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x1163, 16, {0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} }, +{ 0x1173, 16, {0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} }, +{ 0x1183, 13, {0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x1190, 16, {0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01} }, +{ 0x11a0, 2, {0xf3, 0x22} }, +{ 0x11a2, 16, {0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3} }, +{ 0x11b2, 16, {0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60} }, +{ 0x11c2, 6, {0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf} }, +{ 0x11c8, 16, {0x90, 0x7f, 0xae, 0xe0, 0xff, 0xd3, 0x92, 0x00, 0xe4, 0x33, 0xfe, 0xef, 0x4e, 0xf0, 0xd2, 0xe8} }, +{ 0x11d8, 16, {0x43, 0xd8, 0x20, 0x90, 0x7f, 0xde, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xab} }, +{ 0x11e8, 16, {0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9, 0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x11f8, 16, {0xaf, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xae, 0x74, 0x0d, 0xf0, 0xd2, 0xaf, 0xd2, 0x09, 0x12, 0x17} }, +{ 0x1208, 16, {0x13, 0xc2, 0x01, 0xe4, 0xf5, 0x0e, 0xf5, 0x14, 0xc2, 0x07, 0xc2, 0x02, 0x90, 0x7f, 0xd8, 0xe0} }, +{ 0x1218, 16, {0x65, 0x0b, 0x60, 0x06, 0x75, 0x15, 0x0f, 0xe0, 0xf5, 0x0b, 0x30, 0x02, 0x03, 0x12, 0x0f, 0x7a} }, +{ 0x1228, 16, {0x30, 0x01, 0x07, 0xc2, 0x01, 0x12, 0x06, 0x45, 0x80, 0xe2, 0x30, 0x08, 0xdf, 0xc2, 0x08, 0x12} }, +{ 0x1238, 5, {0x17, 0x95, 0x80, 0xd8, 0x22} }, +{ 0x123d, 16, {0xe5, 0x25, 0x55, 0x28, 0x60, 0x6a, 0xe5, 0x27, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x124d, 16, {0x83, 0xe0, 0x70, 0x5c, 0xe5, 0x28, 0xf4, 0x52, 0x25, 0xe5, 0x27, 0x24, 0x26, 0xff, 0xe4, 0x35} }, +{ 0x125d, 16, {0x26, 0xfe, 0xe4, 0xfd, 0x0f, 0xef, 0xaa, 0x06, 0x70, 0x01, 0x0e, 0x14, 0xf5, 0x82, 0x8a, 0x83} }, +{ 0x126d, 16, {0xe0, 0xfc, 0x74, 0x80, 0x2d, 0xf5, 0x82, 0xe4, 0x34, 0x7b, 0xf5, 0x83, 0xec, 0xf0, 0x0d, 0xbd} }, +{ 0x127d, 16, {0x0b, 0xe2, 0x90, 0x7f, 0xc3, 0x74, 0x0b, 0xf0, 0xe5, 0x27, 0x24, 0x3a, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x128d, 16, {0x26, 0xf5, 0x83, 0x74, 0x10, 0xf0, 0xe5, 0x27, 0x24, 0x2e, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x129d, 16, {0x83, 0xe4, 0xf0, 0xe5, 0x27, 0x24, 0x2f, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe4, 0xf0} }, +{ 0x12ad, 1, {0x22} }, +{ 0x12ae, 16, {0xe4, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xf0, 0xf0, 0x90, 0x7f, 0x96, 0xf0, 0xe4} }, +{ 0x12be, 16, {0x90, 0x78, 0x4a, 0xf0, 0x90, 0x7f, 0x94, 0xf0, 0x90, 0x7f, 0x9d, 0x74, 0xff, 0xf0, 0xe4, 0x90} }, +{ 0x12ce, 16, {0x7f, 0x97, 0xf0, 0xe5, 0x0c, 0x54, 0xf0, 0x44, 0x08, 0x90, 0x78, 0x41, 0xf0, 0xe4, 0x90, 0x7f} }, +{ 0x12de, 16, {0x98, 0xf0, 0x90, 0x7f, 0x95, 0xf0, 0x90, 0x7f, 0x9e, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x7f, 0x98} }, +{ 0x12ee, 16, {0xf0, 0x90, 0x7f, 0x93, 0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xf0, 0xf0, 0xe4, 0x90, 0x7f, 0x96, 0xf0} }, +{ 0x12fe, 8, {0x90, 0x7f, 0x92, 0xe0, 0x54, 0xfd, 0xf0, 0x22} }, +{ 0x1306, 16, {0x8f, 0x1b, 0x05, 0x10, 0xe5, 0x10, 0xae, 0x0f, 0x70, 0x02, 0x05, 0x0f, 0x14, 0xf5, 0x82, 0x8e} }, +{ 0x1316, 16, {0x83, 0xe5, 0x1b, 0xf0, 0x12, 0x17, 0xe5, 0x05, 0x10, 0xe5, 0x10, 0xac, 0x0f, 0x70, 0x02, 0x05} }, +{ 0x1326, 16, {0x0f, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x15, 0x08, 0xe5, 0x08, 0x60, 0x1f, 0xe5, 0x27} }, +{ 0x1336, 16, {0x24, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfe, 0x12} }, +{ 0x1346, 14, {0x17, 0xcd, 0x8f, 0x1b, 0xee, 0x4f, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x80, 0xb5, 0x22} }, +{ 0x1354, 2, {0x8f, 0x19} }, +{ 0x1356, 16, {0xe4, 0xf5, 0x1a, 0x75, 0x1b, 0xff, 0x75, 0x1c, 0x19, 0x75, 0x1d, 0x86, 0xab, 0x1b, 0xaa, 0x1c} }, +{ 0x1366, 16, {0xa9, 0x1d, 0x90, 0x00, 0x01, 0x12, 0x11, 0x63, 0xb4, 0x03, 0x1d, 0xaf, 0x1a, 0x05, 0x1a, 0xef} }, +{ 0x1376, 16, {0xb5, 0x19, 0x01, 0x22, 0x12, 0x11, 0x4a, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} }, +{ 0x1386, 14, {0x1b, 0xff, 0xf5, 0x1c, 0x89, 0x1d, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x1394, 1, {0x22} }, +{ 0x1395, 16, {0xe4, 0x90, 0x78, 0x41, 0xf0, 0x90, 0x78, 0x4f, 0x74, 0xc0, 0xf0, 0xe4, 0x90, 0x78, 0x50, 0xf0} }, +{ 0x13a5, 16, {0xe5, 0x0f, 0x90, 0x78, 0x51, 0xf0, 0xae, 0x0f, 0xe5, 0x10, 0x90, 0x78, 0x52, 0xf0, 0x90, 0x78} }, +{ 0x13b5, 16, {0x54, 0xe5, 0x08, 0xf0, 0x90, 0x78, 0x57, 0x74, 0x04, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x10} }, +{ 0x13c5, 16, {0xf0, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0x90, 0x78, 0x55, 0xf0, 0x90, 0x78, 0x55, 0xe0, 0x60, 0xfa} }, +{ 0x13d5, 1, {0x22} }, +{ 0x13d6, 16, {0xe4, 0x90, 0x78, 0x41, 0xf0, 0xe5, 0x0f, 0x90, 0x78, 0x4f, 0xf0, 0xae, 0x0f, 0xe5, 0x10, 0x90} }, +{ 0x13e6, 16, {0x78, 0x50, 0xf0, 0x90, 0x78, 0x51, 0x74, 0xc0, 0xf0, 0xe4, 0x90, 0x78, 0x52, 0xf0, 0x90, 0x78} }, +{ 0x13f6, 16, {0x54, 0xe5, 0x08, 0xf0, 0x90, 0x78, 0x57, 0x74, 0x04, 0xf0, 0xe4, 0x90, 0x78, 0x55, 0xf0, 0x90} }, +{ 0x1406, 6, {0x78, 0x55, 0xe0, 0x60, 0xfa, 0x22} }, +{ 0x140c, 16, {0xe5, 0x27, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x14, 0x60, 0x0f, 0x14} }, +{ 0x141c, 16, {0x60, 0x13, 0x14, 0x60, 0x17, 0x80, 0x00, 0x90, 0x7f, 0xc7, 0xef, 0xf0, 0x80, 0x13, 0x90, 0x7f} }, +{ 0x142c, 16, {0xc9, 0xef, 0xf0, 0x80, 0x0c, 0x90, 0x7f, 0xcb, 0xef, 0xf0, 0x80, 0x05, 0x90, 0x7f, 0xcd, 0xef} }, +{ 0x143c, 6, {0xf0, 0xe5, 0x28, 0x42, 0x0d, 0x22} }, +{ 0x1442, 16, {0xe5, 0x27, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x14, 0x60, 0x0f, 0x14} }, +{ 0x1452, 16, {0x60, 0x13, 0x14, 0x60, 0x17, 0x80, 0x00, 0x90, 0x7f, 0xb7, 0xef, 0xf0, 0x80, 0x13, 0x90, 0x7f} }, +{ 0x1462, 16, {0xb9, 0xef, 0xf0, 0x80, 0x0c, 0x90, 0x7f, 0xbb, 0xef, 0xf0, 0x80, 0x05, 0x90, 0x7f, 0xbd, 0xef} }, +{ 0x1472, 6, {0xf0, 0xe5, 0x28, 0x42, 0x0d, 0x22} }, +{ 0x1478, 16, {0xae, 0x07, 0xe4, 0xff, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0} }, +{ 0x1488, 16, {0x54, 0x7f, 0xfd, 0x12, 0x15, 0x3d, 0x90, 0x78, 0x41, 0x74, 0x01, 0xf0, 0x90, 0xc0, 0x00, 0xee} }, +{ 0x1498, 16, {0xf0, 0xe4, 0xe5, 0x27, 0x24, 0x32, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x44, 0x80} }, +{ 0x14a8, 5, {0xfd, 0x12, 0x15, 0x3d, 0x22} }, +{ 0x14ad, 16, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x78, 0x41, 0x74} }, +{ 0x14bd, 16, {0x02, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24} }, +{ 0x14cd, 16, {0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} }, +{ 0x14dd, 16, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x78, 0x41, 0x74} }, +{ 0x14ed, 16, {0x04, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24} }, +{ 0x14fd, 16, {0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} }, +{ 0x150d, 16, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0x90, 0x78, 0x41, 0x74} }, +{ 0x151d, 16, {0x06, 0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24} }, +{ 0x152d, 16, {0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} }, +{ 0x153d, 16, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5} }, +{ 0x154d, 16, {0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x07, 0xf0, 0x90, 0xc0} }, +{ 0x155d, 15, {0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x05, 0xf0, 0x90, 0xc0, 0x00, 0xed, 0xf0, 0x22} }, +{ 0x156c, 16, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00, 0x74, 0xbf, 0xf0, 0xe4, 0x90, 0x78, 0x41} }, +{ 0x157c, 16, {0xf0, 0x90, 0xc0, 0x00, 0xef, 0xf0, 0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0xe5, 0x27, 0x24, 0x37} }, +{ 0x158c, 15, {0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x54, 0x7f, 0x90, 0xc0, 0x00, 0xf0, 0x22} }, +{ 0x159b, 16, {0xe5, 0x27, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x14, 0x60, 0x0e, 0x14} }, +{ 0x15ab, 16, {0x60, 0x11, 0x14, 0x60, 0x14, 0x80, 0x00, 0x90, 0x7f, 0xc6, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xc8} }, +{ 0x15bb, 15, {0xe0, 0xff, 0x22, 0x90, 0x7f, 0xca, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xcc, 0xe0, 0xff, 0x22} }, +{ 0x15ca, 16, {0xe5, 0x27, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x14, 0x60, 0x0e, 0x14} }, +{ 0x15da, 16, {0x60, 0x11, 0x14, 0x60, 0x14, 0x80, 0x00, 0x90, 0x7f, 0xb6, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xb8} }, +{ 0x15ea, 15, {0xe0, 0xff, 0x22, 0x90, 0x7f, 0xba, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xbc, 0xe0, 0xff, 0x22} }, +{ 0x15f9, 16, {0xe5, 0x27, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x14, 0x60, 0x0e, 0x14} }, +{ 0x1609, 16, {0x60, 0x11, 0x14, 0x60, 0x14, 0x80, 0x00, 0x90, 0x7f, 0xc7, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xc9} }, +{ 0x1619, 15, {0xe0, 0xff, 0x22, 0x90, 0x7f, 0xcb, 0xe0, 0xff, 0x22, 0x90, 0x7f, 0xcd, 0xe0, 0xff, 0x22} }, +{ 0x1628, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x30} }, +{ 0x1638, 16, {0x05, 0x04, 0xc2, 0x05, 0x80, 0x02, 0xd2, 0x08, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} }, +{ 0x1648, 14, {0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x1656, 16, {0x90, 0x7b, 0x41, 0xe0, 0xf5, 0x17, 0x43, 0x25, 0x10, 0xa3, 0xe0, 0x60, 0x09, 0x90, 0x7f, 0xd7} }, +{ 0x1666, 16, {0x74, 0x17, 0xf0, 0x74, 0x37, 0xf0, 0x90, 0x7b, 0x43, 0xe0, 0xf5, 0x18, 0x30, 0x00, 0x07, 0xa3} }, +{ 0x1676, 10, {0xe0, 0x54, 0xf0, 0xf5, 0x0c, 0x22, 0xe4, 0xf5, 0x0c, 0x22} }, +{ 0x1680, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0x90} }, +{ 0x1690, 16, {0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x04, 0xf0, 0xd0, 0x86, 0xd0} }, +{ 0x16a0, 10, {0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x16aa, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xd2} }, +{ 0x16ba, 16, {0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85} }, +{ 0x16ca, 7, {0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x16d1, 16, {0x12, 0x17, 0xb5, 0xae, 0x07, 0x12, 0x17, 0xb5, 0xad, 0x07, 0xee, 0x6d, 0x60, 0x10, 0x12, 0x17} }, +{ 0x16e1, 16, {0xb5, 0xae, 0x07, 0xee, 0x6d, 0x60, 0x07, 0x12, 0x17, 0xb5, 0xad, 0x07, 0x80, 0xec, 0xaf, 0x06} }, +{ 0x16f1, 1, {0x22} }, +{ 0x16f2, 16, {0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} }, +{ 0x1702, 1, {0x22} }, +{ 0x1703, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} }, +{ 0x1713, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x04, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x09, 0x04, 0xe0, 0x44} }, +{ 0x1723, 16, {0x02, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x17, 0x34, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} }, +{ 0x1733, 1, {0x22} }, +{ 0x1734, 16, {0x8e, 0x19, 0x8f, 0x1a, 0xe5, 0x1a, 0x15, 0x1a, 0xae, 0x19, 0x70, 0x02, 0x15, 0x19, 0x4e, 0x60} }, +{ 0x1744, 10, {0x08, 0x12, 0x16, 0xf2, 0x12, 0x16, 0xf2, 0x80, 0xeb, 0x22} }, +{ 0x174e, 16, {0xe5, 0x27, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x26, 0xf5, 0x83, 0xe0, 0x04, 0xff, 0x44, 0x10} }, +{ 0x175e, 9, {0x90, 0x7f, 0xd7, 0xf0, 0xef, 0x44, 0x30, 0xf0, 0x22} }, +{ 0x1767, 16, {0x03, 0x16, 0x80, 0x00, 0x00, 0x03, 0x11, 0x81, 0x00, 0x00, 0xc1, 0x85, 0xc1, 0x81, 0xc1, 0x08} }, +{ 0x1777, 7, {0xc1, 0x00, 0xc1, 0x86, 0x01, 0x09, 0x00} }, +{ 0x177e, 1, {0x00} }, +{ 0x177f, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x0d, 0x7e, 0x00, 0x12, 0x17, 0x34, 0x90, 0x7f} }, +{ 0x178f, 6, {0xd6, 0xe0, 0x54, 0xfe, 0xf0, 0x22} }, +{ 0x1795, 16, {0x12, 0x12, 0xae, 0x12, 0x17, 0x03, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x03, 0x12, 0x17, 0x7f} }, +{ 0x17a5, 4, {0x12, 0x0a, 0x6a, 0x22} }, +{ 0x17a9, 12, {0x90, 0x78, 0x41, 0x74, 0x02, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x17b5, 12, {0x90, 0x78, 0x41, 0x74, 0x03, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x17c1, 12, {0x90, 0x78, 0x41, 0x74, 0x04, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x17cd, 12, {0x90, 0x78, 0x41, 0x74, 0x05, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x17d9, 12, {0x90, 0x78, 0x41, 0x74, 0x06, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x17e5, 11, {0xe4, 0x90, 0x78, 0x41, 0xf0, 0x90, 0xc0, 0x00, 0xe0, 0xff, 0x22} }, +{ 0x17f0, 4, {0x53, 0xd8, 0xef, 0x32} }, +{ 0x1800, 15, {0x02, 0x16, 0xaa, 0x00, 0x02, 0x18, 0x04, 0x00, 0x02, 0x16, 0x80, 0x00, 0x02, 0x16, 0x28} }, +{ 0x1900, 16, {0x12, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x40, 0xcd, 0x06, 0x0a, 0x01, 0x00, 0x00, 0x01, 0x02} }, +{ 0x1910, 16, {0x00, 0x04, 0x09, 0x02, 0x74, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x32, 0x09, 0x04, 0x00, 0x00, 0x0e} }, +{ 0x1920, 16, {0xff, 0x00, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} }, +{ 0x1930, 16, {0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00} }, +{ 0x1940, 16, {0x07, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x06, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} }, +{ 0x1950, 16, {0x07, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x82, 0x02} }, +{ 0x1960, 16, {0x40, 0x00, 0x01, 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x84, 0x02, 0x40, 0x00} }, +{ 0x1970, 16, {0x01, 0x07, 0x05, 0x85, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x86, 0x02, 0x40, 0x00, 0x01, 0x07} }, +{ 0x1980, 16, {0x05, 0x87, 0x02, 0x40, 0x00, 0x01, 0x04, 0x03, 0x09, 0x04, 0x48, 0x03, 0x4b, 0x00, 0x65, 0x00} }, +{ 0x1990, 16, {0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x61, 0x00} }, +{ 0x19a0, 16, {0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00} }, +{ 0x19b0, 16, {0x6e, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x6e, 0x00} }, +{ 0x19c0, 16, {0x6f, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} }, +{ 0x19d0, 16, {0x2e, 0x00, 0x36, 0x03, 0x4b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00} }, +{ 0x19e0, 16, {0x6e, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00} }, +{ 0x19f0, 16, {0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x61, 0x00} }, +{ 0x1a00, 10, {0x70, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00} }, +{ 0xffff, 0, {0x00} } +}; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c new file mode 100644 index 000000000000..490f6226cfc0 --- /dev/null +++ b/drivers/usb/serial/mct_u232.c @@ -0,0 +1,774 @@ +/* + * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver + * + * Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is largely derived from the Belkin USB Serial Adapter Driver + * (see belkin_sa.[ch]). All of the information about the device was acquired + * by using SniffUSB on Windows98. For technical details see mct_u232.h. + * + * William G. Greathouse and Greg Kroah-Hartman provided great help on how to + * do the reverse engineering and how to write a USB serial device driver. + * + * TO BE DONE, TO BE CHECKED: + * DTR/RTS signal handling may be incomplete or incorrect. I have mainly + * implemented what I have seen with SniffUSB or found in belkin_sa.c. + * For further TODOs check also belkin_sa.c. + * + * TEST STATUS: + * Basic tests have been performed with minicom/zmodem transfers and + * modem dialing under Linux 2.4.0-test10 (for me it works fine). + * + * 29-Nov-2000 Greg Kroah-Hartman + * - Added device id table to fit with 2.4.0-test11 structure. + * - took out DEAL_WITH_TWO_INT_IN_ENDPOINTS #define as it's not needed + * (lots of things will change if/when the usb-serial core changes to + * handle these issues. + * + * 27-Nov-2000 Wolfgang Grandegger + * A version for kernel 2.4.0-test10 released to the Linux community + * (via linux-usb-devel). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +#include "usb-serial.h" +#include "mct_u232.h" + + +/* + * Some not properly written applications do not handle the return code of + * write() correctly. This can result in character losses. A work-a-round + * can be compiled in with the following definition. This work-a-round + * should _NOT_ be part of an 'official' kernel release, of course! + */ +#undef FIX_WRITE_RETURN_CODE_PROBLEM +#ifdef FIX_WRITE_RETURN_CODE_PROBLEM +static int write_blocking = 0; /* disabled by default */ +#endif + +/* + * Function prototypes + */ +static int mct_u232_startup (struct usb_serial *serial); +static void mct_u232_shutdown (struct usb_serial *serial); +static int mct_u232_open (struct usb_serial_port *port, + struct file *filp); +static void mct_u232_close (struct usb_serial_port *port, + struct file *filp); +#ifdef FIX_WRITE_RETURN_CODE_PROBLEM +static int mct_u232_write (struct usb_serial_port *port, + int from_user, + const unsigned char *buf, + int count); +static void mct_u232_write_bulk_callback (struct urb *urb); +#endif +static void mct_u232_read_int_callback (struct urb *urb); +static void mct_u232_set_termios (struct usb_serial_port *port, + struct termios * old); +static int mct_u232_ioctl (struct usb_serial_port *port, + struct file * file, + unsigned int cmd, + unsigned long arg); +static void mct_u232_break_ctl (struct usb_serial_port *port, + int break_state ); + +/* + * All of the device info needed for the MCT USB-RS232 converter. + */ +static __devinitdata struct usb_device_id id_table [] = { + { idVendor: MCT_U232_VID, idProduct: MCT_U232_PID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table); + + +struct usb_serial_device_type mct_u232_device = { + name: "Magic Control Technology USB-RS232", + id_table: id_table, + needs_interrupt_in: MUST_HAVE, /* 2 interrupt-in endpoints */ + needs_bulk_in: MUST_HAVE_NOT, /* no bulk-in endpoint */ + needs_bulk_out: MUST_HAVE, /* 1 bulk-out endpoint */ + num_interrupt_in: 2, + num_bulk_in: 0, + num_bulk_out: 1, + num_ports: 1, + open: mct_u232_open, + close: mct_u232_close, +#ifdef FIX_WRITE_RETURN_CODE_PROBLEM + write: mct_u232_write, + write_bulk_callback: mct_u232_write_bulk_callback, +#endif + read_int_callback: mct_u232_read_int_callback, + ioctl: mct_u232_ioctl, + set_termios: mct_u232_set_termios, + break_ctl: mct_u232_break_ctl, + startup: mct_u232_startup, + shutdown: mct_u232_shutdown, +}; + +struct mct_u232_private { + unsigned long control_state; /* Modem Line Setting (TIOCM) */ + unsigned char last_lcr; /* Line Control Register */ + unsigned char last_lsr; /* Line Status Register */ + unsigned char last_msr; /* Modem Status Register */ +}; + +/* + * Handle vendor specific USB requests + */ + +#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ + +static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) +{ + unsigned int divisor; + int rc; + divisor = MCT_U232_BAUD_RATE(value); + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + MCT_U232_SET_BAUD_RATE_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE, + WDR_TIMEOUT); + if (rc < 0) + err("Set BAUD RATE %d failed (error = %d)", value, rc); + dbg("set_baud_rate: 0x%x", divisor); + return rc; +} /* mct_u232_set_baud_rate */ + +static int mct_u232_set_line_ctrl(struct usb_serial *serial, unsigned char lcr) +{ + int rc; + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + MCT_U232_SET_LINE_CTRL_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &lcr, MCT_U232_SET_LINE_CTRL_SIZE, + WDR_TIMEOUT); + if (rc < 0) + err("Set LINE CTRL 0x%x failed (error = %d)", lcr, rc); + dbg("set_line_ctrl: 0x%x", lcr); + return rc; +} /* mct_u232_set_line_ctrl */ + +static int mct_u232_set_modem_ctrl(struct usb_serial *serial, + unsigned long control_state) +{ + int rc; + unsigned char mcr = MCT_U232_MCR_NONE; + + if (control_state & TIOCM_DTR) + mcr |= MCT_U232_MCR_DTR; + if (control_state & TIOCM_RTS) + mcr |= MCT_U232_MCR_RTS; + + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + MCT_U232_SET_MODEM_CTRL_REQUEST, + MCT_U232_SET_REQUEST_TYPE, + 0, 0, &mcr, MCT_U232_SET_MODEM_CTRL_SIZE, + WDR_TIMEOUT); + if (rc < 0) + err("Set MODEM CTRL 0x%x failed (error = %d)", mcr, rc); + dbg("set_modem_ctrl: state=0x%lx ==> mcr=0x%x", control_state, mcr); + + return rc; +} /* mct_u232_set_modem_ctrl */ + +static int mct_u232_get_modem_stat(struct usb_serial *serial, unsigned char *msr) +{ + int rc; + rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + MCT_U232_GET_MODEM_STAT_REQUEST, + MCT_U232_GET_REQUEST_TYPE, + 0, 0, msr, MCT_U232_GET_MODEM_STAT_SIZE, + WDR_TIMEOUT); + if (rc < 0) { + err("Get MODEM STATus failed (error = %d)", rc); + *msr = 0; + } + dbg("get_modem_stat: 0x%x", *msr); + return rc; +} /* mct_u232_get_modem_stat */ + +static void mct_u232_msr_to_state(unsigned long *control_state, unsigned char msr) +{ + /* Translate Control Line states */ + if (msr & MCT_U232_MSR_DSR) + *control_state |= TIOCM_DSR; + else + *control_state &= ~TIOCM_DSR; + if (msr & MCT_U232_MSR_CTS) + *control_state |= TIOCM_CTS; + else + *control_state &= ~TIOCM_CTS; + if (msr & MCT_U232_MSR_RI) + *control_state |= TIOCM_RI; + else + *control_state &= ~TIOCM_RI; + if (msr & MCT_U232_MSR_CD) + *control_state |= TIOCM_CD; + else + *control_state &= ~TIOCM_CD; + dbg("msr_to_state: msr=0x%x ==> state=0x%lx", msr, *control_state); +} /* mct_u232_msr_to_state */ + +/* + * Driver's tty interface functions + */ + +static int mct_u232_startup (struct usb_serial *serial) +{ + struct mct_u232_private *priv; + + /* allocate the private data structure */ + serial->port->private = kmalloc(sizeof(struct mct_u232_private), + GFP_KERNEL); + if (!serial->port->private) + return (-1); /* error */ + priv = (struct mct_u232_private *)serial->port->private; + /* set initial values for control structures */ + priv->control_state = 0; + priv->last_lsr = 0; + priv->last_msr = 0; + + init_waitqueue_head(&serial->port->write_wait); + + return (0); +} /* mct_u232_startup */ + + +static void mct_u232_shutdown (struct usb_serial *serial) +{ + int i; + + dbg (__FUNCTION__); + + /* stop reads and writes on all ports */ + for (i=0; i < serial->num_ports; ++i) { + while (serial->port[i].open_count > 0) { + mct_u232_close (&serial->port[i], NULL); + } + /* My special items, the standard routines free my urbs */ + if (serial->port->private) + kfree(serial->port->private); + } +} /* mct_u232_shutdown */ + +static int mct_u232_open (struct usb_serial_port *port, struct file *filp) +{ + unsigned long flags; + struct usb_serial *serial = port->serial; + struct mct_u232_private *priv = (struct mct_u232_private *)port->private; + + dbg(__FUNCTION__" port %d", port->number); + + spin_lock_irqsave (&port->port_lock, flags); + + ++port->open_count; + MOD_INC_USE_COUNT; + + if (!port->active) { + port->active = 1; + + /* Do a defined restart: the normal serial device seems to + * always turn on DTR and RTS here, so do the same. I'm not + * sure if this is really necessary. But it should not harm + * either. + */ + if (port->tty->termios->c_cflag & CBAUD) + priv->control_state = TIOCM_DTR | TIOCM_RTS; + else + priv->control_state = 0; + mct_u232_set_modem_ctrl(serial, priv->control_state); + + priv->last_lcr = (MCT_U232_DATA_BITS_8 | + MCT_U232_PARITY_NONE | + MCT_U232_STOP_BITS_1); + mct_u232_set_line_ctrl(serial, priv->last_lcr); + + /* Read modem status and update control state */ + mct_u232_get_modem_stat(serial, &priv->last_msr); + mct_u232_msr_to_state(&priv->control_state, priv->last_msr); + + { + /* Puh, that's dirty */ + struct usb_serial_port *rport; + rport = &serial->port[1]; + rport->tty = port->tty; + rport->private = port->private; + port->read_urb = rport->interrupt_in_urb; + } + + port->read_urb->dev = port->serial->dev; + if (usb_submit_urb(port->read_urb)) + err("usb_submit_urb(read bulk) failed"); + + port->interrupt_in_urb->dev = port->serial->dev; + if (usb_submit_urb(port->interrupt_in_urb)) + err(" usb_submit_urb(read int) failed"); + + } + + spin_unlock_irqrestore (&port->port_lock, flags); + + return 0; +} /* mct_u232_open */ + + +static void mct_u232_close (struct usb_serial_port *port, struct file *filp) +{ + unsigned long flags; + + dbg(__FUNCTION__" port %d", port->number); + + spin_lock_irqsave (&port->port_lock, flags); + + --port->open_count; + MOD_DEC_USE_COUNT; + + if (port->open_count <= 0) { + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + /* wgg - do I need this? I think so. */ + usb_unlink_urb (port->interrupt_in_urb); + port->active = 0; + } + + spin_unlock_irqrestore (&port->port_lock, flags); + +} /* mct_u232_close */ + + +#ifdef FIX_WRITE_RETURN_CODE_PROBLEM +/* The generic routines work fine otherwise */ + +static int mct_u232_write (struct usb_serial_port *port, int from_user, + const unsigned char *buf, int count) +{ + struct usb_serial *serial = port->serial; + unsigned long flags; + int result, bytes_sent, size; + + dbg(__FUNCTION__ " - port %d", port->number); + + if (count == 0) { + dbg(__FUNCTION__ " - write request of 0 bytes"); + return (0); + } + + /* only do something if we have a bulk out endpoint */ + if (!serial->num_bulk_out) + return(0);; + + /* another write is still pending? */ + if (port->write_urb->status == -EINPROGRESS) { + dbg (__FUNCTION__ " - already writing"); + return (0); + } + + bytes_sent = 0; + while (count > 0) { + + spin_lock_irqsave (&port->port_lock, flags); + + size = (count > port->bulk_out_size) ? port->bulk_out_size : count; + + usb_serial_debug_data (__FILE__, __FUNCTION__, size, buf); + + if (from_user) { + copy_from_user(port->write_urb->transfer_buffer, buf, size); + } + else { + memcpy (port->write_urb->transfer_buffer, buf, size); + } + + /* set up our urb */ + FILL_BULK_URB(port->write_urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, size, + ((serial->type->write_bulk_callback) ? + serial->type->write_bulk_callback : + mct_u232_write_bulk_callback), + port); + + /* send the data out the bulk port */ + result = usb_submit_urb(port->write_urb); + if (result) { + err(__FUNCTION__ + " - failed submitting write urb, error %d", result); + spin_unlock_irqrestore (&port->port_lock, flags); + return bytes_sent; + } + + spin_unlock_irqrestore (&port->port_lock, flags); + + bytes_sent += size; + if (write_blocking) + interruptible_sleep_on(&port->write_wait); + else + break; + + buf += size; + count -= size; + } + + return bytes_sent; +} /* mct_u232_write */ + +static void mct_u232_write_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = port->serial; + struct tty_struct *tty = port->tty; + + dbg(__FUNCTION__ " - port %d", port->number); + + if (!serial) { + dbg(__FUNCTION__ " - bad serial pointer, exiting"); + return; + } + + if (urb->status) { + dbg(__FUNCTION__ " - nonzero write bulk status received: %d", + urb->status); + return; + } + + if (write_blocking) { + wake_up_interruptible(&port->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + + } else { + /* from generic_write_bulk_callback */ + queue_task(&port->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + return; +} /* mct_u232_write_bulk_callback */ +#endif + +static void mct_u232_read_int_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct mct_u232_private *priv = (struct mct_u232_private *)port->private; + struct usb_serial *serial = port->serial; + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + + dbg(__FUNCTION__ " - port %d", port->number); + + /* The urb might have been killed. */ + if (urb->status) { + dbg(__FUNCTION__ " - nonzero read bulk status received: %d", + urb->status); + return; + } + if (!serial) { + dbg(__FUNCTION__ " - bad serial pointer, exiting"); + return; + } + + usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); + + /* + * Work-a-round: handle the 'usual' bulk-in pipe here + */ + if (urb->transfer_buffer_length > 2) { + int i; + tty = port->tty; + if (urb->actual_length) { + for (i = 0; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + /* INT urbs are automatically re-submitted */ + return; + } + + /* + * The interrupt-in pipe signals exceptional conditions (modem line + * signal changes and errors). data[0] holds MSR, data[1] holds LSR. + */ + priv->last_msr = data[MCT_U232_MSR_INDEX]; + + /* Record Control Line states */ + mct_u232_msr_to_state(&priv->control_state, priv->last_msr); + +#if 0 + /* Not yet handled. See belin_sa.c for further information */ + /* Now to report any errors */ + priv->last_lsr = data[MCT_U232_LSR_INDEX]; + /* + * fill in the flip buffer here, but I do not know the relation + * to the current/next receive buffer or characters. I need + * to look in to this before committing any code. + */ + if (priv->last_lsr & MCT_U232_LSR_ERR) { + tty = port->tty; + /* Overrun Error */ + if (priv->last_lsr & MCT_U232_LSR_OE) { + } + /* Parity Error */ + if (priv->last_lsr & MCT_U232_LSR_PE) { + } + /* Framing Error */ + if (priv->last_lsr & MCT_U232_LSR_FE) { + } + /* Break Indicator */ + if (priv->last_lsr & MCT_U232_LSR_BI) { + } + } +#endif + + /* INT urbs are automatically re-submitted */ +} /* mct_u232_read_int_callback */ + + +static void mct_u232_set_termios (struct usb_serial_port *port, + struct termios *old_termios) +{ + struct usb_serial *serial = port->serial; + struct mct_u232_private *priv = (struct mct_u232_private *)port->private; + unsigned int iflag = port->tty->termios->c_iflag; + unsigned int old_iflag = old_termios->c_iflag; + unsigned int cflag = port->tty->termios->c_cflag; + unsigned int old_cflag = old_termios->c_cflag; + + /* + * Update baud rate + */ + if( (cflag & CBAUD) != (old_cflag & CBAUD) ) { + /* reassert DTR and (maybe) RTS on transition from B0 */ + if( (old_cflag & CBAUD) == B0 ) { + dbg(__FUNCTION__ ": baud was B0"); + priv->control_state |= TIOCM_DTR; + /* don't set RTS if using hardware flow control */ + if (!(old_cflag & CRTSCTS)) { + priv->control_state |= TIOCM_RTS; + } + mct_u232_set_modem_ctrl(serial, priv->control_state); + } + + switch(cflag & CBAUD) { + case B0: /* handled below */ + break; + case B300: mct_u232_set_baud_rate(serial, 300); + break; + case B600: mct_u232_set_baud_rate(serial, 600); + break; + case B1200: mct_u232_set_baud_rate(serial, 1200); + break; + case B2400: mct_u232_set_baud_rate(serial, 2400); + break; + case B4800: mct_u232_set_baud_rate(serial, 4800); + break; + case B9600: mct_u232_set_baud_rate(serial, 9600); + break; + case B19200: mct_u232_set_baud_rate(serial, 19200); + break; + case B38400: mct_u232_set_baud_rate(serial, 38400); + break; + case B57600: mct_u232_set_baud_rate(serial, 57600); + break; + case B115200: mct_u232_set_baud_rate(serial, 115200); + break; + default: err("MCT USB-RS232 converter: unsupported baudrate request, using default of 9600"); + mct_u232_set_baud_rate(serial, 9600); break; + } + if ((cflag & CBAUD) == B0 ) { + dbg(__FUNCTION__ ": baud is B0"); + /* Drop RTS and DTR */ + priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); + mct_u232_set_modem_ctrl(serial, priv->control_state); + } + } + + /* + * Update line control register (LCR) + */ + if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD)) + || (cflag & CSIZE) != (old_cflag & CSIZE) + || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) { + + + priv->last_lcr = 0; + + /* set the parity */ + if (cflag & PARENB) + priv->last_lcr |= (cflag & PARODD) ? + MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; + else + priv->last_lcr |= MCT_U232_PARITY_NONE; + + /* set the number of data bits */ + switch (cflag & CSIZE) { + case CS5: + priv->last_lcr |= MCT_U232_DATA_BITS_5; break; + case CS6: + priv->last_lcr |= MCT_U232_DATA_BITS_6; break; + case CS7: + priv->last_lcr |= MCT_U232_DATA_BITS_7; break; + case CS8: + priv->last_lcr |= MCT_U232_DATA_BITS_8; break; + default: + err("CSIZE was not CS5-CS8, using default of 8"); + priv->last_lcr |= MCT_U232_DATA_BITS_8; + break; + } + + /* set the number of stop bits */ + priv->last_lcr |= (cflag & CSTOPB) ? + MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; + + mct_u232_set_line_ctrl(serial, priv->last_lcr); + } + + /* + * Set flow control: well, I do not really now how to handle DTR/RTS. + * Just do what we have seen with SniffUSB on Win98. + */ + if( (iflag & IXOFF) != (old_iflag & IXOFF) + || (iflag & IXON) != (old_iflag & IXON) + || (cflag & CRTSCTS) != (old_cflag & CRTSCTS) ) { + + /* Drop DTR/RTS if no flow control otherwise assert */ + if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) ) + priv->control_state |= TIOCM_DTR | TIOCM_RTS; + else + priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); + mct_u232_set_modem_ctrl(serial, priv->control_state); + } +} /* mct_u232_set_termios */ + + +static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) +{ + struct usb_serial *serial = port->serial; + struct mct_u232_private *priv = (struct mct_u232_private *)port->private; + unsigned char lcr = priv->last_lcr; + + dbg (__FUNCTION__ "state=%d", break_state); + + if (break_state) + lcr |= MCT_U232_SET_BREAK; + + mct_u232_set_line_ctrl(serial, lcr); +} /* mct_u232_break_ctl */ + + +static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct usb_serial *serial = port->serial; + struct mct_u232_private *priv = (struct mct_u232_private *)port->private; + int ret, mask; + + dbg (__FUNCTION__ "cmd=0x%x", cmd); + + /* Based on code from acm.c and others */ + switch (cmd) { + case TIOCMGET: + return put_user(priv->control_state, (unsigned long *) arg); + break; + + case TIOCMSET: /* Turns on and off the lines as specified by the mask */ + case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ + case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ + if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + + if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) { + /* RTS needs set */ + if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) || + (cmd == TIOCMBIS) ) + priv->control_state |= TIOCM_RTS; + else + priv->control_state &= ~TIOCM_RTS; + } + + if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) { + /* DTR needs set */ + if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) || + (cmd == TIOCMBIS) ) + priv->control_state |= TIOCM_DTR; + else + priv->control_state &= ~TIOCM_DTR; + } + mct_u232_set_modem_ctrl(serial, priv->control_state); + break; + + case TIOCMIWAIT: + /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ + /* TODO */ + return( 0 ); + + case TIOCGICOUNT: + /* return count of modemline transitions */ + /* TODO */ + return 0; + + default: + dbg(__FUNCTION__ ": arg not supported - 0x%04x",cmd); + return(-ENOIOCTLCMD); + break; + } + return 0; +} /* mct_u232_ioctl */ + + +static int __init mct_u232_init (void) +{ + usb_serial_register (&mct_u232_device); + + return 0; +} + + +static void __exit mct_u232_exit (void) +{ + usb_serial_deregister (&mct_u232_device); +} + + +module_init (mct_u232_init); +module_exit(mct_u232_exit); + +MODULE_AUTHOR("Wolfgang Grandegger "); +MODULE_DESCRIPTION("Magic Control Technology USB-RS232 converter driver"); + +#ifdef FIX_WRITE_RETURN_CODE_PROBLEM +MODULE_PARM(write_blocking, "i"); +MODULE_PARM_DESC(write_blocking, + "The write function will block to write out all data"); +#endif diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h new file mode 100644 index 000000000000..d96dd61649ac --- /dev/null +++ b/drivers/usb/serial/mct_u232.h @@ -0,0 +1,362 @@ +/* + * Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver + * + * Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is for the device MCT USB-RS232 Converter (25 pin, Model No. + * U232-P25) from Magic Control Technology Corp. (there is also a 9 pin + * Model No. U232-P9). See http://www.mct.com.tw/p_u232.html for further + * information. The properties of this device are listed at the end of this + * file. This device is available from various distributors. I know Hana, + * http://www.hana.de and D-Link, http://www.dlink.com/products/usb/dsbs25. + * + * All of the information about the device was acquired by using SniffUSB + * on Windows98. The technical details of the reverse engineering are + * summarized at the end of this file. + */ + +#ifndef __LINUX_USB_SERIAL_MCT_U232_H +#define __LINUX_USB_SERIAL_MCT_U232_H + +#define MCT_U232_VID 0x0711 /* Vendor Id */ +#define MCT_U232_PID 0x0210 /* Product Id */ + +/* + * Vendor Request Interface + */ +#define MCT_U232_SET_REQUEST_TYPE 0x40 +#define MCT_U232_GET_REQUEST_TYPE 0xc0 + +#define MCT_U232_GET_MODEM_STAT_REQUEST 2 /* Get Modem Status Register (MSR) */ +#define MCT_U232_GET_MODEM_STAT_SIZE 1 + +#define MCT_U232_GET_LINE_CTRL_REQUEST 6 /* Get Line Control Register (LCR) */ +#define MCT_U232_GET_LINE_CTRL_SIZE 1 /* ... not used by this driver */ + +#define MCT_U232_SET_BAUD_RATE_REQUEST 5 /* Set Baud Rate Divisor */ +#define MCT_U232_SET_BAUD_RATE_SIZE 4 + +#define MCT_U232_SET_LINE_CTRL_REQUEST 7 /* Set Line Control Register (LCR) */ +#define MCT_U232_SET_LINE_CTRL_SIZE 1 + +#define MCT_U232_SET_MODEM_CTRL_REQUEST 10 /* Set Modem Control Register (MCR) */ +#define MCT_U232_SET_MODEM_CTRL_SIZE 1 + +/* + * Baud rate (divisor) + */ +#define MCT_U232_BAUD_RATE(b) (115200/b) + +/* + * Line Control Register (LCR) + */ +#define MCT_U232_SET_BREAK 0x40 + +#define MCT_U232_PARITY_SPACE 0x38 +#define MCT_U232_PARITY_MARK 0x28 +#define MCT_U232_PARITY_EVEN 0x18 +#define MCT_U232_PARITY_ODD 0x08 +#define MCT_U232_PARITY_NONE 0x00 + +#define MCT_U232_DATA_BITS_5 0x00 +#define MCT_U232_DATA_BITS_6 0x01 +#define MCT_U232_DATA_BITS_7 0x02 +#define MCT_U232_DATA_BITS_8 0x03 + +#define MCT_U232_STOP_BITS_2 0x04 +#define MCT_U232_STOP_BITS_1 0x00 + +/* + * Modem Control Register (MCR) + */ +#define MCT_U232_MCR_NONE 0x8 /* Deactivate DTR and RTS */ +#define MCT_U232_MCR_RTS 0xa /* Activate RTS */ +#define MCT_U232_MCR_DTR 0x9 /* Activate DTR */ + +/* + * Modem Status Register (MSR) + */ +#define MCT_U232_MSR_INDEX 0x0 /* data[index] */ +#define MCT_U232_MSR_CD 0x80 /* Current CD */ +#define MCT_U232_MSR_RI 0x40 /* Current RI */ +#define MCT_U232_MSR_DSR 0x20 /* Current DSR */ +#define MCT_U232_MSR_CTS 0x10 /* Current CTS */ +#define MCT_U232_MSR_DCD 0x08 /* Delta CD */ +#define MCT_U232_MSR_DRI 0x04 /* Delta RI */ +#define MCT_U232_MSR_DDSR 0x02 /* Delta DSR */ +#define MCT_U232_MSR_DCTS 0x01 /* Delta CTS */ + +/* + * Line Status Register (LSR) + */ +#define MCT_U232_LSR_INDEX 1 /* data[index] */ +#define MCT_U232_LSR_ERR 0x80 /* OE | PE | FE | BI */ +#define MCT_U232_LSR_TEMT 0x40 /* transmit register empty */ +#define MCT_U232_LSR_THRE 0x20 /* transmit holding register empty */ +#define MCT_U232_LSR_BI 0x10 /* break indicator */ +#define MCT_U232_LSR_FE 0x08 /* framing error */ +#define MCT_U232_LSR_OE 0x02 /* overrun error */ +#define MCT_U232_LSR_PE 0x04 /* parity error */ +#define MCT_U232_LSR_OE 0x02 /* overrun error */ +#define MCT_U232_LSR_DR 0x01 /* receive data ready */ + + +/* ----------------------------------------------------------------------------- + * Technical Specification reverse engineered with SniffUSB on Windows98 + * ===================================================================== + * + * The technical details of the device have been acquired be using "SniffUSB" + * and the vendor-supplied device driver (version 2.3A) under Windows98. To + * identify the USB vendor-specific requests and to assign them to terminal + * settings (flow control, baud rate, etc.) the program "SerialSettings" from + * William G. Greathouse has been proven to be very useful. I also used the + * Win98 "HyperTerminal" and "usb-robot" on Linux for testing. The results and + * observations are summarized below: + * + * The USB requests seem to be directly mapped to the registers of a 8250, + * 16450 or 16550 UART. The FreeBSD handbook (appendix F.4 "Input/Output + * devices") contains a comprehensive description of UARTs and its registers. + * The bit descriptions are actually taken from there. + * + * + * Baud rate (divisor) + * ------------------- + * + * BmRequestType: 0x4 (0100 0000B) + * bRequest: 0x5 + * wValue: 0x0 + * wIndex: 0x0 + * wLength: 0x4 + * Data: divisor = 115200 / baud_rate + * + * + * Line Control Register (LCR) + * --------------------------- + * + * BmRequestType: 0x4 (0100 0000B) 0xc (1100 0000B) + * bRequest: 0x7 0x6 + * wValue: 0x0 + * wIndex: 0x0 + * wLength: 0x1 + * Data: LCR (see below) + * + * Bit 7: Divisor Latch Access Bit (DLAB). When set, access to the data + * transmit/receive register (THR/RBR) and the Interrupt Enable Register + * (IER) is disabled. Any access to these ports is now redirected to the + * Divisor Latch Registers. Setting this bit, loading the Divisor + * Registers, and clearing DLAB should be done with interrupts disabled. + * Bit 6: Set Break. When set to "1", the transmitter begins to transmit + * continuous Spacing until this bit is set to "0". This overrides any + * bits of characters that are being transmitted. + * Bit 5: Stick Parity. When parity is enabled, setting this bit causes parity + * to always be "1" or "0", based on the value of Bit 4. + * Bit 4: Even Parity Select (EPS). When parity is enabled and Bit 5 is "0", + * setting this bit causes even parity to be transmitted and expected. + * Otherwise, odd parity is used. + * Bit 3: Parity Enable (PEN). When set to "1", a parity bit is inserted + * between the last bit of the data and the Stop Bit. The UART will also + * expect parity to be present in the received data. + * Bit 2: Number of Stop Bits (STB). If set to "1" and using 5-bit data words, + * 1.5 Stop Bits are transmitted and expected in each data word. For + * 6, 7 and 8-bit data words, 2 Stop Bits are transmitted and expected. + * When this bit is set to "0", one Stop Bit is used on each data word. + * Bit 1: Word Length Select Bit #1 (WLSB1) + * Bit 0: Word Length Select Bit #0 (WLSB0) + * Together these bits specify the number of bits in each data word. + * 1 0 Word Length + * 0 0 5 Data Bits + * 0 1 6 Data Bits + * 1 0 7 Data Bits + * 1 1 8 Data Bits + * + * SniffUSB observations: Bit 7 seems not to be used. There seem to be two bugs + * in the Win98 driver: the break does not work (bit 6 is not asserted) and the + * sticky parity bit is not cleared when set once. The LCR can also be read + * back with USB request 6 but this has never been observed with SniffUSB. + * + * + * Modem Control Register (MCR) + * ---------------------------- + * + * BmRequestType: 0x4 (0100 0000B) + * bRequest: 0xa + * wValue: 0x0 + * wIndex: 0x0 + * wLength: 0x1 + * Data: MCR (Bit 4..7, see below) + * + * Bit 7: Reserved, always 0. + * Bit 6: Reserved, always 0. + * Bit 5: Reserved, always 0. + * Bit 4: Loop-Back Enable. When set to "1", the UART transmitter and receiver + * are internally connected together to allow diagnostic operations. In + * addition, the UART modem control outputs are connected to the UART + * modem control inputs. CTS is connected to RTS, DTR is connected to + * DSR, OUT1 is connected to RI, and OUT 2 is connected to DCD. + * Bit 3: OUT 2. An auxiliary output that the host processor may set high or + * low. In the IBM PC serial adapter (and most clones), OUT 2 is used + * to tri-state (disable) the interrupt signal from the + * 8250/16450/16550 UART. + * Bit 2: OUT 1. An auxiliary output that the host processor may set high or + * low. This output is not used on the IBM PC serial adapter. + * Bit 1: Request to Send (RTS). When set to "1", the output of the UART -RTS + * line is Low (Active). + * Bit 0: Data Terminal Ready (DTR). When set to "1", the output of the UART + * -DTR line is Low (Active). + * + * SniffUSB observations: Bit 2 and 4 seem not to be used but bit 3 has been + * seen _always_ set. + * + * + * Modem Status Register (MSR) + * --------------------------- + * + * BmRequestType: 0xc (1100 0000B) + * bRequest: 0x2 + * wValue: 0x0 + * wIndex: 0x0 + * wLength: 0x1 + * Data: MSR (see below) + * + * Bit 7: Data Carrier Detect (CD). Reflects the state of the DCD line on the + * UART. + * Bit 6: Ring Indicator (RI). Reflects the state of the RI line on the UART. + * Bit 5: Data Set Ready (DSR). Reflects the state of the DSR line on the UART. + * Bit 4: Clear To Send (CTS). Reflects the state of the CTS line on the UART. + * Bit 3: Delta Data Carrier Detect (DDCD). Set to "1" if the -DCD line has + * changed state one more more times since the last time the MSR was + * read by the host. + * Bit 2: Trailing Edge Ring Indicator (TERI). Set to "1" if the -RI line has + * had a low to high transition since the last time the MSR was read by + * the host. + * Bit 1: Delta Data Set Ready (DDSR). Set to "1" if the -DSR line has changed + * state one more more times since the last time the MSR was read by the + * host. + * Bit 0: Delta Clear To Send (DCTS). Set to "1" if the -CTS line has changed + * state one more times since the last time the MSR was read by the + * host. + * + * SniffUSB observations: the MSR is also returned as first byte on the + * interrupt-in endpoint 0x83 to signal changes of modem status lines. The USB + * request to read MSR cannot be applied during normal device operation. + * + * + * Line Status Register (LSR) + * -------------------------- + * + * Bit 7 Error in Receiver FIFO. On the 8250/16450 UART, this bit is zero. + * This bit is set to "1" when any of the bytes in the FIFO have one or + * more of the following error conditions: PE, FE, or BI. + * Bit 6 Transmitter Empty (TEMT). When set to "1", there are no words + * remaining in the transmit FIFO or the transmit shift register. The + * transmitter is completely idle. + * Bit 5 Transmitter Holding Register Empty (THRE). When set to "1", the FIFO + * (or holding register) now has room for at least one additional word + * to transmit. The transmitter may still be transmitting when this bit + * is set to "1". + * Bit 4 Break Interrupt (BI). The receiver has detected a Break signal. + * Bit 3 Framing Error (FE). A Start Bit was detected but the Stop Bit did not + * appear at the expected time. The received word is probably garbled. + * Bit 2 Parity Error (PE). The parity bit was incorrect for the word received. + * Bit 1 Overrun Error (OE). A new word was received and there was no room in + * the receive buffer. The newly-arrived word in the shift register is + * discarded. On 8250/16450 UARTs, the word in the holding register is + * discarded and the newly- arrived word is put in the holding register. + * Bit 0 Data Ready (DR). One or more words are in the receive FIFO that the + * host may read. A word must be completely received and moved from the + * shift register into the FIFO (or holding register for 8250/16450 + * designs) before this bit is set. + * + * SniffUSB observations: the LSR is returned as second byte on the interrupt-in + * endpoint 0x83 to signal error conditions. Such errors have been seen with + * minicom/zmodem transfers (CRC errors). + * + * + * Flow control + * ------------ + * + * SniffUSB observations: no flow control specific requests have been realized + * apart from DTR/RTS settings. Both signals are dropped for no flow control + * but asserted for hardware or software flow control. + * + * + * Endpoint usage + * -------------- + * + * SniffUSB observations: the bulk-out endpoint 0x1 and interrupt-in endpoint + * 0x81 is used to transmit and receive characters. The second interrupt-in + * endpoint 0x83 signals exceptional conditions like modem line changes and + * errors. The first byte returned is the MSR and the second byte the LSR. + * + * + * Other observations + * ------------------ + * + * Queued bulk transfers like used in visor.c did not work. + * + * + * Properties of the USB device used (as found in /var/log/messages) + * ----------------------------------------------------------------- + * + * Manufacturer: MCT Corporation. + * Product: USB-232 Interfact Controller + * SerialNumber: U2S22050 + * + * Length = 18 + * DescriptorType = 01 + * USB version = 1.00 + * Vendor:Product = 0711:0210 + * MaxPacketSize0 = 8 + * NumConfigurations = 1 + * Device version = 1.02 + * Device Class:SubClass:Protocol = 00:00:00 + * Per-interface classes + * Configuration: + * bLength = 9 + * bDescriptorType = 02 + * wTotalLength = 0027 + * bNumInterfaces = 01 + * bConfigurationValue = 01 + * iConfiguration = 00 + * bmAttributes = c0 + * MaxPower = 100mA + * + * Interface: 0 + * Alternate Setting: 0 + * bLength = 9 + * bDescriptorType = 04 + * bInterfaceNumber = 00 + * bAlternateSetting = 00 + * bNumEndpoints = 03 + * bInterface Class:SubClass:Protocol = 00:00:00 + * iInterface = 00 + * Endpoint: + * bLength = 7 + * bDescriptorType = 05 + * bEndpointAddress = 81 (in) + * bmAttributes = 03 (Interrupt) + * wMaxPacketSize = 0040 + * bInterval = 02 + * Endpoint: + * bLength = 7 + * bDescriptorType = 05 + * bEndpointAddress = 01 (out) + * bmAttributes = 02 (Bulk) + * wMaxPacketSize = 0040 + * bInterval = 00 + * Endpoint: + * bLength = 7 + * bDescriptorType = 05 + * bEndpointAddress = 83 (in) + * bmAttributes = 03 (Interrupt) + * wMaxPacketSize = 0002 + * bInterval = 02 + */ + +#endif /* __LINUX_USB_SERIAL_MCT_U232_H */ + diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 983d3a6e5599..40cf2f74649b 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -1755,11 +1755,11 @@ static void rh_int_timer_do(unsigned long ptr) urbp = (struct urb_priv *)u->hcpriv; if (urbp) { /* Check if the FSBR timed out */ - if (urbp->fsbr && time_after(urbp->inserttime + IDLE_TIMEOUT, jiffies)) + if (urbp->fsbr && time_after_eq(jiffies, urbp->inserttime + IDLE_TIMEOUT)) uhci_fsbr_timeout(uhci, u); /* Check if the URB timed out */ - if (u->timeout && time_after(u->timeout, jiffies)) { + if (u->timeout && time_after_eq(jiffies, u->timeout)) { u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; uhci_unlink_urb(u); } diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 71e014d4f35c..b4cace1dcf79 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -2626,14 +2626,14 @@ _static int process_urb (uhci_t *s, struct list_head *p) // Completion if (urb->complete) { urb->dev = NULL; + spin_unlock(&s->urb_list_lock); urb->complete ((struct urb *) urb); // Re-submit the URB if ring-linked if (is_ring && (urb->status != -ENOENT) && !contains_killed) { urb->dev=usb_dev; - spin_unlock(&s->urb_list_lock); uhci_submit_urb (urb); - spin_lock(&s->urb_list_lock); } + spin_lock(&s->urb_list_lock); } usb_dec_dev_use (usb_dev); diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index e97f458cf739..fad7e48f66fd 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -2053,9 +2053,9 @@ int usb_new_device(struct usb_device *dev) if (err < 0) { err("unable to get device %d configuration (error=%d)", dev->devnum, err); - usb_destroy_configuration(dev); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; + usb_free_dev(dev); return 1; } diff --git a/fs/buffer.c b/fs/buffer.c index f2d10d6770b4..dbe463e0502f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -758,12 +758,6 @@ void init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private) bh->b_private = private; } -static void end_buffer_io_sync(struct buffer_head *bh, int uptodate) -{ - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); -} - static void end_buffer_io_bad(struct buffer_head *bh, int uptodate) { mark_buffer_uptodate(bh, uptodate); @@ -1001,7 +995,7 @@ repeat: * and it is clean. */ if (bh) { - init_buffer(bh, end_buffer_io_sync, NULL); + init_buffer(bh, end_buffer_io_bad, NULL); bh->b_dev = dev; bh->b_blocknr = block; bh->b_state = 1 << BH_Mapped; @@ -1182,67 +1176,6 @@ struct buffer_head * bread(kdev_t dev, int block, int size) return NULL; } -/* - * Ok, breada can be used as bread, but additionally to mark other - * blocks for reading as well. End the argument list with a negative - * number. - */ - -#define NBUF 16 - -struct buffer_head * breada(kdev_t dev, int block, int bufsize, - unsigned int pos, unsigned int filesize) -{ - struct buffer_head * bhlist[NBUF]; - unsigned int blocks; - struct buffer_head * bh; - int index; - int i, j; - - if (pos >= filesize) - return NULL; - - if (block < 0) - return NULL; - - bh = getblk(dev, block, bufsize); - index = BUFSIZE_INDEX(bh->b_size); - - if (buffer_uptodate(bh)) - return(bh); - else ll_rw_block(READ, 1, &bh); - - blocks = (filesize - pos) >> (9+index); - - if (blocks > NBUF) - blocks = NBUF; - - bhlist[0] = bh; - j = 1; - for(i=1; i1) - ll_rw_block(READA, (j-1), bhlist+1); - for(i=1; ibuffers. - */ - head = create_buffers(page, size, 1); - if (page->buffers) - BUG(); - if (!head) - BUG(); - tail = head; - for (bh = head; bh; bh = bh->b_this_page) { - block = *(b++); - - tail = bh; - init_buffer(bh, end_buffer_io_async, NULL); - bh->b_dev = dev; - bh->b_blocknr = block; - - set_bit(BH_Mapped, &bh->b_state); - } - tail->b_this_page = head; - page_cache_get(page); - page->buffers = head; - return 0; -} - static void unmap_buffer(struct buffer_head * bh) { if (buffer_mapped(bh)) { @@ -1515,7 +1414,7 @@ int block_flushpage(struct page *page, unsigned long offset) return 1; } -static void create_empty_buffers(struct page *page, struct inode *inode, unsigned long blocksize) +static void create_empty_buffers(struct page *page, kdev_t dev, unsigned long blocksize) { struct buffer_head *bh, *head, *tail; @@ -1525,7 +1424,7 @@ static void create_empty_buffers(struct page *page, struct inode *inode, unsigne bh = head; do { - bh->b_dev = inode->i_dev; + bh->b_dev = dev; bh->b_blocknr = 0; bh->b_end_io = end_buffer_io_bad; tail = bh; @@ -1586,20 +1485,20 @@ static int __block_write_full_page(struct inode *inode, struct page *page, get_b int err, i; unsigned long block; struct buffer_head *bh, *head; - struct buffer_head *arr[MAX_BUF_PER_PAGE]; - int nr = 0; if (!PageLocked(page)) BUG(); if (!page->buffers) - create_empty_buffers(page, inode, inode->i_sb->s_blocksize); + create_empty_buffers(page, inode->i_dev, inode->i_sb->s_blocksize); head = page->buffers; block = page->index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); bh = head; i = 0; + + /* Stage 1: make sure we have all the buffers mapped! */ do { /* * If the buffer isn't up-to-date, we can't be sure @@ -1616,29 +1515,33 @@ static int __block_write_full_page(struct inode *inode, struct page *page, get_b if (buffer_new(bh)) unmap_underlying_metadata(bh); } - set_bit(BH_Uptodate, &bh->b_state); - set_bit(BH_Dirty, &bh->b_state); + bh = bh->b_this_page; + block++; + } while (bh != head); + + /* Stage 2: lock the buffers, mark them dirty */ + do { + lock_buffer(bh); bh->b_end_io = end_buffer_io_async; atomic_inc(&bh->b_count); - arr[nr++] = bh; + set_bit(BH_Uptodate, &bh->b_state); + set_bit(BH_Dirty, &bh->b_state); bh = bh->b_this_page; - block++; } while (bh != head); - if (nr) { - ll_rw_block(WRITE, nr, arr); - } else { - UnlockPage(page); - } + /* Stage 3: submit the IO */ + do { + submit_bh(WRITE, bh); + bh = bh->b_this_page; + } while (bh != head); + + /* Done - end_buffer_io_async will unlock */ SetPageUptodate(page); return 0; + out: - if (nr) { - ll_rw_block(WRITE, nr, arr); - } else { - UnlockPage(page); - } ClearPageUptodate(page); + UnlockPage(page); return err; } @@ -1654,7 +1557,7 @@ static int __block_prepare_write(struct inode *inode, struct page *page, blocksize = inode->i_sb->s_blocksize; if (!page->buffers) - create_empty_buffers(page, inode, blocksize); + create_empty_buffers(page, inode->i_dev, blocksize); head = page->buffers; bbits = inode->i_sb->s_blocksize_bits; @@ -1669,7 +1572,6 @@ static int __block_prepare_write(struct inode *inode, struct page *page, continue; if (block_start >= to) break; - bh->b_end_io = end_buffer_io_sync; if (!buffer_mapped(bh)) { err = get_block(inode, block, bh, 1); if (err) @@ -1766,14 +1668,13 @@ int block_read_full_page(struct page *page, get_block_t *get_block) unsigned long iblock, lblock; struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; unsigned int blocksize, blocks; - char *kaddr = NULL; int nr, i; if (!PageLocked(page)) PAGE_BUG(page); blocksize = inode->i_sb->s_blocksize; if (!page->buffers) - create_empty_buffers(page, inode, blocksize); + create_empty_buffers(page, inode->i_dev, blocksize); head = page->buffers; blocks = PAGE_CACHE_SIZE >> inode->i_sb->s_blocksize_bits; @@ -1793,35 +1694,40 @@ int block_read_full_page(struct page *page, get_block_t *get_block) continue; } if (!buffer_mapped(bh)) { - if (!kaddr) - kaddr = kmap(page); - memset(kaddr + i*blocksize, 0, blocksize); + memset(kmap(page) + i*blocksize, 0, blocksize); flush_dcache_page(page); + kunmap(page); set_bit(BH_Uptodate, &bh->b_state); continue; } } - init_buffer(bh, end_buffer_io_async, NULL); - atomic_inc(&bh->b_count); arr[nr] = bh; nr++; } while (i++, iblock++, (bh = bh->b_this_page) != head); - if (nr) { - if (Page_Uptodate(page)) - BUG(); - ll_rw_block(READ, nr, arr); - } else { + if (!nr) { /* * all buffers are uptodate - we can set the page * uptodate as well. */ SetPageUptodate(page); UnlockPage(page); + return 0; } - if (kaddr) - kunmap(page); + + /* Stage two: lock the buffers */ + for (i = 0; i < nr; i++) { + struct buffer_head * bh = arr[i]; + lock_buffer(bh); + bh->b_end_io = end_buffer_io_async; + atomic_inc(&bh->b_count); + } + + /* Stage 3: start the IO */ + for (i = 0; i < nr; i++) + submit_bh(READ, arr[i]); + return 0; } @@ -1963,7 +1869,7 @@ int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t goto out; if (!page->buffers) - create_empty_buffers(page, inode, blocksize); + create_empty_buffers(page, inode->i_dev, blocksize); /* Find the buffer that contains "offset" */ bh = page->buffers; @@ -1989,7 +1895,6 @@ int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t if (Page_Uptodate(page)) set_bit(BH_Uptodate, &bh->b_state); - bh->b_end_io = end_buffer_io_sync; if (!buffer_uptodate(bh)) { err = -EIO; ll_rw_block(READ, 1, &bh); @@ -2263,67 +2168,30 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], */ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size) { - struct buffer_head *head, *bh, *arr[MAX_BUF_PER_PAGE]; - int nr, fresh /* temporary debugging flag */, block; + struct buffer_head *head, *bh; if (!PageLocked(page)) panic("brw_page: page not locked for I/O"); -// ClearPageError(page); - /* - * We pretty much rely on the page lock for this, because - * create_page_buffers() might sleep. - */ - fresh = 0; - if (!page->buffers) { - create_page_buffers(rw, page, dev, b, size); - fresh = 1; - } + if (!page->buffers) - BUG(); + create_empty_buffers(page, dev, size); + head = bh = page->buffers; - head = page->buffers; - bh = head; - nr = 0; + /* Stage 1: lock all the buffers */ do { - block = *(b++); + lock_buffer(bh); + bh->b_blocknr = *(b++); + set_bit(BH_Mapped, &bh->b_state); + bh->b_end_io = end_buffer_io_async; + atomic_inc(&bh->b_count); + bh = bh->b_this_page; + } while (bh != head); - if (fresh && (atomic_read(&bh->b_count) != 0)) - BUG(); - if (rw == READ) { - if (!fresh) - BUG(); - if (!buffer_uptodate(bh)) { - arr[nr++] = bh; - atomic_inc(&bh->b_count); - } - } else { /* WRITE */ - if (!bh->b_blocknr) { - if (!block) - BUG(); - bh->b_blocknr = block; - } else { - if (!block) - BUG(); - } - set_bit(BH_Uptodate, &bh->b_state); - set_bit(BH_Dirty, &bh->b_state); - arr[nr++] = bh; - atomic_inc(&bh->b_count); - } + /* Stage 2: start the IO */ + do { + submit_bh(rw, bh); bh = bh->b_this_page; } while (bh != head); - if ((rw == READ) && nr) { - if (Page_Uptodate(page)) - BUG(); - ll_rw_block(rw, nr, arr); - } else { - if (!nr && rw == READ) { - SetPageUptodate(page); - UnlockPage(page); - } - if (nr && (rw == WRITE)) - ll_rw_block(rw, nr, arr); - } return 0; } diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c index 5f5ec196fbe5..c7b63f358c29 100644 --- a/fs/hpfs/buffer.c +++ b/fs/hpfs/buffer.c @@ -127,7 +127,7 @@ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head if (!ahead || secno + ahead >= s->s_hpfs_fs_size) *bhp = bh = bread(dev, secno, 512); - else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9); + else *bhp = bh = bread(dev, secno, 512); if (bh != NULL) return bh->b_data; else { @@ -175,7 +175,7 @@ void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffe if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size) qbh->bh[0] = bh = bread(dev, secno, 512); - else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9); + else qbh->bh[0] = bh = bread(dev, secno, 512); if (!bh) goto bail0; memcpy(data, bh->b_data, 512); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 4821de8d346a..564a47b321eb 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -533,13 +533,12 @@ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) NFS_FILEID(inode) != fattr.fileid) goto out_bad; - /* Filehandle matches? */ - if (memcmp(NFS_FH(inode), fhandle.data, sizeof(struct nfs_fh))) - goto out_bad; - /* Ok, remember that we successfully checked it.. */ nfs_refresh_inode(inode, &fattr); + if (nfs_inode_is_stale(inode, &fhandle, &fattr)) + goto out_bad; + out_valid_renew: nfs_renew_times(dentry); out_valid: diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index a205c3ad4a28..029b673f50dc 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -613,7 +613,7 @@ nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque) return 1; } -static int +int nfs_inode_is_stale(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) { /* Empty inodes are not stale */ @@ -628,7 +628,7 @@ nfs_inode_is_stale(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fat /* Has the filehandle changed? If so is the old one stale? */ if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0 && - __nfs_revalidate_inode(NFS_SERVER(inode),inode) < 0) + __nfs_revalidate_inode(NFS_SERVER(inode),inode) == -ESTALE) return 1; return 0; @@ -866,7 +866,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) inode->i_dev, (long long)NFS_FILEID(inode)); lock_kernel(); - if (!inode || is_bad_inode(inode)) { + if (!inode || is_bad_inode(inode) || NFS_STALE(inode)) { unlock_kernel(); return -ESTALE; } @@ -878,8 +878,8 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) return status; } if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) { - unlock_kernel(); - return 0; + status = NFS_STALE(inode) ? -ESTALE : 0; + goto out_nowait; } } NFS_FLAGS(inode) |= NFS_INO_REVALIDATING; @@ -888,6 +888,10 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) if (status) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) getattr failed, error=%d\n", inode->i_dev, (long long)NFS_FILEID(inode), status); + if (status == -ESTALE) { + NFS_FLAGS(inode) |= NFS_INO_STALE; + remove_inode_hash(inode); + } goto out; } @@ -902,6 +906,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) out: NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING; wake_up(&inode->i_wait); + out_nowait: unlock_kernel(); return status; } diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index b2d5c40998f4..7b62899c12b6 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -124,7 +124,7 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir) qname.len = entry->len; entry->ino = find_inode_number(dentry, &qname); if (!entry->ino) - entry->ino = smb_invent_inos(1); + entry->ino = iunique(dentry->d_sb, 2); } if (filldir(dirent, entry->name, entry->len, @@ -325,7 +325,7 @@ smb_lookup(struct inode *dir, struct dentry *dentry) goto add_entry; if (!error) { error = -EACCES; - finfo.f_ino = smb_invent_inos(1); + finfo.f_ino = iunique(dentry->d_sb, 2); inode = smb_iget(dir->i_sb, &finfo); if (inode) { add_entry: @@ -362,7 +362,7 @@ smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id) goto out_close; smb_renew_times(dentry); - fattr.f_ino = smb_invent_inos(1); + fattr.f_ino = iunique(dentry->d_sb, 2); inode = smb_iget(dentry->d_sb, &fattr); if (!inode) goto out_no_inode; diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index f6c7a4112252..79627f880470 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -48,8 +48,7 @@ smb_readpage_sync(struct dentry *dentry, struct page *page) DENTRY_PATH(dentry), count, offset, rsize); result = smb_open(dentry, SMB_O_RDONLY); - if (result < 0) - { + if (result < 0) { PARANOIA("%s/%s open failed, error=%d\n", DENTRY_PATH(dentry), result); goto io_error; @@ -59,7 +58,7 @@ smb_readpage_sync(struct dentry *dentry, struct page *page) if (count < rsize) rsize = count; - result = smb_proc_read(dentry, offset, rsize, buffer); + result = smb_proc_read(dentry->d_inode, offset, rsize, buffer); if (result < 0) goto io_error; @@ -103,25 +102,27 @@ smb_readpage(struct file *file, struct page *page) * Offset is the data offset within the page. */ static int -smb_writepage_sync(struct dentry *dentry, struct page *page, +smb_writepage_sync(struct inode *inode, struct page *page, unsigned long offset, unsigned int count) { - struct inode *inode = dentry->d_inode; u8 *buffer = page_address(page) + offset; - int wsize = smb_get_wsize(server_from_dentry(dentry)); + int wsize = smb_get_wsize(server_from_inode(inode)); int result, written = 0; offset += page->index << PAGE_CACHE_SHIFT; - VERBOSE("file %s/%s, count=%d@%ld, wsize=%d\n", - DENTRY_PATH(dentry), count, offset, wsize); + VERBOSE("file ino=%ld, fileid=%d, count=%d@%ld, wsize=%d\n", + inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize); do { if (count < wsize) wsize = count; - result = smb_proc_write(dentry, offset, wsize, buffer); - if (result < 0) + result = smb_proc_write(inode, offset, wsize, buffer); + if (result < 0) { + PARANOIA("failed write, wsize=%d, result=%d\n", + wsize, result); break; + } /* N.B. what if result < wsize?? */ #ifdef SMBFS_PARANOIA if (result < wsize) @@ -153,9 +154,7 @@ static int smb_writepage(struct page *page) { struct address_space *mapping = page->mapping; - struct dentry *dentry; struct inode *inode; - struct list_head *head; unsigned long end_index; unsigned offset = PAGE_CACHE_SIZE; int err; @@ -166,12 +165,6 @@ smb_writepage(struct page *page) if (!inode) BUG(); - /* Pick the first dentry for this inode. */ - head = &inode->i_dentry; - if (list_empty(head)) - BUG(); /* We need one, are we guaranteed to have one? */ - dentry = list_entry(head->next, struct dentry, d_alias); - end_index = inode->i_size >> PAGE_CACHE_SHIFT; /* easy case */ @@ -184,7 +177,7 @@ smb_writepage(struct page *page) return -EIO; do_it: get_page(page); - err = smb_writepage_sync(dentry, page, 0, offset); + err = smb_writepage_sync(inode, page, 0, offset); SetPageUptodate(page); UnlockPage(page); put_page(page); @@ -200,7 +193,7 @@ smb_updatepage(struct file *file, struct page *page, unsigned long offset, DEBUG1("(%s/%s %d@%ld)\n", DENTRY_PATH(dentry), count, (page->index << PAGE_CACHE_SHIFT)+offset); - return smb_writepage_sync(dentry, page, offset, count); + return smb_writepage_sync(dentry->d_inode, page, offset, count); } static ssize_t @@ -281,7 +274,7 @@ static int smb_commit_write(struct file *file, struct page *page, struct address_space_operations smb_file_aops = { readpage: smb_readpage, - /* writepage: smb_writepage, */ + writepage: smb_writepage, prepare_write: smb_prepare_write, commit_write: smb_commit_write }; @@ -325,8 +318,16 @@ out: static int smb_file_open(struct inode *inode, struct file * file) { + int result; + struct dentry *dentry = file->f_dentry; + int smb_mode = (file->f_mode & O_ACCMODE) - 1; + lock_kernel(); + result = smb_open(dentry, smb_mode); + if (result) + goto out; inode->u.smbfs_i.openers++; +out: unlock_kernel(); return 0; } diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index bcc900626c46..e502fb60b0e6 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -53,22 +53,6 @@ static struct super_operations smb_sops = statfs: smb_statfs, }; -/* FIXME: Look at all inodes whether so that we do not get duplicate - * inode numbers. */ - -unsigned long -smb_invent_inos(unsigned long n) -{ - static unsigned long ino = 2; - - if (ino + 2*n < ino) - { - /* wrap around */ - ino = 2; - } - ino += n; - return ino; -} /* We are always generating a new inode here */ struct inode * @@ -282,7 +266,7 @@ out: static void smb_delete_inode(struct inode *ino) { - DEBUG1("\n"); + DEBUG1("ino=%ld\n", ino->i_ino); lock_kernel(); if (smb_close(ino)) PARANOIA("could not close inode %ld\n", ino->i_ino); diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index 7863cd2da620..f0444f97d7d4 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -1072,9 +1072,9 @@ smb_close_fileid(struct dentry *dentry, __u16 fileid) file-id would not be valid after a reconnection. */ int -smb_proc_read(struct dentry *dentry, off_t offset, int count, char *data) +smb_proc_read(struct inode *inode, off_t offset, int count, char *data) { - struct smb_sb_info *server = server_from_dentry(dentry); + struct smb_sb_info *server = server_from_inode(inode); __u16 returned_count, data_len; unsigned char *buf; int result; @@ -1082,7 +1082,7 @@ smb_proc_read(struct dentry *dentry, off_t offset, int count, char *data) smb_lock_server(server); smb_setup_header(server, SMBread, 5, 0); buf = server->packet; - WSET(buf, smb_vwv0, dentry->d_inode->u.smbfs_i.fileid); + WSET(buf, smb_vwv0, inode->u.smbfs_i.fileid); WSET(buf, smb_vwv1, count); DSET(buf, smb_vwv2, offset); WSET(buf, smb_vwv4, 0); @@ -1114,25 +1114,26 @@ smb_proc_read(struct dentry *dentry, off_t offset, int count, char *data) result = data_len; out: - VERBOSE("file %s/%s, count=%d, result=%d\n", - DENTRY_PATH(dentry), count, result); + VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", + inode->ino, inode->u.smbfs_i.fileid, count, result); smb_unlock_server(server); return result; } int -smb_proc_write(struct dentry *dentry, off_t offset, int count, const char *data) +smb_proc_write(struct inode *inode, off_t offset, int count, const char *data) { - struct smb_sb_info *server = server_from_dentry(dentry); + struct smb_sb_info *server = server_from_inode(inode); int result; __u8 *p; - VERBOSE("file %s/%s, count=%d@%ld, packet_size=%d\n", - DENTRY_PATH(dentry), count, offset, server->packet_size); + VERBOSE("ino=%ld, fileid=%d, count=%d@%ld, packet_size=%d\n", + inode->ino, inode->u.smbfs_i.fileid, count, offset, + server->packet_size); smb_lock_server(server); p = smb_setup_header(server, SMBwrite, 5, count + 3); - WSET(server->packet, smb_vwv0, dentry->d_inode->u.smbfs_i.fileid); + WSET(server->packet, smb_vwv0, inode->u.smbfs_i.fileid); WSET(server->packet, smb_vwv1, count); DSET(server->packet, smb_vwv2, offset); WSET(server->packet, smb_vwv4, 0); diff --git a/include/linux/apm_bios.h b/include/linux/apm_bios.h index 69ea9cf48686..9dba26eebfa4 100644 --- a/include/linux/apm_bios.h +++ b/include/linux/apm_bios.h @@ -38,13 +38,22 @@ struct apm_bios_info { unsigned short dseg_len; }; - /* Results of APM Installation Check */ +/* Results of APM Installation Check */ #define APM_16_BIT_SUPPORT 0x0001 #define APM_32_BIT_SUPPORT 0x0002 #define APM_IDLE_SLOWS_CLOCK 0x0004 #define APM_BIOS_DISABLED 0x0008 #define APM_BIOS_DISENGAGED 0x0010 +/* + * Data for APM that is persistant across module unload/load + */ +struct apm_info { + struct apm_bios_info bios; + unsigned short connection_version; + int get_power_status_broken; +}; + /* * The APM function codes */ @@ -91,12 +100,9 @@ struct apm_bios_info { #define APM_FUNC_TIMER_GET 2 /* - * in init/main.c + * in arch/i386/kernel/setup.c */ -extern struct apm_bios_info apm_bios_info; - -extern int apm_register_callback(int (*callback)(apm_event_t)); -extern void apm_unregister_callback(int (*callback)(apm_event_t)); +extern struct apm_info apm_info; #endif /* __KERNEL__ */ @@ -176,7 +182,7 @@ extern void apm_unregister_callback(int (*callback)(apm_event_t)); /* * This is the "All Devices" ID communicated to the BIOS */ -#define APM_DEVICE_BALL ((apm_bios_info.version > 0x0100) ? \ +#define APM_DEVICE_BALL ((apm_info.connection_version > 0x0100) ? \ APM_DEVICE_ALL : APM_DEVICE_OLD_ALL) #endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 9d0d0166b4ea..2ef374aa195f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1193,6 +1193,7 @@ extern void file_moveto(struct file *new, struct file *old); extern struct buffer_head * get_hash_table(kdev_t, int, int); extern struct buffer_head * getblk(kdev_t, int, int); extern void ll_rw_block(int, int, struct buffer_head * bh[]); +extern void submit_bh(int, struct buffer_head *); extern int is_read_only(kdev_t); extern void __brelse(struct buffer_head *); static inline void brelse(struct buffer_head *buf) @@ -1209,7 +1210,6 @@ static inline void bforget(struct buffer_head *buf) extern void set_blocksize(kdev_t, int); extern unsigned int get_hardblocksize(kdev_t); extern struct buffer_head * bread(kdev_t, int, int); -extern struct buffer_head * breada(kdev_t, int, int, unsigned int, unsigned int); extern void wakeup_bdflush(int wait); extern int brw_page(int, struct page *, kdev_t, int [], int); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index ee3e8906c5d6..50ee12471896 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -63,6 +63,8 @@ extern int vsprintf(char *buf, const char *, va_list); extern int get_option(char **str, int *pint); extern char *get_options(char *str, int nints, int *ints); extern unsigned long memparse(char *ptr, char **retptr); +extern void dev_probe_lock(void); +extern void dev_probe_unlock(void); extern int session_of_pgrp(int pgrp); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a8a0a5dd6899..a7330e249407 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -92,6 +92,7 @@ do { \ #define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags) #define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING) +#define NFS_STALE(inode) (NFS_FLAGS(inode) & NFS_INO_STALE) #define NFS_FILEID(inode) ((inode)->u.nfs_i.fileid) #define NFS_FSID(inode) ((inode)->u.nfs_i.fsid) @@ -138,6 +139,8 @@ unsigned long page_index(struct page *page) extern struct super_block *nfs_read_super(struct super_block *, void *, int); extern int init_nfs_fs(void); extern void nfs_zap_caches(struct inode *); +extern int nfs_inode_is_stale(struct inode *, struct nfs_fh *, + struct nfs_fattr *); extern struct inode *nfs_fhget(struct dentry *, struct nfs_fh *, struct nfs_fattr *); extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); diff --git a/include/linux/sched.h b/include/linux/sched.h index e795aaacf4ec..d948de258856 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -150,8 +150,9 @@ extern signed long FASTCALL(schedule_timeout(signed long timeout)); asmlinkage void schedule(void); extern int schedule_task(struct tq_struct *task); -extern void run_schedule_tasks(void); +extern void flush_scheduled_tasks(void); extern int start_context_thread(void); +extern int current_is_keventd(void); /* * The default fd array needs to be at least BITS_PER_LONG, diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h index e475057fc134..501ea4b964d6 100644 --- a/include/linux/smb_fs.h +++ b/include/linux/smb_fs.h @@ -126,8 +126,8 @@ int smb_errno(struct smb_sb_info *); int smb_close(struct inode *); int smb_close_fileid(struct dentry *, __u16); int smb_open(struct dentry *, int); -int smb_proc_read(struct dentry *, off_t, int, char *); -int smb_proc_write(struct dentry *, off_t, int, const char *); +int smb_proc_read(struct inode *, off_t, int, char *); +int smb_proc_write(struct inode *, off_t, int, const char *); int smb_proc_create(struct dentry *, __u16, time_t, __u16 *); int smb_proc_mv(struct dentry *, struct dentry *); int smb_proc_mkdir(struct dentry *); diff --git a/include/linux/smb_fs_sb.h b/include/linux/smb_fs_sb.h index 4bad7e928009..0d14b83ae52a 100644 --- a/include/linux/smb_fs_sb.h +++ b/include/linux/smb_fs_sb.h @@ -14,8 +14,9 @@ #include #include -/* Get the server for the specified dentry */ -#define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb +/* structure access macros */ +#define server_from_inode(inode) (&(inode)->i_sb->u.smbfs_sb) +#define server_from_dentry(dentry) (&(dentry)->d_sb->u.smbfs_sb) #define SB_of(server) ((struct super_block *) ((char *)(server) - \ (unsigned long)(&((struct super_block *)0)->u.smbfs_sb))) diff --git a/include/linux/tqueue.h b/include/linux/tqueue.h index 285936f5cd94..a0363ffa6775 100644 --- a/include/linux/tqueue.h +++ b/include/linux/tqueue.h @@ -14,6 +14,7 @@ #define _LINUX_TQUEUE_H #include +#include #include #include @@ -35,15 +36,16 @@ */ struct tq_struct { - struct tq_struct *next; /* linked list of active bh's */ + struct list_head list; /* linked list of active bh's */ unsigned long sync; /* must be initialized to zero */ void (*routine)(void *); /* function to call */ void *data; /* argument to function */ }; -typedef struct tq_struct * task_queue; +typedef struct list_head task_queue; -#define DECLARE_TASK_QUEUE(q) task_queue q = NULL +#define DECLARE_TASK_QUEUE(q) LIST_HEAD(q) +#define TQ_ACTIVE(q) (!list_empty(&q)) extern task_queue tq_timer, tq_immediate, tq_disk; @@ -51,9 +53,10 @@ extern task_queue tq_timer, tq_immediate, tq_disk; * To implement your own list of active bottom halfs, use the following * two definitions: * - * struct tq_struct *my_bh = NULL; + * DECLARE_TASK_QUEUE(my_bh); * struct tq_struct run_my_bh = { - * 0, 0, (void (*)(void *)) run_task_queue, &my_bh + * routine: (void (*)(void *)) run_task_queue, + * data: &my_bh * }; * * To activate a bottom half on your list, use: @@ -82,8 +85,7 @@ static inline int queue_task(struct tq_struct *bh_pointer, if (!test_and_set_bit(0,&bh_pointer->sync)) { unsigned long flags; spin_lock_irqsave(&tqueue_lock, flags); - bh_pointer->next = *bh_list; - *bh_list = bh_pointer; + list_add_tail(&bh_pointer->list, bh_list); spin_unlock_irqrestore(&tqueue_lock, flags); ret = 1; } @@ -95,28 +97,29 @@ static inline int queue_task(struct tq_struct *bh_pointer, */ static inline void run_task_queue(task_queue *list) { - if (*list) { + while (!list_empty(list)) { unsigned long flags; - struct tq_struct *p; + struct list_head *next; spin_lock_irqsave(&tqueue_lock, flags); - p = *list; - *list = NULL; - spin_unlock_irqrestore(&tqueue_lock, flags); - - while (p) { + next = list->next; + if (next != list) { void *arg; void (*f) (void *); - struct tq_struct *save_p; - arg = p -> data; - f = p -> routine; - save_p = p; - p = p -> next; - smp_mb(); - save_p -> sync = 0; + struct tq_struct *p; + + list_del(next); + p = list_entry(next, struct tq_struct, list); + arg = p->data; + f = p->routine; + p->sync = 0; + spin_unlock_irqrestore(&tqueue_lock, flags); + if (f) - (*f)(arg); + f(arg); + continue; } + spin_unlock_irqrestore(&tqueue_lock, flags); } } diff --git a/include/linux/usb.h b/include/linux/usb.h index 42f0011cdc6e..39e9f2eed0c3 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -545,7 +545,7 @@ struct usb_bus { struct list_head inodes; }; -#define USB_MAXCHILDREN (8) /* This is arbitrary */ +#define USB_MAXCHILDREN (16) /* This is arbitrary */ struct usb_device { int devnum; /* Device number on USB bus */ diff --git a/init/main.c b/init/main.c index c1e83578b4c5..7e8ac67a68f6 100644 --- a/init/main.c +++ b/init/main.c @@ -716,14 +716,6 @@ static void __init do_basic_setup(void) /* Mount the root filesystem.. */ mount_root(); -#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET) - /* do this after other 'do this last' stuff, because we want - * to minimize spurious executions of /sbin/hotplug - * during boot-up - */ - net_notifier_init(); -#endif - mount_devfs_fs (); #ifdef CONFIG_BLK_DEV_INITRD diff --git a/kernel/context.c b/kernel/context.c index a8caa863a4ea..864a70131c88 100644 --- a/kernel/context.c +++ b/kernel/context.c @@ -3,7 +3,12 @@ * * Mechanism for running arbitrary tasks in process context * - * dwmw2@redhat.com + * dwmw2@redhat.com: Genesis + * + * andrewm@uow.edu.au: 2.4.0-test12 + * - Child reaping + * - Support for tasks which re-add themselves + * - flush_scheduled_tasks. */ #define __KERNEL_SYSCALLS__ @@ -17,13 +22,42 @@ static DECLARE_TASK_QUEUE(tq_context); static DECLARE_WAIT_QUEUE_HEAD(context_task_wq); +static DECLARE_WAIT_QUEUE_HEAD(context_task_done); static int keventd_running; +static struct task_struct *keventd_task; + +static int need_keventd(const char *who) +{ + if (keventd_running == 0) + printk(KERN_ERR "%s(): keventd has not started\n", who); + return keventd_running; +} + +int current_is_keventd(void) +{ + int ret = 0; + if (need_keventd(__FUNCTION__)) + ret = (current == keventd_task); + return ret; +} +/** + * schedule_task - schedule a function for subsequent execution in process context. + * @task: pointer to a &tq_struct which defines the function to be scheduled. + * + * May be called from interrupt context. The scheduled function is run at some + * time in the near future by the keventd kernel thread. If it can sleep, it + * should be designed to do so for the minimum possible time, as it will be + * stalling all other scheduled tasks. + * + * schedule_task() returns non-zero if the task was successfully scheduled. + * If @task is already residing on a task queue then schedule_task() fails + * to schedule your task and returns zero. + */ int schedule_task(struct tq_struct *task) { int ret; - if (keventd_running == 0) - printk(KERN_ERR "schedule_task(): keventd has not started\n"); + need_keventd(__FUNCTION__); ret = queue_task(task, &tq_context); wake_up(&context_task_wq); return ret; @@ -38,6 +72,7 @@ static int context_thread(void *dummy) daemonize(); strcpy(curtask->comm, "keventd"); keventd_running = 1; + keventd_task = curtask; spin_lock_irq(&curtask->sigmask_lock); siginitsetinv(&curtask->blocked, sigmask(SIGCHLD)); @@ -50,18 +85,19 @@ static int context_thread(void *dummy) siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); - /* - * If one of the functions on a task queue re-adds itself - * to the task queue we call schedule() in state TASK_RUNNING - */ - for (;;) { + /* + * If one of the functions on a task queue re-adds itself + * to the task queue we call schedule() in state TASK_RUNNING + */ + for (;;) { set_task_state(curtask, TASK_INTERRUPTIBLE); - add_wait_queue(&context_task_wq, &wait); - if (tq_context) + add_wait_queue(&context_task_wq, &wait); + if (TQ_ACTIVE(tq_context)) set_task_state(curtask, TASK_RUNNING); schedule(); - remove_wait_queue(&context_task_wq, &wait); - run_task_queue(&tq_context); + remove_wait_queue(&context_task_wq, &wait); + run_task_queue(&tq_context); + wake_up(&context_task_done); if (signal_pending(curtask)) { while (waitpid(-1, (unsigned int *)0, __WALL|WNOHANG) > 0) ; @@ -71,12 +107,43 @@ static int context_thread(void *dummy) } } -/* - * Run the tq_context queue right now. Must be called from process context +/** + * flush_scheduled_tasks - ensure that any scheduled tasks have run to completion. + * + * Forces execution of the schedule_task() queue and blocks until its completion. + * + * If a kernel subsystem uses schedule_task() and wishes to flush any pending + * tasks, it should use this function. This is typically used in driver shutdown + * handlers. + * + * The caller should hold no spinlocks and should hold no semaphores which could + * cause the scheduled tasks to block. */ -void run_schedule_tasks(void) +static struct tq_struct dummy_task; + +void flush_scheduled_tasks(void) { - run_task_queue(&tq_context); + int count; + DECLARE_WAITQUEUE(wait, current); + + /* + * Do it twice. It's possible, albeit highly unlikely, that + * the caller queued a task immediately before calling us, + * and that the eventd thread was already past the run_task_queue() + * but not yet into wake_up(), so it woke us up before completing + * the caller's queued task or our new dummy task. + */ + add_wait_queue(&context_task_done, &wait); + for (count = 0; count < 2; count++) { + set_current_state(TASK_UNINTERRUPTIBLE); + + /* Queue a dummy task to make sure we get kicked */ + schedule_task(&dummy_task); + + /* Wait for it to complete */ + schedule(); + } + remove_wait_queue(&context_task_done, &wait); } int start_context_thread(void) @@ -86,5 +153,5 @@ int start_context_thread(void) } EXPORT_SYMBOL(schedule_task); -EXPORT_SYMBOL(run_schedule_tasks); +EXPORT_SYMBOL(flush_scheduled_tasks); diff --git a/kernel/exit.c b/kernel/exit.c index c60bafe8cdd6..6b7138e33332 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -302,9 +302,9 @@ static inline void __exit_mm(struct task_struct * tsk) { struct mm_struct * mm = tsk->mm; + mm_release(); if (mm) { atomic_inc(&mm->mm_count); - mm_release(); if (mm != tsk->active_mm) BUG(); /* more a memory barrier than a real lock */ task_lock(tsk); diff --git a/kernel/kmod.c b/kernel/kmod.c index 0239b8b0c190..b68392685aa5 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -256,126 +257,107 @@ EXPORT_SYMBOL(hotplug_path); #endif /* CONFIG_HOTPLUG */ - -static int exec_helper (void *arg) -{ - long ret; - void **params = (void **) arg; - char *path = (char *) params [0]; - char **argv = (char **) params [1]; - char **envp = (char **) params [2]; - - ret = exec_usermodehelper (path, argv, envp); - if (ret < 0) - ret = -ret; - do_exit(ret); -} - struct subprocess_info { struct semaphore *sem; char *path; char **argv; char **envp; - int retval; + pid_t retval; }; /* - * This is a standalone child of keventd. It forks off another thread which - * is the desired usermode helper and then waits for the child to exit. - * We return the usermode process's exit code, or some -ve error code. + * This is the task which runs the usermode application */ static int ____call_usermodehelper(void *data) { struct subprocess_info *sub_info = data; - struct task_struct *curtask = current; - void *params [3] = { sub_info->path, sub_info->argv, sub_info->envp }; - pid_t pid, pid2; - mm_segment_t fs; - int retval = 0; - - if (!curtask->fs->root) { - printk(KERN_ERR "call_usermodehelper[%s]: no root fs\n", sub_info->path); - retval = -EPERM; - goto up_and_out; - } - if ((pid = kernel_thread(exec_helper, (void *) params, 0)) < 0) { - printk(KERN_ERR "failed fork2 %s, errno = %d", sub_info->argv[0], -pid); - retval = pid; - goto up_and_out; - } + int retval; - if (retval >= 0) { - /* Block everything but SIGKILL/SIGSTOP */ - spin_lock_irq(&curtask->sigmask_lock); - siginitsetinv(&curtask->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); - recalc_sigpending(curtask); - spin_unlock_irq(&curtask->sigmask_lock); - - /* Allow the system call to access kernel memory */ - fs = get_fs(); - set_fs(KERNEL_DS); - pid2 = waitpid(pid, &retval, __WCLONE); - if (pid2 == -1 && errno < 0) - pid2 = errno; - set_fs(fs); - - if (pid2 != pid) { - printk(KERN_ERR "waitpid(%d) failed, %d\n", pid, pid2); - retval = (pid2 < 0) ? pid2 : -1; - } - } + retval = -EPERM; + if (current->fs->root) + retval = exec_usermodehelper(sub_info->path, sub_info->argv, sub_info->envp); -up_and_out: - sub_info->retval = retval; - curtask->exit_signal = SIGCHLD; /* Wake up parent */ - up_and_exit(sub_info->sem, retval); + /* Exec failed? */ + sub_info->retval = (pid_t)retval; + do_exit(0); } /* - * This is a schedule_task function, so we must not sleep for very long at all. - * But the exec'ed process could do anything at all. So we launch another - * kernel thread. + * This is run by keventd. */ static void __call_usermodehelper(void *data) { struct subprocess_info *sub_info = data; pid_t pid; - if ((pid = kernel_thread (____call_usermodehelper, (void *)sub_info, 0)) < 0) { - printk(KERN_ERR "failed fork1 %s, errno = %d", sub_info->argv[0], -pid); + /* + * CLONE_VFORK: wait until the usermode helper has execve'd successfully + * We need the data structures to stay around until that is done. + */ + pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); + if (pid < 0) sub_info->retval = pid; - up(sub_info->sem); - } + up(sub_info->sem); } -/* - * This function can be called via do_exit->__exit_files, which means that - * we're partway through exitting and things break if we fork children. - * So we use keventd to parent the usermode helper. - * We return the usermode application's exit code or some -ve error. +/** + * call_usermodehelper - start a usermode application + * @path: pathname for the application + * @argv: null-terminated argument list + * @envp: null-terminated environment list + * + * Runs a user-space application. The application is started asynchronously. It + * runs as a child of keventd. It runs with full root capabilities. keventd silently + * reaps the child when it exits. + * + * Must be called from process context. Returns zero on success, else a negative + * error code. */ -int call_usermodehelper (char *path, char **argv, char **envp) +int call_usermodehelper(char *path, char **argv, char **envp) { DECLARE_MUTEX_LOCKED(sem); struct subprocess_info sub_info = { - sem: &sem, - path: path, - argv: argv, - envp: envp, - retval: 0, + sem: &sem, + path: path, + argv: argv, + envp: envp, + retval: 0, }; struct tq_struct tqs = { - next: 0, - sync: 0, routine: __call_usermodehelper, data: &sub_info, }; - schedule_task(&tqs); - down(&sem); /* Wait for an error or completion */ + if (path[0] == '\0') + goto out; + + if (current_is_keventd()) { + /* We can't wait on keventd! */ + __call_usermodehelper(&sub_info); + } else { + schedule_task(&tqs); + down(&sem); /* Wait until keventd has started the subprocess */ + } +out: return sub_info.retval; } +/* + * This is for the serialisation of device probe() functions + * against device open() functions + */ +static DECLARE_MUTEX(dev_probe_sem); + +void dev_probe_lock(void) +{ + down(&dev_probe_sem); +} + +void dev_probe_unlock(void) +{ + up(&dev_probe_sem); +} + EXPORT_SYMBOL(exec_usermodehelper); EXPORT_SYMBOL(call_usermodehelper); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index abbe3d9a5537..4ed94117e634 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -185,7 +185,6 @@ EXPORT_SYMBOL(getblk); EXPORT_SYMBOL(bdget); EXPORT_SYMBOL(bdput); EXPORT_SYMBOL(bread); -EXPORT_SYMBOL(breada); EXPORT_SYMBOL(__brelse); EXPORT_SYMBOL(__bforget); EXPORT_SYMBOL(ll_rw_block); diff --git a/kernel/timer.c b/kernel/timer.c index c30ebb8d5f4d..579b065f3f46 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -680,7 +680,7 @@ void do_timer(struct pt_regs *regs) update_process_times(user_mode(regs)); #endif mark_bh(TIMER_BH); - if (tq_timer) + if (TQ_ACTIVE(tq_timer)) mark_bh(TQUEUE_BH); } diff --git a/net/core/dev.c b/net/core/dev.c index aca470c072aa..c49ae2ff694c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -154,6 +154,12 @@ static void sample_queue(unsigned long dummy); static struct timer_list samp_timer = { function: sample_queue }; #endif +#ifdef CONFIG_HOTPLUG +static int net_run_sbin_hotplug(struct net_device *dev, char *action); +#else +#define net_run_sbin_hotplug(dev, action) ({ 0; }) +#endif + /* * Our notifier list */ @@ -2196,9 +2202,11 @@ int dev_ioctl(unsigned int cmd, void *arg) if (!capable(CAP_NET_ADMIN)) return -EPERM; dev_load(ifr.ifr_name); + dev_probe_lock(); rtnl_lock(); ret = dev_ifsioc(&ifr, cmd); rtnl_unlock(); + dev_probe_unlock(); return ret; case SIOCGIFMEM: @@ -2217,9 +2225,11 @@ int dev_ioctl(unsigned int cmd, void *arg) if (cmd >= SIOCDEVPRIVATE && cmd <= SIOCDEVPRIVATE + 15) { dev_load(ifr.ifr_name); + dev_probe_lock(); rtnl_lock(); ret = dev_ifsioc(&ifr, cmd); rtnl_unlock(); + dev_probe_unlock(); if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; return ret; @@ -2388,10 +2398,12 @@ int register_netdevice(struct net_device *dev) if (ret) return ret; #endif /* CONFIG_NET_DIVERT */ - + /* Notify protocols, that a new device appeared. */ notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); + net_run_sbin_hotplug(dev, "register"); + return 0; } @@ -2475,6 +2487,8 @@ int unregister_netdevice(struct net_device *dev) /* Shutdown queueing discipline. */ dev_shutdown(dev); + net_run_sbin_hotplug(dev, "unregister"); + /* Notify protocols, that we are about to destroy this device. They should clean all the things. */ @@ -2714,29 +2728,15 @@ int __init net_dev_init(void) /* Notify userspace when a netdevice event occurs, * by running '/sbin/hotplug net' with certain * environment variables set. - * - * Currently reported events are listed in netdev_event_names[]. */ -/* /sbin/hotplug ONLY executes for events named here */ -static char *netdev_event_names[] = { - [NETDEV_REGISTER] = "register", - [NETDEV_UNREGISTER] = "unregister", -}; - -static int run_sbin_hotplug(struct notifier_block *this, - unsigned long event, void *ptr) +static int net_run_sbin_hotplug(struct net_device *dev, char *action) { - struct net_device *dev = (struct net_device *) ptr; - char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action[32]; + char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32]; int i; - if ((event >= ARRAY_SIZE(netdev_event_names)) || - !netdev_event_names[event]) - return NOTIFY_DONE; - sprintf(ifname, "INTERFACE=%s", dev->name); - sprintf(action, "ACTION=%s", netdev_event_names[event]); + sprintf(action_str, "ACTION=%s", action); i = 0; argv[i++] = hotplug_path; @@ -2748,27 +2748,9 @@ static int run_sbin_hotplug(struct notifier_block *this, envp [i++] = "HOME=/"; envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp [i++] = ifname; - envp [i++] = action; + envp [i++] = action_str; envp [i] = 0; - call_usermodehelper (argv [0], argv, envp); - - return NOTIFY_DONE; -} - -static struct notifier_block sbin_hotplug = { - notifier_call: run_sbin_hotplug, -}; - -/* - * called from init/main.c, -after- all the initcalls are complete. - * Registers a hook that calls /sbin/hotplug on every netdev - * addition and removal. - */ -void __init net_notifier_init (void) -{ - if (register_netdevice_notifier(&sbin_hotplug)) - printk (KERN_WARNING "unable to register netdev notifier\n" - KERN_WARNING "/sbin/hotplug will not be run.\n"); + return call_usermodehelper(argv [0], argv, envp); } #endif diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 7af589b756a9..276f7bd4f0f1 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -519,6 +519,7 @@ int devinet_ioctl(unsigned int cmd, void *arg) return -EINVAL; } + dev_probe_lock(); rtnl_lock(); if ((dev = __dev_get_by_name(ifr.ifr_name)) == NULL) { @@ -649,10 +650,12 @@ int devinet_ioctl(unsigned int cmd, void *arg) } done: rtnl_unlock(); + dev_probe_unlock(); return ret; rarok: rtnl_unlock(); + dev_probe_unlock(); if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; return 0; -- 2.39.5