diff options
Diffstat (limited to 'drivers/char')
74 files changed, 10910 insertions, 4943 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 451d898fc..5eb585a01 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -56,19 +56,13 @@ if [ "$CONFIG_MOUSE" = "y" ]; then tristate 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE tristate 'Logitech busmouse support' CONFIG_BUSMOUSE tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE - tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE - if [ "$CONFIG_PSMOUSE" != "n" ]; then - bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE - fi + bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE + tristate 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD fi -# IRIX compat stuff for X on the Indy relies on CONFIG_UMISC if [ "$CONFIG_SGI" = "y" ]; then - define_bool CONFIG_UMISC y bool 'Support for SGI graphic devices' CONFIG_SGI_GRAPHICS -elif [ "$CONFIG_MODULES" = "y" ]; then - bool 'Support for user misc device modules' CONFIG_UMISC fi tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE @@ -83,15 +77,6 @@ if [ "$CONFIG_QIC02_TAPE" != "n" ]; then fi fi -bool 'Advanced Power Management BIOS support' CONFIG_APM -if [ "$CONFIG_APM" = "y" ]; then - bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND - bool ' Enable PM at boot time' CONFIG_APM_DO_ENABLE - bool ' Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE - bool ' Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK - bool ' Power off on shutdown' CONFIG_APM_POWER_OFF - bool ' Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND -fi bool 'Watchdog Timer Support' CONFIG_WATCHDOG if [ "$CONFIG_WATCHDOG" != "n" ]; then bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT @@ -138,13 +123,15 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then hex ' SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284 fi dep_tristate 'Zoltrix Radio' CONFIG_RADIO_ZOLTRIX $CONFIG_VIDEO_DEV - if [ "$CONFIG_RADIO_ZOLTRIX" != "n" ]; then + if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then hex ' ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c fi fi tristate '/dev/nvram support' CONFIG_NVRAM -tristate 'PC joystick support' CONFIG_JOYSTICK - +tristate 'Joystick support' CONFIG_JOYSTICK +if [ "$CONFIG_JOYSTICK" != "n" ]; then + source drivers/char/joystick/Config.in +fi mainmenu_option next_comment comment 'Ftape, the floppy tape device driver' tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 36fe0d105..5796396fe 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -11,7 +11,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) ftape +ALL_SUB_DIRS := $(SUB_DIRS) ftape joystick # # This file contains the font map for the default (hardware) font @@ -21,25 +21,26 @@ FONTMAPFILE = cp437.uni L_TARGET := char.a M_OBJS := L_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o -LX_OBJS := pty.o +LX_OBJS := pty.o misc.o ifdef CONFIG_VT L_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o LX_OBJS += console.o selection.o endif -ifdef CONFIG_FB - LX_OBJS += fbmem.o -endif ifeq ($(CONFIG_SERIAL),y) - ifndef CONFIG_SUN_SERIAL - ifndef CONFIG_SGI_SERIAL + ifeq ($(CONFIG_SUN_SERIAL),) + ifeq ($(CONFIG_SGI_SERIAL),) LX_OBJS += serial.o endif endif else ifeq ($(CONFIG_SERIAL),m) - MX_OBJS += serial.o + ifeq ($(CONFIG_SUN_SERIAL),) + ifeq ($(CONFIG_SGI_SERIAL),) + MX_OBJS += serial.o + endif + endif endif endif @@ -138,22 +139,18 @@ else endif ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) -M = y L_OBJS += atixlmouse.o else ifeq ($(CONFIG_ATIXL_BUSMOUSE),m) M_OBJS += atixlmouse.o - MM = m endif endif ifeq ($(CONFIG_BUSMOUSE),y) -M = y L_OBJS += busmouse.o else ifeq ($(CONFIG_BUSMOUSE),m) M_OBJS += busmouse.o - MM = m endif endif @@ -166,147 +163,105 @@ else endif ifeq ($(CONFIG_JOYSTICK),y) -L_OBJS += joystick.o +L_OBJS += joystick/js.o +SUB_DIRS += joystick +MOD_SUB_DIRS += joystick else ifeq ($(CONFIG_JOYSTICK),m) - M_OBJS += joystick.o + MOD_SUB_DIRS += joystick endif endif ifeq ($(CONFIG_MS_BUSMOUSE),y) -M = y L_OBJS += msbusmouse.o else ifeq ($(CONFIG_MS_BUSMOUSE),m) M_OBJS += msbusmouse.o - MM = m endif endif -ifeq ($(CONFIG_PSMOUSE),y) -M = y -L_OBJS += psaux.o +ifeq ($(CONFIG_82C710_MOUSE),y) +L_OBJS += qpmouse.o else - ifeq ($(CONFIG_PSMOUSE),m) - M_OBJS += psaux.o - MM = m + ifeq ($(CONFIG_82C710_MOUSE),m) + M_OBJS += qpmouse.o endif endif -ifeq ($(CONFIG_UMISC),y) -# To support third-party modules, misc.c must reside in the kernel -M = y -endif - ifeq ($(CONFIG_SOFT_WATCHDOG),y) -M = y L_OBJS += softdog.o else ifeq ($(CONFIG_SOFT_WATCHDOG),m) M_OBJS += softdog.o - MM = m endif endif ifeq ($(CONFIG_PCWATCHDOG),y) -M = y L_OBJS += pcwd.o else ifeq ($(CONFIG_PCWATCHDOG),m) M_OBJS += pcwd.o - MM = m endif endif ifeq ($(CONFIG_ACQUIRE_WDT),y) -M = y L_OBJS += acquirewdt.o else ifeq ($(CONFIG_ACQUIRE_WDT),m) M_OBJS += acquirewdt.o - MM = m endif endif ifeq ($(CONFIG_AMIGAMOUSE),y) -M = y L_OBJS += amigamouse.o else ifeq ($(CONFIG_AMIGAMOUSE),m) M_OBJS += amigamouse.o - MM = m endif endif ifeq ($(CONFIG_ATARIMOUSE),y) -M = y L_OBJS += atarimouse.o else ifeq ($(CONFIG_ATARIMOUSE),m) M_OBJS += atarimouse.o - MM = m endif endif -ifdef CONFIG_SGI_GRAPHICS -M = y -endif - -ifeq ($(CONFIG_MACMOUSE),y) -M = y -L_OBJS += macmouse.o +ifeq ($(CONFIG_ADBMOUSE),y) +L_OBJS += adbmouse.o else - ifeq ($(CONFIG_MACMOUSE),m) - M_OBJS += macmouse.o - MM = m + ifeq ($(CONFIG_ADBMOUSE),m) + M_OBJS += adbmouse.o endif endif -ifdef CONFIG_SUN_MOUSE -M = y -endif - ifeq ($(CONFIG_PC110_PAD),y) -M = y L_OBJS += pc110pad.o else ifeq ($(CONFIG_PC110_PAD),m) M_OBJS += pc110pad.o - MM = m - endif -endif - -ifeq ($(CONFIG_SUN_OPENPROMIO),y) -M = y -else - ifeq ($(CONFIG_SUN_OPENPROMIO),m) - MM = m endif endif ifeq ($(CONFIG_WDT),y) -M = y L_OBJS += wdt.o else ifeq ($(CONFIG_WDT),m) M_OBJS += wdt.o - MM = m endif endif ifeq ($(CONFIG_RTC),y) -M = y L_OBJS += rtc.o endif ifeq ($(CONFIG_NVRAM),y) -M = y ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),) L_OBJS += nvram.o endif else ifeq ($(CONFIG_NVRAM),m) - MM = m ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),) M_OBJS += nvram.o endif @@ -441,22 +396,8 @@ else endif endif -ifdef CONFIG_APM -LX_OBJS += apm_bios.o -M = y -endif - ifdef CONFIG_H8 LX_OBJS += h8.o -M = y -endif - -ifdef M -LX_OBJS += misc.o -else - ifdef MM - MX_OBJS += misc.o - endif endif ifeq ($(L_I2C),y) diff --git a/drivers/char/adbmouse.c b/drivers/char/adbmouse.c index 43930ff1b..39b82c0a4 100644 --- a/drivers/char/adbmouse.c +++ b/drivers/char/adbmouse.c @@ -3,6 +3,7 @@ * * 27 Oct 1997 Michael Schmitz * logitech fixes by anthony tong + * further hacking by Paul Mackerras * * Apple mouse protocol according to: * @@ -33,14 +34,21 @@ #include <linux/init.h> #include <asm/adb_mouse.h> -#include <asm/segment.h> +#include <asm/uaccess.h> +#ifdef __powerpc__ #include <asm/processor.h> +#endif +#ifdef __mc68000__ +#include <asm/setup.h> +#endif static struct mouse_status mouse; -static int adb_mouse_x_threshold = 2, adb_mouse_y_threshold = 2; -static int adb_mouse_buttons = 0; +static unsigned char adb_mouse_buttons[16]; -extern void (*adb_mouse_interrupt_hook) (char *, int); +extern void (*adb_mouse_interrupt_hook)(unsigned char *, int); +extern int adb_emulate_buttons; +extern int adb_button2_keycode; +extern int adb_button3_keycode; extern int console_loglevel; @@ -48,11 +56,11 @@ extern int console_loglevel; * XXX: need to figure out what ADB mouse packets mean ... * This is the stuff stolen from the Atari driver ... */ -static void adb_mouse_interrupt(char *buf, int nb) +static void adb_mouse_interrupt(unsigned char *buf, int nb) { - static int buttons = 7; + int buttons, id; - /* +/* Handler 1 -- 100cpi original Apple mouse protocol. Handler 2 -- 200cpi original Apple mouse protocol. @@ -60,76 +68,62 @@ static void adb_mouse_interrupt(char *buf, int nb) contain the following values: BITS COMMENTS - data[0] = 0000 0000 ADB packet identifer. - data[1] = ???? ???? (?) - data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device. - data[3] = bxxx xxxx First button and x-axis motion. - data[4] = byyy yyyy Second button and y-axis motion. - - NOTE: data[0] is confirmed by the parent function and need not be - checked here. - */ + data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd. + data[1] = bxxx xxxx First button and x-axis motion. + data[2] = byyy yyyy Second button and y-axis motion. - /* Handler 4 -- Apple Extended mouse protocol. For Apple's 3-button mouse protocol the data array will contain the following values: BITS COMMENTS - data[0] = 0000 0000 ADB packet identifer. - data[1] = 0100 0000 Extended protocol register. - Bits 6-7 are the device id, which should be 1. - Bits 4-5 are resolution which is in "units/inch". - The Logitech MouseMan returns these bits clear but it has - 200/300cpi resolution. - Bits 0-3 are unique vendor id. - data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device. - Bits 2-3 should be 8 + 4. - Bits 4-7 should be 3 for a mouse device. - data[3] = bxxx xxxx Left button and x-axis motion. - data[4] = byyy yyyy Second button and y-axis motion. - data[5] = byyy bxxx Third button and fourth button. - Y is additiona. high bits of y-axis motion. + data[0] = dddd 1100 ADB command: Talk, register 0, for device dddd. + data[1] = bxxx xxxx Left button and x-axis motion. + data[2] = byyy yyyy Second button and y-axis motion. + data[3] = byyy bxxx Third button and fourth button. + Y is additional high bits of y-axis motion. X is additional high bits of x-axis motion. - NOTE: data[0] and data[2] are confirmed by the parent function and - need not be checked here. - */ + This procedure also gets called from the keyboard code if we + are emulating mouse buttons with keys. In this case data[0] == 0 + (data[0] cannot be 0 for a real ADB packet). - /* - * 'buttons' here means 'button down' states! - * Button 1 (left) : bit 2, busmouse button 3 - * Button 2 (right) : bit 0, busmouse button 1 - * Button 3 (middle): bit 1, busmouse button 2 - */ + 'buttons' here means 'button down' states! + Button 1 (left) : bit 2, busmouse button 3 + Button 2 (middle): bit 1, busmouse button 2 + Button 3 (right) : bit 0, busmouse button 1 +*/ /* x/y and buttons swapped */ - if (nb > 0) { /* real packet : use buttons? */ - if (console_loglevel >= 8) - printk("adb_mouse: real data; "); - /* button 1 (left, bit 2) : always significant ! */ - buttons = (buttons&3) | (buf[1] & 0x80 ? 4 : 0); /* 1+2 unchanged */ - /* button 2 (middle) */ - buttons = (buttons&5) | (buf[2] & 0x80 ? 2 : 0); /* 2+3 unchanged */ - /* button 3 (right) present? - * on a logitech mouseman, the right and mid buttons sometimes behave - * strangely until they both have been pressed after booting. */ - /* data valid only if extended mouse format ! (buf[3] = 0 else) */ - if ( nb == 6 ) - buttons = (buttons&6) | (buf[3] & 0x80 ? 1 : 0); /* 1+3 unchanged */ - } else { /* fake packet : use 2+3 */ - if (console_loglevel >= 8) - printk("adb_mouse: fake data; "); - /* we only see state changes here, but the fake driver takes care - * to preserve state... button 1 state must stay unchanged! */ - buttons = (buttons&4) | ((buf[2] & 0x80 ? 1 : 0) | - (buf[3] & 0x80 ? 2 : 0)); - } - - add_mouse_randomness(((~buttons & 7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f)); - mouse.buttons = buttons & 7; + if (console_loglevel >= 8) + printk("KERN_DEBUG adb_mouse: %s data; ", buf[0]? "real": "fake"); + + id = (buf[0] >> 4) & 0xf; + buttons = adb_mouse_buttons[id]; + + /* button 1 (left, bit 2) */ + buttons = (buttons&3) | (buf[1] & 0x80 ? 4 : 0); /* 1+2 unchanged */ + + /* button 2 (middle) */ + buttons = (buttons&5) | (buf[2] & 0x80 ? 2 : 0); /* 2+3 unchanged */ + + /* button 3 (right) present? + * on a logitech mouseman, the right and mid buttons sometimes behave + * strangely until they both have been pressed after booting. */ + /* data valid only if extended mouse format ! */ + if (nb == 4) + buttons = (buttons&6) | (buf[3] & 0x80 ? 1 : 0); /* 1+3 unchanged */ + + add_mouse_randomness(((~buttons&7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f)); + + adb_mouse_buttons[id] = buttons; + /* a button is down if it is down on any mouse */ + for (id = 0; id < 16; ++id) + buttons &= adb_mouse_buttons[id]; + + mouse.buttons = buttons; mouse.dx += ((buf[2]&0x7f) < 64 ? (buf[2]&0x7f) : (buf[2]&0x7f)-128 ); mouse.dy -= ((buf[1]&0x7f) < 64 ? (buf[1]&0x7f) : (buf[1]&0x7f)-128 ); @@ -141,7 +135,6 @@ static void adb_mouse_interrupt(char *buf, int nb) wake_up_interruptible(&mouse.wait); if (mouse.fasyncptr) kill_fasync(mouse.fasyncptr, SIGIO); - } static int fasync_mouse(int fd, struct file *filp, int on) @@ -167,13 +160,16 @@ static int release_mouse(struct inode *inode, struct file *file) static int open_mouse(struct inode *inode, struct file *file) { + int id; + if (mouse.active++) return 0; mouse.ready = 0; mouse.dx = mouse.dy = 0; - adb_mouse_buttons = 0; + for (id = 0; id < 16; ++id) + adb_mouse_buttons[id] = 7; /* all buttons up */ MOD_INC_USE_COUNT; adb_mouse_interrupt_hook = adb_mouse_interrupt; return 0; @@ -198,24 +194,24 @@ static ssize_t read_mouse(struct file *file, char *buffer, size_t count, dy = mouse.dy; buttons = mouse.buttons; if (dx > 127) - dx = 127; + dx = 127; else if (dx < -128) - dx = -128; + dx = -128; if (dy > 127) - dy = 127; + dy = 127; else if (dy < -128) - dy = -128; + dy = -128; mouse.dx -= dx; mouse.dy -= dy; if (mouse.dx == 0 && mouse.dy == 0) - mouse.ready = 0; + mouse.ready = 0; if (put_user(buttons | 0x80, buffer++) || put_user((char) dx, buffer++) || put_user((char) dy, buffer++)) - return -EFAULT; - if (count > 3) - if (clear_user(buffer, count - 3)) return -EFAULT; + if (count > 3) + if (clear_user(buffer, count - 3)) + return -EFAULT; return count; } @@ -254,40 +250,35 @@ __initfunc(int adb_mouse_init(void)) mouse.ready = 0; mouse.wait = NULL; +#ifdef __powerpc__ if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) return -ENODEV; - printk(KERN_INFO "Macintosh ADB mouse installed.\n"); +#endif +#ifdef __mc68000__ + if (!MACH_IS_MAC) + return -ENODEV; +#endif + printk(KERN_INFO "Macintosh ADB mouse driver installed.\n"); misc_register(&adb_mouse); return 0; } -#define MIN_THRESHOLD 1 -#define MAX_THRESHOLD 20 /* more seems not reasonable... */ - +/* + * XXX this function is misnamed. + * It is called if the kernel is booted with the adb_buttons=xxx + * option, which is about using ADB keyboard buttons to emulate + * mouse buttons. -- paulus + */ __initfunc(void adb_mouse_setup(char *str, int *ints)) { - if (ints[0] < 1) { - printk( "adb_mouse_setup: no arguments!\n" ); - return; - } - else if (ints[0] > 2) { - printk( "adb_mouse_setup: too many arguments\n" ); - } - - if (ints[1] < MIN_THRESHOLD || ints[1] > MAX_THRESHOLD) - printk( "adb_mouse_setup: bad threshold value (ignored)\n" ); - else { - adb_mouse_x_threshold = ints[1]; - adb_mouse_y_threshold = ints[1]; - if (ints[0] > 1) { - if (ints[2] < MIN_THRESHOLD || ints[2] > MAX_THRESHOLD) - printk("adb_mouse_setup: bad threshold value (ignored)\n" ); - else - adb_mouse_y_threshold = ints[2]; + if (ints[0] >= 1) { + adb_emulate_buttons = ints[1] > 0; + if (ints[1] > 1) + adb_button2_keycode = ints[1]; + if (ints[0] >= 2) + adb_button3_keycode = ints[2]; } - } - } #ifdef MODULE diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c deleted file mode 100644 index 08b3a78a2..000000000 --- a/drivers/char/apm_bios.c +++ /dev/null @@ -1,1346 +0,0 @@ -/* -*- linux-c -*- - * APM BIOS driver for Linux - * Copyright 1994-1998 Stephen Rothwell - * (Stephen.Rothwell@canb.auug.org.au) - * - * 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, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * October 1995, Rik Faith (faith@cs.unc.edu): - * Minor enhancements and updates (to the patch set) for 1.3.x - * Documentation - * January 1996, Rik Faith (faith@cs.unc.edu): - * Make /proc/apm easy to format (bump driver version) - * March 1996, Rik Faith (faith@cs.unc.edu): - * Prohibit APM BIOS calls unless apm_enabled. - * (Thanks to Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de>) - * April 1996, Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au) - * Version 1.0 and 1.1 - * May 1996, Version 1.2 - * Feb 1998, Version 1.3 - * Feb 1998, Version 1.4 - * Aug 1998, Version 1.5 - * - * History: - * 0.6b: first version in official kernel, Linux 1.3.46 - * 0.7: changed /proc/apm format, Linux 1.3.58 - * 0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59 - * 0.9: only call bios if bios is present, Linux 1.3.72 - * 1.0: use fixed device number, consolidate /proc/apm into this file, - * Linux 1.3.85 - * 1.1: support user-space standby and suspend, power off after system - * halted, Linux 1.3.98 - * 1.2: When resetting RTC after resume, take care so that the time - * is only incorrect by 30-60mS (vs. 1S previously) (Gabor J. Toth - * <jtoth@princeton.edu>); improve interaction between - * screen-blanking and gpm (Stephen Rothwell); Linux 1.99.4 - * 1.2a:Simple change to stop mysterious bug reports with SMP also added - * levels to the printk calls. APM is not defined for SMP machines. - * The new replacment for it is, but Linux doesn't yet support this. - * Alan Cox Linux 2.1.55 - * 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's - * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by - * Dean Gaudet <dgaudet@arctic.org>. - * C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87 - * 1.5: Fix segment register reloading (in case of bad segments saved - * across BIOS call). - * Stephen ROthwell - * - * APM 1.1 Reference: - * - * Intel Corporation, Microsoft Corporation. Advanced Power Management - * (APM) BIOS Interface Specification, Revision 1.1, September 1993. - * Intel Order Number 241704-001. Microsoft Part Number 781-110-X01. - * - * [This document is available free from Intel by calling 800.628.8686 (fax - * 916.356.6100) or 800.548.4725; or via anonymous ftp from - * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc. It is also - * available from Microsoft by calling 206.882.8080.] - * - * APM 1.2 Reference: - * Intel Corporation, Microsoft Corporation. Advanced Power Management - * (APM) BIOS Interface Specification, Revision 1.2, February 1996. - * - * [This document is available from Intel at: - * http://www.intel.com/IAL/powermgm - * or Microsoft at - * http://www.microsoft.com/windows/thirdparty/hardware/pcfuture.htm - * ] - */ - -#include <linux/config.h> -#include <linux/module.h> - -#include <asm/system.h> -#include <asm/uaccess.h> - -#include <linux/poll.h> -#include <linux/types.h> -#include <linux/stddef.h> -#include <linux/timer.h> -#include <linux/fcntl.h> -#include <linux/malloc.h> -#include <linux/linkage.h> -#ifdef CONFIG_PROC_FS -#include <linux/stat.h> -#include <linux/proc_fs.h> -#endif -#include <linux/miscdevice.h> -#include <linux/apm_bios.h> -#include <linux/init.h> - -EXPORT_SYMBOL(apm_register_callback); -EXPORT_SYMBOL(apm_unregister_callback); - -extern unsigned long get_cmos_time(void); - -/* - * The apm_bios device is one of the misc char devices. - * This is its minor number. - */ -#define APM_MINOR_DEV 134 - -/* Configurable options: - * - * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests. - * This is necessary on the NEC Versa M series, which generates these when - * resuming from SYSTEM SUSPEND. However, enabling this on other laptops - * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate - * USER SUSPEND is ignored -- this may prevent the APM driver from updating - * the system time on a RESUME. - * - * CONFIG_APM_DO_ENABLE: enable APM features at boot time. From page 36 of - * the specification: "When disabled, the APM BIOS does not automatically - * power manage devices, enter the Standby State, enter the Suspend State, - * or take power saving steps in response to CPU Idle calls." This driver - * will make CPU Idle calls when Linux is idle (unless this feature is - * turned off -- see below). This should always save battery power, but - * more complicated APM features will be dependent on your BIOS - * implementation. You may need to turn this option off if your computer - * hangs at boot time when using APM support, or if it beeps continuously - * instead of suspending. Turn this off if you have a NEC UltraLite Versa - * 33/C or a Toshiba T400CDT. This is off by default since most machines - * do fine without this feature. - * - * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the - * idle loop. On some machines, this can activate improved power savings, - * such as a slowed CPU clock rate, when the machine is idle. These idle - * call is made after the idle loop has run for some length of time (e.g., - * 333 mS). On some machines, this will cause a hang at boot time or - * whenever the CPU becomes idle. - * - * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM. Some - * laptops can use this to turn of the LCD backlight when the VC screen - * blanker blanks the screen. Note that this is only used by the VC screen - * blanker, and probably won't turn off the backlight when using X11. Some - * problems have been reported when using this option with gpm (if you'd - * like to debug this, please do so). - * - * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist - * on returning multiple suspend/standby events whenever one occurs. We - * really only need one at a time, so just ignore any beyond the first. - * This is probably safe on most laptops. - * - * If you are debugging the APM support for your laptop, note that code for - * all of these options is contained in this file, so you can #define or - * #undef these on the next line to avoid recompiling the whole kernel. - * - */ - -/* KNOWN PROBLEM MACHINES: - * - * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant - * [Confirmed by TI representative] - * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification - * [Confirmed by BIOS disassembly] - * [This may work now ...] - * P: Toshiba 1950S: battery life information only gets updated after resume - * P: Midwest Micro Soundbook Elite DX2/66 monochrome: screen blanking - * broken in BIOS [Reported by Garst R. Reese <reese@isn.net>] - * - * Legend: U = unusable with APM patches - * P = partially usable with APM patches - */ - -/* - * Define to have debug messages. - */ -#undef APM_DEBUG - -/* - * Define to always call the APM BIOS busy routine even if the clock was - * not slowed by the idle routine. - */ -#define ALWAYS_CALL_BUSY - -/* - * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call - * should turn interrupts on before it does a 'hlt'). - * This reportedly needs undefining for the ThinkPad 600. - */ -#define APM_NOINTS - -/* - * Define to make the APM BIOS calls zero all data segment registers (so - * that an incorrect BIOS implementation will cause a kernel panic if it - * tries to write to arbitrary memory). - */ -#define APM_ZERO_SEGS - -/* - * Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is - * supposed to provide limit information that it recognizes. Many machines - * do this correctly, but many others do not restrict themselves to their - * claimed limit. When this happens, they will cause a segmentation - * violation in the kernel at boot time. Most BIOS's, however, will - * respect a 64k limit, so we use that. If you want to be pedantic and - * hold your BIOS to its claims, then undefine this. - */ -#define APM_RELAX_SEGMENTS - -/* - * Need to poll the APM BIOS every second - */ -#define APM_CHECK_TIMEOUT (HZ) - -/* - * Save a segment register away - */ -#define savesegment(seg, where) __asm__ __volatile__("movw %%" #seg ", %0\n" : "=m" (where)) - -/* - * Forward declarations - */ -static void suspend(void); -static void standby(void); -static void set_time(void); - -static void check_events(void); -static void do_apm_timer(unsigned long); - -static int do_open(struct inode *, struct file *); -static int do_release(struct inode *, struct file *); -static ssize_t do_read(struct file *, char *, size_t , loff_t *); -static unsigned int do_poll(struct file *, poll_table *); -static int do_ioctl(struct inode *, struct file *, u_int, u_long); - -#ifdef CONFIG_PROC_FS -static int apm_get_info(char *, char **, off_t, int, int); -#endif - -extern int apm_register_callback(int (*)(apm_event_t)); -extern void apm_unregister_callback(int (*)(apm_event_t)); - -/* - * Local variables - */ -static asmlinkage struct { - unsigned long offset; - unsigned short segment; -} apm_bios_entry; -static int apm_enabled = 0; -#ifdef CONFIG_APM_CPU_IDLE -static int clock_slowed = 0; -#endif -static int suspends_pending = 0; -static int standbys_pending = 0; -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND -static int waiting_for_resume = 0; -#endif - -static long clock_cmos_diff; -static int got_clock_diff = 0; - -static struct wait_queue * process_list = NULL; -static struct apm_bios_struct * user_list = NULL; - -static struct timer_list apm_timer; - -static char driver_version[] = "1.5"; /* no spaces */ - -#ifdef APM_DEBUG -static char * apm_event_name[] = { - "system standby", - "system suspend", - "normal resume", - "critical resume", - "low battery", - "power status change", - "update time", - "critical suspend", - "user standby", - "user suspend", - "system standby resume", - "capabilities change" -}; -#define NR_APM_EVENT_NAME \ - (sizeof(apm_event_name) / sizeof(apm_event_name[0])) -#endif - -static struct file_operations apm_bios_fops = { - NULL, /* lseek */ - do_read, - NULL, /* write */ - NULL, /* readdir */ - do_poll, - do_ioctl, - NULL, /* mmap */ - do_open, - NULL, /* flush */ - do_release, - NULL, /* fsync */ - NULL /* fasync */ -}; - -static struct miscdevice apm_device = { - APM_MINOR_DEV, - "apm", - &apm_bios_fops -}; - -typedef struct callback_list_t { - int (* callback)(apm_event_t); - struct callback_list_t * next; -} callback_list_t; - -static callback_list_t * callback_list = NULL; - -typedef struct lookup_t { - int key; - char * msg; -} lookup_t; - -static const lookup_t error_table[] = { -/* N/A { APM_SUCCESS, "Operation succeeded" }, */ - { APM_DISABLED, "Power management disabled" }, - { APM_CONNECTED, "Real mode interface already connected" }, - { APM_NOT_CONNECTED, "Interface not connected" }, - { APM_16_CONNECTED, "16 bit interface already connected" }, -/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ - { APM_32_CONNECTED, "32 bit interface already connected" }, - { APM_32_UNSUPPORTED, "32 bit interface not supported" }, - { APM_BAD_DEVICE, "Unrecognized device ID" }, - { APM_BAD_PARAM, "Parameter out of range" }, - { APM_NOT_ENGAGED, "Interface not engaged" }, - { APM_BAD_FUNCTION, "Function not supported" }, - { APM_RESUME_DISABLED, "Resume timer disabled" }, - { APM_BAD_STATE, "Unable to enter requested state" }, -/* N/A { APM_NO_EVENTS, "No events pending" }, */ - { APM_NOT_PRESENT, "No APM present" } -}; -#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t)) - -/* - * These are the actual BIOS calls. Depending on APM_ZERO_SEGS - * and APM_NOINTS, we are being really paranoid here! Not only are - * interrupts disabled, but all the segment registers (except SS) are - * saved and zeroed this means that if the BIOS tries to reference any - * data without explicitly loading the segment registers, the kernel will - * fault immediately rather than have some unforeseen circumstances for - * the rest of the kernel. And it will be very obvious! :-) Doing this - * depends on CS referring to the same physical memory as DS so that DS - * can be zeroed before the call. Unfortunately, we can't do anything - * about the stack segment/pointer. Also, we tell the compiler that - * everything could change. - */ - -static inline int apm_bios_call(u32 eax_in, u32 ebx_in, u32 ecx_in, - u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) -{ - u16 old_fs; - u16 old_gs; - int error; - -#ifdef APM_ZERO_SEGS - savesegment(fs, old_fs); - savesegment(gs, old_gs); -#endif - __asm__ __volatile__( - "pushfl\n\t" -#ifdef APM_NOINTS - "cli\n\t" -#endif -#ifdef APM_ZERO_SEGS - "pushl %%ds\n\t" - "pushl %%es\n\t" - "movw %9, %%ds\n\t" - "movw %9, %%es\n\t" - "movw %9, %%fs\n\t" - "movw %9, %%gs\n\t" -#endif - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" - "movl $0, %%edi\n\t" - "jnc 1f\n\t" - "movl $1, %%edi\n" - "1:\tpopl %%es\n\t" - "popl %%ds\n\t" - "popfl\n\t" - : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx), - "=S" (*esi), "=D" (error) - : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) -#ifdef APM_ZERO_SEGS - , "r" (0) -#endif - : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory"); -#ifdef APM_ZERO_SEGS - loadsegment(fs, old_fs); - loadsegment(gs, old_gs); -#endif - return error; -} - -/* - * This version only returns one value (usually an error code) - */ - -static inline int apm_bios_call_simple(u32 eax_in, u32 ebx_in, u32 ecx_in, u32 *eax) -{ - u16 old_fs; - u16 old_gs; - int error; - -#ifdef APM_ZERO_SEGS - savesegment(fs, old_fs); - savesegment(gs, old_gs); -#endif - __asm__ __volatile__( - "pushfl\n\t" -#ifdef APM_NOINTS - "cli\n\t" -#endif -#ifdef APM_ZERO_SEGS - "pushl %%ds\n\t" - "pushl %%es\n\t" - "movw %5, %%ds\n\t" - "movw %5, %%es\n\t" - "movw %5, %%fs\n\t" - "movw %5, %%gs\n\t" -#endif - "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" - "movl $0, %%edi\n\t" - "jnc 1f\n\t" - "movl $1, %%edi\n" - "1:\tpopl %%es\n\t" - "popl %%ds\n\t" - "popfl\n\t" - : "=a" (*eax), "=D" (error) - : "a" (eax_in), "b" (ebx_in), "c" (ecx_in) -#ifdef APM_ZERO_SEGS - , "r" (0) -#endif - : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory"); -#ifdef APM_ZERO_SEGS - loadsegment(fs, old_fs); - loadsegment(gs, old_gs); -#endif - return error; -} - -static int apm_driver_version(u_short *val) -{ - int error; - u32 eax; - - error = apm_bios_call_simple(0x530e, 0, *val, &eax); - if (error) - return (eax >> 8) & 0xff; - *val = eax; - return APM_SUCCESS; -} - -static int apm_get_event(apm_event_t *event, apm_eventinfo_t *info) -{ - int error; - u32 eax; - u32 ebx; - u32 ecx; - u32 dummy; - - error = apm_bios_call(0x530b, 0, 0, &eax, &ebx, &ecx, &dummy, &dummy); - if (error) - return (eax >> 8) & 0xff; - *event = ebx; - if (apm_bios_info.version < 0x0102) - *info = ~0; /* indicate info not valid */ - else - *info = ecx; - return APM_SUCCESS; -} - -static int set_power_state(u_short what, u_short state) -{ - int error; - u32 eax; - - error = apm_bios_call_simple(0x5307, what, state, &eax); - if (error) - return (eax >> 8) & 0xff; - return APM_SUCCESS; -} - -int apm_set_power_state(u_short state) -{ - return set_power_state(0x0001, state); -} - -#ifdef CONFIG_APM_DISPLAY_BLANK -/* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ -static int apm_set_display_power_state(u_short state) -{ - return set_power_state(0x01ff, state); -} -#endif - -#ifdef CONFIG_APM_DO_ENABLE -/* Called by apm_setup if apm_enabled will be true. */ -static int apm_enable_power_management(void) -{ - int error; - u32 eax; - - error = apm_bios_call_simple(0x5308, - (apm_bios_info.version > 0x100) ? 0x0001 : 0xffff, 1, &eax); - if (error) - return (eax >> 8) & 0xff; - return APM_SUCCESS; -} -#endif - -static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) -{ - int error; - u32 eax; - u32 ebx; - u32 ecx; - u32 edx; - u32 dummy; - - error = apm_bios_call(0x530a, 1, 0, &eax, &ebx, &ecx, &edx, &dummy); - if (error) - return (eax >> 8) & 0xff; - *status = ebx; - *bat = ecx; - *life = edx; - return APM_SUCCESS; -} - -#if 0 -/* not used anywhere */ -static int apm_get_battery_status(u_short which, - u_short *bat, u_short *life, u_short *nbat) -{ - u_short status; - int error; - u32 eax; - u32 ebx; - u32 ecx; - u32 edx; - u32 esi; - - if (apm_bios_info.version < 0x0102) { - /* pretend we only have one battery. */ - if (which != 1) - return APM_BAD_DEVICE; - *nbat = 1; - return apm_get_power_status(&status, bat, life); - } - - error = apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi); - if (error) - return (eax >> 8) & 0xff; - *bat = ecx; - *life = edx; - *nbat = esi; - return APM_SUCCESS; -} -#endif - -static int apm_engage_power_management(u_short device) -{ - int error; - u32 eax; - - error = apm_bios_call_simple(0x530f, device, 1, &eax); - if (error) - return (eax >> 8) & 0xff; - return APM_SUCCESS; -} - -static void apm_error(char *str, int err) -{ - int i; - - for (i = 0; i < ERROR_COUNT; i++) - if (error_table[i].key == err) break; - if (i < ERROR_COUNT) - printk(KERN_NOTICE "apm_bios: %s: %s\n", str, error_table[i].msg); - else - printk(KERN_NOTICE "apm_bios: %s: unknown error code %#2.2x\n", str, err); -} - -/* Called from console driver -- must make sure apm_enabled. */ -int apm_display_blank(void) -{ -#ifdef CONFIG_APM_DISPLAY_BLANK - int error; - - if (!apm_enabled) - return 0; - error = apm_set_display_power_state(APM_STATE_STANDBY); - if (error == APM_SUCCESS) - return 1; - apm_error("set display standby", error); -#endif - return 0; -} - -/* Called from console driver -- must make sure apm_enabled. */ -int apm_display_unblank(void) -{ -#ifdef CONFIG_APM_DISPLAY_BLANK - int error; - - if (!apm_enabled) - return 0; - error = apm_set_display_power_state(APM_STATE_READY); - if (error == APM_SUCCESS) - return 1; - apm_error("set display ready", error); -#endif - return 0; -} - -int apm_register_callback(int (*callback)(apm_event_t)) -{ - callback_list_t * new; - - new = kmalloc(sizeof(callback_list_t), GFP_KERNEL); - if (new == NULL) - return -ENOMEM; - new->callback = callback; - new->next = callback_list; - callback_list = new; - return 0; -} - -void apm_unregister_callback(int (*callback)(apm_event_t)) -{ - callback_list_t ** ptr; - callback_list_t * old; - - ptr = &callback_list; - for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next) - if ((*ptr)->callback == callback) - break; - old = *ptr; - *ptr = old->next; - kfree_s(old, sizeof(callback_list_t)); -} - -static int queue_empty(struct apm_bios_struct * as) -{ - return as->event_head == as->event_tail; -} - -static apm_event_t get_queued_event(struct apm_bios_struct * as) -{ - as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; - return as->events[as->event_tail]; -} - -static int queue_event(apm_event_t event, struct apm_bios_struct *sender) -{ - struct apm_bios_struct * as; - - if (user_list == NULL) - return 0; - for (as = user_list; as != NULL; as = as->next) { - if (as == sender) - continue; - as->event_head = (as->event_head + 1) % APM_MAX_EVENTS; - if (as->event_head == as->event_tail) { - static int notified; - - if (notified == 0) { - printk( "apm_bios: an event queue overflowed\n" ); - notified = 1; - } - as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; - } - as->events[as->event_head] = event; - if (!as->suser) - continue; - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - as->suspends_pending++; - suspends_pending++; - break; - - case APM_SYS_STANDBY: - case APM_USER_STANDBY: - as->standbys_pending++; - standbys_pending++; - break; - } - } - wake_up_interruptible(&process_list); - return 1; -} - -static void set_time(void) -{ - unsigned long flags; - - if (!got_clock_diff) /* Don't know time zone, can't set clock */ - return; - - save_flags(flags); - cli(); - CURRENT_TIME = get_cmos_time() + clock_cmos_diff; - restore_flags(flags); -} - -static void suspend(void) -{ - unsigned long flags; - int err; - - /* Estimate time zone so that set_time can - update the clock */ - save_flags(flags); - clock_cmos_diff = -get_cmos_time(); - cli(); - clock_cmos_diff += CURRENT_TIME; - got_clock_diff = 1; - restore_flags(flags); - - err = apm_set_power_state(APM_STATE_SUSPEND); - if (err) - apm_error("suspend", err); - set_time(); -} - -static void standby(void) -{ - int err; - - err = apm_set_power_state(APM_STATE_STANDBY); - if (err) - apm_error("standby", err); -} - -static apm_event_t get_event(void) -{ - int error; - apm_event_t event; - apm_eventinfo_t info; - - static int notified = 0; - - /* we don't use the eventinfo */ - error = apm_get_event(&event, &info); - if (error == APM_SUCCESS) - return event; - - if ((error != APM_NO_EVENTS) && (notified++ == 0)) - apm_error("get_event", error); - - return 0; -} - -static void send_event(apm_event_t event, apm_event_t undo, - struct apm_bios_struct *sender) -{ - callback_list_t * call; - callback_list_t * fix; - - for (call = callback_list; call != NULL; call = call->next) { - if (call->callback(event) && undo) { - for (fix = callback_list; fix != call; fix = fix->next) - fix->callback(undo); - if (apm_bios_info.version > 0x100) - apm_set_power_state(APM_STATE_REJECT); - return; - } - } - - queue_event(event, sender); -} - -static void check_events(void) -{ - apm_event_t event; - - while ((event = get_event()) != 0) { -#ifdef APM_DEBUG - if (event <= NR_APM_EVENT_NAME) - printk(KERN_DEBUG "APM BIOS received %s notify\n", - apm_event_name[event - 1]); - else - printk(KERN_DEBUG "APM BIOS received unknown " - "event 0x%02x\n", event); -#endif - switch (event) { - case APM_SYS_STANDBY: - case APM_USER_STANDBY: -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - if (waiting_for_resume) { - return; - } - waiting_for_resume = 1; -#endif - send_event(event, APM_STANDBY_RESUME, NULL); - if (standbys_pending <= 0) - standby(); - break; - - case APM_USER_SUSPEND: -#ifdef CONFIG_APM_IGNORE_USER_SUSPEND - if (apm_bios_info.version > 0x100) - apm_set_power_state(APM_STATE_REJECT); - break; -#endif - case APM_SYS_SUSPEND: -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - if (waiting_for_resume) { - return; - } - waiting_for_resume = 1; -#endif - send_event(event, APM_NORMAL_RESUME, NULL); - if (suspends_pending <= 0) - suspend(); - break; - - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - case APM_STANDBY_RESUME: -#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND - waiting_for_resume = 0; -#endif - set_time(); - send_event(event, 0, NULL); - break; - - case APM_LOW_BATTERY: - case APM_POWER_STATUS_CHANGE: - case APM_CAPABILITY_CHANGE: - send_event(event, 0, NULL); - break; - - case APM_UPDATE_TIME: - set_time(); - break; - - case APM_CRITICAL_SUSPEND: - suspend(); - break; - } - } -} - -static void do_apm_timer(unsigned long unused) -{ - int err; - - static int pending_count = 0; - - if (((standbys_pending > 0) || (suspends_pending > 0)) - && (apm_bios_info.version > 0x100) - && (pending_count-- <= 0)) { - pending_count = 4; - - err = apm_set_power_state(APM_STATE_BUSY); - if (err) - apm_error("busy", err); - } - - if (!(((standbys_pending > 0) || (suspends_pending > 0)) - && (apm_bios_info.version == 0x100))) - check_events(); - - init_timer(&apm_timer); - apm_timer.expires = APM_CHECK_TIMEOUT + jiffies; - add_timer(&apm_timer); -} - -/* Called from sys_idle, must make sure apm_enabled. */ -int apm_do_idle(void) -{ -#ifdef CONFIG_APM_CPU_IDLE - int error; - u32 dummy; - - if (!apm_enabled) - return 0; - - error = apm_bios_call_simple(0x5305, 0, 0, &dummy); - if (error) - return 0; - - clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0; - return 1; -#else - return 0; -#endif -} - -/* Called from sys_idle, must make sure apm_enabled. */ -void apm_do_busy(void) -{ -#ifdef CONFIG_APM_CPU_IDLE - u32 dummy; - - if (apm_enabled -#ifndef ALWAYS_CALL_BUSY - && clock_slowed -#endif - ) { - (void) apm_bios_call_simple(0x5306, 0, 0, &dummy); - clock_slowed = 0; - } -#endif -} - -static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func) -{ - if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) { - printk(KERN_ERR "apm_bios: %s passed bad filp", func); - return 1; - } - return 0; -} - -static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos) -{ - struct apm_bios_struct * as; - int i; - apm_event_t event; - struct wait_queue wait = { current, NULL }; - - as = fp->private_data; - if (check_apm_bios_struct(as, "read")) - return -EIO; - if (count < sizeof(apm_event_t)) - return -EINVAL; - if (queue_empty(as)) { - if (fp->f_flags & O_NONBLOCK) - return -EAGAIN; - add_wait_queue(&process_list, &wait); -repeat: - current->state = TASK_INTERRUPTIBLE; - if (queue_empty(as) && !signal_pending(current)) { - schedule(); - goto repeat; - } - current->state = TASK_RUNNING; - remove_wait_queue(&process_list, &wait); - } - i = count; - while ((i >= sizeof(event)) && !queue_empty(as)) { - event = get_queued_event(as); - copy_to_user(buf, &event, sizeof(event)); - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - as->suspends_read++; - break; - - case APM_SYS_STANDBY: - case APM_USER_STANDBY: - as->standbys_read++; - break; - } - buf += sizeof(event); - i -= sizeof(event); - } - if (i < count) - return count - i; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static unsigned int do_poll(struct file *fp, poll_table * wait) -{ - struct apm_bios_struct * as; - - as = fp->private_data; - if (check_apm_bios_struct(as, "select")) - return 0; - poll_wait(fp, &process_list, wait); - if (!queue_empty(as)) - return POLLIN | POLLRDNORM; - return 0; -} - -static int do_ioctl(struct inode * inode, struct file *filp, - u_int cmd, u_long arg) -{ - struct apm_bios_struct * as; - - as = filp->private_data; - if (check_apm_bios_struct(as, "ioctl")) - return -EIO; - if (!as->suser) - return -EPERM; - switch (cmd) { - case APM_IOC_STANDBY: - if (as->standbys_read > 0) { - as->standbys_read--; - as->standbys_pending--; - standbys_pending--; - } - else - send_event(APM_USER_STANDBY, APM_STANDBY_RESUME, as); - if (standbys_pending <= 0) - standby(); - break; - case APM_IOC_SUSPEND: - if (as->suspends_read > 0) { - as->suspends_read--; - as->suspends_pending--; - suspends_pending--; - } - else - send_event(APM_USER_SUSPEND, APM_NORMAL_RESUME, as); - if (suspends_pending <= 0) - suspend(); - break; - default: - return -EINVAL; - } - return 0; -} - -static int do_release(struct inode * inode, struct file * filp) -{ - struct apm_bios_struct * as; - - as = filp->private_data; - filp->private_data = NULL; - if (check_apm_bios_struct(as, "release")) - return 0; - if (as->standbys_pending > 0) { - standbys_pending -= as->standbys_pending; - if (standbys_pending <= 0) - standby(); - } - if (as->suspends_pending > 0) { - suspends_pending -= as->suspends_pending; - if (suspends_pending <= 0) - suspend(); - } - if (user_list == as) - user_list = as->next; - else { - struct apm_bios_struct * as1; - - for (as1 = user_list; - (as1 != NULL) && (as1->next != as); - as1 = as1->next) - ; - if (as1 == NULL) - printk(KERN_ERR "apm_bios: filp not in user list"); - else - as1->next = as->next; - } - kfree_s(as, sizeof(*as)); - return 0; -} - -static int do_open(struct inode * inode, struct file * filp) -{ - struct apm_bios_struct * as; - - as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL); - if (as == NULL) { - printk(KERN_ERR "apm_bios: cannot allocate struct of size %d bytes", - sizeof(*as)); - return -ENOMEM; - } - as->magic = APM_BIOS_MAGIC; - as->event_tail = as->event_head = 0; - as->suspends_pending = as->standbys_pending = 0; - as->suspends_read = as->standbys_read = 0; - /* - * XXX - this is a tiny bit broken, when we consider BSD - * process accounting. If the device is opened by root, we - * instantly flag that we used superuser privs. Who knows, - * we might close the device immediately without doing a - * privileged operation -- cevans - */ - as->suser = capable(CAP_SYS_ADMIN); - as->next = user_list; - user_list = as; - filp->private_data = as; - return 0; -} - -#ifdef CONFIG_PROC_FS -int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) -{ - char * p; - unsigned short bx; - unsigned short cx; - unsigned short dx; - unsigned short error; - unsigned short ac_line_status = 0xff; - unsigned short battery_status = 0xff; - unsigned short battery_flag = 0xff; - int percentage = -1; - int time_units = -1; - char *units = "?"; - - if (!apm_enabled) - return 0; - p = buf; - - if (!(error = apm_get_power_status(&bx, &cx, &dx))) { - ac_line_status = (bx >> 8) & 0xff; - battery_status = bx & 0xff; - if ((cx & 0xff) != 0xff) - percentage = cx & 0xff; - - if (apm_bios_info.version > 0x100) { - battery_flag = (cx >> 8) & 0xff; - if (dx != 0xffff) { - if ((dx & 0x8000) == 0x8000) { - units = "min"; - time_units = dx & 0x7ffe; - } else { - units = "sec"; - time_units = dx & 0x7fff; - } - } - } - } - /* Arguments, with symbols from linux/apm_bios.h. Information is - from the Get Power Status (0x0a) call unless otherwise noted. - - 0) Linux driver version (this will change if format changes) - 1) APM BIOS Version. Usually 1.0 or 1.1. - 2) APM flags from APM Installation Check (0x00): - bit 0: APM_16_BIT_SUPPORT - bit 1: APM_32_BIT_SUPPORT - bit 2: APM_IDLE_SLOWS_CLOCK - bit 3: APM_BIOS_DISABLED - bit 4: APM_BIOS_DISENGAGED - 3) AC line status - 0x00: Off-line - 0x01: On-line - 0x02: On backup power (APM BIOS 1.1 only) - 0xff: Unknown - 4) Battery status - 0x00: High - 0x01: Low - 0x02: Critical - 0x03: Charging - 0xff: Unknown - 5) Battery flag - bit 0: High - bit 1: Low - bit 2: Critical - bit 3: Charging - bit 7: No system battery - 0xff: Unknown - 6) Remaining battery life (percentage of charge): - 0-100: valid - -1: Unknown - 7) Remaining battery life (time units): - Number of remaining minutes or seconds - -1: Unknown - 8) min = minutes; sec = seconds */ - - 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, - ac_line_status, - battery_status, - battery_flag, - percentage, - time_units, - units); - - return p - buf; -} -#endif - -__initfunc(void apm_bios_init(void)) -{ - unsigned short bx; - unsigned short cx; - unsigned short dx; - unsigned short error; - char * power_stat; - char * bat_stat; - static struct proc_dir_entry *ent; - -#ifdef __SMP__ - if (smp_num_cpus > 1) { - printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n"); - return; - } -#endif - if (apm_bios_info.version == 0) { - printk(KERN_INFO "APM BIOS not found.\n"); - return; - } - printk(KERN_INFO "APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n", - ((apm_bios_info.version >> 8) & 0xff) + '0', - (apm_bios_info.version & 0xff) + '0', - apm_bios_info.flags, - driver_version); - if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) { - printk(KERN_INFO " No 32 bit BIOS support\n"); - return; - } - - /* - * 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; - - /* BIOS < 1.2 doesn't set cseg_16_len */ - if (apm_bios_info.version < 0x102) - apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */ - - printk(KERN_INFO " 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) - printk(" cseg len %x, cseg16 len %x, dseg len %x", - apm_bios_info.cseg_len, apm_bios_info.cseg_16_len, - apm_bios_info.dseg_len); - printk("\n"); - - /* - * Set up a segment that references the real mode segment 0x40 - * that extends up to the end of page zero (that we have reserved). - * This is for buggy BIOS's that refer to (real mode) segment 0x40 - * even though they are called in protected mode. - */ - set_base(gdt[APM_40 >> 3], - __va((unsigned long)0x40 << 4)); - set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4)); - - apm_bios_entry.offset = apm_bios_info.offset; - apm_bios_entry.segment = APM_CS; - set_base(gdt[APM_CS >> 3], - __va((unsigned long)apm_bios_info.cseg << 4)); - set_base(gdt[APM_CS_16 >> 3], - __va((unsigned long)apm_bios_info.cseg_16 << 4)); - set_base(gdt[APM_DS >> 3], - __va((unsigned long)apm_bios_info.dseg << 4)); - if (apm_bios_info.version == 0x100) { - set_limit(gdt[APM_CS >> 3], 64 * 1024); - set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); - set_limit(gdt[APM_DS >> 3], 64 * 1024); - } else { -#ifdef APM_RELAX_SEGMENTS - /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ - set_limit(gdt[APM_CS >> 3], 64 * 1024); - /* For some unknown machine. */ - set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); - /* For the DEC Hinote Ultra CT475 (and others?) */ - set_limit(gdt[APM_DS >> 3], 64 * 1024); -#else - set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len); - set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len); - set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len); -#endif - /* The APM 1.2 docs state that the apm_driver_version - * call can fail if we try to connect as 1.2 to a 1.1 bios. - */ - apm_bios_info.version = 0x0102; - error = apm_driver_version(&apm_bios_info.version); - if (error != APM_SUCCESS) { /* Fall back to an APM 1.1 connection. */ - apm_bios_info.version = 0x0101; - error = apm_driver_version(&apm_bios_info.version); - } - if (error != APM_SUCCESS) /* Fall back to an APM 1.0 connection. */ - apm_bios_info.version = 0x100; - else { - apm_engage_power_management(0x0001); - printk( " Connection version %d.%d\n", - (apm_bios_info.version >> 8) & 0xff, - apm_bios_info.version & 0xff ); - } - } - - error = apm_get_power_status(&bx, &cx, &dx); - if (error) - printk(KERN_INFO " Power status not available\n"); - else { - switch ((bx >> 8) & 0xff) { - case 0: power_stat = "off line"; break; - case 1: power_stat = "on line"; break; - case 2: power_stat = "on backup power"; break; - default: power_stat = "unknown"; break; - } - switch (bx & 0xff) { - case 0: bat_stat = "high"; break; - case 1: bat_stat = "low"; break; - case 2: bat_stat = "critical"; break; - case 3: bat_stat = "charging"; break; - default: bat_stat = "unknown"; break; - } - printk(KERN_INFO " AC %s, battery status %s, battery life ", - power_stat, bat_stat); - if ((cx & 0xff) == 0xff) - printk("unknown\n"); - else - printk("%d%%\n", cx & 0xff); - if (apm_bios_info.version > 0x100) { - printk(" battery flag 0x%02x, battery life ", - (cx >> 8) & 0xff); - if (dx == 0xffff) - printk("unknown\n"); - else { - if ((dx & 0x8000)) - printk("%d minutes\n", dx & 0x7ffe ); - else - printk("%d seconds\n", dx & 0x7fff ); - } - } - } - -#ifdef CONFIG_APM_DO_ENABLE - /* - * 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(); - if (error) - apm_error("enable power management", error); - if (error == APM_DISABLED) - return; -#endif - - init_timer(&apm_timer); - apm_timer.function = do_apm_timer; - apm_timer.expires = APM_CHECK_TIMEOUT + jiffies; - add_timer(&apm_timer); - -#ifdef CONFIG_PROC_FS - ent = create_proc_entry("apm", 0, 0); - ent->get_info = apm_get_info; -#endif - - misc_register(&apm_device); - - apm_enabled = 1; -} diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index 19c30d6fd..de2d25b0e 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -28,11 +28,10 @@ composite source from a satellite tuner can deliver different norms depending on tuned channel * mmap VBI data? - * use new PCI routines * fix RAW Composite grabbing for NTSC - * allow for different VDELAY in RAW grabbing? + * fix VBI reading double frames when grabbing is active + * allow for different VDELAYs * extra modules for tda9850, tda8425, any volunteers??? - * support 15bpp */ #include <linux/module.h> @@ -80,12 +79,20 @@ static void bt848_set_risc_jmps(struct bttv *btv); static unsigned int vidmem=0; /* manually set video mem address */ static int triton1=0; +#ifndef USE_PLL +/* 0=no pll, 1=28MHz, 2=34MHz */ +#define USE_PLL 0 +#endif +#ifndef CARD_DEFAULT +/* card type (see bttv.h) 0=autodetect */ +#define CARD_DEFAULT 0 +#endif static unsigned int remap[BTTV_MAX]; /* remap Bt848 */ static unsigned int radio[BTTV_MAX]; -static unsigned int card[BTTV_MAX] = { 0, 0, - 0, 0 }; -static unsigned int pll[BTTV_MAX] = { 0, 0, 0, 0 }; +static unsigned int card[BTTV_MAX] = { CARD_DEFAULT, CARD_DEFAULT, + CARD_DEFAULT, CARD_DEFAULT }; +static unsigned int pll[BTTV_MAX] = { USE_PLL, USE_PLL, USE_PLL, USE_PLL }; static int bttv_num; /* number of Bt848s in use */ static struct bttv bttvs[BTTV_MAX]; @@ -96,9 +103,8 @@ static struct bttv bttvs[BTTV_MAX]; { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } #define I2C_GET() (btread(BT848_I2C)&1) -#define AUDIO_MUTE_DELAY 10000 -#define FREQ_CHANGE_DELAY 20000 #define EEPROM_WRITE_DELAY 20000 +#define BURSTOFFSET 76 /*******************************/ /* Memory management functions */ @@ -344,7 +350,6 @@ static void writeee(struct i2c_bus *bus, unsigned char *eedata) void attach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; - int tunertype; switch (id) { @@ -354,12 +359,9 @@ void attach_inform(struct i2c_bus *bus, int id) case I2C_DRIVERID_TUNER: btv->have_tuner = 1; if (btv->tuner_type != -1) - { - tunertype=btv->tuner_type; i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, - TUNER_SET_TYPE,&tunertype); - } + TUNER_SET_TYPE,&btv->tuner_type); break; } } @@ -401,7 +403,8 @@ static struct i2c_bus bttv_i2c_bus_template = struct tvcard { - int inputs; + int video_inputs; + int audio_inputs; int tuner; int svhs; u32 gpiomask; @@ -413,29 +416,37 @@ struct tvcard static struct tvcard tvcards[] = { /* default */ - { 3, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}}, + { 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}}, /* MIRO */ - { 4, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10}}, + { 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10}}, /* Hauppauge */ - { 3, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, + { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, /* STB */ - { 3, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1}}, + { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1}}, /* Intel??? */ - { 3, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, + { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, /* Diamond DTV2000 */ - { 3, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3}}, + { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3}}, /* AVerMedia TVPhone */ - { 3, 0, 3,15, { 2, 3, 1, 1}, {12, 0,11,11, 0}}, + { 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 0,11,11, 0}}, /* Matrix Vision MV-Delta */ - { 5,-1, 3, 0, { 2, 3, 1, 0, 0}}, + { 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0}}, /* Fly Video II */ - { 3, 0, 2, 0xc00, { 2, 3, 1, 1}, + { 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1}, {0, 0xc00, 0x800, 0x400, 0xc00, 0}}, /* TurboTV */ - { 3, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}}, - /* Newer Hauppauge */ - { 2, 0, 2, 1, { 2, 0, 0, 0}, {0, 1, 2, 3, 4}}, - + { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}}, + /* Newer Hauppauge (bt878) */ + { 3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4}}, + /* MIRO PCTV pro */ + { 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}}, + /* ADS Technologies Channel Surfer TV (and maybe TV+FM) */ + { + 3, 4, 0, 2, 0x0F, + { 0x02, 0x03, 0x01, 0x01}, + { 0x0D, 0x0E, 0x0B, 0x07, 0x00, 0x00}, + 0x00 + }, }; #define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) @@ -546,32 +557,36 @@ static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) static int set_pll(struct bttv *btv) { int i; - unsigned long tv; + unsigned long tv; if (!btv->pll.pll_crystal) - return 0; - if ((btread(BT848_IFORM)&btv->pll.pll_crystal)) - { - /* printk ("switching PLL off\n");*/ + return 0; + + if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { + /* no PLL needed */ + if (btv->pll.pll_current == 0) { + /* printk ("bttv%d: PLL: is off\n",btv->nr); */ + return 0; + } + printk ("bttv%d: PLL: switching off\n",btv->nr); btwrite(0x00,BT848_TGCTRL); btwrite(0x00,BT848_PLL_XCI); - btv->pll.pll_crystal&=~2; + btv->pll.pll_current = 0; return 0; } - - /* do not set pll again if already active */ - if (btv->pll.pll_crystal&2) + + if (btv->pll.pll_ofreq == btv->pll.pll_current) { + /* printk("bttv%d: PLL: no change required\n",btv->nr); */ return 1; + } - /* printk ("setting PLL for PAL/SECAM\n");*/ + printk("bttv%d: PLL: %d => %d ... ",btv->nr, + btv->pll.pll_ifreq, btv->pll.pll_ofreq); set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); - /* - * Let other people run while the PLL stabilizes - */ - - tv=jiffies+HZ/10; /* .1 seconds */ + /* Let other people run while the PLL stabilizes */ + tv=jiffies+HZ/10; /* .1 seconds */ do { schedule(); @@ -585,11 +600,14 @@ static int set_pll(struct bttv *btv) else { btwrite(0x08,BT848_TGCTRL); - btv->pll.pll_crystal|=2; - return 1; + btv->pll.pll_current = btv->pll.pll_ofreq; + printk("ok\n"); + return 1; } - udelay(10000); + mdelay(10); } + btv->pll.pll_current = 0; + printk("oops\n"); return -1; } @@ -602,7 +620,7 @@ static void bt848_muxsel(struct bttv *btv, unsigned int input) btand(~(3<<5), BT848_IFORM); mdelay(10); - input %= tvcards[btv->type].inputs; + input %= tvcards[btv->type].video_inputs; if (input==tvcards[btv->type].svhs) { btor(BT848_CONTROL_COMP, BT848_E_CONTROL); @@ -623,8 +641,9 @@ static void bt848_muxsel(struct bttv *btv, unsigned int input) #define VBIBUF_SIZE 65536 -/* Maximum sample number per VBI line is 2044, can NTSC deliver this? +/* Maximum sample number per VBI line is 2044, NTSC delivers 1600 Note that we write 2048-aligned to keep alignment to memory pages + VBI_RISC is written so that it applies to either 2044 or 1600 */ #define VBI_SPL 2044 @@ -684,7 +703,7 @@ int palette2fmt[] = { BT848_COLOR_FMT_YCrCb422, BT848_COLOR_FMT_YCrCb411, }; -#define PALETTEFMT_MAX 11 +#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int)) static int make_rawrisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, unsigned int *vbuf) @@ -783,199 +802,102 @@ static int make_vrisctab(struct bttv *btv, unsigned int *ro, return 0; } -/* does this really make a difference ???? */ -#define BURST_MAX 4096 - -static inline void write_risc_segment(unsigned int **rp, unsigned long line_adr, unsigned int command, - int *x, uint dx, uint bpp, uint width) +static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h) { - unsigned int flags, len; - - if (!dx) - return; - len=dx*bpp; - -#ifdef LIMIT_DMA - if (command==BT848_RISC_WRITEC) - { - unsigned int dx2=BURST_MAX/bpp; - while (len>BURST_MAX) - { - write_risc_segment(rp, line_adr, command, - &x,dx2, bpp, width); - dx-=dx2; - len=dx*bpp; - } - } -#endif - - /* mask upper 8 bits for 24+8 bit overlay modes */ - flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0); - - if (*x==0) - { - if (command==BT848_RISC_SKIP) - { - if (dx<width) - { - flags|=BT848_RISC_BYTE_ALL; - command=BT848_RISC_WRITE; - } - } - else - if (command==BT848_RISC_WRITEC) - command=BT848_RISC_WRITE; - flags|=BT848_RISC_SOL; - } - if (*x+dx==width) - flags|=BT848_RISC_EOL; - *((*rp)++)=command|flags|len; - if (command==BT848_RISC_WRITE) - *((*rp)++)=line_adr+*x*bpp; - *x+=dx; + int i, j; + /* bitmap is fixed width, 128 bytes (1024 pixels represented) */ + if (x < 0 || y < 0 || w < 0 || h < 0) /* catch bad clips */ + return; + /* out of range data should just fall through */ + for (i = y; i < y + h && i < 625; i++) + for (j = x; j < x + w && j < 1024; j++) + clipmap[(i<<7)+(j>>3)] |= (1<<(j&7)); } static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr) { - int i,t; - int yy, y, x, dx; - struct video_clip first, *cur, *cur2, *nx, first2, *prev, *nx2; - int bpp, bpl, width, height, inter; - unsigned int **rp,*ro,*re; + int i, line, x, y, bpl, width, height, inter; + unsigned int bpp, dx, sx, **rp, *ro, *re, flags, len; unsigned long adr; - int cx,cx2,cy,cy2; + unsigned char *clipmap, cbit, lastbit, outofmem; inter=(btv->win.interlace&1)^1; bpp=btv->win.bpp; + if (bpp==15) /* handle 15bpp as 16bpp in calculations */ + bpp++; bpl=btv->win.bpl; ro=btv->risc_odd; re=btv->risc_even; - width=btv->win.width; - height=btv->win.height; + if((width=btv->win.width)>1023) + width = 1023; /* sanity check */ + if((height=btv->win.height)>625) + height = 625; /* sanity check */ adr=btv->win.vidadr+btv->win.x*bpp+btv->win.y*bpl; - - /* clip clipping rects against viewing window AND screen + if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) { + /* can't clip, don't generate any risc code */ + *(ro++)=BT848_RISC_JUMP; + *(ro++)=btv->bus_vbi_even; + *(re++)=BT848_RISC_JUMP; + *(re++)=btv->bus_vbi_odd; + } + if (ncr < 0) { /* bitmap was pased */ + memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE); + } else { /* convert rectangular clips to a bitmap */ + memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ + for (i=0; i<ncr; i++) + clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, + cr[i].width, cr[i].height); + } + /* clip against viewing window AND screen so we do not have to rely on the user program */ - cx=(btv->win.x<0) ? (-btv->win.x) : 0; - cy=(btv->win.y<0) ? (-btv->win.y) : 0; - cx2=(btv->win.x+width>btv->win.swidth) ? - (btv->win.swidth-btv->win.x) : width; - cy2=(btv->win.y+height>btv->win.sheight) ? - (btv->win.sheight-btv->win.y) : height; - first.next=NULL; - for (i=0; i<ncr; i++) - { - if ((t=cy-cr[i].y)>0) - { - if (cr[i].height<=t) - continue; - cr[i].height-=t; - cr[i].y=cy; - } - if ((t=cy2-cr[i].y)<cr[i].height) - { - if (t<=0) - continue; - cr[i].height=t; - } - if ((t=cx-cr[i].x)>0) - { - if (cr[i].width<=t) - continue; - cr[i].width-=t; - cr[i].x=cx; - } - if ((t=cx2-cr[i].x)<cr[i].width) - { - if (t<=0) - continue; - cr[i].width=t; - } - cur=&first; - while ((nx=cur->next) && (cr[i].y > cur->next->y)) - cur=nx; - cur->next=&(cr[i]); - cr[i].next=nx; - } - first2.next=NULL; + clip_draw_rectangle(clipmap,(btv->win.x+width>btv->win.swidth) ? + (btv->win.swidth-btv->win.x) : width, 0, 1024, 768); + clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ? + (btv->win.sheight-btv->win.y) : height,1024,768); + if (btv->win.x<0) + clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768); + if (btv->win.y<0) + clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y)); *(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0; *(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0; - /* loop through all lines */ - for (yy=0; yy<(height<<inter); yy++) + /* translate bitmap to risc code */ + for (line=outofmem=0; line < (height<<inter) && !outofmem; line++) { - y=yy>>inter; - rp= (yy&1) ? &re : &ro; - - /* remove rects with y2 > y */ - if ((cur=first2.next)) - { - prev=&first2; - do - { - if (cur->y+cur->height <= y) - prev->next=cur->next; - else - prev=cur; - } - while ((cur=cur->next)); - } - - /* add rect to second (x-sorted) list if rect.y == y */ - if ((cur=first.next)) - { - while ((cur) && (cur->y == y)) - { - first.next=cur->next; - cur2=&first2; - while ((nx2=cur2->next) && (cur->x > cur2->next->x)) - cur2=nx2; - cur2->next=cur; - cur->next=nx2; - cur=first.next; - } - } - x=0; - if ((btv->win.y+y<=0)||(btv->win.y+y>=btv->win.sheight)) - write_risc_segment(rp, adr, BT848_RISC_SKIP, &x, - width, bpp, width); - else - { - dx=cx; - for (cur2=first2.next; cur2; cur2=cur2->next) - { - if (x+dx < cur2->x) - { - write_risc_segment(rp, adr, BT848_RISC_SKIP, - &x, dx, bpp, width); - dx=cur2->x-x; - write_risc_segment(rp, adr, BT848_RISC_WRITEC, - &x, dx, bpp, width); - dx=cur2->width; - } - else if (x+dx < cur2->x+cur2->width) - dx=cur2->x+cur2->width-x; - } - if (cx2<width) - { - write_risc_segment(rp, adr, BT848_RISC_SKIP, - &x, dx, bpp, width); - write_risc_segment(rp, adr, BT848_RISC_WRITEC, - &x, cx2-x, bpp, width); - dx=width-x; - } - write_risc_segment(rp, adr, BT848_RISC_SKIP, - &x, dx, bpp, width); - write_risc_segment(rp, adr, BT848_RISC_WRITEC, - &x, width-x, bpp, width); - } - if ((!inter)||(yy&1)) + y = line>>inter; + rp= (line&1) ? &re : &ro; + lastbit=(clipmap[y<<7]&1); + for(x=dx=1,sx=0; x<=width && !outofmem; x++) { + cbit = (clipmap[(y<<7)+(x>>3)] & (1<<(x&7))); + if (x < width && !lastbit == !cbit) + dx++; + else { /* generate the dma controller code */ + len = dx * bpp; + flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0); + flags |= ((!sx) ? BT848_RISC_SOL : 0); + flags |= ((sx + dx == width) ? BT848_RISC_EOL : 0); + if (!lastbit) { + *((*rp)++)=BT848_RISC_WRITE|flags|len; + *((*rp)++)=adr + bpp * sx; + } else + *((*rp)++)=BT848_RISC_SKIP|flags|len; + lastbit=cbit; + sx += dx; + dx = 1; + if (ro - btv->risc_odd > RISCMEM_LEN/2 - 16) + outofmem++; + if (re - btv->risc_even > RISCMEM_LEN/2 - 16) + outofmem++; + } + } + if ((!inter)||(line&1)) adr+=bpl; } - - *(ro++)=BT848_RISC_JUMP; + vfree(clipmap); + /* outofmem flag relies on the following code to discard extra data */ + *(ro++)=BT848_RISC_JUMP; *(ro++)=btv->bus_vbi_even; *(re++)=BT848_RISC_JUMP; *(re++)=btv->bus_vbi_odd; @@ -999,44 +921,51 @@ struct tvnorm u8 adelay, bdelay, iform; u32 scaledtwidth; u16 hdelayx1, hactivex1; - u16 vdelay, fporch; + u16 vdelay; + u8 vbipack; }; static struct tvnorm tvnorms[] = { /* PAL-BDGHI */ - /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ + /* max pal/secam is actually 922, but 924 is divisible by 4 and 3! */ + /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ { 35468950, 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 1135, 186, 924, 0x20}, + 1135, 178, 924, 0x20, 255}, /* { 35468950, 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 944, 186, 922, 0x20}, + 944, 178, 922, 0x20, 255}, */ /* NTSC */ { 28636363, + 768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), + 910, 128, 754, 0x1a, 144}, +/* + { 28636363, 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), - 780, 135, 754, 0x1a}, - /* SECAM */ - { 28636363, - 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), - 780, 135, 754, 0x16}, + 780, 122, 754, 0x1a, 144}, +*/ + /* SECAM - phase means nothing in SECAM, bdelay is useless */ + { 35468950, + 924, 576,1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), + 1135, 178, 924, 0x20, 255}, /* PAL-M */ { 28636363, 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), - 780, 135, 754, 0x16}, + 780, 122, 754, 0x1a, 144}, /* PAL-N */ { 35468950, 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1), - 944, 186, 922, 0x20}, + 944, 178, 922, 0x20, 255}, /* PAL-NC */ { 35468950, 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), - 944, 186, 922, 0x20}, + 944, 178, 922, 0x20, 255}, /* NTSC-Japan */ { 28636363, 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), - 780, 135, 754, 0x16}, + 780, 122, 754, 0x1a, 144}, }; #define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) @@ -1101,6 +1030,8 @@ static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt) btwrite(tvn->adelay, BT848_ADELAY); btwrite(tvn->bdelay, BT848_BDELAY); btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM); + btwrite(1, BT848_VBI_PACK_DEL); + btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE); set_pll(btv); @@ -1172,11 +1103,7 @@ static void bt848_set_winsize(struct bttv *btv) static void set_freq(struct bttv *btv, unsigned short freq) { int fixme = freq; /* XXX */ - int oldAudio = btv->audio; - audio(btv, AUDIO_MUTE); - udelay(AUDIO_MUTE_DELAY); - if (btv->radio) { if (btv->have_tuner) @@ -1204,10 +1131,6 @@ static void set_freq(struct bttv *btv, unsigned short freq) } } - if (!(oldAudio & AUDIO_MUTE)) { - udelay(FREQ_CHANGE_DELAY); - audio(btv, AUDIO_UNMUTE); - } } @@ -1243,10 +1166,15 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) So, better check the total image size ... */ /* - if(mp->height>576 || mp->width>768) + if(mp->height>576 || mp->width>768+BURSTOFFSET) return -EINVAL; */ - if (mp->height*mp->width*fmtbppx2[mp->format&0x0f]/2>BTTV_MAX_FBUF) + if (mp->format >= PALETTEFMT_MAX) + return -EINVAL; + if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2 + > BTTV_MAX_FBUF) + return -EINVAL; + if (!palette2fmt[mp->format]) return -EINVAL; /* @@ -1264,31 +1192,28 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) return -EAGAIN;*/ ro=btv->grisc+(((btv->grabcount++)&1) ? 4096 :0); re=ro+2048; - btv->gwidth=mp->width; - btv->gheight=mp->height; - - if (mp->format > PALETTEFMT_MAX) - return -EINVAL; - btv->gfmt=palette2fmt[mp->format]; - if(btv->gfmt==0) - return -EINVAL; - - make_vrisctab(btv, ro, re, vbuf, btv->gwidth, btv->gheight, btv->gfmt); + make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, palette2fmt[mp->format]); /* bt848_set_risc_jmps(btv); */ - btor(3, BT848_CAP_CTL); - btor(3, BT848_GPIO_DMA_CTL); btv->frame_stat[mp->frame] = GBUFFER_GRABBING; if (btv->grabbing) { + btv->gfmt_next=palette2fmt[mp->format]; + btv->gwidth_next=mp->width; + btv->gheight_next=mp->height; btv->gro_next=virt_to_bus(ro); btv->gre_next=virt_to_bus(re); btv->grf_next=mp->frame; } else { + btv->gfmt=palette2fmt[mp->format]; + btv->gwidth=mp->width; + btv->gheight=mp->height; btv->gro=virt_to_bus(ro); btv->gre=virt_to_bus(re); btv->grf=mp->frame; } if (!(btv->grabbing++)) btv->risc_jmp[12]=BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ; + btor(3, BT848_CAP_CTL); + btor(3, BT848_GPIO_DMA_CTL); /* interruptible_sleep_on(&btv->capq); */ return 0; } @@ -1387,7 +1312,7 @@ static void bttv_close(struct video_device *dev) struct bttv *btv=(struct bttv *)dev; btv->user--; - audio(btv, AUDIO_MUTE); + audio(btv, AUDIO_INTERN); btv->cap&=~3; bt848_set_risc_jmps(btv); @@ -1466,8 +1391,8 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) VID_TYPE_CLIPPING| VID_TYPE_FRAMERAM| VID_TYPE_SCALES; - b.channels = tvcards[btv->type].inputs; - b.audios = tvcards[btv->type].inputs; + b.channels = tvcards[btv->type].video_inputs; + b.audios = tvcards[btv->type].audio_inputs; b.maxwidth = 768; b.maxheight = 576; b.minwidth = 32; @@ -1485,7 +1410,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.tuners=0; v.type=VIDEO_TYPE_CAMERA; v.norm = btv->win.norm; - if (v.channel>=tvcards[btv->type].inputs) + if (v.channel>=tvcards[btv->type].video_inputs) return -EINVAL; if(v.channel==tvcards[btv->type].tuner) { @@ -1512,13 +1437,14 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; - if (v.channel>tvcards[btv->type].inputs) + if (v.channel>=tvcards[btv->type].video_inputs) return -EINVAL; bt848_muxsel(btv, v.channel); if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC &&v.norm!=VIDEO_MODE_SECAM) return -EOPNOTSUPP; btv->win.norm = v.norm; + make_vbitab(btv); bt848_set_winsize(btv); btv->channel=v.channel; return 0; @@ -1604,15 +1530,20 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case VIDIOCSWIN: { struct video_window vw; - struct video_clip *vcp; + struct video_clip *vcp = NULL; int on; if(copy_from_user(&vw,arg,sizeof(vw))) return -EFAULT; - if(vw.flags) + if(vw.flags || vw.width < 16 || vw.height < 16) { + bt848_cap(btv,0); return -EINVAL; - + } + if (btv->win.bpp < 4) { /* adjust and align writes */ + vw.x = (vw.x + 3) & ~3; + vw.width = (vw.width - 3) & ~3; + } btv->win.x=vw.x; btv->win.y=vw.y; btv->win.width=vw.width; @@ -1623,26 +1554,37 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) else btv->win.interlace=0; - on=(btv->cap&3)?1:0; + on=(btv->cap&3); bt848_cap(btv,0); bt848_set_winsize(btv); - if(vw.clipcount>256) - return -EDOM; /* Too many! */ - /* * Do any clips. */ - - vcp=vmalloc(sizeof(struct video_clip)*(vw.clipcount+4)); - if(vcp==NULL) - return -ENOMEM; - if(vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount)) - return -EFAULT; - make_clip_tab(btv,vcp, vw.clipcount); - vfree(vcp); - if(on && btv->win.vidadr!=0) + if(vw.clipcount<0) { + if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) + return -ENOMEM; + if(copy_from_user(vcp, vw.clips, + VIDEO_CLIPMAP_SIZE)) { + vfree(vcp); + return -EFAULT; + } + } else if (vw.clipcount) { + if((vcp=vmalloc(sizeof(struct video_clip)* + (vw.clipcount))) == NULL) + return -ENOMEM; + if(copy_from_user(vcp,vw.clips, + sizeof(struct video_clip)* + vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + } + make_clip_tab(btv, vcp, vw.clipcount); + if (vw.clipcount != 0) + vfree(vcp); + if(on && btv->win.vidadr != 0) bt848_cap(btv,1); return 0; } @@ -1700,7 +1642,9 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EPERM; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; - if(v.depth!=8 && v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32) + if(v.depth!=8 && v.depth!=15 && v.depth!=16 && + v.depth!=24 && v.depth!=32 && v.width > 16 && + v.height > 16 && v.bytesperline > 16) return -EINVAL; btv->win.vidadr=(unsigned long)v.base; btv->win.sheight=v.height; @@ -1774,7 +1718,10 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if(v.flags&VIDEO_AUDIO_MUTE) audio(btv, AUDIO_MUTE); /* One audio source per tuner */ - if(v.audio!=0) + /* if(v.audio!=0) */ + /* Nope... I have three on my ADSTech TV card. The*/ + /* ADSTech TV+FM prolly has 4 <rriggs@tesser.com> */ + if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs) return -EINVAL; bt848_muxsel(btv,v.audio); if(!(v.flags&VIDEO_AUDIO_MUTE)) @@ -1801,9 +1748,8 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case VIDIOCSYNC: if(copy_from_user((void *)&i,arg,sizeof(int))) return -EFAULT; - if(i>1 || i<0) - return -EINVAL; - + if (i>1 || i<0) + return -EINVAL; switch (btv->frame_stat[i]) { case GBUFFER_UNUSED: return -EINVAL; @@ -1832,24 +1778,25 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; break; - case BTTV_FIELDNR: + case BTTV_FIELDNR: if(copy_to_user((void *) arg, (void *) &btv->last_field, sizeof(btv->last_field))) return -EFAULT; break; - - case BTTV_PLLSET: { - struct bttv_pll_info p; - if(!capable(CAP_SYS_ADMIN)) - return -EPERM; - if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) - return -EFAULT; - btv->pll.pll_ifreq = p.pll_ifreq; - btv->pll.pll_ofreq = p.pll_ofreq; - btv->pll.pll_crystal = p.pll_crystal; + case BTTV_PLLSET: + { + struct bttv_pll_info p; + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) + return -EFAULT; + btv->pll.pll_ifreq = p.pll_ifreq; + btv->pll.pll_ofreq = p.pll_ofreq; + btv->pll.pll_crystal = p.pll_crystal; break; - } + } + case VIDIOCMCAPTURE: { struct video_mmap vm; @@ -1893,7 +1840,27 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; return 0; } - + + case BTTV_BURST_ON: + { + tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2; + tvnorms[0].hdelayx1=186-BURSTOFFSET; + return 0; + } + + case BTTV_BURST_OFF: + { + tvnorms[0].scaledtwidth=1135; + tvnorms[0].hdelayx1=186; + return 0; + } + + case BTTV_PICNR: + { + /* return picture */ + return 0; + } + default: return -ENOIOCTLCMD; } @@ -2182,6 +2149,8 @@ struct vidbases }; static struct vidbases vbs[] = { + { PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D, + "Alliance AT3D", PCI_BASE_ADDRESS_0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215CT222, "ATI MACH64 CT", PCI_BASE_ADDRESS_0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX, @@ -2418,12 +2387,11 @@ static void idcard(int i) { btv->type=BTTV_MIRO; - if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0) - { + if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0) { if(btv->id>849) btv->type=BTTV_HAUPPAUGE878; else - btv->type=BTTV_HAUPPAUGE; + btv->type=BTTV_HAUPPAUGE; } else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) @@ -2443,8 +2411,8 @@ static void idcard(int i) if (I2CRead(&(btv->i2c), I2C_TDA8425) >=0) { - btv->audio_chip = TDA8425; - printk("bttv%d: audio chip: TDA8425\n", i); + btv->audio_chip = TDA8425; + printk("bttv%d: audio chip: TDA8425\n", i); } switch(btv->audio_chip) @@ -2459,11 +2427,11 @@ static void idcard(int i) /* How do I detect the tuner type for other cards but Miro ??? */ printk(KERN_INFO "bttv%d: model: ", btv->nr); - sprintf(btv->video_dev.name,"BT%d",btv->id); switch (btv->type) { case BTTV_MIRO: + case BTTV_MIROPRO: printk("MIRO\n"); if (btv->have_tuner) { @@ -2472,7 +2440,7 @@ static void idcard(int i) I2C_DRIVERID_TUNER, TUNER_SET_TYPE,&tunertype); } - strcat(btv->video_dev.name, "(Miro)"); + strcat(btv->video_dev.name,"(Miro)"); break; case BTTV_HAUPPAUGE: case BTTV_HAUPPAUGE878: @@ -2499,8 +2467,13 @@ static void idcard(int i) printk("MATRIX-Vision\n"); strcat(btv->video_dev.name,"(MATRIX-Vision)"); break; + case BTTV_ADSTECH_TV: + printk("ADSTech Channel Surfer TV\n"); + strcat(btv->video_dev.name,"(ADSTech Channel Surfer TV)"); + btv->tuner_type=8; + break; } - audio(btv, AUDIO_MUTE); + audio(btv, AUDIO_INTERN); } @@ -2609,7 +2582,10 @@ static int init_bt848(int i) btv->grabcount=0; btv->grab=0; btv->lastgrab=0; - btv->field=btv->last_field=0; + btv->field=btv->last_field=0; + btv->video_dev.minor = -1; + btv->vbi_dev.minor = -1; + btv->radio_dev.minor = -1; /* i2c */ memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus)); @@ -2650,11 +2626,6 @@ static int init_bt848(int i) /* select direct input */ btwrite(0x00, BT848_GPIO_REG_INP); - - btwrite(0xff, BT848_VBI_PACK_SIZE); - btwrite(1, BT848_VBI_PACK_DEL); - - btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI, BT848_IFORM); @@ -2759,6 +2730,7 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) if (astat&BT848_INT_VSYNC) { IDEBUG(printk ("bttv%d: IRQ_VSYNC\n", btv->nr)); + btv->field++; } if (astat&BT848_INT_SCERR) { IDEBUG(printk ("bttv%d: IRQ_SCERR\n", btv->nr)); @@ -2776,18 +2748,22 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) if (stat&(1<<28)) { btv->vbip=0; + /* inc vbi frame count for detecting drops */ + (*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++; wake_up_interruptible(&btv->vbiq); } /* captured full frame */ if (stat&(2<<28)) { - wake_up_interruptible(&btv->capq); btv->last_field=btv->field; btv->grab++; btv->frame_stat[btv->grf] = GBUFFER_DONE; if ((--btv->grabbing)) { + btv->gfmt = btv->gfmt_next; + btv->gwidth = btv->gwidth_next; + btv->gheight = btv->gheight_next; btv->gro = btv->gro_next; btv->gre = btv->gre_next; btv->grf = btv->grf_next; @@ -2844,10 +2820,12 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) } if (astat&BT848_INT_HLOCK) { +#if 0 if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) audio(btv, AUDIO_ON); else audio(btv, AUDIO_OFF); +#endif } if (astat&BT848_INT_I2CDONE) @@ -2923,18 +2901,28 @@ int configure_bt848(struct pci_dev *dev, int bttv_num) printk("irq: %d, ",btv->irq); printk("memory: 0x%08x.\n", btv->bt848_adr); - btv->pll.pll_ifreq=0; - btv->pll.pll_ifreq=0; - btv->pll.pll_crystal=0; - if(pll[btv->nr]) - if (!(btv->id==848 && btv->revision==0x11)) - { - printk(KERN_INFO "bttv%d: internal PLL, single crystal operation enabled\n",bttv_num); - btv->pll.pll_ofreq=28636363; + btv->pll.pll_ifreq=0; + btv->pll.pll_ofreq=0; + btv->pll.pll_crystal=0; + btv->pll.pll_current=0; + if (!(btv->id==848 && btv->revision==0x11)) { + switch (pll[btv->nr]) { + case 0: + /* off */ + break; + case 1: + /* 28 MHz crystal installed */ + btv->pll.pll_ifreq=28636363; + btv->pll.pll_crystal=BT848_IFORM_XT0; + break; + case 2: + /* 35 MHz crystal installed */ btv->pll.pll_ifreq=35468950; btv->pll.pll_crystal=BT848_IFORM_XT1; + break; } - + } + btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); /* clear interrupt mask */ @@ -2997,7 +2985,7 @@ static int find_bt848(void) dev = dev->next; } if(bttv_num) - printk(KERN_INFO "bttv: %d BT8xx card(s) found.\n", bttv_num); + printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num); return bttv_num; } @@ -3113,4 +3101,3 @@ void cleanup_module(void) * tab-width: 8 * End: */ - diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h index f8da6c6d0..f780de12b 100644 --- a/drivers/char/bttv.h +++ b/drivers/char/bttv.h @@ -31,12 +31,11 @@ #include "bt848.h" #include <linux/videodev.h> -#define MAX_CLIPRECS 100 #define MAX_GBUFFERS 2 #define RISCMEM_LEN (32744*2) /* maximum needed buffer size for extended VBI frame mode capturing */ -#define BTTV_MAX_FBUF 0x151000 +#define BTTV_MAX_FBUF 0x190000 #ifdef __KERNEL__ @@ -56,11 +55,11 @@ struct bttv_window ushort depth; }; - struct bttv_pll_info { - unsigned int pll_ifreq; /* PLL input frequency */ - unsigned int pll_ofreq; /* PLL output frequency */ + unsigned int pll_ifreq; /* PLL input frequency */ + unsigned int pll_ofreq; /* PLL output frequency */ unsigned int pll_crystal; /* Crystal used for input */ + unsigned int pll_current; /* Current programmed ofrq*/ }; struct bttv @@ -116,6 +115,7 @@ struct bttv struct gbuffer *ogbuffers; struct gbuffer *egbuffers; u16 gwidth, gheight, gfmt; + u16 gwidth_next, gheight_next, gfmt_next; u32 *grisc; unsigned long gro; @@ -143,6 +143,7 @@ struct bttv int i2c_command; int triton1; }; + #endif /*The following should be done in more portable way. It depends on define @@ -167,6 +168,10 @@ struct bttv #define BTTV_GRAB _IOR('v' , BASE_VIDIOCPRIVATE+2, struct gbuf) #define BTTV_FIELDNR _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int) #define BTTV_PLLSET _IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info) +#define BTTV_BURST_ON _IOR('v' , BASE_VIDIOCPRIVATE+4, int) +#define BTTV_BURST_OFF _IOR('v' , BASE_VIDIOCPRIVATE+5, int) +#define BTTV_NAGRAVERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int) +#define BTTV_PICNR _IOR('v' , BASE_VIDIOCPRIVATE+7, int) #define BTTV_UNKNOWN 0x00 @@ -178,7 +183,10 @@ struct bttv #define BTTV_AVERMEDIA 0x06 #define BTTV_MATRIX_VISION 0x07 #define BTTV_FLYVIDEO 0x08 -#define BTTV_HAUPPAUGE878 0x09 +#define BTTV_TURBOTV 0x09 +#define BTTV_HAUPPAUGE878 0x0a +#define BTTV_MIROPRO 0x0b +#define BTTV_ADSTECH_TV 0x0c #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 diff --git a/drivers/char/bw-qcam.c b/drivers/char/bw-qcam.c index 48de68db3..9f8935e8b 100644 --- a/drivers/char/bw-qcam.c +++ b/drivers/char/bw-qcam.c @@ -214,8 +214,7 @@ static int qc_waithand(struct qcam_device *q, int val) if(runs++>1000) { current->state=TASK_INTERRUPTIBLE; - current->timeout = jiffies+HZ/10; - schedule(); + schedule_timeout(HZ/10); } if(runs>1050) return -1; @@ -232,8 +231,7 @@ static int qc_waithand(struct qcam_device *q, int val) if(runs++>1000) { current->state=TASK_INTERRUPTIBLE; - current->timeout = jiffies+HZ/10; - schedule(); + schedule_timeout(HZ/10); } if(runs++>1050) /* 5 seconds */ return -1; @@ -263,8 +261,7 @@ static unsigned int qc_waithand2(struct qcam_device *q, int val) if(runs++>1000) { current->state=TASK_INTERRUPTIBLE; - current->timeout = jiffies+HZ/10; - schedule(); + schedule_timeout(HZ/10); } if(runs++>1050) /* 5 seconds */ return 0; @@ -296,7 +293,7 @@ static int qc_detect(struct qcam_device *q) if (reg != lastreg) count++; lastreg = reg; - mdelay(1); + mdelay(2); } /* Be liberal in what you accept... */ diff --git a/drivers/char/c-qcam.c b/drivers/char/c-qcam.c index e6d4c16b5..6ffb80a54 100644 --- a/drivers/char/c-qcam.c +++ b/drivers/char/c-qcam.c @@ -58,8 +58,7 @@ static inline unsigned int qcam_await_ready1(struct qcam_device *qcam, int value if (qcam_ready1(qcam) == value) return 0; current->state=TASK_INTERRUPTIBLE; - current->timeout = jiffies+HZ/10; - schedule(); + schedule_timeout(HZ/10); } /* Probably somebody pulled the plug out. Not much we can do. */ @@ -85,8 +84,7 @@ static inline unsigned int qcam_await_ready2(struct qcam_device *qcam, int value if (qcam_ready2(qcam) == value) return 0; current->state=TASK_INTERRUPTIBLE; - current->timeout = jiffies+HZ/10; - schedule(); + schedule_timeout(HZ/10); } /* Probably somebody pulled the plug out. Not much we can do. */ diff --git a/drivers/char/console.c b/drivers/char/console.c index d5b315328..77d80f241 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -27,8 +27,6 @@ * Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94 * <poe@daimi.aau.dk> * - * MIPS support by Ralf Baechle, Andreas Busse and Wayne Hodgen. - * * User-defined bell sound, new setterm control sequences and printk * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95 * @@ -49,7 +47,6 @@ * - video_num_columns * - video_num_lines * - video_size_row - * - video_screen_size * - can_do_color * * The abstract console driver provides a generic interface for a text @@ -102,6 +99,7 @@ #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> +#include <asm/bitops.h> #include <asm/linux_logo.h> @@ -110,8 +108,6 @@ struct consw *conswitchp = NULL; -static int vesa_blank_mode = 0; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ - /* A bitmap for codes <32. A bit of 1 indicates that the code * corresponding to that bit number invokes some special action * (such as cursor movement) and should not be displayed as a @@ -135,21 +131,28 @@ static struct termios *console_termios[MAX_NR_CONSOLES]; static struct termios *console_termios_locked[MAX_NR_CONSOLES]; struct vc vc_cons [MAX_NR_CONSOLES]; +#ifndef VT_SINGLE_DRIVER +static struct consw *con_driver_map[MAX_NR_CONSOLES]; +#endif + static int con_open(struct tty_struct *, struct file *); static void vc_init(unsigned int console, unsigned int rows, unsigned int cols, int do_clear); static void blank_screen(void); -static void unblank_screen(void); static void gotoxy(int currcons, int new_x, int new_y); static void save_cur(int currcons); static void reset_terminal(int currcons, int do_clear); static void con_flush_chars(struct tty_struct *tty); +static void set_vesa_blanking(unsigned long arg); +static void set_cursor(int currcons); +static void hide_cursor(int currcons); static int printable = 0; /* Is console ready for printing? */ int do_poke_blanked_console = 0; int console_blanked = 0; +static int vesa_blank_mode = 0; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ static int blankinterval = 10*60*HZ; static int vesa_off_interval = 0; @@ -181,11 +184,16 @@ static struct vc_data *master_display_fg = NULL; DECLARE_TASK_QUEUE(con_task_queue); /* + * For the same reason, we defer scrollback to the console_bh. + */ +static int scrollback_delta = 0; + +/* * Low-Level Functions */ #define IS_FG (currcons == fg_console) -#define IS_VISIBLE (*display_fg == vc_cons[currcons].d) +#define IS_VISIBLE CON_IS_VISIBLE(vc_cons[currcons].d) #ifdef VT_BUF_VRAM_ONLY #define DO_UPDATE 0 @@ -199,13 +207,10 @@ static inline unsigned short *screenpos(int currcons, int offset, int viewed) return p; } -static void scrolldelta(int lines) +static inline void scrolldelta(int lines) { - int currcons = fg_console; - - clear_selection(); - if (vcmode == KD_TEXT) - sw->con_scrolldelta(vc_cons[currcons].d, lines); + scrollback_delta += lines; + mark_bh(CONSOLE_BH); } static void scrup(int currcons, unsigned int t, unsigned int b, int nr) @@ -286,8 +291,11 @@ static void do_update_region(int currcons, unsigned long start, int count) void update_region(int currcons, unsigned long start, int count) { - if (DO_UPDATE) + if (DO_UPDATE) { + hide_cursor(currcons); do_update_region(currcons, start, count); + set_cursor(currcons); + } } /* Structure of attributes is hardware-dependent */ @@ -372,7 +380,8 @@ void invert_screen(int currcons, int offset, int count, int viewed) } } #endif - update_region(currcons, (unsigned long) p, count); + if (DO_UPDATE) + do_update_region(currcons, (unsigned long) p, count); } /* used by selection: complement pointer position */ @@ -480,7 +489,7 @@ static void hide_cursor(int currcons) sw->con_cursor(vc_cons[currcons].d,CM_ERASE); } -void set_cursor(int currcons) +static void set_cursor(int currcons) { if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS) return; @@ -505,9 +514,8 @@ static void set_origin(int currcons) pos = origin + video_size_row*y + 2*x; } -static inline void save_screen(void) +static inline void save_screen(int currcons) { - int currcons = fg_console; if (sw->con_save_screen) sw->con_save_screen(vc_cons[currcons].d); } @@ -516,45 +524,55 @@ static inline void save_screen(void) * Redrawing of screen */ -void update_screen(int new_console) +void redraw_screen(int new_console, int is_switch) { - int currcons = fg_console; int redraw = 1; - int old_console; + int currcons, old_console; static int lock = 0; - struct vc_data **display; if (lock) return; if (!vc_cons_allocated(new_console)) { /* strange ... */ - printk("update_screen: tty %d not allocated ??\n", new_console+1); + printk("redraw_screen: tty %d not allocated ??\n", new_console+1); return; } lock = 1; - hide_cursor(currcons); - if (fg_console != new_console) { - display = vc_cons[new_console].d->vc_display_fg; - old_console = (*display) ? (*display)->vc_num : fg_console; - *display = vc_cons[new_console].d; - fg_console = new_console; - currcons = old_console; - if (!IS_VISIBLE) - set_origin(currcons); + if (is_switch) { + currcons = fg_console; + hide_cursor(currcons); + if (fg_console != new_console) { + struct vc_data **display = vc_cons[new_console].d->vc_display_fg; + old_console = (*display) ? (*display)->vc_num : fg_console; + *display = vc_cons[new_console].d; + fg_console = new_console; + currcons = old_console; + if (!IS_VISIBLE) { + save_screen(currcons); + set_origin(currcons); + } + currcons = new_console; + if (old_console == new_console) + redraw = 0; + } + } else { currcons = new_console; - if (old_console == new_console) - redraw = 0; + hide_cursor(currcons); } + if (redraw) { set_origin(currcons); - if (sw->con_switch(vc_cons[currcons].d)) + set_palette(currcons); + if (sw->con_switch(vc_cons[currcons].d) && vcmode != KD_GRAPHICS) /* Update the screen contents */ do_update_region(currcons, origin, screenbuf_size/2); } set_cursor(currcons); - set_leds(); - compute_shiftstate(); + if (is_switch) { + set_leds(); + compute_shiftstate(); + } lock = 0; } @@ -567,27 +585,33 @@ int vc_cons_allocated(unsigned int i) return (i < MAX_NR_CONSOLES && vc_cons[i].d); } -void visual_init(int currcons) +static void visual_init(int currcons, int init) { /* ++Geert: sw->con_init determines console size */ sw = conswitchp; +#ifndef VT_SINGLE_DRIVER + if (con_driver_map[currcons]) + sw = con_driver_map[currcons]; +#endif cons_num = currcons; display_fg = &master_display_fg; vc_cons[currcons].d->vc_uni_pagedir_loc = &vc_cons[currcons].d->vc_uni_pagedir; vc_cons[currcons].d->vc_uni_pagedir = 0; hi_font_mask = 0; complement_mask = 0; - sw->con_init(vc_cons[currcons].d, 1); + can_do_color = 0; + sw->con_init(vc_cons[currcons].d, init); if (!complement_mask) complement_mask = can_do_color ? 0x7700 : 0x0800; + s_complement_mask = complement_mask; video_size_row = video_num_columns<<1; - video_screen_size = video_num_lines*video_size_row; + screenbuf_size = video_num_lines*video_size_row; } -int vc_allocate(unsigned int currcons, int init) /* return 0 on success */ +int vc_allocate(unsigned int currcons) /* return 0 on success */ { if (currcons >= MAX_NR_CONSOLES) - return -ENXIO; + return -ENXIO; if (!vc_cons[currcons].d) { long p, q; @@ -597,7 +621,7 @@ int vc_allocate(unsigned int currcons, int init) /* return 0 on success */ /* due to the granularity of kmalloc, we waste some memory here */ /* the alloc is done in two steps, to optimize the common situation - of a 25x80 console (structsize=216, video_screen_size=4000) */ + of a 25x80 console (structsize=216, screenbuf_size=4000) */ /* although the numbers above are not valid since long ago, the point is still up-to-date and the comment still has its value even if only as a historical artifact. --mj, July 1998 */ @@ -606,22 +630,19 @@ int vc_allocate(unsigned int currcons, int init) /* return 0 on success */ return -ENOMEM; vc_cons[currcons].d = (struct vc_data *)p; vt_cons[currcons] = (struct vt_struct *)(p+sizeof(struct vc_data)); - visual_init(currcons); - q = (long)kmalloc(video_screen_size, GFP_KERNEL); + visual_init(currcons, 1); + if (!*vc_cons[currcons].d->vc_uni_pagedir_loc) + con_set_default_unimap(currcons); + q = (long)kmalloc(screenbuf_size, GFP_KERNEL); if (!q) { kfree_s((char *) p, structsize); vc_cons[currcons].d = NULL; vt_cons[currcons] = NULL; return -ENOMEM; } - con_set_default_unimap(currcons); screenbuf = (unsigned short *) q; kmalloced = 1; - screenbuf_size = video_screen_size; - if (!sw->con_save_screen) - init = 0; /* If console does not have save_screen routine, - we should clear the screen */ - vc_init(currcons, video_num_lines, video_num_columns, !init); + vc_init(currcons, video_num_lines, video_num_columns, 1); } return 0; } @@ -671,12 +692,12 @@ int vc_resize(unsigned int lines, unsigned int cols, oll = video_num_lines; occ = video_num_columns; osr = video_size_row; - oss = video_screen_size; + oss = screenbuf_size; video_num_lines = ll; video_num_columns = cc; video_size_row = sr; - video_screen_size = ss; + screenbuf_size = ss; rlth = MIN(osr, sr); rrem = sr - rlth; @@ -721,11 +742,10 @@ int vc_resize(unsigned int lines, unsigned int cols, *cws = ws; } - if (IS_FG && vt_cons[fg_console]->vc_mode == KD_TEXT) - update_screen(fg_console); + if (IS_VISIBLE) + update_screen(currcons); } - set_cursor(fg_console); return 0; } @@ -1140,7 +1160,7 @@ static void set_mode(int currcons, int on_off) case 5: /* Inverted screen on/off */ if (decscnm != on_off) { decscnm = on_off; - invert_screen(currcons, 0, video_screen_size, 0); + invert_screen(currcons, 0, screenbuf_size, 0); update_attr(currcons); } break; @@ -1352,6 +1372,7 @@ static void reset_terminal(int currcons, int do_clear) set_leds(); cursor_type = CUR_DEFAULT; + complement_mask = s_complement_mask; default_attr(currcons); update_attr(currcons); @@ -1489,7 +1510,7 @@ static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c) vc_state = ESpalette; return; } else if (c=='R') { /* reset palette */ - reset_palette (currcons); + reset_palette(currcons); vc_state = ESnormal; } else vc_state = ESnormal; @@ -1505,7 +1526,7 @@ static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c) palette[i++] += par[j++]; palette[i] = 16*par[j++]; palette[i] += par[j]; - set_palette() ; + set_palette(currcons); vc_state = ESnormal; } } else @@ -1550,6 +1571,16 @@ static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c) return; } break; + case 'm': + if (ques) { + clear_selection(); + if (par[0]) + complement_mask = par[0]<<8 | par[1]; + else + complement_mask = s_complement_mask; + return; + } + break; case 'n': if (!ques) { if (par[0] == 5) @@ -1903,7 +1934,6 @@ static void console_bh(void) if (want_console >= 0) { if (want_console != fg_console && vc_cons_allocated(want_console)) { hide_cursor(fg_console); - save_screen(); change_console(want_console); /* we only changed when the console had already been allocated - a new console is not created @@ -1915,25 +1945,38 @@ static void console_bh(void) do_poke_blanked_console = 0; poke_blanked_console(); } + if (scrollback_delta) { + int currcons = fg_console; + clear_selection(); + if (vcmode == KD_TEXT) + sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta); + scrollback_delta = 0; + } } +#ifdef CONFIG_VT_CONSOLE + /* * Console on virtual terminal + * + * NOTE NOTE NOTE! This code can do no global locking. In particular, + * we can't disable interrupts or bottom half handlers globally, because + * we can be called from contexts that hold critical spinlocks, and + * trying do get a global lock at this point will lead to deadlocks. */ -#ifdef CONFIG_VT_CONSOLE void vt_console_print(struct console *co, const char * b, unsigned count) { int currcons = fg_console; unsigned char c; - static int printing = 0; + static unsigned long printing = 0; const ushort *start; ushort cnt = 0; ushort myx = x; - if (!printable || printing) - return; /* console not yet initialized */ - printing = 1; + /* console busy or not yet initialized */ + if (!printable || test_and_set_bit(0, &printing)) + return; if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) currcons = kmsg_redirect - 1; @@ -1944,6 +1987,9 @@ void vt_console_print(struct console *co, const char * b, unsigned count) goto quit; } + if (vcmode != KD_TEXT) + goto quit; + /* undraw cursor first */ if (IS_FG) hide_cursor(currcons); @@ -1952,11 +1998,8 @@ void vt_console_print(struct console *co, const char * b, unsigned count) /* Contrived structure to try to emulate original need_wrap behaviour * Problems caused when we have need_wrap set on '\n' character */ - disable_bh(CONSOLE_BH); while (count--) { - enable_bh(CONSOLE_BH); c = *b++; - disable_bh(CONSOLE_BH); if (c == 10 || c == 13 || c == 8 || need_wrap) { if (cnt > 0) { if (IS_VISIBLE) @@ -1998,12 +2041,11 @@ void vt_console_print(struct console *co, const char * b, unsigned count) need_wrap = 1; } } - enable_bh(CONSOLE_BH); set_cursor(currcons); poke_blanked_console(); quit: - printing = 0; + clear_bit(0, &printing); } static kdev_t vt_console_device(struct console *c) @@ -2017,7 +2059,7 @@ struct console vt_console_driver = { NULL, vt_console_device, keyboard_wait_for_keypress, - do_unblank_screen, + unblank_screen, NULL, CON_PRINTBUFFER, -1, @@ -2047,7 +2089,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) case 3: return paste_selection(tty); case 4: - do_unblank_screen(); + unblank_screen(); return 0; case 5: return sel_loadlut(arg); @@ -2175,7 +2217,7 @@ static int con_open(struct tty_struct *tty, struct file * filp) currcons = MINOR(tty->device) - tty->driver.minor_start; - i = vc_allocate(currcons, 0); + i = vc_allocate(currcons); if (i) return i; @@ -2196,7 +2238,7 @@ static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, video_num_columns = cols; video_num_lines = rows; video_size_row = cols<<1; - video_screen_size = video_num_lines * video_size_row; + screenbuf_size = video_num_lines * video_size_row; set_origin(currcons); pos = origin; @@ -2281,9 +2323,9 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) kmem_start += sizeof(struct vc_data); vt_cons[currcons] = (struct vt_struct *) kmem_start; kmem_start += sizeof(struct vt_struct); - visual_init(currcons); + visual_init(currcons, 1); screenbuf = (unsigned short *) kmem_start; - kmem_start += video_screen_size; + kmem_start += screenbuf_size; kmalloced = 0; vc_init(currcons, video_num_lines, video_num_columns, currcons || !sw->con_save_screen); @@ -2297,11 +2339,10 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) currcons = fg_console = 0; master_display_fg = vc_cons[currcons].d; set_origin(currcons); - save_screen(); + save_screen(currcons); gotoxy(currcons,x,y); csi_J(currcons, 0); update_screen(fg_console); - set_cursor(currcons); printk("Console: %s %s %dx%d", can_do_color ? "colour" : "mono", display_desc, video_num_columns, video_num_lines); @@ -2317,38 +2358,80 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) return kmem_start; } +#ifndef VT_SINGLE_DRIVER + +static void clear_buffer_attributes(int currcons) +{ + unsigned short *p = (unsigned short *) origin; + int count = screenbuf_size/2; + int mask = hi_font_mask | 0xff; + + for (; count > 0; count--, p++) { + scr_writew((scr_readw(p)&mask) | (video_erase_char&~mask), p); + } +} + /* * If we support more console drivers, this function is used * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. */ -#ifndef VT_BUF_VRAM_ONLY - void take_over_console(struct consw *csw, int first, int last, int deflt) { - int i; + int i, j = -1; const char *desc; - if (deflt) - conswitchp = csw; desc = csw->con_startup(); if (!desc) return; + if (deflt) + conswitchp = csw; for (i = first; i <= last; i++) { + int old_was_color; + int currcons = i; + + con_driver_map[i] = csw; + if (!vc_cons[i].d || !vc_cons[i].d->vc_sw) continue; - if (i == fg_console && - vc_cons[i].d->vc_sw->con_save_screen) - vc_cons[i].d->vc_sw->con_save_screen(vc_cons[i].d); + + j = i; + if (IS_VISIBLE) + save_screen(i); + old_was_color = vc_cons[i].d->vc_can_do_color; vc_cons[i].d->vc_sw->con_deinit(vc_cons[i].d); - vc_cons[i].d->vc_sw = csw; - vc_cons[i].d->vc_sw->con_init(vc_cons[i].d, 0); - } - printk("Console: switching to %s %s %dx%d\n", - vc_cons[fg_console].d->vc_can_do_color ? "colour" : "mono", - desc, vc_cons[fg_console].d->vc_cols, vc_cons[fg_console].d->vc_rows); - set_palette(); + visual_init(i, 0); + update_attr(i); + + /* If the console changed between mono <-> color, then + * the attributes in the screenbuf will be wrong. The + * following resets all attributes to something sane. + */ + if (old_was_color != vc_cons[i].d->vc_can_do_color) + clear_buffer_attributes(i); + + if (IS_VISIBLE) + update_screen(i); + } + printk("Console: switching "); + if (!deflt) + printk("consoles %d-%d ", first+1, last+1); + if (j >= 0) + printk("to %s %s %dx%d\n", + vc_cons[j].d->vc_can_do_color ? "colour" : "mono", + desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows); + else + printk("to %s\n", desc); +} + +void give_up_console(struct consw *csw) +{ + int i; + + for(i = 0; i < MAX_NR_CONSOLES; i++) + if (con_driver_map[i] == csw) + con_driver_map[i] = NULL; } #endif @@ -2357,7 +2440,7 @@ void take_over_console(struct consw *csw, int first, int last, int deflt) * Screen blanking */ -void set_vesa_blanking(unsigned long arg) +static void set_vesa_blanking(unsigned long arg) { char *argp = (char *)arg + 1; unsigned int mode; @@ -2365,13 +2448,7 @@ void set_vesa_blanking(unsigned long arg) vesa_blank_mode = (mode < 4) ? mode : 0; } -void vesa_blank(void) -{ - struct vc_data *c = vc_cons[fg_console].d; - c->vc_sw->con_blank(c, vesa_blank_mode + 1); -} - -void vesa_powerdown(void) +static void vesa_powerdown(void) { struct vc_data *c = vc_cons[fg_console].d; /* @@ -2391,7 +2468,7 @@ void vesa_powerdown(void) } } -void vesa_powerdown_screen(void) +static void vesa_powerdown_screen(void) { timer_active &= ~(1<<BLANK_TIMER); timer_table[BLANK_TIMER].fn = unblank_screen; @@ -2399,7 +2476,7 @@ void vesa_powerdown_screen(void) vesa_powerdown(); } -void do_blank_screen(int nopowersave) +void do_blank_screen(int entering_gfx) { int currcons = fg_console; int i; @@ -2408,9 +2485,9 @@ void do_blank_screen(int nopowersave) return; /* entering graphics mode? */ - if (nopowersave) { + if (entering_gfx) { hide_cursor(currcons); - save_screen(); + save_screen(currcons); sw->con_blank(vc_cons[currcons].d, -1); console_blanked = fg_console + 1; set_origin(currcons); @@ -2418,13 +2495,13 @@ void do_blank_screen(int nopowersave) } /* don't blank graphics */ - if (vt_cons[fg_console]->vc_mode != KD_TEXT) { + if (vcmode != KD_TEXT) { console_blanked = fg_console + 1; return; } - hide_cursor(fg_console); - if(vesa_off_interval && !nopowersave) { + hide_cursor(currcons); + if (vesa_off_interval) { timer_table[BLANK_TIMER].fn = vesa_powerdown_screen; timer_table[BLANK_TIMER].expires = jiffies + vesa_off_interval; timer_active |= (1<<BLANK_TIMER); @@ -2433,26 +2510,25 @@ void do_blank_screen(int nopowersave) timer_table[BLANK_TIMER].fn = unblank_screen; } - save_screen(); + save_screen(currcons); /* In case we need to reset origin, blanking hook returns 1 */ i = sw->con_blank(vc_cons[currcons].d, 1); console_blanked = fg_console + 1; if (i) set_origin(currcons); - if(!nopowersave) - { #ifdef CONFIG_APM - if (apm_display_blank()) - return; + if (apm_display_blank()) + return; #endif - vesa_blank(); - } + if (vesa_blank_mode) + sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } -void do_unblank_screen(void) +void unblank_screen(void) { int currcons; + if (!console_blanked) return; if (!vc_cons_allocated(fg_console)) { @@ -2482,11 +2558,6 @@ static void blank_screen(void) do_blank_screen(0); } -static void unblank_screen(void) -{ - do_unblank_screen(); -} - void poke_blanked_console(void) { timer_active &= ~(1<<BLANK_TIMER); @@ -2494,7 +2565,7 @@ void poke_blanked_console(void) return; if (console_blanked) { timer_table[BLANK_TIMER].fn = unblank_screen; - timer_table[BLANK_TIMER].expires = 0; + timer_table[BLANK_TIMER].expires = jiffies; /* Now */ timer_active |= 1<<BLANK_TIMER; } else if (blankinterval) { timer_table[BLANK_TIMER].expires = jiffies + blankinterval; @@ -2506,13 +2577,13 @@ void poke_blanked_console(void) * Palettes */ -void set_palette(void) +void set_palette(int currcons) { - if (vt_cons[fg_console]->vc_mode != KD_GRAPHICS) - vc_cons[fg_console].d->vc_sw->con_set_palette(vc_cons[fg_console].d, color_table); + if (vcmode != KD_GRAPHICS) + sw->con_set_palette(vc_cons[currcons].d, color_table); } -int set_get_cmap(unsigned char *arg, int set) +static int set_get_cmap(unsigned char *arg, int set) { int i, j, k; @@ -2528,13 +2599,14 @@ int set_get_cmap(unsigned char *arg, int set) } if (set) { for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc_cons_allocated(i)) + if (vc_cons_allocated(i)) { for (j = k = 0; j < 16; j++) { vc_cons[i].d->vc_palette[k++] = default_red[j]; vc_cons[i].d->vc_palette[k++] = default_grn[j]; vc_cons[i].d->vc_palette[k++] = default_blu[j]; } - set_palette(); + set_palette(i); + } } return 0; } @@ -2544,25 +2616,25 @@ int set_get_cmap(unsigned char *arg, int set) * map, 3 bytes per colour, 16 colours, range from 0 to 255. */ -int con_set_cmap (unsigned char *arg) +int con_set_cmap(unsigned char *arg) { return set_get_cmap (arg,1); } -int con_get_cmap (unsigned char *arg) +int con_get_cmap(unsigned char *arg) { return set_get_cmap (arg,0); } -void reset_palette (int currcons) +void reset_palette(int currcons) { - int j, k ; + int j, k; for (j=k=0; j<16; j++) { palette[k++] = default_red[j]; palette[k++] = default_grn[j]; palette[k++] = default_blu[j]; } - set_palette() ; + set_palette(currcons); } /* @@ -2637,7 +2709,9 @@ int con_font_op(int currcons, struct console_font_op *op) } op->data = temp; } + disable_bh(CONSOLE_BH); rc = sw->con_font_op(vc_cons[currcons].d, op); + enable_bh(CONSOLE_BH); op->data = old_op.data; if (!rc && !set) { int c = (op->width+7)/8 * 32 * op->charcount; @@ -2723,7 +2797,9 @@ EXPORT_SYMBOL(default_grn); EXPORT_SYMBOL(default_blu); EXPORT_SYMBOL(video_font_height); EXPORT_SYMBOL(video_scan_lines); +EXPORT_SYMBOL(vc_resize); -#ifndef VT_BUF_VRAM_ONLY +#ifndef VT_SINGLE_DRIVER EXPORT_SYMBOL(take_over_console); +EXPORT_SYMBOL(give_up_console); #endif diff --git a/drivers/char/console_macros.h b/drivers/char/console_macros.h index f10984ed0..83dfb3ded 100644 --- a/drivers/char/console_macros.h +++ b/drivers/char/console_macros.h @@ -26,8 +26,6 @@ #define utf (vc_cons[currcons].d->vc_utf) #define utf_count (vc_cons[currcons].d->vc_utf_count) #define utf_char (vc_cons[currcons].d->vc_utf_char) -#define video_mem_start (vc_cons[currcons].d->vc_video_mem_start) -#define video_mem_end (vc_cons[currcons].d->vc_video_mem_end) #define video_erase_char (vc_cons[currcons].d->vc_video_erase_char) #define disp_ctrl (vc_cons[currcons].d->vc_disp_ctrl) #define toggle_meta (vc_cons[currcons].d->vc_toggle_meta) @@ -64,6 +62,7 @@ #define cursor_type (vc_cons[currcons].d->vc_cursor_type) #define display_fg (vc_cons[currcons].d->vc_display_fg) #define complement_mask (vc_cons[currcons].d->vc_complement_mask) +#define s_complement_mask (vc_cons[currcons].d->vc_s_complement_mask) #define hi_font_mask (vc_cons[currcons].d->vc_hi_font_mask) #define vcmode (vt_cons[currcons]->vc_mode) diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index e63386d7c..95bb6f204 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c @@ -576,6 +576,24 @@ con_set_default_unimap(int con) } int +con_copy_unimap(int dstcon, int srccon) +{ + struct vc_data *sconp = vc_cons[srccon].d; + struct vc_data *dconp = vc_cons[dstcon].d; + struct uni_pagedir *q; + + if (!vc_cons_allocated(srccon) || !*sconp->vc_uni_pagedir_loc) + return -EINVAL; + if (*dconp->vc_uni_pagedir_loc == *sconp->vc_uni_pagedir_loc) + return 0; + con_free_unimap(dstcon); + q = (struct uni_pagedir *)*sconp->vc_uni_pagedir_loc; + q->refcount++; + *dconp->vc_uni_pagedir_loc = (long)q; + return 0; +} + +int con_get_unimap(int con, ushort ct, ushort *uct, struct unipair *list) { int i, j, k, ect; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 026ced7e8..62e31d9c9 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,7 +1,7 @@ #define BLOCKMOVE #define Z_WAKE static char rcsid[] = -"$Revision: 2.2.1.7 $$Date: 1998/09/03 12:07:28 $"; +"$Revision: 2.2.1.8 $$Date: 1998/11/13 12:46:20 $"; /* * linux/drivers/char/cyclades.c @@ -31,6 +31,10 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.2.1.8 1998/11/13 12:46:20 ivan + * cy_close function now resets (correctly) the tty->closing flag; + * JIFFIES_DIFF macro fixed. + * * Revision 2.2.1.7 1998/09/03 12:07:28 ivan * Fixed bug in cy_close function, which was not informing HW of * which port should have the reception disabled before doing so; @@ -612,7 +616,7 @@ static unsigned long cy_get_user(unsigned long *addr) #define STD_COM_FLAGS (0) -#define JIFFIES_DIFF(n, j) ((n) - (j)) +#define JIFFIES_DIFF(n, j) ((j) - (n)) static DECLARE_TASK_QUEUE(tq_cyclades); @@ -2590,8 +2594,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) #endif current->state = TASK_INTERRUPTIBLE; current->counter = 0; /* make us low-priority */ - current->timeout = jiffies + char_time; - schedule(); + schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && ((orig_jiffies + timeout) < jiffies)) @@ -2604,8 +2607,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) /* Run one more char cycle */ current->state = TASK_INTERRUPTIBLE; current->counter = 0; /* make us low-priority */ - current->timeout = jiffies + (char_time * 5); - schedule(); + schedule_timeout(char_time * 5); current->state = TASK_RUNNING; #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk("Clean (jiff=%lu)...done\n", jiffies); @@ -2732,13 +2734,13 @@ cy_close(struct tty_struct * tty, struct file * filp) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); + tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index c9c22f695..4d697cd44 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -63,8 +63,7 @@ #define wait_some(n) \ { \ current->state = TASK_INTERRUPTIBLE; \ - current->timeout = jiffies + n; \ - schedule(); \ + schedule_timeout(n); \ } #define handshake(count, maxio, timeout, ENABLE, f) \ diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 9ddf7c33d..f4224bd55 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -647,8 +647,7 @@ static void pc_close(struct tty_struct * tty, struct file * filp) if (ch->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + ch->close_delay; - schedule(); + schedule_timeout(ch->close_delay); } wake_up_interruptible(&ch->open_wait); diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 522569077..bf852fb20 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -102,7 +102,7 @@ static struct esp_pio_buffer *free_pio_buf; #define WAKEUP_CHARS 1024 static char *serial_name = "ESP serial driver"; -static char *serial_version = "2.1"; +static char *serial_version = "2.2"; static DECLARE_TASK_QUEUE(tq_esp); @@ -395,15 +395,20 @@ static _INLINE_ void receive_chars_pio(struct esp_struct *info, int num_bytes) static _INLINE_ void receive_chars_dma(struct esp_struct *info, int num_bytes) { + unsigned long flags; info->stat_flags &= ~ESP_STAT_RX_TIMEOUT; dma_bytes = num_bytes; info->stat_flags |= ESP_STAT_DMA_RX; + + flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_READ); set_dma_addr(dma, virt_to_bus(dma_buffer)); set_dma_count(dma, dma_bytes); enable_dma(dma); + release_dma_lock(flags); + serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX); } @@ -412,12 +417,17 @@ static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, { struct tty_struct *tty = info->tty; int num_bytes; - + unsigned long flags; + + + flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); info->stat_flags &= ~ESP_STAT_DMA_RX; num_bytes = dma_bytes - get_dma_residue(dma); + release_dma_lock(flags); + info->icount.rx += num_bytes; memcpy(tty->flip.char_buf_ptr, dma_buffer, num_bytes); @@ -534,6 +544,8 @@ static _INLINE_ void transmit_chars_pio(struct esp_struct *info, static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int num_bytes) { + unsigned long flags; + dma_bytes = num_bytes; if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) { @@ -564,34 +576,46 @@ static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int num_bytes) } info->stat_flags |= ESP_STAT_DMA_TX; + + flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_WRITE); set_dma_addr(dma, virt_to_bus(dma_buffer)); set_dma_count(dma, dma_bytes); enable_dma(dma); + release_dma_lock(flags); + serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info) { int num_bytes; + unsigned long flags; + + flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); num_bytes = dma_bytes - get_dma_residue(dma); info->icount.tx += dma_bytes; + release_dma_lock(flags); if (dma_bytes != num_bytes) { dma_bytes -= num_bytes; memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes); + + flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_WRITE); set_dma_addr(dma, virt_to_bus(dma_buffer)); set_dma_count(dma, dma_bytes); enable_dma(dma); + release_dma_lock(flags); + serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } else { dma_bytes = 0; @@ -1003,7 +1027,7 @@ static int startup(struct esp_struct * info) */ static void shutdown(struct esp_struct * info) { - unsigned long flags; + unsigned long flags, f; if (!(info->flags & ASYNC_INITIALIZED)) return; @@ -1025,8 +1049,11 @@ static void shutdown(struct esp_struct * info) /* stop a DMA transfer on the port being closed */ if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) { + f=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); + release_dma_lock(f); + dma_bytes = 0; } @@ -2120,8 +2147,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } @@ -2155,8 +2181,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) (serial_in(info, UART_ESI_STAT2) != 0xff)) { current->state = TASK_INTERRUPTIBLE; current->counter = 0; - current->timeout = jiffies + char_time; - schedule(); + schedule_timeout(char_time); if (signal_pending(current)) break; @@ -2615,6 +2640,9 @@ __initfunc(int espserial_init(void)) } memset((void *)info, 0, sizeof(struct esp_struct)); + /* rx_trigger, tx_trigger are needed by autoconfig */ + info->config.rx_trigger = rx_trigger; + info->config.tx_trigger = tx_trigger; i = 0; offset = 0; @@ -2644,8 +2672,6 @@ __initfunc(int espserial_init(void)) info->callout_termios = esp_callout_driver.init_termios; info->normal_termios = esp_driver.init_termios; info->config.rx_timeout = rx_timeout; - info->config.rx_trigger = rx_trigger; - info->config.tx_trigger = tx_trigger; info->config.flow_on = flow_on; info->config.flow_off = flow_off; info->config.pio_threshold = pio_threshold; @@ -2681,6 +2707,9 @@ __initfunc(int espserial_init(void)) } memset((void *)info, 0, sizeof(struct esp_struct)); + /* rx_trigger, tx_trigger are needed by autoconfig */ + info->config.rx_trigger = rx_trigger; + info->config.tx_trigger = tx_trigger; if (offset == 56) { i++; diff --git a/drivers/char/fbmem.c b/drivers/char/fbmem.c deleted file mode 100644 index d80d5e5b3..000000000 --- a/drivers/char/fbmem.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * linux/drivers/char/fbmem.c - * - * Copyright (C) 1994 Martin Schaller - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#include <linux/config.h> -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/malloc.h> -#include <linux/mman.h> -#include <linux/tty.h> -#include <linux/console.h> -#include <linux/console_struct.h> -#include <linux/init.h> -#ifdef CONFIG_PROC_FS -#include <linux/proc_fs.h> -#endif -#ifdef CONFIG_KMOD -#include <linux/kmod.h> -#endif - -#ifdef __mc68000__ -#include <asm/setup.h> -#endif -#ifdef __powerpc__ -#include <asm/io.h> -#endif -#include <asm/uaccess.h> -#include <asm/page.h> -#include <asm/pgtable.h> - -#include <linux/fb.h> - - - /* - * Frame buffer device initialization and setup routines - */ - -extern unsigned long acornfb_init(void); -extern void acornfb_setup(char *options, int *ints); -extern void amifb_init(void); -extern void amifb_setup(char *options, int *ints); -extern void atafb_init(void); -extern void atafb_setup(char *options, int *ints); -extern void macfb_init(void); -extern void macfb_setup(char *options, int *ints); -extern void cyberfb_init(void); -extern void cyberfb_setup(char *options, int *ints); -extern void retz3fb_init(void); -extern void retz3fb_setup(char *options, int *ints); -extern void clgenfb_init(void); -extern void clgenfb_setup(char *options, int *ints); -extern void vfb_init(void); -extern void vfb_setup(char *options, int *ints); -extern void offb_init(void); -extern void offb_setup(char *options, int *ints); -extern void atyfb_init(void); -extern void atyfb_setup(char *options, int *ints); -extern void dnfb_init(void); -extern void tgafb_init(void); -extern void virgefb_init(void); -extern void virgefb_setup(char *options, int *ints); -extern void resolver_video_setup(char *options, int *ints); -extern void s3triofb_init(void); -extern void s3triofb_setup(char *options, int *ints); -extern void vgafb_init(void); -extern void vgafb_setup(char *options, int *ints); -extern void vesafb_init(void); -extern void vesafb_setup(char *options, int *ints); -extern void mdafb_init(void); -extern void mdafb_setup(char *options, int *ints); -extern void hpfb_init(void); -extern void hpfb_setup(char *options, int *ints); -extern void sbusfb_init(void); -extern void sbusfb_setup(char *options, int *ints); -extern void g364fb_init(void); - -static struct { - const char *name; - void (*init)(void); - void (*setup)(char *options, int *ints); -} fb_drivers[] __initdata = { -#ifdef CONFIG_FB_RETINAZ3 - { "retz3", retz3fb_init, retz3fb_setup }, -#endif -#ifdef CONFIG_FB_ACORN - { "acorn", acornfb_init, acornfb_setup }, -#endif -#ifdef CONFIG_FB_AMIGA - { "amifb", amifb_init, amifb_setup }, -#endif -#ifdef CONFIG_FB_ATARI - { "atafb", atafb_init, atafb_setup }, -#endif -#ifdef CONFIG_FB_MAC - { "macfb", macfb_init, macfb_setup }, -#endif -#ifdef CONFIG_FB_CYBER - { "cyber", cyberfb_init, cyberfb_setup }, -#endif -#ifdef CONFIG_FB_CLGEN - { "clgen", clgenfb_init, clgenfb_setup }, -#endif -#ifdef CONFIG_FB_OF - { "offb", offb_init, offb_setup }, -#endif -#ifdef CONFIG_FB_ATY - { "atyfb", atyfb_init, atyfb_setup }, -#endif -#ifdef CONFIG_APOLLO - { "apollo", dnfb_init, NULL }, -#endif -#ifdef CONFIG_FB_S3TRIO - { "s3trio", s3triofb_init, s3triofb_setup }, -#endif -#ifdef CONFIG_FB_TGA - { "tga", tgafb_init, NULL }, -#endif -#ifdef CONFIG_FB_VIRGE - { "virge", virgefb_init, virgefb_setup }, -#endif -#ifdef CONFIG_FB_VGA - { "vga", vgafb_init, vgafb_setup }, -#endif -#ifdef CONFIG_FB_VESA - { "vesa", vesafb_init, vesafb_setup }, -#endif -#ifdef CONFIG_FB_MDA - { "mda", mdafb_init, mdafb_setup }, -#endif -#ifdef CONFIG_FB_HP300 - { "hpfb", hpfb_init, hpfb_setup }, -#endif -#ifdef CONFIG_FB_SBUS - { "sbus", sbusfb_init, sbusfb_setup }, -#endif -#ifdef CONFIG_FB_G364 - { "g364", g364fb_init, NULL }, -#endif -#ifdef CONFIG_GSP_RESOLVER - /* Not a real frame buffer device... */ - { "resolver", NULL, resolver_video_setup }, -#endif -#ifdef CONFIG_FB_VIRTUAL - /* Must be last to avoid that vfb becomes your primary display */ - { "vfb", vfb_init, vfb_setup }, -#endif -}; - -#define NUM_FB_DRIVERS (sizeof(fb_drivers)/sizeof(*fb_drivers)) - -static void (*pref_init_funcs[FB_MAX])(void); -static int num_pref_init_funcs __initdata = 0; - - -#define GET_INODE(i) MKDEV(FB_MAJOR, (i) << FB_MODES_SHIFT) -#define GET_FB_VAR_IDX(node) (MINOR(node) & ((1 << FB_MODES_SHIFT)-1)) - -struct fb_info *registered_fb[FB_MAX]; -int num_registered_fb = 0; - -char con2fb_map[MAX_NR_CONSOLES]; - -static inline int PROC_CONSOLE(void) -{ - if (!current->tty) - return fg_console; - - if (current->tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) - /* XXX Should report error here? */ - return fg_console; - - if (MINOR(current->tty->device) < 1) - return fg_console; - - return MINOR(current->tty->device) - 1; -} - -#ifdef CONFIG_PROC_FS -static int fbmem_read_proc(char *buf, char **start, off_t offset, - int len, int *eof, void *private) -{ - struct fb_info **fi; - - len = 0; - for (fi = registered_fb; fi < ®istered_fb[FB_MAX] && len < 4000; fi++) - if (*fi) - len += sprintf(buf + len, "%d %s\n", - GET_FB_IDX((*fi)->node), - (*fi)->modename); - *start = buf + offset; - return len > offset ? len - offset : 0; -} -#endif - -static ssize_t -fb_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - unsigned long p = *ppos; - struct inode *inode = file->f_dentry->d_inode; - int fbidx = GET_FB_IDX(inode->i_rdev); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; - struct fb_fix_screeninfo fix; - char *base_addr; - ssize_t copy_size; - - if (! fb || ! info->disp) - return -ENODEV; - - fb->fb_get_fix(&fix,PROC_CONSOLE(), info); - base_addr=info->disp->screen_base; - copy_size=(count + p <= fix.smem_len ? count : fix.smem_len - p); - if (copy_to_user(buf, base_addr+p, copy_size)) - return -EFAULT; - *ppos += copy_size; - return copy_size; -} - -static ssize_t -fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos) -{ - unsigned long p = *ppos; - struct inode *inode = file->f_dentry->d_inode; - int fbidx = GET_FB_IDX(inode->i_rdev); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; - struct fb_fix_screeninfo fix; - char *base_addr; - ssize_t copy_size; - - if (! fb || ! info->disp) - return -ENODEV; - - fb->fb_get_fix(&fix, PROC_CONSOLE(), info); - base_addr=info->disp->screen_base; - copy_size=(count + p <= fix.smem_len ? count : fix.smem_len - p); - if (copy_from_user(base_addr+p, buf, copy_size)) - return -EFAULT; - file->f_pos += copy_size; - return copy_size; -} - - -static void set_con2fb_map(int unit, int newidx) -{ - int oldidx = con2fb_map[unit]; - struct fb_info *oldfb, *newfb; - struct vc_data *conp; - - if (newidx != con2fb_map[unit]) { - oldfb = registered_fb[oldidx]; - newfb = registered_fb[newidx]; - if (newfb->fbops->fb_open(newfb,0)) - return; - oldfb->fbops->fb_release(oldfb,0); - conp = fb_display[unit].conp; - con2fb_map[unit] = newidx; - fb_display[unit] = *(newfb->disp); - fb_display[unit].conp = conp; - fb_display[unit].fb_info = newfb; - if (!newfb->changevar) - newfb->changevar = oldfb->changevar; - /* tell console var has changed */ - if (newfb->changevar) - newfb->changevar(unit); - } -} - -#ifdef CONFIG_KMOD -static void try_to_load(int fb) -{ - char modname[16]; - - sprintf(modname, "fb%d", fb); - request_module(modname); -} -#endif /* CONFIG_KMOD */ - -static int -fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int fbidx = GET_FB_IDX(inode->i_rdev); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; - struct fb_cmap cmap; - struct fb_var_screeninfo var; - struct fb_fix_screeninfo fix; - struct fb_con2fbmap con2fb; - int i; - - if (! fb) - return -ENODEV; - switch (cmd) { - case FBIOGET_VSCREENINFO: - if ((i = fb->fb_get_var(&var, PROC_CONSOLE(), info))) - return i; - return copy_to_user((void *) arg, &var, - sizeof(var)) ? -EFAULT : 0; - case FBIOPUT_VSCREENINFO: - if (copy_from_user(&var, (void *) arg, sizeof(var))) - return -EFAULT; - if ((i = fb->fb_set_var(&var, PROC_CONSOLE(), info))) - return i; - if (copy_to_user((void *) arg, &var, sizeof(var))) - return -EFAULT; - return 0; - case FBIOGET_FSCREENINFO: - if ((i = fb->fb_get_fix(&fix, PROC_CONSOLE(), info))) - return i; - return copy_to_user((void *) arg, &fix, sizeof(fix)) ? - -EFAULT : 0; - case FBIOPUTCMAP: - if (copy_from_user(&cmap, (void *) arg, sizeof(cmap))) - return -EFAULT; - return (fb->fb_set_cmap(&cmap, 0, PROC_CONSOLE(), info)); - case FBIOGETCMAP: - if (copy_from_user(&cmap, (void *) arg, sizeof(cmap))) - return -EFAULT; - return (fb->fb_get_cmap(&cmap, 0, PROC_CONSOLE(), info)); - case FBIOPAN_DISPLAY: - if (copy_from_user(&var, (void *) arg, sizeof(var))) - return -EFAULT; - if ((i=fb->fb_pan_display(&var, PROC_CONSOLE(), info))) - return i; - if (copy_to_user((void *) arg, &var, sizeof(var))) - return -EFAULT; - return i; - case FBIOGET_CON2FBMAP: - if (copy_from_user(&con2fb, (void *)arg, sizeof(con2fb))) - return -EFAULT; - if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) - return -EINVAL; - con2fb.framebuffer = con2fb_map[con2fb.console-1]; - return copy_to_user((void *)arg, &con2fb, - sizeof(con2fb)) ? -EFAULT : 0; - case FBIOPUT_CON2FBMAP: - if (copy_from_user(&con2fb, (void *)arg, sizeof(con2fb))) - return - EFAULT; - if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES) - return -EINVAL; - if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) - return -EINVAL; -#ifdef CONFIG_KMOD - if (!registered_fb[con2fb.framebuffer]) - try_to_load(con2fb.framebuffer); -#endif /* CONFIG_KMOD */ - if (!registered_fb[con2fb.framebuffer]) - return -EINVAL; - if (con2fb.console != 0) - set_con2fb_map(con2fb.console-1, con2fb.framebuffer); - else - /* set them all */ - for (i = 0; i < MAX_NR_CONSOLES; i++) - set_con2fb_map(i, con2fb.framebuffer); - return 0; - default: - return fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE(), - info); - } -} - -static int -fb_mmap(struct file *file, struct vm_area_struct * vma) -{ - int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; - struct fb_fix_screeninfo fix; - struct fb_var_screeninfo var; - unsigned long start; - u32 len; - - if (!fb) - return -ENODEV; - if (fb->fb_mmap) - return fb->fb_mmap(info, file, vma); - fb->fb_get_fix(&fix, PROC_CONSOLE(), info); - - /* frame buffer memory */ - start = (unsigned long)fix.smem_start; - len = (start & ~PAGE_MASK)+fix.smem_len; - start &= PAGE_MASK; - len = (len+~PAGE_MASK) & PAGE_MASK; - if (vma->vm_offset >= len) { - /* memory mapped io */ - vma->vm_offset -= len; - fb->fb_get_var(&var, PROC_CONSOLE(), info); - if (var.accel_flags) - return -EINVAL; - start = (unsigned long)fix.mmio_start; - len = (start & ~PAGE_MASK)+fix.mmio_len; - start &= PAGE_MASK; - len = (len+~PAGE_MASK) & PAGE_MASK; - } - if ((vma->vm_end - vma->vm_start + vma->vm_offset) > len) - return -EINVAL; - vma->vm_offset += start; - if (vma->vm_offset & ~PAGE_MASK) - return -ENXIO; -#if defined(__mc68000__) - if (CPU_IS_020_OR_030) - pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; - if (CPU_IS_040_OR_060) { - pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; - /* Use no-cache mode, serialized */ - pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; - } -#elif defined(__powerpc__) - pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED; -#elif defined(__alpha__) - /* Caching is off in the I/O space quadrant by design. */ -#elif defined(__sparc__) - /* Should never get here, all fb drivers should have their own - mmap routines */ -#elif defined(__i386__) - if (boot_cpu_data.x86 > 3) - pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; -#elif defined(__mips__) - pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; - pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; -#else -#warning What do we have to do here?? -#endif - if (remap_page_range(vma->vm_start, vma->vm_offset, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - vma->vm_file = file; - file->f_count++; - return 0; -} - -static int -fb_open(struct inode *inode, struct file *file) -{ - int fbidx = GET_FB_IDX(inode->i_rdev); - struct fb_info *info; - -#ifdef CONFIG_KMOD - if (!(info = registered_fb[fbidx])) - try_to_load(fbidx); -#endif /* CONFIG_KMOD */ - if (!(info = registered_fb[fbidx])) - return -ENODEV; - return info->fbops->fb_open(info,1); -} - -static int -fb_release(struct inode *inode, struct file *file) -{ - int fbidx = GET_FB_IDX(inode->i_rdev); - struct fb_info *info = registered_fb[fbidx]; - - info->fbops->fb_release(info,1); - return 0; -} - -static struct file_operations fb_fops = { - NULL, /* lseek */ - fb_read, /* read */ - fb_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - fb_ioctl, /* ioctl */ - fb_mmap, /* mmap */ - fb_open, /* open */ - NULL, /* flush */ - fb_release, /* release */ - NULL /* fsync */ -}; - -int -register_framebuffer(struct fb_info *fb_info) -{ - int i, j; - static int fb_ever_opened[FB_MAX]; - static int first = 1; - - if (num_registered_fb == FB_MAX) - return -ENXIO; - num_registered_fb++; - for (i = 0 ; i < FB_MAX; i++) - if (!registered_fb[i]) - break; - fb_info->node=GET_INODE(i); - registered_fb[i] = fb_info; - if (!fb_ever_opened[i]) { - /* - * We assume initial frame buffer devices can be opened this - * many times - */ - for (j = 0; j < MAX_NR_CONSOLES; j++) - if (con2fb_map[j] == i) - fb_info->fbops->fb_open(fb_info,0); - fb_ever_opened[i] = 1; - } - - if (first) { - first = 0; - take_over_console(&fb_con, 0, MAX_NR_CONSOLES-1, 1); - } - - return 0; -} - -int -unregister_framebuffer(const struct fb_info *fb_info) -{ - int i, j; - - i = GET_FB_IDX(fb_info->node); - for (j = 0; j < MAX_NR_CONSOLES; j++) - if (con2fb_map[j] == i) - return -EBUSY; - if (!registered_fb[i]) - return -EINVAL; - registered_fb[i]=NULL; - num_registered_fb--; - return 0; -} - -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_fbmem; -#endif - -__initfunc(void -fbmem_init(void)) -{ - int i; - -#ifdef CONFIG_PROC_FS - proc_fbmem = create_proc_entry("fb", 0, 0); - if (proc_fbmem) - proc_fbmem->read_proc = fbmem_read_proc; -#endif - - if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) - printk("unable to get major %d for fb devs\n", FB_MAJOR); - - /* - * Probe for all builtin frame buffer devices - */ - for (i = 0; i < num_pref_init_funcs; i++) - pref_init_funcs[i](); - - for (i = 0; i < NUM_FB_DRIVERS; i++) - if (fb_drivers[i].init) - fb_drivers[i].init(); -} - - -int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal, - const struct fb_info *fb_info) -{ -#if 0 - /* - * long long divisions .... $#%%#$ - */ - unsigned long long hpicos, vpicos; - const unsigned long long _1e12 = 1000000000000ULL; - const struct fb_monspecs *monspecs = &fb_info->monspecs; - - hpicos = (unsigned long long)htotal*(unsigned long long)pixclock; - vpicos = (unsigned long long)vtotal*(unsigned long long)hpicos; - if (!vpicos) - return 0; - - if (monspecs->hfmin == 0) - return 1; - - if (hpicos*monspecs->hfmin > _1e12 || hpicos*monspecs->hfmax < _1e12 || - vpicos*monspecs->vfmin > _1e12 || vpicos*monspecs->vfmax < _1e12) - return 0; -#endif - return 1; -} - -int fbmon_dpms(const struct fb_info *fb_info) -{ - return fb_info->monspecs.dpms; -} - - - /* - * Command line options - */ - -__initfunc(void video_setup(char *options, int *ints)) -{ - int i, j; - - if (!options || !*options) - return; - - if (!strncmp(options, "map:", 4)) { - options += 4; - if (*options) - for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { - if (!options[j]) - j = 0; - con2fb_map[i] = (options[j++]-'0') % FB_MAX; - } - return; - } - - if (num_pref_init_funcs == FB_MAX) - return; - - for (i = 0; i < NUM_FB_DRIVERS; i++) { - j = strlen(fb_drivers[i].name); - if (!strncmp(options, fb_drivers[i].name, j) && - options[j] == ':') { - if (!strcmp(options+j+1, "off")) - fb_drivers[i].init = NULL; - else { - if (fb_drivers[i].init) { - pref_init_funcs[num_pref_init_funcs++] = - fb_drivers[i].init; - fb_drivers[i].init = NULL; - } - if (fb_drivers[i].setup) - fb_drivers[i].setup(options+j+1, ints); - } - return; - } - } - /* - * If we get here no fb was specified and we default to pass the - * options to the first frame buffer that has an init and a setup - * function. - */ - for (i = 0; i < NUM_FB_DRIVERS; i++) { - if (fb_drivers[i].init && fb_drivers[i].setup) { - pref_init_funcs[num_pref_init_funcs++] = - fb_drivers[i].init; - fb_drivers[i].init = NULL; - - fb_drivers[i].setup(options, ints); - return; - } - } -} - - - /* - * Visible symbols for modules - */ - -EXPORT_SYMBOL(register_framebuffer); -EXPORT_SYMBOL(unregister_framebuffer); diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c index 5441daee9..9fb9269f8 100644 --- a/drivers/char/ftape/lowlevel/fdc-io.c +++ b/drivers/char/ftape/lowlevel/fdc-io.c @@ -388,6 +388,8 @@ int fdc_interrupt_wait(unsigned int time) struct wait_queue wait = {current, NULL}; sigset_t old_sigmask; static int resetting = 0; + long timeout; + TRACE_FUN(ft_t_fdc_dma); #if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16) @@ -400,8 +402,7 @@ int fdc_interrupt_wait(unsigned int time) } #endif /* timeout time will be up to USPT microseconds too long ! */ - current->timeout = jiffies + (1000 * time + FT_USPT - 1) / FT_USPT; - current->state = TASK_INTERRUPTIBLE; + timeout = (1000 * time + FT_USPT - 1) / FT_USPT; spin_lock_irq(¤t->sigmask_lock); old_sigmask = current->blocked; @@ -409,9 +410,10 @@ int fdc_interrupt_wait(unsigned int time) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); + current->state = TASK_INTERRUPTIBLE; add_wait_queue(&ftape_wait_intr, &wait); - while (!ft_interrupt_seen && current->state != TASK_RUNNING) { - schedule(); /* sets TASK_RUNNING on timeout */ + while (!ft_interrupt_seen && (current->state == TASK_INTERRUPTIBLE)) { + timeout = schedule_timeout(timeout); } spin_lock_irq(¤t->sigmask_lock); @@ -433,7 +435,6 @@ int fdc_interrupt_wait(unsigned int time) */ current->state = TASK_RUNNING; if (ft_interrupt_seen) { /* woken up by interrupt */ - current->timeout = 0; /* interrupt hasn't cleared this */ ft_interrupt_seen = 0; TRACE_EXIT 0; } diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c index e43da1e27..2fda1578b 100644 --- a/drivers/char/ftape/lowlevel/ftape-io.c +++ b/drivers/char/ftape/lowlevel/ftape-io.c @@ -89,17 +89,18 @@ void ftape_sleep(unsigned int time) /* Time too small for scheduler, do a busy wait ! */ ftape_udelay(time); } else { + long timeout; unsigned long flags; unsigned int ticks = (time + FT_USPT - 1) / FT_USPT; TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks); - current->timeout = jiffies + ticks; + timeout = ticks; current->state = TASK_INTERRUPTIBLE; save_flags(flags); sti(); do { while (current->state != TASK_RUNNING) { - schedule(); + timeout = schedule_timeout(timeout); } /* Mmm. Isn't current->blocked == 0xffffffff ? */ @@ -108,7 +109,7 @@ void ftape_sleep(unsigned int time) "awoken by non-blocked signal :-("); break; /* exit on signal */ } - } while (current->timeout > 0); + } while (timeout); restore_flags(flags); } TRACE_EXIT; diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c index 13594d02a..bafc7e1a9 100644 --- a/drivers/char/ftape/zftape/zftape-buffers.c +++ b/drivers/char/ftape/zftape/zftape-buffers.c @@ -122,9 +122,8 @@ void *zft_kmalloc(size_t size) void *new; while ((new = kmalloc(size, GFP_KERNEL)) == NULL) { - current->timeout = HZ/10; current->state = TASK_INTERRUPTIBLE; - schedule(); + schedule_timeout(HZ/10); } memset(new, 0, size); used_memory += size; diff --git a/drivers/char/hfmodem/main.c b/drivers/char/hfmodem/main.c index de5ed5cc2..d940bd232 100644 --- a/drivers/char/hfmodem/main.c +++ b/drivers/char/hfmodem/main.c @@ -148,14 +148,6 @@ struct hfmodem_state hfmodem_state[NR_DEVICE]; #define SP_PAR 2 #define SP_MIDI 4 -/* ---------------------------------------------------------------------- */ - -static int parptt_preempt(void *handle) -{ - /* we cannot relinquish the port in the middle of an operation */ - return 1; -} - /* --------------------------------------------------------------------- */ static void parptt_wakeup(void *handle) @@ -176,8 +168,8 @@ __initfunc(static int check_lpt(struct hfmodem_state *dev, unsigned int iobase)) pp = pp->next; if (!pp) return 0; - if (!(dev->ptt_out.pardev = parport_register_device(pp, hfmodem_drvname, parptt_preempt, parptt_wakeup, - NULL, PARPORT_DEV_LURK, dev))) + if (!(dev->ptt_out.pardev = parport_register_device(pp, hfmodem_drvname, NULL, parptt_wakeup, + NULL, PARPORT_DEV_EXCL, dev))) return 0; return 1; } diff --git a/drivers/char/i2c.c b/drivers/char/i2c.c index 9741f4be4..851929e94 100644 --- a/drivers/char/i2c.c +++ b/drivers/char/i2c.c @@ -21,7 +21,7 @@ #define I2C_DEBUG(x) if (i2c_debug) (x) static int scan = 0; -static int verbose = 1; +static int verbose = 0; static int i2c_debug = 0; MODULE_PARM(scan,"i"); diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index b41dd0690..f52f452b5 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -170,7 +170,7 @@ static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); */ static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; static char *stli_drvname = "istallion"; -static char *stli_drvversion = "5.4.6"; +static char *stli_drvversion = "5.4.7"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -635,16 +635,21 @@ static inline int stli_initports(stlibrd_t *brdp); * board. This is also a very useful debugging tool. */ static struct file_operations stli_fsiomem = { - NULL, - stli_memread, - stli_memwrite, - NULL, - NULL, - stli_memioctl, - NULL, - stli_memopen, - stli_memclose, - NULL + NULL, /* llseek */ + stli_memread, /* read */ + stli_memwrite, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + stli_memioctl, /* ioctl */ + NULL, /* mmap */ + stli_memopen, /* open */ + NULL, /* flush */ + stli_memclose, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ }; /*****************************************************************************/ @@ -1287,8 +1292,7 @@ static void stli_delay(int len) #endif if (len > 0) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + len; - schedule(); + schedule_timeout(len); current->state = TASK_RUNNING; } } @@ -2247,7 +2251,8 @@ static void stli_breakctl(struct tty_struct *tty, int state) { stlibrd_t *brdp; stliport_t *portp; - long arg, savestate, savetime; + long arg; + /* long savestate, savetime; */ #if DEBUG printk("stli_breakctl(tty=%x,state=%d)\n", (int) tty, state); @@ -2267,15 +2272,18 @@ static void stli_breakctl(struct tty_struct *tty, int state) /* * Due to a bug in the tty send_break() code we need to preserve * the current process state and timeout... - */ savetime = current->timeout; savestate = current->state; + */ arg = (state == -1) ? BREAKON : BREAKOFF; stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); +/* + * current->timeout = savetime; current->state = savestate; + */ } /*****************************************************************************/ diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c deleted file mode 100644 index 4f940f4d5..000000000 --- a/drivers/char/joystick.c +++ /dev/null @@ -1,861 +0,0 @@ -/* - * linux/drivers/char/joystick.c Version 1.0.9 - * Copyright (C) 1996-1998 Vojtech Pavlik - */ - -/* - * This is joystick driver for Linux. It supports up to two analog joysticks - * on a PC compatible machine. See Documentation/joystick.txt for changelog - * and credits. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/errno.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/malloc.h> -#include <linux/poll.h> -#include <linux/major.h> -#include <linux/joystick.h> -#include <asm/io.h> - -#define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */ - -#define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */ - -#define JS_TIMER_PERIOD HZ/50 /* button valid time (20 ms) */ -#define JS_BH_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */ -#define JS_BH_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */ - -#define JS_FIFO_SIZE 16 /* number of FIFO entries */ -#define JS_BUFF_SIZE 32 /* output buffer size */ -#define JS_RETRIES 4 /* number of retries */ -#define JS_DEF_PREC 16 /* initial precision for all axes */ - -#define JS_NUM 2 /* number of joysticks */ - -#define JS_AXES 0x0f /* bit mask for all axes */ -#define JS_BUTTONS 0xf0 /* bit mask for all buttons */ - -#define PIT_MODE 0x43 /* timer mode port */ -#define PIT_DATA 0x40 /* timer 0 data port */ -#define JS_PORT 0x201 /* joystick port */ - -#define JS_TRIGGER 0xff /* triggers one-shots */ -#define PIT_READ_TIMER 0x00 /* to read timer 0 */ - -#define DELTA(X,Y,Z) ((X)-(Y)+(((X)>=(Y))?0:Z)) /* cyclic delta */ -#define DELTA_T(X,Y) DELTA((X),(Y),(PIT_HZ/HZ)) /* for time measurement */ -#define DELTA_TX(X,Y,Z) DELTA_T((X),((Y)&0xFF)|(((Z)&0xFF)<<8)) -#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) -#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) -#define GOFF(X) (((X)==JS_FIFO_SIZE-1)?0:(X)+1) -#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) - -struct js_data { - int ahead; - int bhead; - int tail; - struct js_event buff[JS_BUFF_SIZE]; - struct js_list *list; - struct wait_queue *wait; - unsigned int exist; -}; - -struct js_axis { - int value; - struct js_corr corr; -}; - -struct js_list { - struct js_list *next; /* next-in-list pointer */ - unsigned long time; /* when the device was open */ - int tail; /* a tail for js_buff */ - char startup; -}; - -struct js_fifo { - unsigned long time; - unsigned long event; -}; - -static struct js_data jsd[JS_NUM]; /* joystick data */ -static struct timer_list js_timer; /* joystick timer */ - -static unsigned char js_fifo_head = 0; /* head of the fifo */ -static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1; /* tail of the fifo */ -static struct js_fifo js_fifo[JS_FIFO_SIZE]; /* the fifo */ - -static unsigned char js_last_buttons = 0; /* last read button state */ -static unsigned long js_bh_time = 0; /* last read axis time */ -static unsigned long js_mark_time = 0; - -static unsigned char js_axes_exist; /* all axes that exist */ -static unsigned char js_buttons_exist; /* all buttons that exist */ - -static struct js_axis js_axis[4]; -static unsigned int js_buttons = 0; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>"); -MODULE_SUPPORTED_DEVICE("js"); -MODULE_PARM(js, "1-2b"); - -static char js[] = {0, 0}; - -/* - * get_pit() returns the immediate state of PIT0. Must be run - * with interrupts disabled. - */ - -static inline int get_pit(void) -{ - int t, flags; - - save_flags(flags); - cli(); - outb(PIT_READ_TIMER, PIT_MODE); - t = inb(PIT_DATA); - t |= (int) inb(PIT_DATA) << 8; - restore_flags(flags); - return t; -} - -/* - * count_bits() counts set bits in a byte. - */ - -static int count_bits(unsigned int c) -{ - int i = 0; - while (c) { - i += c & 1; - c >>= 1; - } - return i; -} - -/* - * js_correct() performs correction of raw joystick data. - */ - -static int js_correct(int value, struct js_corr *corr) -{ - int t; - - if (corr->type == JS_CORR_NONE) return value; - t = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : value - corr->coef[1]) : value - corr->coef[0]; - - switch (corr->type) { - case JS_CORR_BROKEN: - t = t < 0 ? ((corr->coef[2] * t) >> 14) : ((corr->coef[3] * t) >> 14); - break; - default: - return 0; - } - - if (t < -32767) return -32767; - if (t > 32767) return 32767; - - return t; -} - -/* - * js_compare() compares two close axis values and decides - * whether they are "same". - */ - -static int js_compare(int x, int y, int prec) -{ - return (x < y + prec) && (y < x + prec); -} - -/* - * js_probe() probes for joysticks - */ - -inline int js_probe(void) -{ - int t; - - outb(JS_TRIGGER, JS_PORT); - t = get_pit(); - while (DELTA_T(t, get_pit()) < JS_MAXTIME); - t = inb(JS_PORT); - - if (js[0] || js[1]) { - jsd[0].exist = js[0] & ~(t & JS_AXES); - jsd[1].exist = js[1] & ~(t & JS_AXES); - } else - switch (t & JS_AXES) { - case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */ - case 0x03: jsd[0].exist = 0xcc; jsd[1].exist = 0x00; break; /* joystick 1 connected */ - case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break; /* 3-axis joystick connected */ - case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break; /* joysticks 0 and 1 connected */ - default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */ - } - - js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES; - js_buttons_exist = (jsd[0].exist | jsd[1].exist) & JS_BUTTONS; - - return 0; -} - -/* - * js_do_timer() controls the action by adding entries to the event - * fifo each time a button changes its state or axis valid time - * expires. - */ - -static void js_do_timer(unsigned long data) -{ - int t = ~inb(JS_PORT) & js_buttons_exist; - if ((js_last_buttons != t) && (js_fifo_head != js_fifo_tail)) { - js_fifo[js_fifo_head].event = js_last_buttons = t; - js_fifo[js_fifo_head].time = jiffies; - js_fifo_head++; - if (js_fifo_head == JS_FIFO_SIZE) js_fifo_head = 0; - if (!js_mark_time) { - js_mark_time = jiffies; - mark_bh(JS_BH); - } - } - else - if ((jiffies > js_bh_time + JS_BH_MAX_PERIOD) && !js_mark_time) { - js_mark_time = jiffies; - mark_bh(JS_BH); - } - js_timer.expires = jiffies + JS_TIMER_PERIOD; - add_timer(&js_timer); -} - - -/* - * Put an event in the buffer. This requires additional queue processing - * done by js_sync_buff, otherwise the buffer will be corrupted. - */ - -static void js_add_event(int i, __u32 time, __u8 type, __u8 number, __u16 value) -{ - int ahead = jsd[i].ahead++; - jsd[i].buff[ahead].time = time; - jsd[i].buff[ahead].type = type; - jsd[i].buff[ahead].number = number; - jsd[i].buff[ahead].value = value; - if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead=0; -} - -/* - * This checks for all owerflows caused by recent additions to the buffer. - * It does anything only if some processes are reading the data too slowly. - */ - -static void js_sync_buff(void) -{ - int i; - - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) - if (jsd[i].bhead != jsd[i].ahead) { - if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) { - struct js_list *curl; - curl = jsd[i].list; - while (curl) { - if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) { - curl->tail = jsd[i].ahead; - curl->startup = jsd[i].exist; - } - curl = curl->next; - } - jsd[i].tail = jsd[i].ahead; - } - jsd[i].bhead = jsd[i].ahead; - wake_up_interruptible(&jsd[i].wait); - } -} - -/* - * js_do_bh() does the main processing and adds events to output buffers. - */ - -static void js_do_bh(void) -{ - - int i, j, k; - unsigned int t; - - if (jiffies > js_bh_time + JS_BH_MIN_PERIOD) { - - unsigned int old_axis[4]; - unsigned int t_low, t_high; - unsigned int flags, joy_state; - unsigned int t1l, t1h, jsm; - unsigned char jss; - unsigned char again; - unsigned char retries = 0; - - for (i = 0; i < 4; i++) - old_axis[i] = js_axis[i].value; - - do { - i = 0; - again = 0; - t_low = 0; - t_high = 0; - joy_state = JS_AXES; - -/* - * Measure the axes. - */ - - save_flags(flags); - cli(); /* no interrupts */ - outb(JS_TRIGGER, JS_PORT); /* trigger one-shots */ - outb(PIT_READ_TIMER, PIT_MODE); /* read timer */ - t = (t1l = inb(PIT_DATA)) | - (t1h = inb(PIT_DATA)) << 8; - restore_flags(flags); - - do { - jss = inb(JS_PORT); - if ((jss ^ joy_state) & js_axes_exist) { - t_low = (t_low << 8) | t1l; - t_high = (t_high << 8) | t1h; - joy_state = (joy_state << 8) | jss; - i++; - } - - cli(); - outb(PIT_READ_TIMER, PIT_MODE); - t1l = inb(PIT_DATA); - t1h = inb(PIT_DATA); - restore_flags(flags); - - } while ((jss & js_axes_exist) && (DELTA_TX(t, t1l, t1h) < JS_MAXTIME)); - -/* - * Process the gathered axis data in joy_state. - */ - - joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */ - - for (; i > 0; i--) { - for (j = 0; j < 4; j++) - if (joy_state & js_axes_exist & (1 << j)) { - jsm = DELTA_TX(t, t_low, t_high); - if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) { - if (jsm < js_axis[j].value || !retries) - js_axis[j].value = jsm; - again = 1; - } - } - joy_state = joy_state >> 8; - t_low = t_low >> 8; - t_high = t_high >> 8; - } - - } while (retries++ < JS_RETRIES && again); - -/* - * Check if joystick lost. - */ - - for (i = 0; i < JS_NUM; i++) { - - if (jsd[i].exist && ((jss & jsd[i].exist & JS_AXES) == (jsd[i].exist & JS_AXES))) { - printk(KERN_WARNING "js%d: joystick lost.\n", i); - js_buttons_exist &= ~jsd[i].exist; - js_axes_exist &= ~jsd[i].exist; - jsd[i].exist = 0; - wake_up_interruptible(&jsd[i].wait); - } - - if ((jss & jsd[i].exist & JS_AXES)) { - printk(KERN_WARNING "js%d: joystick broken. Check cables.\n", i); - } - - } - -/* - * Put changed axes into output buffer. - */ - - if (retries > 1) - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) { - k = 0; - for (j = 0; j < 4; j++) - if ((1 << j) & jsd[i].exist) { - if ((t = js_correct(js_axis[j].value, &js_axis[j].corr)) != - js_correct(old_axis[j], &js_axis[j].corr)) - js_add_event(i, js_mark_time, JS_EVENT_AXIS, k, t); - k++; - } - } - js_bh_time = jiffies; - } - js_mark_time = 0; - -/* - * And now process the button fifo. - */ - - while (js_fifo_head != (t = GOFF(js_fifo_tail))) { - for (i = 0; i < JS_NUM; i++) - if (jsd[i].list) { - k = 0; - for (j = 4; j < 8; j++) - if ((1 << j) & jsd[i].exist) { - if ((1 << j) & (js_buttons ^ js_fifo[t].event)) - js_add_event(i, js_fifo[t].time, JS_EVENT_BUTTON, k, (js_fifo[t].event >> j) & 1); - k++; - } - } - js_buttons = js_fifo[js_fifo_tail = t].event; - } - -/* - * Synchronize the buffer. - */ - - js_sync_buff(); - -} - -/* - * js_lseek() just returns with error. - */ - -static loff_t js_lseek(struct file *file, loff_t offset, int origin) -{ - return -ESPIPE; -} - -/* - * js_read() copies one or more entries from jsd[].buff to user - * space. - */ - -static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); - struct wait_queue wait = { current, NULL }; - struct js_list *curl = file->private_data; - struct js_event *buff = (void *) buf; - unsigned long blocks = count / sizeof(struct js_event); - unsigned long i = 0, j; - int t, u = curl->tail; - int retval = 0; - -/* - * Check user data. - */ - - if (MAJOR(file->f_dentry->d_inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - if (file->f_pos < 0) - return -EINVAL; - if (!blocks) - return -EINVAL; - if (!curl) - return -EINVAL; - - if (minor > JS_NUM) - return -ENODEV; - if (!jsd[minor].exist) - return -ENODEV; - -/* - * Handle (non)blocking i/o. - */ - - - if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE)) - || (curl->startup && !js_bh_time)) { - - add_wait_queue(&jsd[minor].wait, &wait); - current->state = TASK_INTERRUPTIBLE; - - while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup && count != sizeof(struct JS_DATA_TYPE)) - || (curl->startup && !js_bh_time)) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - if (!jsd[minor].exist) { - retval = -ENODEV; - break; - } - } - current->state = TASK_RUNNING; - remove_wait_queue(&jsd[minor].wait, &wait); - } - - if (retval) return retval; - -/* - * Do the i/o. - */ - if (count != sizeof(struct JS_DATA_TYPE)) { - - if (curl->startup) { - struct js_event tmpevent; -/* - * Initial button state. - */ - - t = 0; - for (j = 0; j < 4 && (i < blocks) && !retval; j++) - if (jsd[minor].exist & (1 << j)) { - if (curl->startup & (1 << j)) { - tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; - tmpevent.number = t; - tmpevent.value = js_correct(js_axis[j].value, &js_axis[j].corr); - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) - retval = -EFAULT; - curl->startup &= ~(1 << j); - i++; - } - t++; - } - -/* - * Initial axis state. - */ - - t = 0; - for (j = 4; j < 8 && (i < blocks) && !retval; j++) - if (jsd[minor].exist & (1 << j)) { - if (curl->startup & (1 << j)) { - tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - tmpevent.number = t; - tmpevent.value = (js_buttons >> j) & 1; - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) - retval = -EFAULT; - curl->startup &= ~(1 << j); - i++; - } - t++; - } - } - -/* - * Buffer data. - */ - - while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) { - if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)((jsd[minor].buff[t].time - curl->time) * (1000/HZ)), &buff[i].time)) - retval = -EFAULT; - curl->tail = t; - i++; - } - - } - - else - -/* - * Handle version 0.x compatibility. - */ - - { - struct JS_DATA_TYPE *bufo = (void *) buf; - int buttons = 0; - - while (~jsd[minor].exist & (1<<i)) i++; - copy_to_user(&bufo->x, &js_axis[i].value, sizeof(int)); - - i++; - while (~jsd[minor].exist & (1<<i)) i++; - copy_to_user(&bufo->y, &js_axis[i].value, sizeof(int)); - - i = 0; - for (j = 4; j < 8; j++) - if ((1 << j) & jsd[minor].exist) - buttons |= (!!(js_last_buttons & (1 << j))) << (i++); - copy_to_user(&bufo->buttons, &buttons, sizeof(int)); - - curl->startup = 0; - curl->tail = GOB(jsd[minor].ahead); - retval = sizeof(struct JS_DATA_TYPE); - } - -/* - * Check main tail and move it. - */ - - if (u == jsd[minor].tail) { - t = curl->tail; - curl = jsd[minor].list; - while (curl && curl->tail != jsd[minor].tail) { - if (ROT(jsd[minor].ahead, t, curl->tail) || - (jsd[minor].ahead == curl->tail)) t = curl->tail; - curl = curl->next; - } - if (!curl) jsd[minor].tail = t; - } - - return retval ? retval : i*sizeof(struct js_event); -} - -/* - * js_poll() does select() support. - */ - -static unsigned int js_poll(struct file *file, poll_table *wait) -{ - struct js_list *curl; - unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); - curl = file->private_data; - - poll_wait(file, &jsd[minor].wait, wait); - if (GOF(curl->tail) != jsd[minor].ahead) - return POLLIN | POLLRDNORM; - return 0; -} - -/* - * js_ioctl handles misc ioctl calls. - */ - -static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - unsigned int minor = MINOR(inode->i_rdev); - int i, j; - - if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - if (minor > JS_NUM) - return -ENODEV; - if (!jsd[minor].exist) - return -ENODEV; - - switch (cmd) { - case JSIOCGVERSION: - if(put_user(JS_VERSION, (__u32 *) arg)) return -EFAULT; - break; - case JSIOCGAXES: - if(put_user(count_bits(jsd[minor].exist & JS_AXES), (__u8 *) arg)) return -EFAULT; - break; - case JSIOCGBUTTONS: - if(put_user(count_bits(jsd[minor].exist & JS_BUTTONS), (__u8 *) arg)) return -EFAULT; - break; - case JSIOCSCORR: - j = 0; - for (i = 0; i < 4; i++) - if ((1 << i) & jsd[minor].exist) { - if (copy_from_user(&js_axis[i].corr, (void *) arg + j * sizeof(struct js_corr), - sizeof(struct js_corr))) return -EFAULT; - j++; - } - js_bh_time = 0; - break; - case JSIOCGCORR: - j = 0; - for (i = 0; i < 4; i++) - if ((1 << i) & jsd[minor].exist) { - if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr, - sizeof(struct js_corr))) return -EFAULT; - j++; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -/* - * js_open() performs necessary initialization and adds - * an entry to the linked list. - */ - -static int js_open(struct inode *inode, struct file *file) -{ - unsigned int minor = MINOR(inode->i_rdev); - struct js_list *curl; - int t; - - if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - if (minor > JS_NUM) - return -ENODEV; - if (!jsd[minor].exist) { - js_probe(); - if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n", - minor, count_bits(jsd[minor].exist & JS_AXES), count_bits(jsd[minor].exist & JS_BUTTONS), JS_PORT); - else return -ENODEV; - } - - MOD_INC_USE_COUNT; - - if (!jsd[0].list && !jsd[1].list) { - js_timer.expires = jiffies + JS_TIMER_PERIOD; - add_timer(&js_timer); - } - - curl = jsd[minor].list; - jsd[minor].list = kmalloc(sizeof(struct js_list), GFP_KERNEL); - jsd[minor].list->next = curl; - jsd[minor].list->startup = jsd[minor].exist; - jsd[minor].list->tail = t = GOB(jsd[minor].ahead); - jsd[minor].list->time = jiffies; - - file->private_data = jsd[minor].list; - - return 0; -} - -/* - * js_release() removes an entry from list and deallocates memory - * used by it. - */ - -static int js_release(struct inode *inode, struct file *file) -{ - unsigned int minor = MINOR(inode->i_rdev); - struct js_list **curp, *curl; - int t; - - curp = &jsd[minor].list; - curl = file->private_data; - - while (*curp && (*curp != curl)) curp = &((*curp)->next); - *curp = (*curp)->next; - - if (jsd[minor].list) { - if (curl->tail == jsd[minor].tail) { - curl = jsd[minor].list; - t = curl->tail; - while (curl && curl->tail != jsd[minor].tail) { - if (ROT(jsd[minor].ahead, t, curl->tail) || - (jsd[minor].ahead == curl->tail)) t = curl->tail; - curl = curl->next; - } - if (!curl) jsd[minor].tail = t; - } - } - - kfree(file->private_data); - if (!jsd[0].list && !jsd[1].list) del_timer(&js_timer); - - MOD_DEC_USE_COUNT; - return 0; -} - -/* - * The operations structure. - */ - -static struct file_operations js_fops = -{ - js_lseek, /* js_lseek */ - js_read, /* js_read */ - NULL, /* js_write */ - NULL, /* js_readdir */ - js_poll, /* js_poll */ - js_ioctl, /* js_ioctl */ - NULL, /* js_mmap */ - js_open, /* js_open */ - NULL, /* js_flush */ - js_release, /* js_release */ - NULL /* js_sync */ -}; - -/* - * js_setup() parses kernel command line parametres. - */ - -#ifndef MODULE -__initfunc(void js_setup(char *str, int *ints)) - -{ - js[0] = ((ints[0] > 0) ? ints[1] : 0 ); - js[1] = ((ints[0] > 1) ? ints[2] : 0 ); -} -#endif - -/* - * js_init() registres the driver and calls the probe function. - * also initializes some crucial variables. - */ - -#ifdef MODULE -int init_module(void) -#else -__initfunc(int js_init(void)) -#endif -{ - int i; - - if (check_region(JS_PORT, 1)) { - printk(KERN_ERR "js: port %#x already in use\n", JS_PORT); - return -EBUSY; - } - - if (js_probe() < 0) { - printk(KERN_INFO "js: no joysticks found\n"); - return -ENODEV; - } - - if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { - printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); - return -EBUSY; - } - - for (i = 0; i < JS_NUM; i++) { - if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis %d-button joystick at %#x\n", - i, count_bits(jsd[i].exist & JS_AXES), count_bits(jsd[i].exist & JS_BUTTONS), JS_PORT); - jsd[i].ahead = jsd[i].bhead = 0; - jsd[i].tail = JS_BUFF_SIZE - 1; - jsd[i].list = NULL; - jsd[i].wait = NULL; - memset(jsd[i].buff, 0, JS_BUFF_SIZE * sizeof(struct js_event)); - } - - for (i = 0; i < 4; i++) { - js_axis[i].corr.type = JS_CORR_NONE; - js_axis[i].corr.prec = JS_DEF_PREC; - } - - request_region(JS_PORT, 1, "js"); - init_bh(JS_BH, &js_do_bh); - enable_bh(JS_BH); - init_timer(&js_timer); - js_timer.function = js_do_timer; - - return 0; -} - -/* - * cleanup_module() handles module removal. - */ - -#ifdef MODULE -void cleanup_module(void) -{ - if (MOD_IN_USE) - printk(KERN_NOTICE "js: device busy, remove delayed\n"); - else { - del_timer(&js_timer); - disable_bh(JS_BH); - if (unregister_chrdev(JOYSTICK_MAJOR, "js")) - printk(KERN_ERR "js: module cleanup failed\n"); - release_region(JS_PORT, 1); - } -} -#endif diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in new file mode 100644 index 000000000..db5585cd1 --- /dev/null +++ b/drivers/char/joystick/Config.in @@ -0,0 +1,19 @@ +# +# Joystick lowlevel driver configuration +# + +dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK +dep_tristate ' FPGaming and MadCatz A3D controllers' CONFIG_JOY_ASSASIN $CONFIG_JOYSTICK +dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK +dep_tristate ' Logitech Digital joysticks and gamepads' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK +dep_tristate ' Microsoft SideWinder, Genius Digital joysticks and gamepads' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK +dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK +dep_tristate ' PDPI Lightning 4 gamecards' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK +if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' NES, SNES, PSX, Multisystem joysticks and gamepads' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK + dep_tristate ' Sega, Multisystem joysticks and gamepads' CONFIG_JOY_DB9 $CONFIG_JOYSTICK + dep_tristate ' TurboGraFX Multisystem joystick interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK +fi +if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK +fi diff --git a/drivers/char/joystick/Makefile b/drivers/char/joystick/Makefile new file mode 100644 index 000000000..3339fc83f --- /dev/null +++ b/drivers/char/joystick/Makefile @@ -0,0 +1,112 @@ +# +# Makefile for the joystick drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +O_TARGET := js.o +O_OBJS := +M_OBJS := + +ifeq ($(CONFIG_JOYSTICK),y) +O_OBJS += joystick.o +else + ifeq ($(CONFIG_JOYSTICK),m) + M_OBJS += joystick.o + endif +endif + +ifeq ($(CONFIG_JOY_AMIGA),y) +O_OBJS += joy-amiga.o +else + ifeq ($(CONFIG_JOY_AMIGA),m) + M_OBJS += joy-amiga.o + endif +endif + +ifeq ($(CONFIG_JOY_ANALOG),y) +O_OBJS += joy-analog.o +else + ifeq ($(CONFIG_JOY_ANALOG),m) + M_OBJS += joy-analog.o + endif +endif + +ifeq ($(CONFIG_JOY_ASSASIN),y) +O_OBJS += joy-assasin.o +else + ifeq ($(CONFIG_JOY_ASSASIN),m) + M_OBJS += joy-assasin.o + endif +endif + +ifeq ($(CONFIG_JOY_CONSOLE),y) +O_OBJS += joy-console.o +else + ifeq ($(CONFIG_JOY_CONSOLE),m) + M_OBJS += joy-console.o + endif +endif + +ifeq ($(CONFIG_JOY_DB9),y) +O_OBJS += joy-db9.o +else + ifeq ($(CONFIG_JOY_DB9),m) + M_OBJS += joy-db9.o + endif +endif + +ifeq ($(CONFIG_JOY_GRAVIS),y) +O_OBJS += joy-gravis.o +else + ifeq ($(CONFIG_JOY_GRAVIS),m) + M_OBJS += joy-gravis.o + endif +endif + +ifeq ($(CONFIG_JOY_LIGHTNING),y) +O_OBJS += joy-lightning.o +else + ifeq ($(CONFIG_JOY_LIGHTNING),m) + M_OBJS += joy-lightning.o + endif +endif + +ifeq ($(CONFIG_JOY_LOGITECH),y) +O_OBJS += joy-logitech.o +else + ifeq ($(CONFIG_JOY_LOGITECH),m) + M_OBJS += joy-logitech.o + endif +endif + +ifeq ($(CONFIG_JOY_SIDEWINDER),y) +O_OBJS += joy-sidewinder.o +else + ifeq ($(CONFIG_JOY_SIDEWINDER),m) + M_OBJS += joy-sidewinder.o + endif +endif + +ifeq ($(CONFIG_JOY_THRUSTMASTER),y) +O_OBJS += joy-thrustmaster.o +else + ifeq ($(CONFIG_JOY_THRUSTMASTER),m) + M_OBJS += joy-thrustmaster.o + endif +endif + +ifeq ($(CONFIG_JOY_TURBOGRAFX),y) +O_OBJS += joy-turbografx.o +else + ifeq ($(CONFIG_JOY_TURBOGRAFX),m) + M_OBJS += joy-turbografx.o + endif +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/char/joystick/joy-amiga.c b/drivers/char/joystick/joy-amiga.c new file mode 100644 index 000000000..520f262a4 --- /dev/null +++ b/drivers/char/joystick/joy-amiga.c @@ -0,0 +1,157 @@ +/* + * joy-amiga.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * microswitch based joystick connected to Amiga joystick port. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/system.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/amigahw.h> + +static struct js_port* js_am_port __initdata = NULL; + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_PARM(js_am, "1-2i"); + +static int js_am[]={0,0}; + +/* + * js_am_read() reads and Amiga joystick data. + */ + +static int js_am_read(void *info, int **axes, int **buttons) +{ + int data = 0; + + switch (*(int*)info) { + case 0: + data = ~custom.joy0dat; + buttons[0][0] = (~ciaa.pra >> 6) & 1; + break; + + case 1: + data = ~custom.joy1dat; + buttons[0][0] = (~ciaa.pra >> 7) & 1; + break; + + default: + return -1; + } + + axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); + data = ~(data ^ (data << 1)); + axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); + + return 0; +} + +/* + * js_am_open() is a callback from the file open routine. + */ + +static int js_am_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_am_close() is a callback from the file release routine. + */ + +static int js_am_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_am_init_corr() initializes correction values of + * Amiga joysticks. + */ + +static void __init js_am_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } +} + +#ifndef MODULE +void __init js_am_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_am_init(void) +#endif +{ + int i; + + for (i = 0; i < 2; i++) + if (js_am[i]) { + js_am_port = js_register_port(js_am_port, &i, 1, sizeof(int), js_am_read); + printk(KERN_INFO "js%d: Amiga joystick at joy%ddat\n", + js_register_device(js_am_port, 0, 2, 1, "Amiga joystick", js_am_open, js_am_close), i); + js_am_init_corr(js_am_port->corr); + } + if (js_am_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-amiga: no joysticks specified\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + while (js_am_port != NULL) { + if (js_am_port->devs[0] != NULL) + js_unregister_device(js_am_port->devs[0]); + js_am_port = js_unregister_port(js_am_port); + } +} +#endif diff --git a/drivers/char/joystick/joy-analog.c b/drivers/char/joystick/joy-analog.c new file mode 100644 index 000000000..fdf04b212 --- /dev/null +++ b/drivers/char/joystick/joy-analog.c @@ -0,0 +1,208 @@ +/* + * joy-analog.c Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * up to two analog (or CHF/FCS) joysticks on a single joystick port. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_AN_MAX_TIME 3000 + +static int js_an_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_an_port __initdata = NULL; + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_PARM(js_an, "2-24i"); + +static int js_an[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; + +#include "joy-analog.h" + +/* + * js_an_read() reads analog joystick data. + */ + +static int js_an_read(void *xinfo, int **axes, int **buttons) +{ + struct js_an_info *info = xinfo; + unsigned char buf[4]; + int time[4]; + unsigned char u, v, a; + unsigned int t, t1; + int i, j; + int timeout; + int io = info->io; + + timeout = (JS_AN_MAX_TIME * js_time_speed_a) >> 10; + + info->buttons = (~inb(io) & JS_AN_BUTTONS_STD) >> 4; + + i = 0; + u = a = ((info->mask[0] | info->mask[1]) & JS_AN_AXES_STD) | (info->extensions & JS_AN_HAT_FCS) + | ((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); + + outb(0xff,io); + t = js_get_time_a(); + do { + v = inb(io) & a; + t1 = js_get_time_a(); + if (u ^ v) { + time[i] = js_delta_a(t1,t); + buf[i] = u ^ v; + u = v; + i++; + } + } while (v && js_delta_a(t1,t) < timeout); + + for (--i; i >= 0; i--) + for (j = 0; j < 4; j++) + if (buf[i] & (1 << j)) info->axes[j] = (time[i] << 10) / js_time_speed_a; + + js_an_decode(info, axes, buttons); + + return 0; +} + +/* + * js_an_open() is a callback from the file open routine. + */ + +static int js_an_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_an_close() is a callback from the file release routine. + */ + +static int js_an_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_an_probe() probes for analog joysticks. + */ + +static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port) +{ + struct js_an_info info; + int i, numdev; + unsigned char u; + + if (io < 0) return port; + + if (check_region(io, 1)) return port; + + if (((u = inb(io)) & 3) == 3) return port; + outb(0xff,io); + u = inb(io); + udelay(JS_AN_MAX_TIME); + u = (inb(io) ^ u) & u; + + if (!u) return port; + if (u & 0xf0) return port; + + if ((numdev = js_an_probe_devs(&info, u, mask0, mask1, port)) <= 0) + return port; + + info.io = io; + request_region(info.io, 1, "joystick (analog)"); + port = js_register_port(port, &info, numdev, sizeof(struct js_an_info), js_an_read); + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, js_an_axes(i, &info), js_an_buttons(i, &info), + js_an_name(i, &info), js_an_open, js_an_close), + js_an_name(i, &info), info.io); + + js_an_read(port->info, port->axes, port->buttons); + js_an_init_corr(port->info, port->axes, port->corr, 8); + + return port; +} + +#ifndef MODULE +void __init js_an_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_an_init(void) +#endif +{ + int i; + + if (js_an[0] >= 0) { + for (i = 0; (js_an[i*3] >= 0) && i < 8; i++) + js_an_port = js_an_probe(js_an[i*3], js_an[i*3+1], js_an[i*3+2], js_an_port); + } else { + for (i = 0; js_an_port_list[i]; i++) + js_an_port = js_an_probe(js_an_port_list[i], 0, 0, js_an_port); + } + if (js_an_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-analog: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_an_info *info; + + while (js_an_port != NULL) { + for (i = 0; i < js_an_port->ndevs; i++) + if (js_an_port->devs[i] != NULL) + js_unregister_device(js_an_port->devs[i]); + info = js_an_port->info; + release_region(info->io, 1); + js_an_port = js_unregister_port(js_an_port); + } + +} +#endif diff --git a/drivers/char/joystick/joy-analog.h b/drivers/char/joystick/joy-analog.h new file mode 100644 index 000000000..06c0079a4 --- /dev/null +++ b/drivers/char/joystick/joy-analog.h @@ -0,0 +1,297 @@ +/* + * joy-analog.h Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This file is designed to be included in any joystick driver + * that communicates with standard analog joysticks. This currently + * is: joy-analog.c, joy-assasin.c, and joy-lightning.c + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define JS_AN_AXES_STD 0x0f +#define JS_AN_BUTTONS_STD 0xf0 + +#define JS_AN_BUTTONS_CHF 0x01 +#define JS_AN_HAT1_CHF 0x02 +#define JS_AN_HAT2_CHF 0x04 +#define JS_AN_ANY_CHF 0x07 +#define JS_AN_HAT_FCS 0x08 +#define JS_AN_HATS_ALL 0x0e +#define JS_AN_BUTTON_PXY_X 0x10 +#define JS_AN_BUTTON_PXY_Y 0x20 +#define JS_AN_BUTTON_PXY_U 0x40 +#define JS_AN_BUTTON_PXY_V 0x80 +#define JS_AN_BUTTONS_PXY_XY 0x30 +#define JS_AN_BUTTONS_PXY_UV 0xc0 +#define JS_AN_BUTTONS_PXY 0xf0 + +static struct { + int x; + int y; +} js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}}; + +struct js_an_info { + int io; + unsigned char mask[2]; + unsigned int extensions; + int axes[4]; + int initial[4]; + unsigned char buttons; +}; + +/* + * js_an_decode() decodes analog joystick data. + */ + +static void js_an_decode(struct js_an_info *info, int **axes, int **buttons) +{ + int i, j, k; + int hat1, hat2, hat3; + + hat1 = hat2 = hat3 = 0; + if (info->mask[0] & JS_AN_BUTTONS_STD) buttons[0][0] = 0; + if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0; + + if (info->extensions & JS_AN_ANY_CHF) { + switch (info->buttons) { + case 0x1: buttons[0][0] = 0x01; break; + case 0x2: buttons[0][0] = 0x02; break; + case 0x4: buttons[0][0] = 0x04; break; + case 0x8: buttons[0][0] = 0x08; break; + case 0x5: buttons[0][0] = 0x10; break; + case 0x9: buttons[0][0] = 0x20; break; + case 0xf: hat1 = 1; break; + case 0xb: hat1 = 2; break; + case 0x7: hat1 = 3; break; + case 0x3: hat1 = 4; break; + case 0xe: hat2 = 1; break; + case 0xa: hat2 = 2; break; + case 0x6: hat2 = 3; break; + case 0xc: hat2 = 4; break; + } + k = info->extensions & JS_AN_BUTTONS_CHF ? 6 : 4; + } else { + for (i = 1; i >= 0; i--) + for (j = k = 0; j < 4; j++) + if (info->mask[i] & (0x10 << j)) + buttons[i][0] |= ((info->buttons >> j) & 1) << k++; + } + + if (info->extensions & JS_AN_BUTTON_PXY_X) + buttons[0][0] |= (info->axes[2] < (info->initial[2] >> 1)) << k++; + if (info->extensions & JS_AN_BUTTON_PXY_Y) + buttons[0][0] |= (info->axes[3] < (info->initial[3] >> 1)) << k++; + if (info->extensions & JS_AN_BUTTON_PXY_U) + buttons[0][0] |= (info->axes[2] > (info->initial[2] + (info->initial[2] >> 1))) << k++; + if (info->extensions & JS_AN_BUTTON_PXY_V) + buttons[0][0] |= (info->axes[3] > (info->initial[3] + (info->initial[3] >> 1))) << k++; + + if (info->extensions & JS_AN_HAT_FCS) + for (j = 0; j < 4; j++) + if (info->axes[3] < ((info->initial[3] * ((j << 1) + 1)) >> 3)) { + hat3 = j + 1; + break; + } + + for (i = 1; i >= 0; i--) + for (j = k = 0; j < 4; j++) + if (info->mask[i] & (1 << j)) + axes[i][k++] = info->axes[j]; + + if (info->extensions & JS_AN_HAT1_CHF) { + axes[0][k++] = js_an_hat_to_axis[hat1].x; + axes[0][k++] = js_an_hat_to_axis[hat1].y; + } + if (info->extensions & JS_AN_HAT2_CHF) { + axes[0][k++] = js_an_hat_to_axis[hat2].x; + axes[0][k++] = js_an_hat_to_axis[hat2].y; + } + if (info->extensions & JS_AN_HAT_FCS) { + axes[0][k++] = js_an_hat_to_axis[hat3].x; + axes[0][k++] = js_an_hat_to_axis[hat3].y; + } +} + +/* + * js_an_count_bits() counts set bits in a byte. + */ + +static inline int js_an_count_bits(unsigned long c) +{ + int i = 0; + while (c) { + i += c & 1; + c >>= 1; + } + return i; +} + +/* + * js_an_init_corr() initializes the correction values for + * analog joysticks. + */ + +static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct js_corr **corr, int prec) +{ + int i, j, t; + + for (i = 0; i < 2; i++) + for (j = 0; j < js_an_count_bits(info->mask[i] & 0xf); j++) { + + if ((j == 2 && (info->mask[i] & 0xb) == 0xb) || + (j == 3 && (info->mask[i] & 0xf) == 0xf)) { + t = (axes[i][0] + axes[i][1]) >> 1; + } else { + t = axes[i][j]; + } + + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = prec; + corr[i][j].coef[0] = t - (t >> 3); + corr[i][j].coef[1] = t + (t >> 3); + corr[i][j].coef[2] = (1 << 29) / (t - (t >> 2) + 1); + corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1); + } + + i = js_an_count_bits(info->mask[0] & 0xf); + + for (j = i; j < i + (js_an_count_bits(info->extensions & JS_AN_HATS_ALL) << 1); j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = (1 << 29); + corr[0][j].coef[3] = (1 << 29); + } + + for (i = 0; i < 4; i++) + info->initial[i] = info->axes[i]; +} + + +/* + * js_an_probe_devs() probes for analog joysticks. + */ + +static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0, int mask1, struct js_port *port) +{ + info->mask[0] = info->mask[1] = info->extensions = 0; + + if (mask0 || mask1) { + info->mask[0] = mask0 & (exist | 0xf0); + info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0]; + info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) | + ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF); + if (info->extensions & JS_AN_BUTTONS_PXY) { + info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2); + info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); + info->mask[1] = 0; + } + if (info->extensions & JS_AN_HAT_FCS) { + info->mask[0] &= ~JS_AN_HAT_FCS; + info->mask[1] = 0; + info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_U); + } + if (info->extensions & JS_AN_ANY_CHF) { + info->mask[0] |= 0xf0; + info->mask[1] = 0; + } + if (!(info->mask[0] | info->mask[1])) return -1; + } else { + switch (exist) { + case 0x0: + return -1; + case 0x3: + info->mask[0] = 0xf3; /* joystick 0, assuming 4-button */ + break; + case 0xb: + info->mask[0] = 0xfb; /* 3-axis, 4-button joystick */ + break; + case 0xc: + info->mask[0] = 0xcc; /* joystick 1 */ + break; + case 0xf: + info->mask[0] = 0x33; /* joysticks 0 and 1 */ + info->mask[1] = 0xcc; + break; + default: + printk(KERN_WARNING "joy-analog: Unknown joystick device detected " + "(data=%#x), contact <vojtech@ucw.cz>\n", exist); + return -1; + } + } + + return !!info->mask[0] + !!info->mask[1]; +} + +/* + * js_an_axes() returns the number of axes for an analog joystick. + */ + +static inline int js_an_axes(int i, struct js_an_info *info) +{ + return js_an_count_bits(info->mask[i] & 0x0f) + js_an_count_bits(info->extensions & JS_AN_HATS_ALL) * 2; +} + +/* + * js_an_buttons() returns the number of buttons for an analog joystick. + */ + +static inline int js_an_buttons(int i, struct js_an_info *info) +{ + return js_an_count_bits(info->mask[i] & 0xf0) + + (info->extensions & JS_AN_BUTTONS_CHF) * 2 + + js_an_count_bits(info->extensions & JS_AN_BUTTONS_PXY); +} + +/* + * js_an_name() constructs a name for an analog joystick. + */ + +static char js_an_name_buf[128] __initdata = ""; + +static char __init *js_an_name(int i, struct js_an_info *info) +{ + + sprintf(js_an_name_buf, "Analog %d-axis %d-button", + js_an_count_bits(info->mask[i] & 0x0f), + js_an_buttons(i, info)); + + if (info->extensions & JS_AN_HATS_ALL) + sprintf(js_an_name_buf, "%s %d-hat", + js_an_name_buf, + js_an_count_bits(info->extensions & JS_AN_HATS_ALL)); + + strcat(js_an_name_buf, " joystick"); + + if (info->extensions) + sprintf(js_an_name_buf, "%s with%s%s%s extensions", + js_an_name_buf, + info->extensions & JS_AN_ANY_CHF ? " CHF" : "", + info->extensions & JS_AN_HAT_FCS ? " FCS" : "", + info->extensions & JS_AN_BUTTONS_PXY ? " XY-button" : ""); + + return js_an_name_buf; +} diff --git a/drivers/char/joystick/joy-assasin.c b/drivers/char/joystick/joy-assasin.c new file mode 100644 index 000000000..64d16d7b0 --- /dev/null +++ b/drivers/char/joystick/joy-assasin.c @@ -0,0 +1,423 @@ +/* + * joy-assasin.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * joysticks using FP-Gaming's Assasin 3D protocol. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_AS_MAX_START 250 +#define JS_AS_MAX_STROBE 50 +#define JS_AS_MAX_TIME 2400 +#define JS_AS_MAX_LENGTH 40 + +#define JS_AS_MODE_A3D 1 /* Assasin 3D */ +#define JS_AS_MODE_PAN 2 /* Panther */ +#define JS_AS_MODE_OEM 3 /* Panther OEM version */ +#define JS_AS_MODE_PXL 4 /* Panther XL */ + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_PARM(js_as, "2-24i"); + +static int js_as[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; + +static int js_as_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_as_port __initdata = NULL; + +#include "joy-analog.h" + +struct js_as_info { + int io; + char mode; + char rudder; + struct js_an_info an; +}; + +/* + * js_as_read_packet() reads an Assasin 3D packet. + */ + +static int js_as_read_packet(int io, int length, char *data) +{ + unsigned char u, v; + int i; + unsigned int t, t1; + unsigned long flags; + + int start = (js_time_speed * JS_AS_MAX_START) >> 10; + int strobe = (js_time_speed * JS_AS_MAX_STROBE) >> 10; + + i = 0; + + __save_flags(flags); + __cli(); + outb(0xff,io); + + u = inb(io); + t = js_get_time(); + + do { + v = inb(io); + t1 = js_get_time(); + } while (u == v && js_delta(t1, t) < start); + + t = t1; + + do { + v = inb(io); + t1 = js_get_time(); + if ((u ^ v) & u & 0x10) { + data[i++] = v >> 5; + t = t1; + } + u = v; + } while (i < length && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + return i; +} + +/* + * js_as_csum() computes checksum of triplet packet + */ + +static int js_as_csum(char *data, int count) +{ + int i, csum = 0; + for (i = 0; i < count - 2; i++) csum += data[i]; + return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); +} + +/* + * js_as_read() reads and analyzes A3D joystick data. + */ + +static int js_as_read(void *xinfo, int **axes, int **buttons) +{ + struct js_as_info *info = xinfo; + char data[JS_AS_MAX_LENGTH]; + + switch (info->mode) { + + case JS_AS_MODE_A3D: + case JS_AS_MODE_OEM: + case JS_AS_MODE_PAN: + + if (js_as_read_packet(info->io, 29, data) != 29) return -1; + if (data[0] != info->mode) return -1; + if (js_as_csum(data, 29)) return -1; + + axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7); + axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7); + + buttons[0][0] = (data[2] << 2) | (data[3] >> 1); + + info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; + + info->an.buttons = ((data[3] << 3) | data[4]) & 0xf; + + js_an_decode(&info->an, axes + 1, buttons + 1); + + return 0; + + case JS_AS_MODE_PXL: + + if (js_as_read_packet(info->io, 33, data) != 33) return -1; + if (data[0] != info->mode) return -1; + if (js_as_csum(data, 33)) return -1; + + axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128; + axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128; + info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128; + axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128; + + axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1); + axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1); + axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1); + axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1); + + axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7); + axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7); + + buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8]; + + if (info->rudder) axes[1][0] = info->an.axes[0]; + + return 0; + + default: + printk("Error.\n"); + return -1; + } +} + +/* + * js_as_open() is a callback from the file open routine. + */ + +static int js_as_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_as_close() is a callback from the file release routine. + */ + +static int js_as_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_as_pxl_init_corr() initializes the correction values for + * the Panther XL. + */ + +static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = axes[0][i] - 4; + corr[0][i].coef[1] = axes[0][i] + 4; + corr[0][i].coef[2] = (1 << 29) / (127 - 32); + corr[0][i].coef[3] = (1 << 29) / (127 - 32); + } + + corr[0][2].type = JS_CORR_BROKEN; + corr[0][2].prec = 0; + corr[0][2].coef[0] = 127 - 4; + corr[0][2].coef[1] = 128 + 4; + corr[0][2].coef[2] = (1 << 29) / (127 - 6); + corr[0][2].coef[3] = (1 << 29) / (127 - 6); + + for (i = 3; i < 7; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } + + for (i = 7; i < 9; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = -1; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (104 << 14); + corr[0][i].coef[3] = (104 << 14); + } +} + +/* + * js_as_as_init_corr() initializes the correction values for + * the Panther and Assasin. + */ + +static void __init js_as_as_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = -1; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (104 << 14); + corr[0][i].coef[3] = (104 << 14); + } +} + +/* + * js_as_rudder_init_corr() initializes the correction values for + * the Panther XL connected rudder. + */ + +static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes) +{ + corr[1][0].type = JS_CORR_BROKEN; + corr[1][0].prec = 0; + corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3); + corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3); + corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); + corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); +} + +/* + * js_as_probe() probes for A3D joysticks. + */ + +static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port) +{ + struct js_as_info iniinfo; + struct js_as_info *info = &iniinfo; + char *name; + char data[JS_AS_MAX_LENGTH]; + unsigned char u; + int i; + int numdev; + + memset(info, 0, sizeof(struct js_as_info)); + + if (io < 0) return port; + + if (check_region(io, 1)) return port; + if (((u = inb(io)) & 3) == 3) return port; + outb(0xff,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + if (js_as_read_packet(io, 1, data) != 1) return port; + + if (data[0] && data[0] <= 4) { + info->mode = data[0]; + info->io = io; + request_region(io, 1, "joystick (assasin)"); + port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); + info = port->info; + } else { + printk(KERN_WARNING "joy-assasin: unknown joystick device detected " + "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n", io, data[0]); + return port; + } + + udelay(JS_AS_MAX_TIME); + + if (info->mode == JS_AS_MODE_PXL) { + printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", + js_register_device(port, 0, 9, 9, "MadCatz Panther XL", js_as_open, js_as_close), + info->io); + js_as_read(port->info, port->axes, port->buttons); + js_as_pxl_init_corr(port->corr, port->axes); + if (info->an.axes[0] < 254) { + printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n", + js_register_device(port, 1, 1, 0, "Analog rudder", js_as_open, js_as_close)); + info->rudder = 1; + port->axes[1][0] = info->an.axes[0]; + js_as_rudder_init_corr(port->corr, port->axes); + } + return port; + } + + switch (info->mode) { + case JS_AS_MODE_A3D: name = "FP-Gaming Assasin 3D"; break; + case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; + case JS_AS_MODE_OEM: name = "OEM Assasin 3D"; break; + default: name = "This cannot happen"; break; + } + + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, 2, 3, name, js_as_open, js_as_close), + name, info->io); + + js_as_as_init_corr(port->corr); + + js_as_read(port->info, port->axes, port->buttons); + + for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_as_open, js_as_close), + js_an_name(i, &info->an), name); + + js_an_decode(&info->an, port->axes + 1, port->buttons + 1); + js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0); + + return port; +} + +#ifndef MODULE +void __init js_as_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_as_init(void) +#endif +{ + int i; + + if (js_as[0] >= 0) { + for (i = 0; (js_as[i*3] >= 0) && i < 8; i++) + js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port); + } else { + for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port); + } + if (js_as_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-assasin: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_as_info *info; + + while (js_as_port != NULL) { + for (i = 0; i < js_as_port->ndevs; i++) + if (js_as_port->devs[i] != NULL) + js_unregister_device(js_as_port->devs[i]); + info = js_as_port->info; + release_region(info->io, 1); + js_as_port = js_unregister_port(js_as_port); + } + +} +#endif diff --git a/drivers/char/joystick/joy-console.c b/drivers/char/joystick/joy-console.c new file mode 100644 index 000000000..ad4be3076 --- /dev/null +++ b/drivers/char/joystick/joy-console.c @@ -0,0 +1,596 @@ +/* + * joy-console.c Version 0.11V + * + * Copyright (c) 1998 Andree Borrmann + */ + +/* + * This is a module for the Linux joystick driver, supporting + * console (NES, SNES, Multi1, Multi2, PSX) gamepads connected + * via parallel port. Up to five such controllers can be + * connected to one parallel port. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> + + +MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>"); +MODULE_PARM(js_console, "2-6i"); +MODULE_PARM(js_console2,"2-6i"); +MODULE_PARM(js_console3,"2-6i"); + + +#define JS_NO_PAD 0 +#define JS_SNES_PAD 1 +#define JS_NES_PAD 2 +#define JS_NES4_PAD 3 +#define JS_MULTI_STICK 4 +#define JS_MULTI2_STICK 5 +#define JS_PSX_PAD 6 + +#define JS_MAX_PAD JS_PSX_PAD + +struct js_console_info { +#ifdef USE_PARPORT + struct pardevice *port; /* parport device */ +#else + int port; /* hw port */ +#endif + int pads; /* total number of pads */ + int snes; /* SNES pads */ + int nes; /* NES pads */ + int multi; /* Multi joysticks */ + int multi2; /* Multi joysticks with 2 buttons */ + int psx; /* Normal PSX controllers */ + int negcon; /* PSX NEGCON controllers */ +}; + +static struct js_port* js_console_port = NULL; + +static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int js_console3[] __initdata = { -1, 0, 0, 0, 0, 0 }; + +static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; + +/* + * NES/SNES support. + */ + +#define JS_NES_DELAY 6 /* Delay between bits - 6us */ + +#define JS_NES_LENGTH 8 /* The NES pads use 8 bits of data */ + +#define JS_NES_A 0 +#define JS_NES_B 1 +#define JS_NES_START 2 +#define JS_NES_SELECT 3 +#define JS_NES_UP 4 +#define JS_NES_DOWN 5 +#define JS_NES_LEFT 6 +#define JS_NES_RIGHT 7 + +#define JS_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ + +#define JS_SNES_B 0 +#define JS_SNES_Y 1 +#define JS_SNES_START 2 +#define JS_SNES_SELECT 3 +#define JS_SNES_UP 4 +#define JS_SNES_DOWN 5 +#define JS_SNES_LEFT 6 +#define JS_SNES_RIGHT 7 +#define JS_SNES_A 8 +#define JS_SNES_X 9 +#define JS_SNES_L 10 +#define JS_SNES_R 11 + +#define JS_NES_POWER 0xf8 +#define JS_NES_CLOCK 0x01 +#define JS_NES_LATCH 0x02 + +/* + * js_nes_read_packet() reads a NES/SNES packet. + * Each pad uses one bit per byte. So all pads connected to + * this port are read in parallel. + */ + +static void js_nes_read_packet(struct js_console_info *info, int length, unsigned char *data) +{ + int i; + + JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK + JS_NES_LATCH, info->port); + udelay(JS_NES_DELAY * 2); + JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + + for (i = 0; i < length; i++) { + udelay(JS_NES_DELAY); + JS_PAR_DATA_OUT(JS_NES_POWER, info->port); + data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + udelay(JS_NES_DELAY); + JS_PAR_DATA_OUT(JS_NES_POWER + JS_NES_CLOCK, info->port); + } +} + +/* + * Multisystem joystick support + */ + +#define JS_MULTI_LENGTH 5 /* Multi system joystick packet lenght is 5 */ +#define JS_MULTI2_LENGTH 6 /* One more bit for one more button */ + +#define JS_MULTI_UP 0 +#define JS_MULTI_DOWN 1 +#define JS_MULTI_LEFT 2 +#define JS_MULTI_RIGHT 3 +#define JS_MULTI_BUTTON 4 +#define JS_MULTI_BUTTON2 5 + +/* + * js_multi_read_packet() reads a Multisystem joystick packet. + */ + +static void js_multi_read_packet(struct js_console_info *info, int length, unsigned char *data) +{ + int i; + + for (i = 0; i < length; i++) { + JS_PAR_DATA_OUT(~(1 << i), info->port); + data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + } +} + +/* + * PSX support + */ + +#define JS_PSX_DELAY 10 + +#define JS_PSX_LENGTH 8 + +#define JS_PSX_NORMAL 0x41 +#define JS_PSX_NEGCON 0x23 +#define JS_PSX_MOUSE 0x12 + +#define JS_PSX_SELBUT 0x01 +#define JS_PSX_START 0x08 +#define JS_PSX_UP 0x10 +#define JS_PSX_RIGHT 0x20 +#define JS_PSX_DOWN 0x40 +#define JS_PSX_LEFT 0x80 + +#define JS_PSX_CLOCK 0x01 +#define JS_PSX_COMMAND 0x02 +#define JS_PSX_POWER 0xf8 +#define JS_PSX_NOPOWER 0x04 +#define JS_PSX_SELECT 0x08 + +#define JS_PSX_CTRL_OUT(X,Y) JS_PAR_CTRL_OUT((X)^0x0f, Y) + +/* + * js_psx_command() writes 8bit command and reads 8bit data from + * the psx pad. + */ + +static int js_psx_command(struct js_console_info *info, int b) +{ + int i, cmd, ret=0; + + cmd = (b&1)?JS_PSX_COMMAND:0; + for (i=0; i<8; i++) { + JS_PSX_CTRL_OUT(cmd, info->port); + udelay(JS_PSX_DELAY); + ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0; + cmd = (b&1)?JS_PSX_COMMAND:0; + JS_PSX_CTRL_OUT(JS_PSX_CLOCK | cmd, info->port); + udelay(JS_PSX_DELAY); + b >>= 1; + } + return ret; +} + +/* + * js_psx_read_packet() reads a whole psx packet and returns + * device identifier code. + */ + +static int js_psx_read_packet(struct js_console_info *info, int length, unsigned char *data) +{ + int i, ret; + unsigned long flags; + + __save_flags(flags); + __cli(); + + JS_PAR_DATA_OUT(JS_PSX_POWER, info->port); + + JS_PSX_CTRL_OUT(JS_PSX_CLOCK | JS_PSX_SELECT, info->port); /* Select pad */ + udelay(JS_PSX_DELAY*2); + js_psx_command(info, 0x01); /* Access pad */ + ret = js_psx_command(info, 0x42); /* Get device id */ + if (js_psx_command(info, 0)=='Z') /* okay? */ + for (i=0; i<length; i++) + data[i]=js_psx_command(info, 0); + else ret = -1; + + JS_PSX_CTRL_OUT(JS_PSX_SELECT | JS_PSX_CLOCK, info->port); + __restore_flags(flags); + + return ret; +} + + +/* + * js_console_read() reads and analyzes console pads data. + */ + +#define JS_MAX_LENGTH JS_SNES_LENGTH + +static int js_console_read(void *xinfo, int **axes, int **buttons) +{ + struct js_console_info *info = xinfo; + unsigned char data[JS_MAX_LENGTH]; + + int i, s; + int n = 0; + +/* + * NES and SNES pads + */ + + if (info->nes || info->snes) { + + js_nes_read_packet(info, info->snes ? JS_SNES_LENGTH : JS_NES_LENGTH, data); + + for (i = 0; i < 5; i++) { + s = status_bit[i]; + if (info->nes & s) { + axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); + axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); + + buttons[n][0] = ((data[JS_NES_A] &s)?1:0) | ((data[JS_NES_B] &s)?2:0) + | ((data[JS_NES_START]&s)?4:0) | ((data[JS_NES_SELECT]&s)?8:0); + + n++; + } else + if (info->snes & s) { + axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); + axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); + + buttons[n][0] = ((data[JS_SNES_A] &s)?0x01:0) | ((data[JS_SNES_B] &s)?0x02:0) + | ((data[JS_SNES_X] &s)?0x04:0) | ((data[JS_SNES_Y] &s)?0x08:0) + | ((data[JS_SNES_L] &s)?0x10:0) | ((data[JS_SNES_R] &s)?0x20:0) + | ((data[JS_SNES_START]&s)?0x40:0) | ((data[JS_SNES_SELECT]&s)?0x80:0); + n++; + } + } + } + +/* + * Multi and Multi2 joysticks + */ + + if (info->multi || info->multi2) { + + js_multi_read_packet(info, info->multi2 ? JS_MULTI2_LENGTH : JS_MULTI_LENGTH, data); + + for (i = 0; i < 5; i++) { + s = status_bit[i]; + if (info->multi & s) { + axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); + axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); + + buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0; + + n++; + } else + if (info->multi2 & s) { + axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); + axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); + + buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0; + + n++; + } + } + } + +/* + * PSX controllers + */ + + if (info->psx && (js_psx_read_packet(info, 2, data) == JS_PSX_NORMAL)) { /* FIXME? >1 PSX pads? */ + + axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); + axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); + + buttons[n][0] = ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | + (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); + + n++; + } + + return -(n != info->pads); +} + +/* + * open callback: claim parport. + */ + +int js_console_open(struct js_dev *dev) +{ +#ifdef USE_PARPORT + struct js_console_info *info = dev->port->info; + if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY; +#endif + MOD_INC_USE_COUNT; + return 0; +} + +/* + * close callback: release parport + */ + +int js_console_close(struct js_dev *dev) +{ +#ifdef USE_PARPORT + struct js_console_info *info = dev->port->info; +#endif + MOD_DEC_USE_COUNT; +#ifdef USE_PARPORT + if (!MOD_IN_USE) parport_release(info->port); +#endif + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_console_info *info; + int i; + + while (js_console_port != NULL) { + for (i = 0; i < js_console_port->ndevs; i++) + if (js_console_port->devs[i] != NULL) + js_unregister_device(js_console_port->devs[i]); + info = js_console_port->info; +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); +#endif + js_console_port = js_unregister_port(js_console_port); + } +} +#endif + +/* + * js_console_init_corr() initializes correction values of + * console gamepads. + */ + +static void __init js_console_init_corr(int num_axes, struct js_corr *corr) +{ + int i; + + for (i = 0; i < num_axes; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } +} + +/* + * js_console_probe() probes for console gamepads. + * Only PSX pads can really be probed for. + */ + +static struct js_port __init *js_console_probe(int *config, struct js_port *port) +{ + char *name[5]; + int i, psx, axes[5], buttons[5]; + unsigned char data[2]; /* used for PSX probe */ + struct js_console_info info; + + memset(&info, 0, sizeof(struct js_console_info)); + + if (config[0] < 0) return port; + +#ifdef USE_PARPORT + { + struct parport *pp; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; + + if (pp == NULL) { + printk(KERN_ERR "joy-console: no such parport\n"); + return port; + } + + info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info.port) + return port; + } + + if (parport_claim(info.port)) + { + parport_unregister_device(info.port); /* port currently not available ... */ + return port; + } +#else + info.port = config[0]; + if (check_region(info.port, 3)) return port; + request_region(info.port, 3, "joystick (console pad)"); +#endif + + for (i = 0; i < 5; i++) + switch(config[i+1]) { + + case JS_NO_PAD: + + break; + + case JS_SNES_PAD: + + axes[info.pads] = 2; + buttons[info.pads] = 8; + name[info.pads] = "SNES pad"; + info.snes |= status_bit[i]; + info.pads++; + break; + + case JS_NES_PAD: + + axes[info.pads] = 2; + buttons[info.pads] = 4; + name[info.pads] = "NES pad"; + info.nes |= status_bit[i]; + info.pads++; + break; + + case JS_MULTI_STICK: + + axes[info.pads] = 2; + buttons[info.pads] = 1; + name[info.pads] = "Multisystem joystick"; + info.multi |= status_bit[i]; + info.pads++; + break; + + case JS_MULTI2_STICK: + + axes[info.pads] = 2; + buttons[info.pads] = 2; + name[info.pads] = "Multisystem joystick (2 fire)"; + info.multi |= status_bit[i]; + info.pads++; + break; + + case JS_PSX_PAD: + + info.psx |= status_bit[i]; + psx = js_psx_read_packet(&info, 2, data); + psx = js_psx_read_packet(&info, 2, data); + info.psx &= ~status_bit[i]; + + switch(psx) { + case JS_PSX_NORMAL: + axes[info.pads] = 2; + buttons[info.pads] = 10; + name[info.pads] = "PSX controller"; + info.psx |= status_bit[i]; + info.pads++; + break; + case JS_PSX_NEGCON: + printk(KERN_WARNING "joy-console: NegCon not yet supported...\n"); + break; + case JS_PSX_MOUSE: + printk(KERN_WARNING "joy-console: PSX mouse not supported...\n"); + break; + case -1: + printk(KERN_ERR "joy-console: no PSX controller found...\n"); + break; + default: + printk(KERN_WARNING "joy-console: unknown PSX controller 0x%x\n", psx); + } + break; + + default: + + printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]); + } + + if (!info.pads) { +#ifdef USE_PARPORT + parport_release(info.port); + parport_unregister_device(info.port); +#else + release_region(info.port, 3); +#endif + return port; + } + + port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read); + + for (i = 0; i < info.pads; i++) { +#ifdef USE_PARPORT + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), + name[i], info.port->port->name); +#else + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes[i], buttons[i], name[i], js_console_open, js_console_close), + name[i], info.port); +#endif + + js_console_init_corr(axes[i], port->corr[i]); + } + +#ifdef USE_PARPORT + parport_release(info.port); +#endif + return port; +} + +#ifndef MODULE +void __init js_console_setup(char *str, int *ints) +{ + int i; + + if (!strcmp(str,"js_console")) + for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; + if (!strcmp(str,"js_console2")) + for (i = 0; i <= ints[0] && i < 6; i++) js_console2[i] = ints[i+1]; + if (!strcmp(str,"js_console3")) + for (i = 0; i <= ints[0] && i < 6; i++) js_console3[i] = ints[i+1]; + +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_console_init(void) +#endif +{ + js_console_port = js_console_probe(js_console, js_console_port); + js_console_port = js_console_probe(js_console2, js_console_port); + js_console_port = js_console_probe(js_console3, js_console_port); + + if (js_console_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-console: no joysticks specified\n"); +#endif + return -ENODEV; +} diff --git a/drivers/char/joystick/joy-db9.c b/drivers/char/joystick/joy-db9.c new file mode 100644 index 000000000..82a5c6a0f --- /dev/null +++ b/drivers/char/joystick/joy-db9.c @@ -0,0 +1,406 @@ +/* + * joy-db9.c Version 0.5V + * + * Copyright (c) 1998 Andree Borrmann + */ + +/* + * This is a module for the Linux joystick driver, supporting + * console (Atari, Amstrad, Commodore, Amiga, Sega) joysticks + * and gamepads connected to the parallel port. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> + +MODULE_AUTHOR("Andree Borrmann <A.Borrmann@tu-bs.de>"); +MODULE_PARM(js_db9, "2i"); +MODULE_PARM(js_db9_2, "2i"); +MODULE_PARM(js_db9_3, "2i"); + +#define JS_MULTI_STICK 0x01 +#define JS_MULTI2_STICK 0x02 +#define JS_GENESIS_PAD 0x03 +#define JS_GENESIS5_PAD 0x05 +#define JS_GENESIS6_PAD 0x06 +#define JS_SATURN_PAD 0x07 +#define JS_MULTI_0802 0x08 +#define JS_MAX_PAD 0x09 + +#define JS_DB9_UP 0x01 +#define JS_DB9_DOWN 0x02 +#define JS_DB9_LEFT 0x04 +#define JS_DB9_RIGHT 0x08 +#define JS_DB9_FIRE1 0x10 +#define JS_DB9_FIRE2 0x20 +#define JS_DB9_FIRE3 0x40 +#define JS_DB9_FIRE4 0x80 + +#define JS_DB9_NORMAL 0x22 +#define JS_DB9_NOSELECT 0x20 + +#define JS_DB9_SATURN0 0x20 +#define JS_DB9_SATURN1 0x22 +#define JS_DB9_SATURN2 0x24 +#define JS_DB9_SATURN3 0x26 + +#define JS_GENESIS6_DELAY 14 + +static struct js_port* js_db9_port = NULL; + +static int js_db9[] __initdata = { -1, 0 }; +static int js_db9_2[] __initdata = { -1, 0 }; +static int js_db9_3[] __initdata = { -1, 0 }; + +struct js_db9_info { +#ifdef USE_PARPORT + struct pardevice *port; /* parport device */ +#else + int port; /* hw port */ +#endif + int mode; /* pad mode */ +}; + +/* + * js_db9_read() reads and analyzes db9 joystick data. + */ + +static int js_db9_read(void *xinfo, int **axes, int **buttons) +{ + struct js_db9_info *info = xinfo; + int data; + + switch(info->mode) + { + case JS_MULTI_0802: + + data = JS_PAR_STATUS(info->port) >> 3; + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?1:0); + + break; + + case JS_MULTI_STICK: + + data = JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:1); + + break; + + case JS_MULTI2_STICK: + + data=JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:2); + + break; + + case JS_GENESIS_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); + data = JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:2) | (data&JS_DB9_FIRE2?0:4); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:8); + + break; + + case JS_GENESIS5_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); + data=JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08) | + (data&JS_DB9_LEFT ?0:0x10) | (data&JS_DB9_RIGHT?0:0x20); + break; + + case JS_GENESIS6_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); /* 1 */ + udelay(JS_GENESIS6_DELAY); + data=JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + udelay(JS_GENESIS6_DELAY); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08); + + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 2 */ + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */ + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + data=JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN?0:0x20) | (data&JS_DB9_UP?0:0x40); + + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */ + udelay(JS_GENESIS6_DELAY); + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + + break; + + case JS_SATURN_PAD: + + JS_PAR_CTRL_OUT(JS_DB9_SATURN0, info->port); + data = JS_PAR_DATA_IN(info->port); + + buttons[0][0] = (data&JS_DB9_UP ?0:0x20) | (data&JS_DB9_DOWN ?0:0x10) | + (data&JS_DB9_LEFT?0:0x08) | (data&JS_DB9_RIGHT?0:0x40); + + JS_PAR_CTRL_OUT(JS_DB9_SATURN2, info->port); + data = JS_PAR_DATA_IN(info->port); + + axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); + axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); + + JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); + data = JS_PAR_DATA_IN(info->port); + + buttons[0][0] |= (data&JS_DB9_UP ?0:0x02) | (data&JS_DB9_DOWN ?0:0x04) | + (data&JS_DB9_LEFT?0:0x01) | (data&JS_DB9_RIGHT?0:0x80); + + + break; + + default: + return -1; + } + + return 0; +} + +/* + * open callback: claim parport. + */ + +int js_db9_open(struct js_dev *dev) +{ + struct js_db9_info *info = dev->port->info; + + if (!MOD_IN_USE) { +#ifdef USE_PARPORT + if (parport_claim(info->port)) return -EBUSY; +#endif + + JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ + JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); /* reverse direction, enable Select signal */ + } + + MOD_INC_USE_COUNT; + return 0; +} + +/* + * close callback: release parport + */ + +int js_db9_close(struct js_dev *dev) +{ + struct js_db9_info *info = dev->port->info; + + MOD_DEC_USE_COUNT; + + if (!MOD_IN_USE) { + + JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ + JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ + +#ifdef USE_PARPORT + parport_release(info->port); +#endif + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_db9_info *info; + + while (js_db9_port != NULL) { + js_unregister_device(js_db9_port->devs[0]); + info = js_db9_port->info; +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); + release_region(info->port+0x402, 1); +#endif + js_db9_port = js_unregister_port(js_db9_port); + } + +} +#endif + +/* + * js_db9_init_corr() initializes correction values of + * db9 gamepads. + */ + +static void __init js_db9_init_corr(struct js_corr **corr) +{ + int i; + + for (i = 0; i < 2; i++) { + corr[0][i].type = JS_CORR_BROKEN; + corr[0][i].prec = 0; + corr[0][i].coef[0] = 0; + corr[0][i].coef[1] = 0; + corr[0][i].coef[2] = (1 << 29); + corr[0][i].coef[3] = (1 << 29); + } +} + +/* + * js_db9_probe() probes for db9 gamepads. + */ + +static struct js_port __init *js_db9_probe(int *config, struct js_port *port) +{ + struct js_db9_info info; + char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,7,8,1}; + char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", + NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick"}; + + if (config[0] < 0) return port; + if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port; + +#ifdef USE_PARPORT + { + struct parport *pp; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; + + if (pp == NULL) { + printk(KERN_ERR "joy-db9: no such parport\n"); + return port; + } + + if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2))) { + printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); + return port; + } + + info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info.port) + return port; + } +#else + info.port = config[0]; + if (check_region(info.port, 3) || check_region(info.port+0x402,1)) return port; + request_region(info.port, 3, "joystick (db9)"); + request_region(info.port+0x402, 1, "joystick (db9)"); +#endif + + info.mode = config[1]; + + port = js_register_port(port, &info, 1, sizeof(struct js_db9_info), js_db9_read); + +#ifdef USE_PARPORT + printk(KERN_INFO "js%d: %s on %s\n", + js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), + name[info.mode], info.port->port->name); +#else + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, 2, buttons[info.mode], name[info.mode], js_db9_open, js_db9_close), + name[info.mode], info.port); +#endif + + js_db9_init_corr(port->corr); + + return port; +} + +#ifndef MODULE +void __init js_db9_setup(char *str, int *ints) +{ + int i; + + if (!strcmp(str,"js_db9")) + for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; + if (!strcmp(str,"js_db9_2")) + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; + if (!strcmp(str,"js_db9_3")) + for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; + +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_db9_init(void) +#endif +{ + js_db9_port = js_db9_probe(js_db9, js_db9_port); + js_db9_port = js_db9_probe(js_db9_2, js_db9_port); + js_db9_port = js_db9_probe(js_db9_3, js_db9_port); + + if (js_db9_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-db9: no joysticks specified\n"); +#endif + return -ENODEV; +} diff --git a/drivers/char/joystick/joy-gravis.c b/drivers/char/joystick/joy-gravis.c new file mode 100644 index 000000000..9185d3113 --- /dev/null +++ b/drivers/char/joystick/joy-gravis.c @@ -0,0 +1,401 @@ +/* + * joy-gravis.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Gravis GrIP digital joystick family. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_GR_MODE_GPP 1 +#define JS_GR_LENGTH_GPP 24 +#define JS_GR_STROBE_GPP 75 + +#define JS_GR_MODE_XT 2 +#define JS_GR_MODE_BD 3 +#define JS_GR_LENGTH_XT 4 +#define JS_GR_STROBE_XT 30 +#define JS_GR_MAX_CHUNKS_XT 10 +#define JS_GR_MAX_BITS_XT 30 + +static int js_gr_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_gr_port __initdata = NULL; + +struct js_gr_info { + int io; + unsigned char mode[2]; +}; + +/* + * js_gr_gpp_read_packet() reads a Gravis GamePad Pro packet. + */ + +static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) +{ + unsigned int t, t1; + unsigned char u, v; + int i; + unsigned long flags; + + int strobe = (js_time_speed * JS_GR_STROBE_GPP) >> 10; + + i = 0; + data[0] = 0; + + __save_flags(flags); + __cli(); + u = inb(io) >> shift; + t = js_get_time(); + + do { + v = (inb(io) >> shift) & 3; + t1 = js_get_time(); + if ((u ^ v) & u & 1) { + data[0] |= (v >> 1) << i++; + t = t1; + } + u = v; + } while (i < JS_GR_LENGTH_GPP && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + if (i < JS_GR_LENGTH_GPP) return -1; + + for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) + data[0] = data[0] >> 1 | (data[0] & 1) << 23; + + return -(i == JS_GR_LENGTH_GPP); +} + +/* + * js_gr_xt_read_packet() reads a Gravis Xterminator packet. + */ + +static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) +{ + unsigned int t, t1; + unsigned char u, v, w; + unsigned int i, j, buf, crc; + unsigned long flags; + char status; + + int strobe = (js_time_speed * JS_GR_STROBE_XT) >> 10; + + data[0] = data[1] = data[2] = data[3] = 0; + status = buf = i = j = 0; + + __save_flags(flags); + __cli(); + + v = w = (inb(io) >> shift) & 3; + t = js_get_time(); + + do { + u = (inb(io) >> shift) & 3; + t1 = js_get_time(); + + if (u ^ v) { + + if ((u ^ v) & 1) { + buf = (buf << 1) | (u >> 1); + i++; + } else + + if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + if (i == 20) { + crc = buf ^ (buf >> 7) ^ (buf >> 14); + if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { + data[buf >> 18] = buf >> 4; + status |= 1 << (buf >> 18); + } + j++; + } + buf = 0; + i = 0; + } + + t = t1; + w = v; + v = u; + } + + } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + return -(status != 0xf); +} + +/* + * js_gr_read() reads and analyzes GrIP joystick data. + */ + +static int js_gr_read(void *xinfo, int **axes, int **buttons) +{ + struct js_gr_info *info = xinfo; + unsigned int data[JS_GR_LENGTH_XT]; + int i; + + for (i = 0; i < 2; i++) + switch (info->mode[i]) { + + case JS_GR_MODE_GPP: + + if (js_gr_gpp_read_packet(info->io, (i << 1) + 4, data)) return -1; + + axes[i][0] = ((data[0] >> 15) & 1) - ((data[0] >> 16) & 1); + axes[i][1] = ((data[0] >> 13) & 1) - ((data[0] >> 12) & 1); + + data[0] = ((data[0] >> 6) & 0x37) | (data[0] & 0x08) | ((data[0] << 1) & 0x40) | + ((data[0] << 5) & 0x80) | ((data[0] << 8) & 0x300); + + buttons[i][0] = (data[0] & 0xfc) | ((data[0] >> 1) & 0x101) | ((data[0] << 1) & 0x202); + + break; + + case JS_GR_MODE_XT: + + if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; + + axes[i][0] = (data[0] >> 2) & 0x3f; + axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); + axes[i][2] = (data[1] >> 2) & 0x3f; + axes[i][3] = (data[1] >> 8) & 0x3f; + axes[i][4] = (data[2] >> 8) & 0x3f; + + axes[i][5] = ((data[2] >> 1) & 1) - ( data[2] & 1); + axes[i][6] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); + axes[i][7] = ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1); + axes[i][8] = ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1); + + buttons[i][0] = (data[3] >> 3) & 0x7ff; + + break; + + case JS_GR_MODE_BD: + + if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; + + axes[i][0] = (data[0] >> 2) & 0x3f; + axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); + axes[i][2] = (data[2] >> 8) & 0x3f; + + axes[i][3] = ((data[2] >> 1) & 1) - ( data[2] & 1); + axes[i][4] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); + + buttons[i][0] = ((data[3] >> 6) & 0x01) | ((data[3] >> 3) & 0x06) + | ((data[3] >> 4) & 0x18); + + break; + + default: + break; + + } + + + return 0; +} + +/* + * js_gr_open() is a callback from the file open routine. + */ + +static int js_gr_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_gr_close() is a callback from the file release routine. + */ + +static int js_gr_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_gr_init_corr() initializes correction values of + * GrIP joysticks. + */ + +static void __init js_gr_init_corr(int mode, struct js_corr *corr) +{ + int i; + + switch (mode) { + + case JS_GR_MODE_GPP: + + for (i = 0; i < 2; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + case JS_GR_MODE_XT: + + for (i = 0; i < 5; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 31 - 4; + corr[i].coef[1] = 32 + 4; + corr[i].coef[2] = (1 << 29) / (32 - 14); + corr[i].coef[3] = (1 << 29) / (32 - 14); + } + + for (i = 5; i < 9; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + case JS_GR_MODE_BD: + + for (i = 0; i < 3; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 31 - 4; + corr[i].coef[1] = 32 + 4; + corr[i].coef[2] = (1 << 29) / (32 - 14); + corr[i].coef[3] = (1 << 29) / (32 - 14); + } + + for (i = 3; i < 5; i++) { + corr[i].type = JS_CORR_BROKEN; + corr[i].prec = 0; + corr[i].coef[0] = 0; + corr[i].coef[1] = 0; + corr[i].coef[2] = (1 << 29); + corr[i].coef[3] = (1 << 29); + } + + break; + + } +} + +/* + * js_gr_probe() probes fro GrIP joysticks. + */ + +static struct js_port __init *js_gr_probe(int io, struct js_port *port) +{ + struct js_gr_info info; + char *names[] = { NULL, "Gravis GamePad Pro", "Gravis Xterminator", "Gravis Blackhawk Digital"}; + char axes[] = { 0, 2, 9, 5}; + char buttons[] = { 0, 10, 11, 5}; + unsigned int data[JS_GR_LENGTH_XT]; + int i; + + if (check_region(io, 1)) return port; + + info.mode[0] = info.mode[1] = 0; + + for (i = 0; i < 2; i++) { + if (!js_gr_gpp_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_GPP; + if (!js_gr_xt_read_packet(io, (i << 1) + 4, data)) { + if ((data[3] & 7) == 7) + info.mode[i] = JS_GR_MODE_XT; + if ((data[3] & 7) == 0) + info.mode[i] = JS_GR_MODE_BD; + } + } + + if (!info.mode[0] && !info.mode[1]) return port; + + info.io = io; + + request_region(io, 1, "joystick (gravis)"); + port = js_register_port(port, &info, 2, sizeof(struct js_gr_info), js_gr_read); + + for (i = 0; i < 2; i++) + if (info.mode[i]) { + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], + names[info.mode[i]], js_gr_open, js_gr_close), + names[info.mode[i]], io); + js_gr_init_corr(info.mode[i], port->corr[i]); + } + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_gr_init(void) +#endif +{ + int *p; + + for (p = js_gr_port_list; *p; p++) js_gr_port = js_gr_probe(*p, js_gr_port); + if (js_gr_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-gravis: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_gr_info *info; + + while (js_gr_port != NULL) { + for (i = 0; i < js_gr_port->ndevs; i++) + if (js_gr_port->devs[i] != NULL) + js_unregister_device(js_gr_port->devs[i]); + info = js_gr_port->info; + release_region(info->io, 1); + js_gr_port = js_unregister_port(js_gr_port); + } +} +#endif diff --git a/drivers/char/joystick/joy-lightning.c b/drivers/char/joystick/joy-lightning.c new file mode 100644 index 000000000..fc36c3d50 --- /dev/null +++ b/drivers/char/joystick/joy-lightning.c @@ -0,0 +1,366 @@ +/* + * joy-lightning.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * PDPI Lightning 4 gamecards and analog joysticks connected + * to them. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_L4_PORT 0x201 +#define JS_L4_SELECT_ANALOG 0xa4 +#define JS_L4_SELECT_DIGITAL 0xa5 +#define JS_L4_SELECT_SECONDARY 0xa6 +#define JS_L4_CMD_ID 0x80 +#define JS_L4_CMD_GETCAL 0x92 +#define JS_L4_CMD_SETCAL 0x93 +#define JS_L4_ID 0x04 +#define JS_L4_BUSY 0x01 +#define JS_L4_TIMEOUT 80 /* 80 us */ + +static struct js_port* __initdata js_l4_port = NULL; + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_PARM(js_l4, "2-24i"); + +static int js_l4[]={-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0}; + +#include "joy-analog.h" + +struct js_l4_info { + int port; + struct js_an_info an; +}; + +/* + * js_l4_wait_ready() waits for the L4 to become ready. + */ + +static int js_l4_wait_ready(void) +{ + unsigned int t, t1, timeout; + timeout = (JS_L4_TIMEOUT * js_time_speed) >> 10; + t = t1 = js_get_time(); + while ((inb(JS_L4_PORT) & JS_L4_BUSY) && (js_delta(t1 = js_get_time(), t) < timeout)); + return -(js_delta(t1, t) >= timeout); +} + +/* + * js_l4_read() reads data from the Lightning 4. + */ + +static int js_l4_read(void *xinfo, int **axes, int **buttons) +{ + struct js_l4_info *info = xinfo; + int i; + unsigned char status; + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + (info->port >> 2), JS_L4_PORT); + + if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; + outb(info->port & 3, JS_L4_PORT); + + if (js_l4_wait_ready()) return -1; + status = inb(JS_L4_PORT); + + for (i = 0; i < 4; i++) + if (status & (1 << i)) { + if (js_l4_wait_ready()) return -1; + info->an.axes[i] = inb(JS_L4_PORT); + } + + if (status & 0x10) { + if (js_l4_wait_ready()) return -1; + info->an.buttons = inb(JS_L4_PORT); + } + + js_an_decode(&info->an, axes, buttons); + + return 0; +} + +/* + * js_l4_getcal() reads the L4 with calibration values. + */ + +static int js_l4_getcal(int port, int *cal) +{ + int i; + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); + + if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; + outb(JS_L4_CMD_GETCAL, JS_L4_PORT); + + if (js_l4_wait_ready()) return -1; + if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1; + + if (js_l4_wait_ready()) return -1; + outb(port & 3, JS_L4_PORT); + + for (i = 0; i < 4; i++) { + if (js_l4_wait_ready()) return -1; + cal[i] = inb(JS_L4_PORT); + } + + return 0; +} + +/* + * js_l4_setcal() programs the L4 with calibration values. + */ + +static int js_l4_setcal(int port, int *cal) +{ + int i; + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); + + if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; + outb(JS_L4_CMD_SETCAL, JS_L4_PORT); + + if (js_l4_wait_ready()) return -1; + if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1; + + if (js_l4_wait_ready()) return -1; + outb(port & 3, JS_L4_PORT); + + for (i = 0; i < 4; i++) { + if (js_l4_wait_ready()) return -1; + outb(cal[i], JS_L4_PORT); + } + + return 0; +} + +/* + * js_l4_calibrate() calibrates the L4 for the attached device, so + * that the device's resistance fits into the L4's 8-bit range. + */ + +static void js_l4_calibrate(struct js_l4_info *info) +{ + int i; + int cal[4]; + int axes[4]; + int t; + + js_l4_getcal(info->port, cal); + + for (i = 0; i < 4; i++) + axes[i] = info->an.axes[i]; + + if ((info->an.extensions & JS_AN_BUTTON_PXY_X) && !(info->an.extensions & JS_AN_BUTTON_PXY_U)) + axes[2] >>= 1; /* Pad button X */ + + if ((info->an.extensions & JS_AN_BUTTON_PXY_Y) && !(info->an.extensions & JS_AN_BUTTON_PXY_V)) + axes[3] >>= 1; /* Pad button Y */ + + if (info->an.extensions & JS_AN_HAT_FCS) + axes[3] >>= 1; /* FCS hat */ + + if (((info->an.mask[0] & 0xb) == 0xb) || ((info->an.mask[1] & 0xb) == 0xb)) + axes[3] = (axes[0] + axes[1]) >> 1; /* Throttle */ + + for (i = 0; i < 4; i++) { + t = (axes[i] * cal[i]) / 100; + if (t > 255) t = 255; + info->an.axes[i] = (info->an.axes[i] * cal[i]) / t; + cal[i] = t; + } + + js_l4_setcal(info->port, cal); +} + +/* + * js_l4_open() is a callback from the file open routine. + */ + +static int js_l4_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_l4_close() is a callback from the file release routine. + */ + +static int js_l4_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_l4_probe() probes for joysticks on the L4 cards. + */ + +static struct js_port __init *js_l4_probe(unsigned char *cards, int l4port, int mask0, int mask1, struct js_port *port) +{ + struct js_l4_info iniinfo; + struct js_l4_info *info = &iniinfo; + int cal[4] = {255,255,255,255}; + int i, numdev; + unsigned char u; + + if (l4port < 0) return port; + if (!cards[(l4port >> 2)]) return port; + + memset(info, 0, sizeof(struct js_l4_info)); + info->port = l4port; + + if (cards[l4port >> 2] > 0x28) js_l4_setcal(info->port, cal); + if (js_l4_read(info, NULL, NULL)) return port; + + for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 253) u |= 1 << i; + + if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) + return port; + + port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read); + + for (i = 0; i < numdev; i++) + printk(KERN_INFO "js%d: %s on L4 port %d\n", + js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), + js_an_name(i, &info->an), js_l4_open, js_l4_close), + js_an_name(i, &info->an), info->port); + + info = port->info; + + js_l4_calibrate(info); + js_l4_read(info, port->axes, port->buttons); + js_an_init_corr(&info->an, port->axes, port->corr, 0); + + return port; +} + +/* + * js_l4_card_probe() probes for presence of the L4 card(s). + */ + +static void __init js_l4_card_probe(unsigned char *cards) +{ + int i; + unsigned char rev = 0; + + if (check_region(JS_L4_PORT, 1)) return; + + for (i = 0; i < 2; i++) { + + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + outb(JS_L4_SELECT_DIGITAL + i, JS_L4_PORT); /* Select card 0-1 */ + + if (inb(JS_L4_PORT) & JS_L4_BUSY) continue; + outb(JS_L4_CMD_ID, JS_L4_PORT); /* Get card ID & rev */ + + if (js_l4_wait_ready()) continue; + if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + i) continue; + + if (js_l4_wait_ready()) continue; + if (inb(JS_L4_PORT) != JS_L4_ID) continue; + + if (js_l4_wait_ready()) continue; + rev = inb(JS_L4_PORT); + + cards[i] = rev; + + printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d found at %#x\n", + i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT); + } + +} + +#ifndef MODULE +void __init js_l4_setup(char *str, int *ints) +{ + int i; + for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1]; +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_l4_init(void) +#endif +{ + int i; + unsigned char cards[2] = {0, 0}; + + js_l4_card_probe(cards); + + if (js_l4[0] >= 0) { + for (i = 0; (js_l4[i*3] >= 0) && i < 8; i++) + js_l4_port = js_l4_probe(cards, js_l4[i*3], js_l4[i*3+1], js_l4[i*3+2], js_l4_port); + } else { + for (i = 0; i < 8; i++) + js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port); + } + + if (js_l4_port == NULL) { +#ifdef MODULE + printk(KERN_WARNING "joy-lightning: no joysticks found\n"); +#endif + return -ENODEV; + } + + request_region(JS_L4_PORT, 1, "joystick (lightning)"); + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + int cal[4] = {59, 59, 59, 59}; + struct js_l4_info *info; + + while (js_l4_port != NULL) { + for (i = 0; i < js_l4_port->ndevs; i++) + if (js_l4_port->devs[i] != NULL) + js_unregister_device(js_l4_port->devs[i]); + info = js_l4_port->info; + js_l4_setcal(info->port, cal); + js_l4_port = js_unregister_port(js_l4_port); + } + outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); + release_region(JS_L4_PORT, 1); +} +#endif diff --git a/drivers/char/joystick/joy-logitech.c b/drivers/char/joystick/joy-logitech.c new file mode 100644 index 000000000..2dcafbfeb --- /dev/null +++ b/drivers/char/joystick/joy-logitech.c @@ -0,0 +1,371 @@ +/* + * joy-logitech.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Logitech Digital joystick family. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_LT_MAX_START 250 +#define JS_LT_MAX_STROBE 25 +#define JS_LT_MAX_LENGTH 72 + +#define JS_LT_MAX_DELAY 12000 + +#define JS_LT_MODE_WMED 1 +#define JS_LT_MODE_CM2 2 +#define JS_LT_MODE_TPD 3 + +static int js_lt_seq_init[] __initdata = { 6000, 11000, 7000, 9000, 6000, 11000, 7000, 9000, 0 }; +static int js_lt_seq_reset[] __initdata = { 2000, 3000, 2000, 3000, 0 }; + +static int js_lt_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_lt_port __initdata = NULL; + +static struct { + int x; + int y; +} js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct js_lt_info { + int io; + unsigned char mode; +}; + +/* + * js_lt_read_packet() reads a Logitech packet. + */ + +static int js_lt_read_packet(int io, __u64 *data) +{ + + static unsigned char buf[JS_LT_MAX_LENGTH]; + unsigned char u, v, w, mask = 0; + int i; + unsigned long flags; + unsigned int t, t1; + + int start = (js_time_speed * JS_LT_MAX_START) >> 10; + int strobe = (js_time_speed * JS_LT_MAX_STROBE) >> 10; + + u = inb(io) >> 4; + + if (u == 0xc) mask = 0x10; + if (u == 0x0) mask = 0x50; + if (!mask) return 0; + + i = 0; + + __save_flags(flags); + __cli(); + + outb(0xff,io); + + u = inb(io); + t = js_get_time(); + + if ((u & 0xc) != 0xc) mask = 0x10; + + do { + u = inb(io); + t1 = js_get_time(); + } while ((((u >> 1) ^ u) & mask) != mask && js_delta(t1,t) < start); + + t = t1; + + do { + v = inb(io); + t1 = js_get_time(); + w = u ^ v; + if ((((w >> 1) ^ w) & mask) == mask) { + buf[i++] = w; + t = t1; + u = v; + } + } while (i < JS_LT_MAX_LENGTH && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + t = i; + *data = 0; + + if (mask == 0x10) { + for (i = 0; i < t; i++) + *data = ((buf[i] >> 5) & 1) | (*data << 1); + return t; + } + if (mask == 0x50) { + for (i = 0; i < t; i++) + *data = ((__u64)(buf[i] & 0x20) << (t - 5)) | (buf[i] >> 7) | (*data << 1); + return t << 1; + } + return 0; +} + +/* + * js_lt_reverse() reverses the order of bits in a byte. + */ + +static unsigned char js_lt_reverse(unsigned char u) +{ + u = ((u & 0x0f) << 4) | ((u >> 4) & 0x0f); + u = ((u & 0x33) << 2) | ((u >> 2) & 0x33); + u = ((u & 0x55) << 1) | ((u >> 1) & 0x55); + return u; +} + +/* + * js_lt_read() reads and analyzes Logitech joystick data. + */ + +static int js_lt_read(void *xinfo, int **axes, int **buttons) +{ + struct js_lt_info *info = xinfo; + __u64 data; + int hat; + + switch (info->mode) { + + case JS_LT_MODE_TPD: + + if (js_lt_read_packet(info->io, &data) != 20) return -1; + + axes[0][0] = ((data >> 6) & 1) - ((data >> 4) & 1); + axes[0][1] = ((data >> 5) & 1) - ((data >> 7) & 1); + + buttons[0][0] = js_lt_reverse((data & 0x0f) | ((data >> 4) & 0xf0)); + + return 0; + + case JS_LT_MODE_WMED: + + if (js_lt_read_packet(info->io, &data) != 42) return -1; + if ((hat = data & 0xf) > 8) return -1; + + axes[0][0] = (data >> 26) & 0xff; + axes[0][1] = (data >> 18) & 0xff; + axes[0][2] = (data >> 10) & 0xff; + axes[0][3] = js_lt_hat_to_axis[hat].x; + axes[0][4] = js_lt_hat_to_axis[hat].y; + + buttons[0][0] = js_lt_reverse((data >> 2) & 0xfc); + + return 0; + + case JS_LT_MODE_CM2: + + if (js_lt_read_packet(info->io, &data) != 64) return -1; + + axes[0][0] = (data >> 48) & 0xff; + axes[0][1] = (data >> 40) & 0xff; + axes[0][2] = (data >> 32) & 0xff; + axes[0][3] = (data >> 24) & 0xff; + axes[0][4] = (data >> 16) & 0xff; + axes[0][5] = (data >> 8) & 0xff; + + buttons[0][0] = js_lt_reverse(data & 0xff); + + return 0; + } + + return -1; +} + +/* + * js_lt_open() is a callback from the file open routine. + */ + +static int js_lt_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_lt_close() is a callback from the file release routine. + */ + +static int js_lt_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_lt_trigger_sequence() sends a trigger & delay sequence + * to reset/initialize a Logitech joystick. + */ + +static void __init js_lt_trigger_sequence(int io, int *seq) +{ + while (*seq) { + outb(0xff,io); + udelay(*seq++); + } +} + +/* + * js_lt_init_corr() initializes the correction values for + * Logitech joysticks. + */ + +static void __init js_lt_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) +{ + int j; + + for (j = 0; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 2; + corr[0][j].coef[0] = axes[0][j] - 8; + corr[0][j].coef[1] = axes[0][j] + 8; + corr[0][j].coef[2] = (1 << 29) / (127 - 32); + corr[0][j].coef[3] = (1 << 29) / (127 - 32); + } + + switch (mode) { + case JS_LT_MODE_TPD: j = 0; break; + case JS_LT_MODE_WMED: j = 3; break; + case JS_LT_MODE_CM2: j = 6; break; + default: j = 0; break; + } + + for (; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = (1 << 29); + corr[0][j].coef[3] = (1 << 29); + } + +} + +/* + * js_lt_probe() probes for Logitech type joysticks. + */ + +static struct js_port __init *js_lt_probe(int io, struct js_port *port) +{ + struct js_lt_info info; + char *name; + int axes, buttons, i; + __u64 data; + unsigned char u; + + if (check_region(io, 1)) return port; + + if (((u = inb(io)) & 3) == 3) return port; + outb(0xff,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + if (!(i = js_lt_read_packet(io, &data))) { + udelay(JS_LT_MAX_DELAY); + js_lt_trigger_sequence(io, js_lt_seq_reset); + js_lt_trigger_sequence(io, js_lt_seq_init); + i = js_lt_read_packet(io, &data); + } + + switch (i) { + case 0: + return port; + case 20: + info.mode = JS_LT_MODE_TPD; + axes = 2; buttons = 8; name = "Logitech ThunderPad Digital"; + break; + case 42: + info.mode = JS_LT_MODE_WMED; + axes = 5; buttons = 6; name = "Logitech WingMan Extreme Digital"; + break; + case 64: + info.mode = JS_LT_MODE_CM2; + axes = 6; buttons = 8; name = "Logitech CyberMan 2"; + break; + case 72: + case 144: + return port; + default: + printk(KERN_WARNING "joy-logitech: unknown joystick device detected " + "(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n", + io, i, (int)(data >> 32), (int)(data & 0xffffffff)); + return port; + } + + info.io = io; + + request_region(io, 1, "joystick (logitech)"); + port = js_register_port(port, &info, 1, sizeof(struct js_lt_info), js_lt_read); + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, 0, axes, buttons, name, js_lt_open, js_lt_close), name, io); + + udelay(JS_LT_MAX_DELAY); + + js_lt_read(port->info, port->axes, port->buttons); + js_lt_init_corr(axes, info.mode, port->axes, port->corr); + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_lt_init(void) +#endif +{ + int *p; + + for (p = js_lt_port_list; *p; p++) js_lt_port = js_lt_probe(*p, js_lt_port); + if (js_lt_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-logitech: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_lt_info *info; + + while (js_lt_port != NULL) { + js_unregister_device(js_lt_port->devs[0]); + info = js_lt_port->info; + release_region(info->io, 1); + js_lt_port = js_unregister_port(js_lt_port); + } +} +#endif diff --git a/drivers/char/joystick/joy-sidewinder.c b/drivers/char/joystick/joy-sidewinder.c new file mode 100644 index 000000000..3fb0d8cef --- /dev/null +++ b/drivers/char/joystick/joy-sidewinder.c @@ -0,0 +1,476 @@ +/* + * joy-sidewinder.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Microsoft SideWinder digital joystick family. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_SW_MAX_START 250 +#define JS_SW_MIN_STROBE 25 +#define JS_SW_EXT_STROBE 45 +#define JS_SW_MIN_TIME 1500 +#define JS_SW_MAX_TIME 4000 + +#define JS_SW_MAX_LENGTH 72 + +#define JS_SW_MODE_3DP 1 +#define JS_SW_MODE_PP 2 +#define JS_SW_MODE_GP 3 + +static int js_sw_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_sw_port __initdata = NULL; + +static struct { + int x; + int y; +} js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct js_sw_info { + int io; + unsigned char mode; + unsigned char number; + unsigned char optimize; +}; + +/* + * js_sw_init_digital() switches a SideWinder into digital mode. + */ + +static void __init js_sw_init_digital(int io) +{ + unsigned int t; + unsigned int timeout = (js_time_speed * JS_SW_MAX_TIME) >> 10; + int delays[] = {140, 140+726, 140+300, 0}; + int i = 0; + unsigned long flags; + + __save_flags(flags); + __cli(); + do { + outb(0xff,io); + t = js_get_time(); + while ((inb(io) & 1) && (js_delta(js_get_time(),t) < timeout)); + udelay(delays[i]); + } while (delays[i++]); + __restore_flags(flags); + + for (i = 0; i < 4; i++) { + udelay(300); + outb(0xff, io); + } + + return; +} + +/* + * js_sw_read_packet() reads a SideWinder packet. + */ + +static int js_sw_read_packet(int io, int l1, int l2, int strobe, __u64 *data) +{ + static unsigned char buf[JS_SW_MAX_LENGTH]; + unsigned char u, v; + int i; + unsigned long flags; + unsigned int t, t1; + + int length = l1 < l2 ? l2 : l1; + int start = (js_time_speed * JS_SW_MAX_START) >> 10; + strobe = (js_time_speed * strobe) >> 10; + + i = 0; + + __save_flags(flags); + __cli(); + outb(0xff,io); + + v = inb(io); + t = js_get_time(); + + do { + u = v; + v = inb(io); + t1 = js_get_time(); + } while (!((u ^ v) & u & 0x10) && js_delta(t1, t) < start); + + t = t1; + + do { + v = inb(io); + t1 = js_get_time(); + if ((u ^ v) & v & 0x10) { + buf[i++] = v >> 5; + t = t1; + } + u = v; + } while (i < length && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + *data = 0; + + if (i == l1) { + t = i > 64 ? 64 : i; + for (i = 0; i < t; i++) + *data |= (__u64) (buf[i] & 1) << i; + return t; + } + if (i == l2) { + t = i > 22 ? 22 : i; + for (i = 0; i < t; i++) + *data |= (__u64) buf[i] << (3 * i); + return t * 3; + } + + return i; +} + +/* + * js_sw_parity computes parity of __u64 + */ + +static int js_sw_parity(__u64 t) +{ + t ^= t >> 32; + t ^= t >> 16; + t ^= t >> 8; + t ^= t >> 4; + t ^= t >> 2; + t ^= t >> 1; + return t & 1; +} + +/* + * js_sw_csum() computes checksum of nibbles in __u64 + */ + +static int js_sw_csum(__u64 t) +{ + char sum = 0; + while (t) { + sum += t & 0xf; + t >>= 4; + } + return sum & 0xf; +} + +/* + * js_sw_read() reads and analyzes SideWinder joystick data. + */ + +static int js_sw_read(void *xinfo, int **axes, int **buttons) +{ + struct js_sw_info *info = xinfo; + __u64 data; + int hat, i; + + switch (info->mode) { + + case JS_SW_MODE_3DP: + + if (info->optimize) { + i = js_sw_read_packet(info->io, -1, 22, JS_SW_EXT_STROBE, &data); + } else { + i = js_sw_read_packet(info->io, 64, 66, JS_SW_EXT_STROBE, &data); + if (i == 198) info->optimize = 1; + } + + if (i < 60) { + js_sw_init_digital(info->io); + info->optimize = 0; + return -1; + } + + if (((data & 0x8080808080808080ULL) ^ 0x80) || js_sw_csum(data) || + (hat = ((data >> 3) & 0x08) | ((data >> 60) & 0x07)) > 8) { + info->optimize = 0; + return -1; + } + axes[0][0] = ((data << 4) & 0x380) | ((data >> 16) & 0x07f); + axes[0][1] = ((data << 7) & 0x380) | ((data >> 24) & 0x07f); + axes[0][2] = ((data >> 28) & 0x180) | ((data >> 40) & 0x07f); + axes[0][3] = ((data >> 25) & 0x380) | ((data >> 48) & 0x07f); + axes[0][4] = js_sw_hat_to_axis[hat].x; + axes[0][5] = js_sw_hat_to_axis[hat].y; + buttons[0][0] = ((~data >> 31) & 0x80) | ((~data >> 8) & 0x7f); + + return 0; + + case JS_SW_MODE_PP: + + if (js_sw_read_packet(info->io, 48, 16, JS_SW_EXT_STROBE, &data) != 48) return -1; + if (!js_sw_parity(data) || (hat = (data >> 42) & 0xf) > 8) return -1; + + axes[0][0] = (data >> 9) & 0x3ff; + axes[0][1] = (data >> 19) & 0x3ff; + axes[0][2] = (data >> 29) & 0x07f; + axes[0][3] = (data >> 36) & 0x03f; + axes[0][4] = js_sw_hat_to_axis[hat].x; + axes[0][5] = js_sw_hat_to_axis[hat].y; + buttons[0][0] = ~data & 0x1ff; + + return 0; + + case JS_SW_MODE_GP: + + if (js_sw_read_packet(info->io, 15 * info->number, 5 * info->number, + JS_SW_EXT_STROBE, &data) != 15 * info->number) return -1; + if (js_sw_parity(data)) return -1; + + for (i = 0; i < info->number; i++) { + axes[i][0] = ((data >> 3) & 1) - ((data >> 2) & 1); + axes[i][1] = ( data & 1) - ((data >> 1) & 1); + buttons[i][0] = (~data >> 4) & 0x3ff; + data >>= 15; + } + + return 0; + + default: + return -1; + } +} + +/* + * js_sw_open() is a callback from the file open routine. + */ + +static int js_sw_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_sw_close() is a callback from the file release routine. + */ + +static int js_sw_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_sw_init_corr() initializes the correction values for + * SideWinders. + */ + +static void __init js_sw_init_corr(int num_axes, int mode, int number, struct js_corr **corr) +{ + int i, j; + + for (i = 0; i < number; i++) { + + for (j = 0; j < num_axes; j++) { + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = 8; + corr[i][j].coef[0] = 511 - 32; + corr[i][j].coef[1] = 512 + 32; + corr[i][j].coef[2] = (1 << 29) / (511 - 32); + corr[i][j].coef[3] = (1 << 29) / (511 - 32); + } + + switch (mode) { + + case JS_SW_MODE_3DP: + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 4; + corr[i][2].coef[0] = 255 - 16; + corr[i][2].coef[1] = 256 + 16; + corr[i][2].coef[2] = (1 << 29) / (255 - 16); + corr[i][2].coef[3] = (1 << 29) / (255 - 16); + + j = 4; + + break; + + case JS_SW_MODE_PP: + + corr[i][2].type = JS_CORR_BROKEN; + corr[i][2].prec = 1; + corr[i][2].coef[0] = 63 - 4; + corr[i][2].coef[1] = 64 + 4; + corr[i][2].coef[2] = (1 << 29) / (63 - 4); + corr[i][2].coef[3] = (1 << 29) / (63 - 4); + + corr[i][3].type = JS_CORR_BROKEN; + corr[i][3].prec = 0; + corr[i][3].coef[0] = 31 - 2; + corr[i][3].coef[1] = 32 + 2; + corr[i][3].coef[2] = (1 << 29) / (31 - 2); + corr[i][3].coef[3] = (1 << 29) / (31 - 2); + + j = 4; + + break; + + default: + + j = 0; + + } + + for (; j < num_axes; j++) { + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = 0; + corr[i][j].coef[0] = 0; + corr[i][j].coef[1] = 0; + corr[i][j].coef[2] = (1 << 29); + corr[i][j].coef[3] = (1 << 29); + } + } +} + +/* + * js_sw_probe() probes for SideWinder type joysticks. + */ + +static struct js_port __init *js_sw_probe(int io, struct js_port *port) +{ + struct js_sw_info info; + char *name; + int i, j, axes, buttons; + __u64 data; + unsigned char u; + + + if (check_region(io, 1)) return port; + if (((u = inb(io)) & 3) == 3) return port; + outb(0xff,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); + + if (!i) { + udelay(JS_SW_MIN_TIME); + js_sw_init_digital(io); + udelay(JS_SW_MAX_TIME); + i = js_sw_read_packet(io, JS_SW_MAX_LENGTH, -1, JS_SW_EXT_STROBE, &data); + } + + switch (i) { + case 0: + return port; + case 5: + case 10: + case 15: + case 20: + case 30: + case 45: + case 60: + info.mode = JS_SW_MODE_GP; + outb(0xff,io); /* Kick into 3-bit mode */ + udelay(JS_SW_MAX_TIME); + i = js_sw_read_packet(io, 60, -1, JS_SW_EXT_STROBE, &data); /* Get total length */ + udelay(JS_SW_MIN_TIME); + j = js_sw_read_packet(io, 15, -1, JS_SW_MIN_STROBE, &data); /* Get subpacket length */ + if (!i || !j) { + printk(KERN_WARNING "joy-sidewinder: SideWinder GamePad detected (%d,%d)," + " but not idenfitied.\n", i, j); + return port; + } + info.number = i / j; + axes = 2; buttons = 10; name = "SideWinder GamePad"; + break; + case 16: + case 48: + info.mode = JS_SW_MODE_PP; info.number = 1; + axes = 6; buttons = 9; name = "SideWinder Precision Pro"; + break; + case 64: + case 66: + info.mode = JS_SW_MODE_3DP; info.number = 1; info.optimize = 0; + axes = 6; buttons = 8; name = "SideWinder 3D Pro"; + break; + case 72: + return port; + default: + printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " + "(io=%#x, count=%d, data=0x%08x%08x), contact <vojtech@ucw.cz>\n", + io, i, (int)(data >> 32), (int)(data & 0xffffffff)); + return port; + } + + info.io = io; + + request_region(io, 1, "joystick (sidewinder)"); + port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read); + for (i = 0; i < info.number; i++) + printk(KERN_INFO "js%d: %s at %#x\n", + js_register_device(port, i, axes, buttons, name, js_sw_open, js_sw_close), name, io); + js_sw_init_corr(axes, info.mode, info.number, port->corr); + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_sw_init(void) +#endif +{ + int *p; + + for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port); + if (js_sw_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-sidewinder: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + int i; + struct js_sw_info *info; + + while (js_sw_port != NULL) { + for (i = 0; i < js_sw_port->ndevs; i++) + if (js_sw_port->devs[i] != NULL) + js_unregister_device(js_sw_port->devs[i]); + info = js_sw_port->info; + release_region(info->io, 1); + js_sw_port = js_unregister_port(js_sw_port); + } + +} +#endif diff --git a/drivers/char/joystick/joy-thrustmaster.c b/drivers/char/joystick/joy-thrustmaster.c new file mode 100644 index 000000000..3ea71416d --- /dev/null +++ b/drivers/char/joystick/joy-thrustmaster.c @@ -0,0 +1,331 @@ +/* + * joy-thrustmaster.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * ThrustMaster DirectConnect (BSP) joystick family. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> + +#define JS_TM_MAX_START 400 +#define JS_TM_MAX_STROBE 25 +#define JS_TM_MAX_LENGTH 13 + +#define JS_TM_MODE_M3DI 1 +#define JS_TM_MODE_3DRP 3 +#define JS_TM_MODE_WCS3 4 + +#define JS_TM_MODE_MAX 5 /* Last mode + 1 */ + +#define JS_TM_BYTE_A0 0 +#define JS_TM_BYTE_A1 1 +#define JS_TM_BYTE_A2 3 +#define JS_TM_BYTE_A3 4 +#define JS_TM_BYTE_A4 6 +#define JS_TM_BYTE_A5 7 + +#define JS_TM_BYTE_D0 2 +#define JS_TM_BYTE_D1 5 +#define JS_TM_BYTE_D2 8 +#define JS_TM_BYTE_D3 9 + +#define JS_TM_BYTE_ID 10 +#define JS_TM_BYTE_REV 11 +#define JS_TM_BYTE_DEF 12 + +static int js_tm_port_list[] __initdata = {0x201, 0}; +static struct js_port* js_tm_port __initdata = NULL; + +struct js_tm_info { + int io; + unsigned char mode; +}; + +static int js_tm_id_to_def[JS_TM_MODE_MAX] = {0x00, 0x42, 0x00, 0x22, 0x00}; + +/* + * js_tm_read_packet() reads a ThrustMaster packet. + */ + +static int js_tm_read_packet(int io, unsigned char *data) +{ + unsigned int t, t1; + unsigned char u, v, error; + int i, j; + unsigned long flags; + + int start = (js_time_speed * JS_TM_MAX_START) >> 10; + int strobe = (js_time_speed * JS_TM_MAX_STROBE) >> 10; + + error = 0; + i = j = 0; + + __save_flags(flags); + __cli(); + outb(0xff,io); + + t = js_get_time(); + + do { + u = inb(io); + t1 = js_get_time(); + } while ((u & 1) && js_delta(t1, t) < start); + + t = t1; + u >>= 4; + + do { + v = inb(io) >> 4; + t1 = js_get_time(); + if ((u ^ v) & u & 2) { + if (j) { + if (j < 9) { /* Data bit */ + data[i] |= (~v & 1) << (j - 1); + j++; + } else { /* Stop bit */ + error |= v & 1; + j = 0; + i++; + } + } else { /* Start bit */ + data[i] = 0; + error |= ~v & 1; + j++; + } + t = t1; + } + u = v; + } while (!error && i < JS_TM_MAX_LENGTH && js_delta(t1,t) < strobe); + + __restore_flags(flags); + + return -(i != JS_TM_MAX_LENGTH); +} + +/* + * js_tm_read() reads and analyzes ThrustMaster joystick data. + */ + +static int js_tm_read(void *xinfo, int **axes, int **buttons) +{ + struct js_tm_info *info = xinfo; + unsigned char data[JS_TM_MAX_LENGTH]; + + if (js_tm_read_packet(info->io, data)) { + printk(KERN_WARNING "joy-thrustmaster: failed to read data packet\n"); + return -1; + } + if (data[JS_TM_BYTE_ID] != info->mode) { + printk(KERN_WARNING "joy-thrustmaster: ID (%d) != mode (%d)\n", + data[JS_TM_BYTE_ID], info->mode); + return -1; + } + if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info->mode]) { + printk(KERN_WARNING "joy-thrustmaster: DEF (%d) != def(mode) (%d)\n", + data[JS_TM_BYTE_DEF], js_tm_id_to_def[info->mode]); + return -1; + } + + switch (info->mode) { + + case JS_TM_MODE_M3DI: + + axes[0][0] = data[JS_TM_BYTE_A0]; + axes[0][1] = data[JS_TM_BYTE_A1]; + axes[0][2] = data[JS_TM_BYTE_A2]; + axes[0][3] = data[JS_TM_BYTE_A3]; + + axes[0][4] = ((data[JS_TM_BYTE_D0] >> 3) & 1) - ((data[JS_TM_BYTE_D0] >> 1) & 1); + axes[0][5] = ((data[JS_TM_BYTE_D0] >> 2) & 1) - ( data[JS_TM_BYTE_D0] & 1); + + buttons[0][0] = ((data[JS_TM_BYTE_D0] >> 6) & 0x01) | ((data[JS_TM_BYTE_D0] >> 3) & 0x06) + | ((data[JS_TM_BYTE_D0] >> 4) & 0x08) | ((data[JS_TM_BYTE_D1] >> 2) & 0x30); + + return 0; + + case JS_TM_MODE_3DRP: + + axes[0][0] = data[JS_TM_BYTE_A0]; + axes[0][1] = data[JS_TM_BYTE_A1]; + + buttons[0][0] = ( data[JS_TM_BYTE_D0] & 0x3f) | ((data[JS_TM_BYTE_D1] << 6) & 0xc0) + | (( ((int) data[JS_TM_BYTE_D0]) << 2) & 0x300); + + return 0; + + } + + return -1; +} + +/* + * js_tm_open() is a callback from the file open routine. + */ + +static int js_tm_open(struct js_dev *jd) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * js_tm_close() is a callback from the file release routine. + */ + +static int js_tm_close(struct js_dev *jd) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_tm_init_corr() initializes the correction values for + * ThrustMaster joysticks. + */ + +static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) +{ + int j; + + for (j = 0; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 127 - 2; + corr[0][j].coef[1] = 128 + 2; + corr[0][j].coef[2] = (1 << 29) / (127 - 4); + corr[0][j].coef[3] = (1 << 29) / (127 - 4); + } + + switch (mode) { + case JS_TM_MODE_M3DI: j = 4; break; + case JS_TM_MODE_3DRP: j = 2; break; + default: j = 0; break; + } + + for (; j < num_axes; j++) { + corr[0][j].type = JS_CORR_BROKEN; + corr[0][j].prec = 0; + corr[0][j].coef[0] = 0; + corr[0][j].coef[1] = 0; + corr[0][j].coef[2] = (1 << 29); + corr[0][j].coef[3] = (1 << 29); + } + +} + +/* + * js_tm_probe() probes for ThrustMaster type joysticks. + */ + +static struct js_port __init *js_tm_probe(int io, struct js_port *port) +{ + struct js_tm_info info; + char *names[JS_TM_MODE_MAX] = { NULL, "ThrustMaster Millenium 3D Inceptor", NULL, + "ThrustMaster Rage 3D Gamepad", "ThrustMaster WCS III" }; + char axes[JS_TM_MODE_MAX] = { 0, 6, 0, 2, 0 }; + char buttons[JS_TM_MODE_MAX] = { 0, 5, 0, 10, 0 }; + + unsigned char data[JS_TM_MAX_LENGTH]; + unsigned char u; + + if (check_region(io, 1)) return port; + + if (((u = inb(io)) & 3) == 3) return port; + outb(0xff,io); + if (!((inb(io) ^ u) & ~u & 0xf)) return port; + + if(js_tm_read_packet(io, data)) { + printk(KERN_WARNING "joy-thrustmaster: probe - can't read packet\n"); + return port; + } + + info.io = io; + info.mode = data[JS_TM_BYTE_ID]; + + if (!info.mode) return port; + + if (info.mode >= JS_TM_MODE_MAX || !names[info.mode]) { + printk(KERN_WARNING "joy-thrustmaster: unknown device detected " + "(io=%#x, id=%d), contact <vojtech@ucw.cz>\n", + io, info.mode); + return port; + } + + if (data[JS_TM_BYTE_DEF] != js_tm_id_to_def[info.mode]) { + printk(KERN_WARNING "joy-thrustmaster: wrong DEF (%d) for ID %d - should be %d\n", + data[JS_TM_BYTE_DEF], info.mode, js_tm_id_to_def[info.mode]); + } + + request_region(io, 1, "joystick (thrustmaster)"); + port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read); + printk(KERN_INFO "js%d: %s revision %d at %#x\n", + js_register_device(port, 0, axes[info.mode], buttons[info.mode], + names[info.mode], js_tm_open, js_tm_close), names[info.mode], data[JS_TM_BYTE_REV], io); + js_tm_init_corr(axes[info.mode], info.mode, port->axes, port->corr); + + return port; +} + +#ifdef MODULE +int init_module(void) +#else +int __init js_tm_init(void) +#endif +{ + int *p; + + for (p = js_tm_port_list; *p; p++) js_tm_port = js_tm_probe(*p, js_tm_port); + if (js_tm_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-thrustmaster: no joysticks found\n"); +#endif + + return -ENODEV; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_tm_info *info; + + while (js_tm_port != NULL) { + js_unregister_device(js_tm_port->devs[0]); + info = js_tm_port->info; + release_region(info->io, 1); + js_tm_port = js_unregister_port(js_tm_port); + } +} +#endif diff --git a/drivers/char/joystick/joy-turbografx.c b/drivers/char/joystick/joy-turbografx.c new file mode 100644 index 000000000..fdf1aa433 --- /dev/null +++ b/drivers/char/joystick/joy-turbografx.c @@ -0,0 +1,281 @@ +/* + * joy-turbografx.c Version 1.2 + * + * Copyright (c) 1998 Vojtech Pavlik + */ + +/* + * This is a module for the Linux joystick driver, supporting + * Steffen Schwenke's <schwenke@burg-halle.de> TurboGraFX parallel port + * interface. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/delay.h> + + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_PARM(js_tg, "2-8i"); +MODULE_PARM(js_tg_2, "2-8i"); +MODULE_PARM(js_tg_3, "2-8i"); + +#define JS_TG_BUTTON1 0x08 +#define JS_TG_UP 0x10 +#define JS_TG_DOWN 0x20 +#define JS_TG_LEFT 0x40 +#define JS_TG_RIGHT 0x80 + +#define JS_TG_BUTTON2 0x02 +#define JS_TG_BUTTON3 0x04 +#define JS_TG_BUTTON4 0x01 +#define JS_TG_BUTTON5 0x08 + +static struct js_port* js_tg_port = NULL; + +static int js_tg[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int js_tg_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int js_tg_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; + +struct js_tg_info { +#ifdef USE_PARPORT + struct pardevice *port; /* parport device */ +#else + int port; /* hw port */ +#endif + int sticks; /* joysticks connected */ +}; + +/* + * js_tg_read() reads and analyzes tg joystick data. + */ + +static int js_tg_read(void *xinfo, int **axes, int **buttons) +{ + struct js_tg_info *info = xinfo; + int data1, data2, i; + + for (i = 0; i < 7; i++) + if ((info->sticks >> i) & 1) { + + JS_PAR_DATA_OUT(~(1 << i), info->port); + data1 = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; + data2 = JS_PAR_CTRL_IN(info->port) ^ JS_PAR_CTRL_INVERT; + + axes[i][0] = ((data1 & JS_TG_RIGHT) ? 1 : 0) - ((data1 & JS_TG_LEFT) ? 1 : 0); + axes[i][1] = ((data1 & JS_TG_DOWN ) ? 1 : 0) - ((data1 & JS_TG_UP ) ? 1 : 0); + + buttons[i][0] = ((data1 & JS_TG_BUTTON1) ? 0x01 : 0) | ((data2 & JS_TG_BUTTON2) ? 0x02 : 0) + | ((data2 & JS_TG_BUTTON3) ? 0x04 : 0) | ((data2 & JS_TG_BUTTON4) ? 0x08 : 0) + | ((data2 & JS_TG_BUTTON5) ? 0x10 : 0); + + } + + return 0; +} + +/* + * open callback: claim parport. + */ + +int js_tg_open(struct js_dev *dev) +{ + struct js_tg_info *info = dev->port->info; + + if (!MOD_IN_USE) { +#ifdef USE_PARPORT + if (parport_claim(info->port)) return -EBUSY; +#endif + JS_PAR_CTRL_OUT(0x04, info->port); + } + MOD_INC_USE_COUNT; + return 0; +} + +/* + * close callback: release parport + */ + +int js_tg_close(struct js_dev *dev) +{ + struct js_tg_info *info = dev->port->info; + + MOD_DEC_USE_COUNT; + if (!MOD_IN_USE) { + JS_PAR_CTRL_OUT(0x00, info->port); +#ifdef USE_PARPORT + parport_release(info->port); +#endif + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + struct js_tg_info *info; + int i; + + while (js_tg_port != NULL) { + for (i = 0; i < js_tg_port->ndevs; i++) + if (js_tg_port->devs[i] != NULL) + js_unregister_device(js_tg_port->devs[i]); + info = js_tg_port->info; +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); +#endif + js_tg_port = js_unregister_port(js_tg_port); + } +} +#endif + +/* + * js_tg_init_corr() initializes correction values of + * tg gamepads. + */ + +static void __init js_tg_init_corr(int sticks, struct js_corr **corr) +{ + int i, j; + + for (i = 0; i < 7; i++) + if ((sticks >> i) & 1) + for (j = 0; j < 2; j++) { + corr[i][j].type = JS_CORR_BROKEN; + corr[i][j].prec = 0; + corr[i][j].coef[0] = 0; + corr[i][j].coef[1] = 0; + corr[i][j].coef[2] = (1 << 29); + corr[i][j].coef[3] = (1 << 29); + } +} + +/* + * js_tg_probe() probes for tg gamepads. + */ + +static struct js_port __init *js_tg_probe(int *config, struct js_port *port) +{ + struct js_tg_info iniinfo; + struct js_tg_info *info = &iniinfo; + int i; + + if (config[0] < 0) return port; + +#ifdef USE_PARPORT + { + struct parport *pp; + + if (config[0] > 0x10) + for (pp=parport_enumerate(); pp != NULL && (pp->base!=config[0]); pp=pp->next); + else + for (pp=parport_enumerate(); pp != NULL && (config[0]>0); pp=pp->next) config[0]--; + + if (pp == NULL) { + printk(KERN_ERR "joy-tg: no such parport\n"); + return port; + } + + info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + if (!info->port) + return port; + } +#else + info->port = config[0]; + if (check_region(info->port, 3)) return port; + request_region(info->port, 3, "joystick (turbografx)"); +#endif + + port = js_register_port(port, info, 7, sizeof(struct js_tg_info), js_tg_read); + info = port->info; + + info->sticks = 0; + + for (i = 0; i < 7; i++) + if (config[i+1] > 0 && config[i+1] < 6) { +#ifdef USE_PARPORT + printk(KERN_INFO "js%d: Multisystem joystick on %s\n", + js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close), + info->port->port->name); +#else + printk(KERN_INFO "js%d: Multisystem joystick at %#x\n", + js_register_device(port, i, 2, config[i+1], "Multisystem joystick", js_tg_open, js_tg_close), + info->port); +#endif + info->sticks |= (1 << i); + } + + if (!info->sticks) { +#ifdef USE_PARPORT + parport_unregister_device(info->port); +#else + release_region(info->port, 3); +#endif + return port; + } + + js_tg_init_corr(info->sticks, port->corr); + + return port; +} + +#ifndef MODULE +void __init js_tg_setup(char *str, int *ints) +{ + int i; + + if (!strcmp(str,"js_tg")) + for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; + if (!strcmp(str,"js_tg_2")) + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; + if (!strcmp(str,"js_tg_3")) + for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; + +} +#endif + +#ifdef MODULE +int init_module(void) +#else +int __init js_tg_init(void) +#endif +{ + js_tg_port = js_tg_probe(js_tg, js_tg_port); + js_tg_port = js_tg_probe(js_tg_2, js_tg_port); + js_tg_port = js_tg_probe(js_tg_3, js_tg_port); + + if (js_tg_port) return 0; + +#ifdef MODULE + printk(KERN_WARNING "joy-tg: no joysticks specified\n"); +#endif + return -ENODEV; +} diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c new file mode 100644 index 000000000..aff1acd17 --- /dev/null +++ b/drivers/char/joystick/joystick.c @@ -0,0 +1,1231 @@ +/* + * joystick.c Version 1.2 + * + * Copyright (c) 1996-1998 Vojtech Pavlik + */ + +/* + * This is the main joystick driver for Linux. It doesn't support any + * devices directly, rather is lets you use sub-modules to do that job. See + * Documentation/joystick.txt for more info. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <linux/config.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/joystick.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/malloc.h> +#include <linux/mm.h> +#include <linux/module.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include <asm/spinlock.h> +#include <linux/poll.h> +#endif + +/* + * Configurable parameters. + */ + +#define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */ + +/* + * Buffer macros. + */ + +#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) +#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) +#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) +#define DIFF(X,Y) ((X)>(Y)?(X)-(Y):(Y)-(X)) + +/* + * Global variables. + */ + +static struct JS_DATA_SAVE_TYPE js_comp_glue; +static struct js_port *js_port = NULL; +static struct js_dev *js_dev = NULL; +static struct timer_list js_timer; +spinlock_t js_lock = SPIN_LOCK_UNLOCKED; +static int js_use_count = 0; + +/* + * Exported variables. + */ + +unsigned int js_time_speed = 0; +js_time_func js_get_time; +js_delta_func js_delta; + +unsigned int js_time_speed_a = 0; +js_time_func js_get_time_a; +js_delta_func js_delta_a; + +/* + * Module info. + */ + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); +MODULE_SUPPORTED_DEVICE("js"); + +/* + * js_get_time_*() are different functions to get current time. + * js_delta_*() are functions to compute time difference. + */ + +#ifdef __i386__ + +static unsigned int js_get_time_rdtsc(void) +{ + unsigned int x; + __asm__ __volatile__ ( "rdtsc" : "=A" (x) ); + return x; +} + +static unsigned int js_get_time_pit(void) +{ + unsigned long flags; + unsigned int x; + + __save_flags(flags); + __cli(); + outb(0, 0x43); + x = inb(0x40); + x |= inb(0x40) << 8; + __restore_flags(flags); + + return x; +} + +static int js_delta_pit(unsigned int x, unsigned int y) +{ + return y - x + ( y < x ? 1193180L / HZ : 0 ); +} + +static unsigned int js_get_time_counter(void) +{ + static int time_counter = 0; + return time_counter++; +} + +#else +#ifdef __alpha__ + +static unsigned int js_get_time_rpcc(void) +{ + unsigned int x; + __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ); + return x; +} + +#else + +#ifndef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +static unsigned int js_get_time_system(void) +{ + static struct timeval js_tv; + get_fast_time(&js_tv); + return js_tv.tv_sec * 1000000L + js_tv.tv_usec; +} +#endif +#endif + +#endif +#endif + +static int js_delta_normal(unsigned int x, unsigned int y) +{ + return x - y; +} + +/* + * js_calibrate_time() calibrates a given timer. + */ + +static int __init js_calibrate_time(js_time_func get_time, js_delta_func delta) +{ + unsigned int t1, t2, t3; + unsigned long flags; + + __save_flags(flags); + __cli(); + t1 = get_time(); + udelay(1000); + t2 = get_time(); + t3 = get_time(); + __restore_flags(flags); + + return delta(t2, t1) - delta(t3, t2); +} + +/* + * js_calibrate_time_counter() calibrates the counter timer, which can't + * be calibrated using the above function. + */ + +#ifdef __i386__ + +static int __init js_calibrate_time_counter(void) +{ + unsigned int i, j, t1, t2, t3; + + j = jiffies; do { inb(0x201); t1 = js_get_time_counter(); } while (j == jiffies); + j = jiffies; do { inb(0x201); t2 = js_get_time_counter(); } while (j == jiffies); + + j = (t2 - t1) * HZ / 1000; + + t1 = js_get_time_pit(); + for (i = 0; i < 1000; i++) { + inb(0x201); + js_get_time_counter(); + } + t2 = js_get_time_pit(); + t3 = js_get_time_pit(); + + i = 1193180L / (js_delta_pit(t2, t1) - js_delta_pit(t3, t2)); + + if (DIFF(i,j) > 5) + printk(KERN_WARNING "js: Counter timer calibration unsure," + " pass1 (0.%d MHz) and pass2 (0.%d MHz) differ.\n", j, i); + + return (i + j) >> 1; +} + +#endif + +/* + * js_setup_time chooses the best available timers + * on the system and calibrates them. + */ + +static int __init js_setup_time(void) +{ + int t; + char *name, *name_a; + + name = ""; + name_a = ""; + js_time_speed = 0; + js_time_speed_a = 0; + +#ifdef __i386__ + + t = js_calibrate_time(js_get_time_pit, js_delta_pit); + + if (DIFF(t, 1193) > 5) + printk(KERN_WARNING "js: Measured PIT speed is %d.%03d MHz, but should be 1.193 MHz.\n" + KERN_WARNING "js: This is probably caused by wrong BogoMIPS value. It is: %ld, should be: %ld.\n", + t / 1000, t % 1000, loops_per_sec / 500000, loops_per_sec / (t * 500000 / 1193)); + + if (JS_HAS_RDTSC && (t = js_calibrate_time(js_get_time_rdtsc, js_delta_normal)) > 0) { + + js_time_speed_a = t; + js_get_time_a = js_get_time_rdtsc; + js_delta_a = js_delta_normal; + js_time_speed = t; + js_get_time = js_get_time_rdtsc; + js_delta = js_delta_normal; + name = "RDTSC"; + + } else { + + js_time_speed_a = t; + js_get_time_a = js_get_time_pit; + js_delta_a = js_delta_pit; + name_a = "PIT"; + + t = js_calibrate_time_counter(); + + js_time_speed = t; + js_get_time = js_get_time_counter; + js_delta = js_delta_normal; + name = "counter"; + + } + +#else +#ifdef __alpha__ + + t = js_calibrate_time(js_get_time_rpcc, js_delta_normal); + + js_time_speed_a = t; + js_get_time_a = js_get_time_rpcc; + js_delta_a = js_delta_normal; + js_time_speed = t; + js_get_time = js_get_time_rpcc; + js_delta = js_delta_normal; + name = "RPCC"; + +#else + +#ifndef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + t = js_calibrate_time(js_get_time_system, js_delta_normal); + + js_time_speed_a = t; + js_get_time_a = js_get_time_system; + js_delta_a = js_delta_normal; + js_time_speed = t; + js_get_time = js_get_time_system; + js_delta = js_delta_normal; + name = "system"; +#endif +#endif + +#endif +#endif + + printk(KERN_INFO "js: Version %d.%d.%d ", + JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); + + if (js_time_speed_a <= 0 || js_time_speed <= 0) { + printk("\n"); + return -1; + } + + printk("using "); + + if (js_time_speed > 10000) { + t = js_time_speed / 1000 + (js_time_speed % 1000 >= 500); + printk("%d MHz ", t); + } else { + t = js_time_speed / 10 + (js_time_speed % 10 >= 5); + printk("%d.%02d MHz ", t / 100, t % 100); + } + + if (js_get_time_a != js_get_time) { + t = js_time_speed_a / 10 + (js_time_speed_a % 10 >= 5); + printk("%s timer and %d.%02d MHz %s timer.\n", + name, t / 100, t % 100, name_a); + } else { + printk("%s timer.\n", name); + } + + return 0; +} + + +/* + * js_correct() performs correction of raw joystick data. + */ + +static int js_correct(int value, struct js_corr *corr) +{ + switch (corr->type) { + case JS_CORR_NONE: + break; + case JS_CORR_BROKEN: + value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : + ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : + ((corr->coef[2] * (value - corr->coef[0])) >> 14); + break; + + default: + return 0; + } + + if (value < -32767) return -32767; + if (value > 32767) return 32767; + + return value; +} + +/* + * js_button() returns value of button number i. + */ + +static inline int js_button(int *buttons, int i) +{ + return (buttons[i >> 5] >> (i & 0x1f)) & 1; +} + + +/* + * js_add_event() adds an event to the buffer. This requires additional + * queue post-processing done by js_sync_buff. + */ + +static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value) +{ + jd->buff[jd->ahead].time = time; + jd->buff[jd->ahead].type = type; + jd->buff[jd->ahead].number = number; + jd->buff[jd->ahead].value = value; + if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0; +} + +/* + * js_flush_data() does the same as js_process_data, except for that it doesn't + * generate any events - it just copies the data from new to cur. + */ + +static void js_flush_data(struct js_dev *jd) +{ + int i; + + for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++) + jd->cur.buttons[i] = jd->new.buttons[i]; + for (i = 0; i < jd->num_axes; i++) + jd->cur.axes[i] = jd->new.axes[i]; +} + +/* + * js_process_data() finds changes in button states and axis positions and adds + * them as events to the buffer. + */ + +static void js_process_data(struct js_dev *jd) +{ + int i, t; + + for (i = 0; i < jd->num_buttons; i++) + if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) { + js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t); + jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f)); + } + + for (i = 0; i < jd->num_axes; i++) { + t = js_correct(jd->new.axes[i], &jd->corr[i]); + if (((jd->corr[i].prec == -1) && t) || + ((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) && + (t != js_correct(jd->cur.axes[i], &jd->corr[i])))) { + js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t); + jd->cur.axes[i] = jd->new.axes[i]; + } + } +} + +/* + * js_sync_buff() checks for all overflows caused by recent additions to the buffer. + * These happen only if some process is reading the data too slowly. It + * wakes up any process waiting for data. + */ + +static void js_sync_buff(struct js_dev *jd) +{ + struct js_list *curl = jd->list; + + if (jd->bhead != jd->ahead) { + if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) { + while (curl) { + if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) { + curl->tail = jd->ahead; + curl->startup = 0; + } + curl = curl->next; + } + jd->tail = jd->ahead; + } + jd->bhead = jd->ahead; + wake_up_interruptible(&jd->wait); + } +} + +/* + * js_do_timer() acts as an interrupt replacement. It reads the data + * from all ports and then generates events for all devices. + */ + +static void js_do_timer(unsigned long data) +{ + struct js_port *curp = js_port; + struct js_dev *curd = js_dev; + unsigned long flags; + + while (curp != NULL) { + curp->read(curp->info, curp->axes, curp->buttons); + curp = curp->next; + } + + spin_lock_irqsave(&js_lock, flags); + + while (curd != NULL) { + if (data) { + js_process_data(curd); + js_sync_buff(curd); + } else { + js_flush_data(curd); + } + curd = curd->next; + } + + spin_unlock_irqrestore(&js_lock, flags); + + js_timer.expires = jiffies + JS_REFRESH_TIME; + add_timer(&js_timer); +} + +/* + * js_read() copies one or more entries from jsd[].buff to user + * space. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) +#else +static int js_read(struct inode *inode, struct file *file, char *buf, int count) +#endif +{ + struct wait_queue wait = { current, NULL }; + struct js_event *buff = (void *) buf; + struct js_list *curl; + struct js_dev *jd; + unsigned long blocks = count / sizeof(struct js_event); + int written = 0; + int new_tail, orig_tail; + int retval = 0; + unsigned long flags; + + curl = file->private_data; + jd = curl->dev; + orig_tail = curl->tail; + +/* + * Check user data. + */ + + if (!blocks) + return -EINVAL; + +/* + * Lock it. + */ + + spin_lock_irqsave(&js_lock, flags); + +/* + * Handle (non)blocking i/o. + */ + if (count != sizeof(struct JS_DATA_TYPE)) { + + if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { + + add_wait_queue(&jd->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (GOF(curl->tail) == jd->bhead) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + spin_unlock_irqrestore(&js_lock, flags); + schedule(); + spin_lock_irqsave(&js_lock, flags); + + } + + current->state = TASK_RUNNING; + remove_wait_queue(&jd->wait, &wait); + } + + if (retval) { + spin_unlock_irqrestore(&js_lock, flags); + return retval; + } + +/* + * Initial state. + */ + + while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) { + + struct js_event tmpevent; + + if (curl->startup < jd->num_buttons) { + tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; + tmpevent.value = js_button(jd->cur.buttons, curl->startup); + tmpevent.number = curl->startup; + } else { + tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; + tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons], + &jd->corr[curl->startup - jd->num_buttons]); + tmpevent.number = curl->startup - jd->num_buttons; + } + + tmpevent.time = jiffies * (1000/HZ); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) + retval = -EFAULT; +#else + if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) + memcpy_tofs(&buff[written], &tmpevent, sizeof(struct js_event)); +#endif + + curl->startup++; + written++; + } + +/* + * Buffer data. + */ + + while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) { + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) + retval = -EFAULT; + if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) + retval = -EFAULT; +#else + if (!(retval = verify_area(VERIFY_WRITE, &buff[written], sizeof(struct js_event)))) { + memcpy_tofs(&buff[written], &jd->buff[new_tail], sizeof(struct js_event)); + put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time); + } +#endif + curl->tail = new_tail; + written++; + } + } + + else + +/* + * Handle version 0.x compatibility. + */ + + { + struct JS_DATA_TYPE data; + + data.buttons = jd->new.buttons[0]; + data.x = jd->num_axes < 1 ? 0 : + ((js_correct(jd->new.axes[0], &jd->corr[0]) / 256) + 128) >> js_comp_glue.JS_CORR.x; + data.y = jd->num_axes < 2 ? 0 : + ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; +#else + if (!(retval = verify_area(VERIFY_WRITE, buf, sizeof(struct JS_DATA_TYPE)))) { + memcpy_tofs(buf, &data, sizeof(struct JS_DATA_TYPE)); + } +#endif + + curl->startup = 0; + curl->tail = GOB(jd->bhead); + if (!retval) retval = sizeof(struct JS_DATA_TYPE); + } + +/* + * Check main tail and move it. + */ + + if (orig_tail == jd->tail) { + new_tail = curl->tail; + curl = jd->list; + while (curl != NULL && curl->tail != jd->tail) { + if (ROT(jd->bhead, new_tail, curl->tail) || + (jd->bhead == curl->tail)) new_tail = curl->tail; + curl = curl->next; + } + if (curl == NULL) jd->tail = new_tail; + } + + spin_unlock_irqrestore(&js_lock, flags); + + return retval ? retval : written * sizeof(struct js_event); +} + +/* + * js_poll() does select() support. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + +static unsigned int js_poll(struct file *file, poll_table *wait) +{ + struct js_list *curl = file->private_data; + unsigned long flags; + int retval = 0; + poll_wait(file, &curl->dev->wait, wait); + spin_lock_irqsave(&js_lock, flags); + if (GOF(curl->tail) != curl->dev->bhead || + curl->startup < curl->dev->num_axes + curl->dev->num_buttons) retval = POLLIN | POLLRDNORM; + spin_unlock_irqrestore(&js_lock, flags); + return retval; +} + +#else + +static int js_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) +{ + struct js_list *curl = file->private_data; + if (sel_type == SEL_IN) { + if (GOF(curl->tail) != curl->dev->bhead) return 1; + select_wait(&curl->dev->wait, wait); + } + return 0; +} + +#endif + +/* + * js_ioctl handles misc ioctl calls. + */ + +static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct js_list *curl; + struct js_dev *jd; + int len; + + curl = file->private_data; + jd = curl->dev; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + + switch (cmd) { + +/* + * 0.x compatibility + */ + + case JS_SET_CAL: + return copy_from_user(&js_comp_glue.JS_CORR, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_GET_CAL: + return copy_to_user((struct JS_DATA_TYPE *) arg, &js_comp_glue.JS_CORR, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_SET_TIMEOUT: + return get_user(js_comp_glue.JS_TIMEOUT, (int *) arg); + case JS_GET_TIMEOUT: + return put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); + case JS_SET_TIMELIMIT: + return get_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); + case JS_GET_TIMELIMIT: + return put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); + case JS_SET_ALL: + return copy_from_user(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + case JS_GET_ALL: + return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + +/* + * 1.x ioctl calls + */ + + case JSIOCGVERSION: + return put_user(JS_VERSION, (__u32 *) arg); + case JSIOCGAXES: + return put_user(jd->num_axes, (__u8 *) arg); + case JSIOCGBUTTONS: + return put_user(jd->num_buttons, (__u8 *) arg); + case JSIOCSCORR: + return copy_from_user(jd->corr, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; + case JSIOCGCORR: + return copy_to_user((struct js_corr *) arg, jd->corr, + sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; + default: + if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { + len = strlen(jd->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + if (copy_to_user((char *) arg, jd->name, len)) return -EFAULT; + return len; + } + } + +#else + + switch (cmd) { + +/* + * 0.x compatibility + */ + + case JS_SET_CAL: + if (verify_area(VERIFY_READ, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE))) return -EFAULT; + memcpy_fromfs(&js_comp_glue.JS_CORR, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_TYPE)); + return 0; + case JS_GET_CAL: + if (verify_area(VERIFY_WRITE, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE))) return -EFAULT; + memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue.JS_CORR, + sizeof(struct JS_DATA_TYPE)); + return 0; + case JS_SET_TIMEOUT: + if (verify_area(VERIFY_READ, (int *) arg, sizeof(int))) return -EFAULT; + js_comp_glue.JS_TIMEOUT = get_user((int *) arg); + return 0; + case JS_GET_TIMEOUT: + if (verify_area(VERIFY_WRITE, (int *) arg, sizeof(int))) return -EFAULT; + put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); + return 0; + case JS_SET_TIMELIMIT: + if (verify_area(VERIFY_READ, (long *) arg, sizeof(long))) return -EFAULT; + js_comp_glue.JS_TIMELIMIT = get_user((long *) arg); + return 0; + case JS_GET_TIMELIMIT: + if (verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))) return -EFAULT; + put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); + return 0; + case JS_SET_ALL: + if (verify_area(VERIFY_READ, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; + memcpy_fromfs(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE)); + return 0; + case JS_GET_ALL: + if (verify_area(VERIFY_WRITE, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE))) return -EFAULT; + memcpy_tofs((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, + sizeof(struct JS_DATA_SAVE_TYPE)); + return 0; + +/* + * 1.x ioctl calls + */ + + case JSIOCGVERSION: + if (verify_area(VERIFY_WRITE, (__u32 *) arg, sizeof(__u32))) return -EFAULT; + put_user(JS_VERSION, (__u32 *) arg); + return 0; + case JSIOCGAXES: + if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; + put_user(jd->num_axes, (__u8 *) arg); + return 0; + case JSIOCGBUTTONS: + if (verify_area(VERIFY_WRITE, (__u8 *) arg, sizeof(__u8))) return -EFAULT; + put_user(jd->num_buttons, (__u8 *) arg); + return 0; + case JSIOCSCORR: + if (verify_area(VERIFY_READ, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; + memcpy_fromfs(jd->corr, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes); + return 0; + case JSIOCGCORR: + if (verify_area(VERIFY_WRITE, (struct js_corr *) arg, + sizeof(struct js_corr) * jd->num_axes)) return -EFAULT; + memcpy_tofs((struct js_corr *) arg, + jd->corr, sizeof(struct js_corr) * jd->num_axes); + return 0; + default: + if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { + len = strlen(jd->name) + 1; + if (verify_area(VERIFY_WRITE, (char *) arg, len)) return -EFAULT; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + memcpy_tofs((char *) arg, jd->name, len); + return len; + } + } + +#endif + + return -EINVAL; +} + +/* + * js_open() performs necessary initialization and adds + * an entry to the linked list. + */ + +static int js_open(struct inode *inode, struct file *file) +{ + struct js_list *curl, *new; + struct js_dev *jd = js_dev; + int i = MINOR(inode->i_rdev); + unsigned long flags; + int result; + + if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) + return -EINVAL; + + spin_lock_irqsave(&js_lock, flags); + + while (i > 0 && jd != NULL) { + jd = jd->next; + i--; + } + + spin_unlock_irqrestore(&js_lock, flags); + + if (jd == NULL) return -ENODEV; + + if ((result = jd->open(jd))) return result; + + MOD_INC_USE_COUNT; + if (!js_use_count++) js_do_timer(0); + + if ((new = kmalloc(sizeof(struct js_list), GFP_KERNEL)) != NULL) { + + spin_lock_irqsave(&js_lock, flags); + + curl = jd->list; + + jd->list = new; + jd->list->next = curl; + jd->list->dev = jd; + jd->list->startup = 0; + jd->list->tail = GOB(jd->bhead); + file->private_data = jd->list; + + spin_unlock_irqrestore(&js_lock, flags); + + } else { + result = -ENOMEM; + } + + return result; +} + +/* + * js_release() removes an entry from list and deallocates memory + * used by it. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +static int js_release(struct inode *inode, struct file *file) +#else +static void js_release(struct inode *inode, struct file *file) +#endif +{ + struct js_list *curl = file->private_data; + struct js_dev *jd = curl->dev; + struct js_list **curp = &jd->list; + int new_tail; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*curp && (*curp != curl)) curp = &((*curp)->next); + *curp = (*curp)->next; + + if (jd->list != NULL) + if (curl->tail == jd->tail) { + curl = jd->list; + new_tail = curl->tail; + while (curl != NULL && curl->tail != jd->tail) { + if (ROT(jd->bhead, new_tail, curl->tail) || + (jd->bhead == curl->tail)) new_tail = curl->tail; + curl = curl->next; + } + if (!curl) jd->tail = new_tail; + } + + spin_unlock_irqrestore(&js_lock, flags); + + kfree(file->private_data); + + if (!--js_use_count) del_timer(&js_timer); + MOD_DEC_USE_COUNT; + + jd->close(jd); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + return 0; +#endif +} + +/* + * js_dump_mem() dumps all data structures in memory. + * It's used for debugging only. + */ + +#if 0 +static void js_dump_mem(void) +{ + + struct js_port *curp = js_port; + struct js_dev *curd = js_dev; + int i; + + printk(",--- Dumping Devices:\n"); + printk("| js_dev = %x\n", (int) js_dev); + + while (curd != NULL) { + printk("| %s-device %x, next %x axes %d, buttons %d, port %x - %#x\n", + curd->next ? "|":"`", + (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port, curd->port->io); + curd = curd->next; + } + + printk(">--- Dumping ports:\n"); + printk("| js_port = %x\n", (int) js_port); + + while (curp != NULL) { + printk("| %s-port %x, next %x, io %#x, devices %d\n", + curp->next ? "|":"`", + (int) curp, (int) curp->next, curp->io, curp->ndevs); + for (i = 0; i < curp->ndevs; i++) { + curd = curp->devs[i]; + if (curd) + printk("| %s %s-device %x, next %x axes %d, buttons %d, port %x\n", + curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`", + (int) curd, (int) curd->next, curd->num_axes, curd->num_buttons, (int) curd->port); + else + printk("| %s %s-device %x, not there\n", + curp->next ? "|":" ", (i < curp->ndevs-1) ? "|":"`", (int) curd); + + } + curp = curp->next; + } + + printk("`--- Done\n"); +} +#endif + + +struct js_port *js_register_port(struct js_port *port, + void *info, int devs, int infos, js_read_func read) +{ + struct js_port **ptrp = &js_port; + struct js_port *curp; + void *all; + int i; + unsigned long flags; + + if ((all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL)) == NULL) + return NULL; + + curp = all; + + curp->next = NULL; + curp->prev = port; + curp->read = read; + curp->ndevs = devs; + + curp->devs = all += sizeof(struct js_port); + for (i = 0; i < devs; i++) curp->devs[i] = NULL; + + curp->axes = all += devs * sizeof(void*); + curp->buttons = (void*) all += devs * sizeof(void*); + curp->corr = all += devs * sizeof(void*); + + if (infos) { + curp->info = all += devs * sizeof(void*); + memcpy(curp->info, info, infos); + } else { + curp->info = NULL; + } + + spin_lock_irqsave(&js_lock, flags); + + while (*ptrp != NULL) ptrp=&((*ptrp)->next); + *ptrp = curp; + + spin_unlock_irqrestore(&js_lock, flags); + + return curp; +} + +struct js_port *js_unregister_port(struct js_port *port) +{ + struct js_port **curp = &js_port; + struct js_port *prev; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*curp != NULL && (*curp != port)) curp = &((*curp)->next); + *curp = (*curp)->next; + + spin_unlock_irqrestore(&js_lock, flags); + + prev = port->prev; + kfree(port); + + return prev; +} + +int js_register_device(struct js_port *port, int number, int axes, int buttons, char *name, + js_ops_func open, js_ops_func close) +{ + struct js_dev **ptrd = &js_dev; + struct js_dev *curd; + void *all; + int i = 0; + unsigned long flags; + + if ((all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + + 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) + + axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL)) == NULL) + return -1; + + curd = all; + + curd->next = NULL; + curd->list = NULL; + curd->port = port; + curd->wait = NULL; + curd->open = open; + curd->close = close; + + curd->ahead = 0; + curd->bhead = 0; + curd->tail = JS_BUFF_SIZE - 1; + curd->num_axes = axes; + curd->num_buttons = buttons; + + curd->cur.axes = all += sizeof(struct js_dev); + curd->cur.buttons = all += axes * sizeof(int); + curd->new.axes = all += (((buttons - 1) >> 5) + 1) * sizeof(int); + curd->new.buttons = all += axes * sizeof(int); + curd->corr = all += (((buttons -1 ) >> 5) + 1) * sizeof(int); + + curd->name = all += axes * sizeof(struct js_corr); + strcpy(curd->name, name); + + port->devs[number] = curd; + port->axes[number] = curd->new.axes; + port->buttons[number] = curd->new.buttons; + port->corr[number] = curd->corr; + + spin_lock_irqsave(&js_lock, flags); + + while (*ptrd != NULL) { ptrd=&(*ptrd)->next; i++; } + *ptrd = curd; + + spin_unlock_irqrestore(&js_lock, flags); + + return i; +} + +void js_unregister_device(struct js_dev *dev) +{ + struct js_dev **curd = &js_dev; + unsigned long flags; + + spin_lock_irqsave(&js_lock, flags); + + while (*curd != NULL && (*curd != dev)) curd = &((*curd)->next); + *curd = (*curd)->next; + + spin_unlock_irqrestore(&js_lock, flags); + + kfree(dev); +} + +/* + * The operations structure. + */ + +static struct file_operations js_fops = +{ + read: js_read, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) + poll: js_poll, +#else + select: js_select, +#endif + ioctl: js_ioctl, + open: js_open, + release: js_release, +}; + +/* + * js_init() registers the driver and calls the probe function. + * also initializes some crucial variables. + */ + +#ifdef MODULE +int init_module(void) +#else +int __init js_init(void) +#endif +{ + int result; + + js_setup_time(); + + if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { + printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); + return -EBUSY; + } + + spin_lock_init(&js_lock); + + init_timer(&js_timer); + js_timer.function = js_do_timer; + js_timer.data = 1; + + memset(&js_comp_glue, 0, sizeof(struct JS_DATA_SAVE_TYPE)); + js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT; + js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT; + +#ifdef MODULE + result = 0; +#else + result = -ENODEV; +#ifdef CONFIG_JOY_LIGHTNING + if (!js_l4_init()) result = 0; +#endif +#ifdef CONFIG_JOY_SIDEWINDER + if (!js_sw_init()) result = 0; +#endif +#ifdef CONFIG_JOY_ASSASIN + if (!js_as_init()) result = 0; +#endif +#ifdef CONFIG_JOY_LOGITECH + if (!js_lt_init()) result = 0; +#endif +#ifdef CONFIG_JOY_THRUSTMASTER + if (!js_tm_init()) result = 0; +#endif +#ifdef CONFIG_JOY_GRAVIS + if (!js_gr_init()) result = 0; +#endif +#ifdef CONFIG_JOY_ANALOG + if (!js_an_init()) result = 0; +#endif +#ifdef CONFIG_JOY_CONSOLE + if (!js_console_init()) result = 0; +#endif +#ifdef CONFIG_JOY_DB9 + if (!js_db9_init()) result = 0; +#endif +#ifdef CONFIG_JOY_AMIGA + if (!js_am_init()) result = 0; +#endif + if (result) printk(KERN_ERR "js: no joysticks found\n"); +#endif + + return result; +} + +/* + * cleanup_module() handles module removal. + */ + +#ifdef MODULE +void cleanup_module(void) +{ + del_timer(&js_timer); + if (unregister_chrdev(JOYSTICK_MAJOR, "js")) + printk(KERN_ERR "js: can't unregister device\n"); +} +#endif diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 8804df949..3eb454663 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -201,7 +201,12 @@ void handle_scancode(unsigned char scancode) add_keyboard_randomness(scancode); tty = ttytab? ttytab[fg_console]: NULL; - kbd = kbd_table + fg_console; + if (tty && (!tty->driver_data)) { + /* This is to workaround ugly bug in tty_io.c, which + does not do locking when it should */ + tty = NULL; + } + kbd = kbd_table + fg_console; if ((raw_mode = (kbd->kbdmode == VC_RAW))) { put_queue(scancode); /* we do not return yet, because we want to maintain diff --git a/drivers/char/lp.c b/drivers/char/lp.c index b8d143fbe..ae769f619 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -16,8 +16,17 @@ * Parport sharing hacking by Andrea Arcangeli * Fixed kernel_(to/from)_user memory copy to check for errors * by Riccardo Facchetti <fizban@tin.it> - * Interrupt handling workaround for printers with buggy handshake - * by Andrea Arcangeli, 11 May 98 + * Redesigned interrupt handling for handle printers with buggy handshake + * by Andrea Arcangeli, 11 May 1998 + * Full efficient handling of printer with buggy irq handshake (now I have + * understood the meaning of the strange handshake). This is done sending new + * characters if the interrupt is just happened, even if the printer say to + * be still BUSY. This is needed at least with Epson Stylus Color. To enable + * the new TRUST_IRQ mode read the `LP OPTIMIZATION' section below... + * Fixed the irq on the rising edge of the strobe case. + * Obsoleted the CAREFUL flag since a printer that doesn' t work with + * CAREFUL will block a bit after in lp_check_status(). + * Andrea Arcangeli, 15 Oct 1998 */ /* This driver should, in theory, work with any parallel port that has an @@ -49,6 +58,74 @@ * # insmod lp.o reset=1 */ +/* + * LP OPTIMIZATIONS + * + * - TRUST_IRQ flag + * + * Epson Stylus Color, HP and many other new printers want the TRUST_IRQ flag + * set when printing with interrupts. This is a long story. Such printers + * use a broken handshake (see the timing graph below) when printing with + * interrupts. The lp driver as default is just able to handle such bogus + * handshake, but setting such flag cause lp to go faster and probably do + * what such printers want (even if not documented). + * + * NOTE that setting the TRUST_IRQ flag in some printer can cause the irq + * printing to fail completly. You must try, to know if your printer + * will handle it. I suggest a graphics printing to force a major flow of + * characters to the printer for do the test. NOTE also that the TRUST_IRQ + * flag _should_ be fine everywhere but there is a lot of buggy hardware out + * there, so I am forced to implement it as a not-default thing. + * WARNING: before to do the test, be sure to have not played with the + * `-w' parameter of tunelp! + * + * Note also that lp automagically warn you (with a KERN_WARNING) if it + * detects that you could _try_ to set the TRUST_IRQ flag to speed up the + * printing and decrease the CPU load. + * + * To set the TRUST_IRQ flag you can use this command: + * + * tunelp /dev/lp? -T on + * + * If you have an old tunelp executable you can (hack and) use this simple + * C lazy proggy to set the flag in the lp driver: + +-------------------------- cut here ------------------------------------- +#include <fcntl.h> +#include <sys/ioctl.h> + +#define LPTRUSTIRQ 0x060f + +int main(int argc, char **argv) +{ + int fd = open("/dev/lp0", O_RDONLY); + ioctl(fd, LPTRUSTIRQ, argc - 1); + if (argc - 1) + printf("trusting the irq\n"); + else + printf("untrusting the irq\n"); + return 0; +} +-------------------------- cut here ------------------------------------- + + * - LP_WAIT time + * + * You can use this setting if your printer is fast enough and/or your + * machine is slow enough ;-). + * + * tunelp /dev/lp? -w 0 + * + * - LP_CHAR tries + * + * If you print with irqs probably you can decrease the CPU load a lot using + * this setting. This is not the default because the printing is reported to + * be jerky somewhere... + * + * tunelp /dev/lp? -c 1 + * + * 11 Nov 1998, Andrea Arcangeli + */ + /* COMPATIBILITY WITH OLD KERNELS * * Under Linux 2.0 and previous versions, lp devices were bound to ports at @@ -79,6 +156,12 @@ * ftp://e-mind.com/pub/linux/pscan/ * * 11 May 98, Andrea Arcangeli + * + * My printer scanner run on an Epson Stylus Color show that such printer + * generates the irq on the _rising_ edge of the STROBE. Now lp handle + * this case fine too. + * + * 15 Oct 1998, Andrea Arcangeli */ #include <linux/module.h> @@ -95,7 +178,6 @@ #include <linux/parport.h> #undef LP_STATS -#undef LP_NEED_CAREFUL #include <linux/lp.h> #include <asm/irq.h> @@ -115,16 +197,14 @@ struct lp_struct lp_table[LP_NO] = NULL, 0, 0, 0} }; -/* Test if printer is ready (and optionally has no error conditions) */ -#ifdef LP_NEED_CAREFUL -#define LP_READY(minor, status) \ - ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : ((status) & LP_PBUSY)) -#define _LP_CAREFUL_READY(status) \ - ((status) & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \ - (LP_PBUSY|LP_PSELECD|LP_PERRORP) -#else -#define LP_READY(minor, status) ((status) & LP_PBUSY) -#endif +/* Test if printer is ready */ +#define LP_READY(status) ((status) & LP_PBUSY) +/* Test if the printer is not acking the strobe */ +#define LP_NO_ACKING(status) ((status) & LP_PACK) +/* Test if the printer has error conditions */ +#define LP_NO_ERROR(status) \ + (((status) & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \ + (LP_PSELECD|LP_PERRORP)) #undef LP_DEBUG #undef LP_READ_DEBUG @@ -162,17 +242,17 @@ static __inline__ void lp_yield (int minor) lp_table[minor].irq_missed = 1; } -static __inline__ void lp_schedule(int minor) +static __inline__ void lp_schedule(int minor, long timeout) { struct pardevice *dev = lp_table[minor].dev; register unsigned long int timeslip = (jiffies - dev->time); if ((timeslip > dev->timeslice) && (dev->port->waithead != NULL)) { lp_parport_release(minor); lp_table[minor].irq_missed = 1; - schedule (); + schedule_timeout(timeout); lp_parport_claim(minor); } else - schedule(); + schedule_timeout(timeout); } static int lp_reset(int minor) @@ -187,60 +267,124 @@ static int lp_reset(int minor) return retval; } +#define lp_wait(minor) udelay(LP_WAIT(minor)) + static inline int lp_char(char lpchar, int minor) { - unsigned int wait = 0; unsigned long count = 0; #ifdef LP_STATS struct lp_stats *stats; #endif + if (signal_pending(current)) + return 0; + for (;;) { + unsigned char status; + int irq_ok = 0; + + /* + * Give a chance to other pardevice to run in the meantime. + */ lp_yield(minor); - if (LP_READY(minor, r_str(minor))) - break; - if (++count == LP_CHAR(minor) || signal_pending(current)) - return 0; + + status = r_str(minor); + if (LP_NO_ERROR(status)) + { + if (LP_READY(status)) + break; + + /* + * This is a crude hack that should be well known + * at least by Epson device driver developers. -arca + */ + irq_ok = (!LP_POLLED(minor) && + LP_NO_ACKING(status) && + lp_table[minor].irq_detected); + if ((LP_F(minor) & LP_TRUST_IRQ) && irq_ok) + break; + } + /* + * NOTE: if you run with irqs you _must_ use + * `tunelp /dev/lp? -c 1' to be rasonable efficient! + */ + if (++count == LP_CHAR(minor)) + { + if (irq_ok) + { + static int first_time = 1; + /* + * The printer is using a buggy handshake, so + * revert to polling to not overload the + * machine and warn the user that its printer + * could get optimized trusting the irq. -arca + */ + lp_table[minor].irq_missed = 1; + if (first_time) + { + first_time = 0; + printk(KERN_WARNING "lp%d: the " + "printing could be optimized " + "using the TRUST_IRQ flag, " + "see the top of " + "linux/drivers/char/lp.c\n", + minor); + } + } + return 0; + } } w_dtr(minor, lpchar); + #ifdef LP_STATS stats = &LP_STAT(minor); stats->chars++; #endif + /* must wait before taking strobe high, and after taking strobe low, according spec. Some printers need it, others don't. */ -#ifndef __sparc__ - while (wait != LP_WAIT(minor)) /* FIXME: should be a udelay() */ - wait++; -#else - udelay(1); -#endif + lp_wait(minor); + /* control port takes strobe high */ - w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE); -#ifndef __sparc__ - while (wait) /* FIXME: should be a udelay() */ - wait--; -#else - udelay(1); -#endif - /* take strobe low */ if (LP_POLLED(minor)) - /* take strobe low */ - w_ctr(minor, LP_PSELECP | LP_PINITP); - else { + w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE); + lp_wait(minor); + w_ctr(minor, LP_PSELECP | LP_PINITP); + } else { + /* + * Epson Stylus Color generate the IRQ on the rising edge of + * strobe so clean the irq's information before playing with + * the strobe. -arca + */ lp_table[minor].irq_detected = 0; lp_table[minor].irq_missed = 0; + /* + * Be sure that the CPU doesn' t reorder instructions. -arca + */ + mb(); + w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PSTROBE | LP_PINTEN); + lp_wait(minor); w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN); } + /* + * Give to the printer a chance to put BUSY low. Really we could + * remove this because we could _guess_ that we are slower to reach + * again lp_char() than the printer to put BUSY low, but I' d like + * to remove this variable from the function I go solve + * when I read bug reports ;-). -arca + */ + lp_wait(minor); + #ifdef LP_STATS /* update waittime statistics */ if (count > stats->maxwait) { #ifdef LP_DEBUG - printk(KERN_DEBUG "lp%d success after %d counts.\n", minor, count); + printk(KERN_DEBUG "lp%d success after %d counts.\n", + minor, count); #endif stats->maxwait = count; } @@ -269,9 +413,8 @@ static void lp_error(int minor) { if (LP_POLLED(minor) || LP_PREEMPTED(minor)) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + LP_TIMEOUT_POLLED; lp_parport_release(minor); - schedule(); + schedule_timeout(LP_TIMEOUT_POLLED); lp_parport_claim(minor); lp_table[minor].irq_missed = 1; } @@ -326,7 +469,10 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) lp_table[minor].irq_detected = 0; lp_table[minor].irq_missed = 1; - w_ctr(minor, LP_PSELECP | LP_PINITP); + if (LP_POLLED(minor)) + w_ctr(minor, LP_PSELECP | LP_PINITP); + else + w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN); do { bytes_written = 0; @@ -381,8 +527,7 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n", minor, lp->runchars, LP_TIME(minor)); #endif current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + LP_TIME(minor); - lp_schedule (minor); + lp_schedule(minor, LP_TIME(minor)); } else { cli(); if (LP_PREEMPTED(minor)) @@ -398,10 +543,7 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) goto lp_polling; } if (!lp_table[minor].irq_detected) - { - current->timeout = jiffies + LP_TIMEOUT_INTERRUPT; - interruptible_sleep_on(&lp->wait_q); - } + interruptible_sleep_on_timeout(&lp->wait_q, LP_TIMEOUT_INTERRUPT); sti(); } } @@ -456,102 +598,97 @@ static int lp_read_nibble(int minor) return (i & 0x0f); } -static inline void lp_select_in_high(int minor) -{ - parport_frob_control(lp_table[minor].dev->port, 8, 8); +static void lp_read_terminate(struct parport *port) { + parport_write_control(port, (parport_read_control(port) & ~2) | 8); + /* SelectIN high, AutoFeed low */ + if (parport_wait_peripheral(port, 0x80, 0)) + /* timeout, SelectIN high, Autofeed low */ + return; + parport_write_control(port, parport_read_control(port) | 2); + /* AutoFeed high */ + parport_wait_peripheral(port, 0x80, 0x80); + /* no timeout possible, Autofeed low, SelectIN high */ + parport_write_control(port, (parport_read_control(port) & ~2) | 8); } /* Status readback confirming to ieee1284 */ static ssize_t lp_read(struct file * file, char * buf, - size_t count, loff_t *ppos) + size_t length, loff_t *ppos) { - unsigned char z=0, Byte=0, status; - char *temp; - ssize_t retval; - unsigned int counter=0; - unsigned int i; + int i; unsigned int minor=MINOR(file->f_dentry->d_inode->i_rdev); - - /* Claim Parport or sleep until it becomes available - */ - lp_parport_claim (minor); + char *temp = buf; + ssize_t count = 0; + unsigned char z = 0; + unsigned char Byte = 0; + struct parport *port = lp_table[minor].dev->port; - temp=buf; -#ifdef LP_READ_DEBUG - printk(KERN_INFO "lp%d: read mode\n", minor); -#endif + lp_parport_claim (minor); - retval = verify_area(VERIFY_WRITE, buf, count); - if (retval) - return retval; - if (parport_ieee1284_nibble_mode_ok(lp_table[minor].dev->port, 0)==0) { -#ifdef LP_READ_DEBUG - printk(KERN_INFO "lp%d: rejected IEEE1284 negotiation.\n", - minor); -#endif - lp_select_in_high(minor); - parport_release(lp_table[minor].dev); - return temp-buf; /* End of file */ + switch (parport_ieee1284_nibble_mode_ok(port, 0)) + { + case 0: + /* Handshake failed. */ + lp_read_terminate(port); + lp_parport_release (minor); + return -EIO; + case 1: + /* No data. */ + lp_read_terminate(port); + lp_parport_release (minor); + return 0; + default: + /* Data available. */ + + /* Hack: Wait 10ms (between events 6 and 7) */ + schedule_timeout((HZ+99)/100); + break; } - for (i=0; i<=(count*2); i++) { - parport_frob_control(lp_table[minor].dev->port, 2, 2); /* AutoFeed high */ - do { - status = (r_str(minor) & 0x40); - udelay(50); - counter++; - if (current->need_resched) - schedule (); - } while ((status == 0x40) && (counter < 20)); - if (counter == 20) { - /* Timeout */ + + for (i=0; ; i++) { + parport_frob_control(port, 2, 2); /* AutoFeed high */ + if (parport_wait_peripheral(port, 0x40, 0)) { #ifdef LP_READ_DEBUG - printk(KERN_DEBUG "lp_read: (Autofeed high) timeout\n"); + /* Some peripherals just time out when they've sent + all their data. */ + printk("%s: read1 timeout.\n", port->name); #endif - parport_frob_control(lp_table[minor].dev->port, 2, 0); - lp_select_in_high(minor); - parport_release(lp_table[minor].dev); - return temp-buf; /* end the read at timeout */ + parport_frob_control(port, 2, 0); /* AutoFeed low */ + break; } - counter=0; z = lp_read_nibble(minor); - parport_frob_control(lp_table[minor].dev->port, 2, 0); /* AutoFeed low */ - do { - status=(r_str(minor) & 0x40); - udelay(20); - counter++; - if (current->need_resched) - schedule (); - } while ( (status == 0) && (counter < 20) ); - if (counter == 20) { /* Timeout */ -#ifdef LP_READ_DEBUG - printk(KERN_DEBUG "lp_read: (Autofeed low) timeout\n"); -#endif - if (signal_pending(current)) { - lp_select_in_high(minor); - parport_release(lp_table[minor].dev); - if (temp !=buf) - return temp-buf; - else - return -EINTR; - } - current->state=TASK_INTERRUPTIBLE; - current->timeout=jiffies + LP_TIME(minor); - schedule (); + parport_frob_control(port, 2, 0); /* AutoFeed low */ + if (parport_wait_peripheral(port, 0x40, 0x40)) { + printk("%s: read2 timeout.\n", port->name); + break; } + if ((i & 1) != 0) { + Byte |= (z<<4); + if (temp) { + if (__put_user (Byte, temp)) + { + count = -EFAULT; + temp = NULL; + } else { + temp++; - counter=0; - - if (( i & 1) != 0) { - Byte= (Byte | z<<4); - if (put_user(Byte, (char *)temp)) - return -EFAULT; - temp++; - } else Byte=z; + if (++count == length) + temp = NULL; + } + } + /* Does the error line indicate end of data? */ + if ((parport_read_status(port) & LP_PERRORP) == + LP_PERRORP) + break; + } else + Byte=z; } - lp_select_in_high(minor); - lp_parport_release(minor); - return temp-buf; + lp_read_terminate(port); + + lp_parport_release (minor); + + return count; } #endif @@ -649,7 +786,7 @@ static int lp_ioctl(struct inode *inode, struct file *file, else LP_F(minor) &= ~LP_ABORTOPEN; break; -#ifdef LP_NEED_CAREFUL +#ifdef OBSOLETED case LPCAREFUL: if (arg) LP_F(minor) |= LP_CAREFUL; @@ -657,6 +794,12 @@ static int lp_ioctl(struct inode *inode, struct file *file, LP_F(minor) &= ~LP_CAREFUL; break; #endif + case LPTRUSTIRQ: + if (arg) + LP_F(minor) |= LP_TRUST_IRQ; + else + LP_F(minor) &= ~LP_TRUST_IRQ; + break; case LPWAIT: LP_WAIT(minor) = arg; break; @@ -700,7 +843,6 @@ static int lp_ioctl(struct inode *inode, struct file *file, return retval; } - static struct file_operations lp_fops = { lp_lseek, #ifdef CONFIG_PRINTER_READBACK @@ -768,7 +910,7 @@ int lp_register(int nr, struct parport *port) lp_table[nr].dev = parport_register_device(port, "lp", lp_preempt, NULL, lp_interrupt, - PARPORT_DEV_TRAN, + 0, (void *) &lp_table[nr]); if (lp_table[nr].dev == NULL) return 1; diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c index dc0300751..2660c2ca2 100644 --- a/drivers/char/lp_m68k.c +++ b/drivers/char/lp_m68k.c @@ -477,7 +477,7 @@ EXPORT_SYMBOL(lp_interrupt); EXPORT_SYMBOL(register_parallel); EXPORT_SYMBOL(unregister_parallel); -__initfunc(int lp_init(void)) +__initfunc(int lp_m68k_init(void)) { extern char m68k_debug_device[]; @@ -517,12 +517,12 @@ __initfunc(void lp_setup(char *str, int *ints)) #ifdef MODULE int init_module(void) { -return lp_init(); + return lp_m68k_init(); } void cleanup_module(void) { -unregister_chrdev(LP_MAJOR, "lp"); + unregister_chrdev(LP_MAJOR, "lp"); } #endif diff --git a/drivers/char/mac_SCC.c b/drivers/char/mac_SCC.c index 0295ab4b4..159a8336e 100644 --- a/drivers/char/mac_SCC.c +++ b/drivers/char/mac_SCC.c @@ -67,6 +67,7 @@ #include <asm/system.h> #include <asm/segment.h> #include <asm/bitops.h> +#include <asm/hwtest.h> #include "mac_SCC.h" @@ -1300,6 +1301,12 @@ static void probe_sccs(void) /* testing: fix up broken 24 bit addresses (ClassicII) */ if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase) mac_bi_data.sccbase |= 0x50000000; + + if ( !hwreg_present((void *)mac_bi_data.sccbase)) + { + printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n"); + return; + } for(n=0;n<2;n++) { @@ -1459,11 +1466,7 @@ int mac_SCC_init(void) printk("Mac68K Z8530 serial driver version 1.01\n"); /* SCC present at all? */ - if (MACH_IS_ATARI || MACH_IS_AMIGA -#if 0 - || !(MACHW_PRESENT(SCC) || MACHW_PRESENT(ST_ESCC)) -#endif - ) + if (!MACH_IS_MAC) return( -ENODEV ); if (zs_chain == 0) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index b8ff8c980..f4cd55fdd 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -35,6 +35,9 @@ void soundcard_init(void); void dmasound_init(void); #endif #endif +#ifdef CONFIG_SPARCAUDIO +extern int sparcaudio_init(void); +#endif #ifdef CONFIG_ISDN int isdn_init(void); #endif @@ -44,6 +47,15 @@ extern int videodev_init(void); #ifdef CONFIG_FB extern void fbmem_init(void); #endif +#ifdef CONFIG_PROM_CONSOLE +extern void prom_con_init(void); +#endif +#ifdef CONFIG_MDA_CONSOLE +extern void mda_console_init(void); +#endif +#if defined(CONFIG_PPC) || defined(CONFIG_MAC) +extern void adbdev_init(void); +#endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, const char * buf, size_t count, loff_t *ppos) @@ -500,9 +512,11 @@ static int memory_open(struct inode * inode, struct file * filp) case 3: filp->f_op = &null_fops; break; +#if !defined(CONFIG_PPC) && !defined(__mc68000__) case 4: filp->f_op = &port_fops; break; +#endif case 5: filp->f_op = &zero_fops; break; @@ -545,20 +559,20 @@ __initfunc(int chr_dev_init(void)) #if defined (CONFIG_FB) fbmem_init(); #endif +#if defined (CONFIG_PROM_CONSOLE) + prom_con_init(); +#endif +#if defined (CONFIG_MDA_CONSOLE) + mda_console_init(); +#endif tty_init(); #ifdef CONFIG_PRINTER lp_init(); #endif -#if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \ - defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \ - defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \ - defined (CONFIG_AMIGAMOUSE) || defined (CONFIG_ATARIMOUSE) || \ - defined (CONFIG_MACMOUSE) || defined (CONFIG_PCWATCHDOG) || \ - defined (CONFIG_APM) || defined (CONFIG_RTC) || \ - defined (CONFIG_SGI_DS1286) || defined (CONFIG_SUN_MOUSE) || \ - defined (CONFIG_NVRAM) - misc_init(); +#ifdef CONFIG_M68K_PRINTER + lp_m68k_init(); #endif + misc_init(); #ifdef CONFIG_SOUND soundcore_init(); #ifdef CONFIG_SOUND_OSS @@ -568,6 +582,9 @@ __initfunc(int chr_dev_init(void)) dmasound_init(); #endif #endif +#ifdef CONFIG_SPARCAUDIO + sparcaudio_init(); +#endif #ifdef CONFIG_JOYSTICK /* * Some joysticks only appear when the sound card they are @@ -587,6 +604,9 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_VIDEO_BT848 i2c_init(); #endif +#if defined(CONFIG_PPC) || defined(CONFIG_MAC) + adbdev_init(); +#endif #ifdef CONFIG_VIDEO_DEV videodev_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 8f3968639..a961a1a4d 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -31,8 +31,8 @@ Cyrus Durgin <cider@speakeasy.org> */ -#include <linux/config.h> #include <linux/module.h> +#include <linux/config.h> #include <linux/fs.h> #include <linux/errno.h> @@ -64,15 +64,12 @@ static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list } #define DYNAMIC_MINORS 64 /* like dynamic majors */ static unsigned char misc_minors[DYNAMIC_MINORS / 8]; -#ifndef MODULE -extern int adbdev_init(void); extern int bus_mouse_init(void); -extern int psaux_init(void); +extern int qpmouse_init(void); extern int ms_bus_mouse_init(void); extern int atixl_busmouse_init(void); extern int amiga_mouse_init(void); extern int atari_mouse_init(void); -extern int mac_mouse_init(void); extern int sun_mouse_init(void); extern int adb_mouse_init(void); extern void gfx_register(void); @@ -104,7 +101,6 @@ static int misc_read_proc(char *buf, char **start, off_t offset, } #endif /* PROC_FS */ -#endif /* !MODULE */ static int misc_open(struct inode * inode, struct file * file) { @@ -160,9 +156,13 @@ int misc_register(struct miscdevice * misc) } if (misc->minor < DYNAMIC_MINORS) misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); - MOD_INC_USE_COUNT; - misc->next = &misc_list; - misc->prev = misc_list.prev; + + /* + * Add it to the front, so that later devices can "override" + * earlier defaults + */ + misc->prev = &misc_list; + misc->next = misc_list.next; misc->prev->next = misc; misc->next->prev = misc; return 0; @@ -173,7 +173,6 @@ int misc_deregister(struct miscdevice * misc) int i = misc->minor; if (!misc->next || !misc->prev) return -EINVAL; - MOD_DEC_USE_COUNT; misc->prev->next = misc->next; misc->next->prev = misc->prev; misc->next = NULL; @@ -184,40 +183,25 @@ int misc_deregister(struct miscdevice * misc) return 0; } -#ifdef MODULE - -#define misc_init init_module - -void cleanup_module(void) -{ - unregister_chrdev(MISC_MAJOR, "misc"); -} - -#endif - EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); -#if defined(CONFIG_PROC_FS) && !defined(MODULE) +#if defined(CONFIG_PROC_FS) static struct proc_dir_entry *proc_misc; #endif -__initfunc(int misc_init(void)) +int __init misc_init(void) { -#ifndef MODULE #ifdef CONFIG_PROC_FS proc_misc = create_proc_entry("misc", 0, 0); if (proc_misc) proc_misc->read_proc = misc_read_proc; #endif /* PROC_FS */ -#ifdef CONFIG_MAC - adbdev_init(); -#endif #ifdef CONFIG_BUSMOUSE bus_mouse_init(); #endif -#if defined CONFIG_PSMOUSE - psaux_init(); +#if defined CONFIG_82C710_MOUSE + qpmouse_init(); #endif #ifdef CONFIG_MS_BUSMOUSE ms_bus_mouse_init(); @@ -231,13 +215,10 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_ATARIMOUSE atari_mouse_init(); #endif -#ifdef CONFIG_MACMOUSE - mac_mouse_init(); -#endif #ifdef CONFIG_SUN_MOUSE sun_mouse_init(); #endif -#ifdef CONFIG_MACMOUSE +#ifdef CONFIG_ADBMOUSE adb_mouse_init(); #endif #ifdef CONFIG_PC110_PAD @@ -267,6 +248,12 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_H8 h8_init(); #endif +#ifdef CONFIG_MVME16x + rtc_MK48T08_init(); +#endif +#ifdef CONFIG_BVME6000 + rtc_DP8570A_init(); +#endif #if defined(CONFIG_RTC) || defined(CONFIG_SUN_MOSTEK_RTC) rtc_init(); #endif @@ -295,7 +282,6 @@ __initfunc(int misc_init(void)) gfx_register (); streamable_init (); #endif -#endif /* !MODULE */ if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index 6068ff3d7..651d5b733 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -554,8 +554,7 @@ static int msp3400c_thread(void *data) UNLOCK_I2C_BUS(msp->bus); current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ/25; - schedule(); + schedule_timeout(HZ/25); if (signal_pending(current)) goto done; if (msp->restart) { @@ -589,8 +588,7 @@ static int msp3400c_thread(void *data) UNLOCK_I2C_BUS(msp->bus); current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ/25; - schedule(); + schedule_timeout(HZ/25); if (signal_pending(current)) goto done; if (msp->restart) { @@ -715,8 +713,7 @@ static int msp3410d_thread(void *data) /* wait 1 sec */ current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ; - schedule(); + schedule_timeout(HZ); if (signal_pending(current)) goto done; if (msp->restart) { diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 07272723d..5456640cb 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -419,7 +419,7 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty) char buf[64]; tty->num_overrun++; - if (tty->overrun_time < (jiffies - HZ)) { + if (time_before(tty->overrun_time, jiffies - HZ)) { printk("%s: %d input overrun(s)\n", tty_name(tty, buf), tty->num_overrun); tty->overrun_time = jiffies; @@ -872,6 +872,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, int minimum, time; ssize_t retval = 0; ssize_t size; + long timeout; do_it_again: @@ -900,7 +901,7 @@ do_it_again: } minimum = time = 0; - current->timeout = (unsigned long) -1; + timeout = MAX_SCHEDULE_TIMEOUT; if (!tty->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); @@ -911,9 +912,9 @@ do_it_again: (tty->minimum_to_wake > minimum)) tty->minimum_to_wake = minimum; } else { - current->timeout = 0; + timeout = 0; if (time) { - current->timeout = time + jiffies; + timeout = time; time = 0; } tty->minimum_to_wake = minimum = 1; @@ -949,7 +950,7 @@ do_it_again: } if (tty_hung_up_p(file)) break; - if (!current->timeout) + if (!timeout) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; @@ -960,7 +961,7 @@ do_it_again: break; } enable_bh(TQUEUE_BH); - schedule(); + timeout = schedule_timeout(timeout); disable_bh(TQUEUE_BH); continue; } @@ -1021,7 +1022,7 @@ do_it_again: if (b - buf >= minimum) break; if (time) - current->timeout = time + jiffies; + timeout = time; } enable_bh(TQUEUE_BH); remove_wait_queue(&tty->read_wait, &wait); @@ -1030,7 +1031,6 @@ do_it_again: tty->minimum_to_wake = minimum; current->state = TASK_RUNNING; - current->timeout = 0; size = b - buf; if (size) { retval = size; diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index faebe09c3..a795cf21c 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -5,57 +5,116 @@ * See keyboard.c for the whole history. * * Major cleanup by Martin Mares, May 1997 + * + * Combined the keyboard and PS/2 mouse handling into one file, + * because they share the same hardware. + * Johan Myreen <jem@iki.fi> 1998-10-08. + * */ #include <linux/config.h> +#include <asm/spinlock.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/mm.h> #include <linux/signal.h> -#include <linux/ioport.h> #include <linux/init.h> #include <linux/kbd_ll.h> #include <linux/delay.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/malloc.h> #include <asm/keyboard.h> #include <asm/bitops.h> -#include <asm/io.h> +#include <asm/uaccess.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/irq.h> /* Some configuration switches are present in the include file... */ -#include "pc_keyb.h" +#include <linux/pc_keyb.h> + +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pckbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif -unsigned char pckbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */ +static void kbd_write_command_w(int data); +static void kbd_write_output_w(int data); + +spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; /* used only by send_data - set by keyboard_interrupt */ static volatile unsigned char reply_expected = 0; static volatile unsigned char acknowledge = 0; static volatile unsigned char resend = 0; + +#if defined CONFIG_PSMOUSE /* - * Wait for keyboard controller input buffer is empty. + * PS/2 Auxiliary Device + */ + +static int __init psaux_init(void); + +static struct aux_queue *queue; /* Mouse data buffer. */ +static int aux_count = 0; + +#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) +#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) + +#define MAX_RETRIES 60 /* some aux operations take long time*/ +#endif /* CONFIG_PSMOUSE */ + +/* + * Wait for keyboard controller input buffer is empty. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. * - * Don't use 'jiffies' so that we don't depend on - * interrupts.. + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be written only when + * the input-buffer-full bit and output-buffer-full bit in the + * Controller Status register are set 0." */ static inline void kb_wait(void) { unsigned long timeout = KBC_TIMEOUT; + unsigned char status; do { - if (! (kbd_read_status() & KBD_STAT_IBF)) + status = kbd_read_status(); + kbd_pause(); + if (status & KBD_STAT_OBF) { + if (status & KBD_STAT_MOUSE_OBF) + kbd_read_input(); /* Flush */ + kbd_pause(); + } + + status = kbd_read_status(); + kbd_pause(); + if (!(status & KBD_STAT_IBF)) return; mdelay(1); timeout--; } while (timeout); #ifdef KBD_REPORT_TIMEOUTS - printk(KERN_WARNING "Keyboard timed out\n"); + printk(KERN_WARNING "Keyboard timed out[1]\n"); #endif } @@ -202,7 +261,7 @@ static inline void send_cmd(unsigned char c) kbd_write_command(c); } -#define disable_keyboard() do { send_cmd(KBD_CCMD_KBD_DISABLE); kb_wait(); } while (0) +#define disable_keyboard() send_cmd(KBD_CCMD_KBD_DISABLE) #define enable_keyboard() send_cmd(KBD_CCMD_KBD_ENABLE) #else #define disable_keyboard() /* nothing */ @@ -355,33 +414,46 @@ char pckbd_unexpected_up(unsigned char keycode) void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + unsigned long flags; unsigned char status; - kbd_pt_regs = regs; + spin_lock_irqsave(&kbd_controller_lock, flags); disable_keyboard(); + kbd_pt_regs = regs; status = kbd_read_status(); - do { + while (status & KBD_STAT_OBF) { unsigned char scancode; - /* mouse data? */ - if (status & pckbd_read_mask & KBD_STAT_MOUSE_OBF) { -#if defined(CONFIG_SGI) && defined(CONFIG_PSMOUSE) - scancode = kbd_read_input(); - aux_interrupt(status, scancode); + scancode = kbd_read_input(); + + if (status & KBD_STAT_MOUSE_OBF) { +#ifdef CONFIG_PSMOUSE + /* Mouse data. */ + if (aux_count) { + int head = queue->head; + queue->buf[head] = scancode; + add_mouse_randomness(scancode); + head = (head + 1) & (AUX_BUF_SIZE-1); + if (head != queue->tail) { + queue->head = head; + if (queue->fasync) + kill_fasync(queue->fasync, SIGIO); + wake_up_interruptible(&queue->proc_list); + } + } #endif - break; + } else { + if (do_acknowledge(scancode)) + handle_scancode(scancode); + mark_bh(KEYBOARD_BH); } - scancode = kbd_read_input(); - if ((status & KBD_STAT_OBF) && do_acknowledge(scancode)) - handle_scancode(scancode); - status = kbd_read_status(); - } while (status & KBD_STAT_OBF); + } - mark_bh(KEYBOARD_BH); enable_keyboard(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); } /* @@ -398,12 +470,10 @@ static int send_data(unsigned char data) do { unsigned long timeout = KBD_TIMEOUT; - kb_wait(); - acknowledge = 0; + acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */ resend = 0; reply_expected = 1; - kbd_write_output(data); - kbd_pause(); + kbd_write_output_w(data); for (;;) { if (acknowledge) return 1; @@ -412,7 +482,7 @@ static int send_data(unsigned char data) mdelay(1); if (!--timeout) { #ifdef KBD_REPORT_TIMEOUTS - printk(KERN_WARNING "Keyboard timeout\n"); + printk(KERN_WARNING "Keyboard timeout[2]\n"); #endif return 0; } @@ -493,25 +563,39 @@ static int __init kbd_wait_for_input(void) return -1; } -static void __init kbd_write_command_w(int data) +static void kbd_write_command_w(int data) { - int status; + unsigned long flags; - do { - status = kbd_read_status(); - } while (status & KBD_STAT_IBF); + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); kbd_write_command(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); } -static void __init kbd_write_output_w(int data) +static void kbd_write_output_w(int data) { - int status; + unsigned long flags; - do { - status = kbd_read_status(); - } while (status & KBD_STAT_IBF); + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); kbd_write_output(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +#if defined CONFIG_PSMOUSE +static void kbd_write_cmd(int cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MODE); + kb_wait(); + kbd_write_output(cmd); + spin_unlock_irqrestore(&kbd_controller_lock, flags); } +#endif /* CONFIG_PSMOUSE */ static char * __init initialize_kbd(void) { @@ -614,7 +698,7 @@ static char * __init initialize_kbd(void) void __init pckbd_init_hw(void) { - keyboard_setup(); + kbd_request_region(); /* Flush any pending input. */ kbd_clear_input(); @@ -624,4 +708,226 @@ void __init pckbd_init_hw(void) if (msg) printk(KERN_WARNING "initialize_kbd: %s\n", msg); } + +#if defined CONFIG_PSMOUSE + psaux_init(); +#endif + + /* Ok, finally allocate the IRQ, and off we go.. */ + kbd_request_irq(keyboard_interrupt); +} + +#if defined CONFIG_PSMOUSE +/* + * Send a byte to the mouse. + */ +static void aux_write_dev(int val) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MOUSE); + kb_wait(); + kbd_write_output(val); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +static unsigned int get_from_queue(void) +{ + unsigned int result; + unsigned long flags; + + save_flags(flags); + cli(); + result = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); + restore_flags(flags); + return result; +} + + +static inline int queue_empty(void) +{ + return queue->head == queue->tail; +} + +static int fasync_aux(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &queue->fasync); + if (retval < 0) + return retval; + return 0; +} + + +static int release_aux(struct inode * inode, struct file * file) +{ + fasync_aux(-1, file, 0); + if (--aux_count) + return 0; + kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ + kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE); + aux_free_irq(inode); + return 0; +} + +/* + * Install interrupt handler. + * Enable auxiliary device. + */ + +static int open_aux(struct inode * inode, struct file * file) +{ + if (aux_count++) { + return 0; + } + queue->head = queue->tail = 0; /* Flush input queue */ + if (aux_request_irq(keyboard_interrupt, inode)) { + aux_count--; + return -EBUSY; + } + kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable the + auxiliary port on + controller. */ + aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ + kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */ + + return 0; } + +/* + * Put bytes from input queue to buffer. + */ + +static ssize_t read_aux(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + struct wait_queue wait = { current, NULL }; + ssize_t i = count; + unsigned char c; + + if (queue_empty()) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + add_wait_queue(&queue->proc_list, &wait); +repeat: + current->state = TASK_INTERRUPTIBLE; + if (queue_empty() && !signal_pending(current)) { + schedule(); + goto repeat; + } + current->state = TASK_RUNNING; + remove_wait_queue(&queue->proc_list, &wait); + } + while (i > 0 && !queue_empty()) { + c = get_from_queue(); + put_user(c, buffer++); + i--; + } + if (count-i) { + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + return count-i; + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* + * Write to the aux device. + */ + +static ssize_t write_aux(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + ssize_t retval = 0; + + if (count) { + ssize_t written = 0; + + if (count > 32) + count = 32; /* Limit to 32 bytes. */ + do { + char c; + get_user(c, buffer++); + aux_write_dev(c); + written++; + } while (--count); + retval = -EIO; + if (written) { + retval = written; + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + } + } + + return retval; +} + +static unsigned int aux_poll(struct file *file, poll_table * wait) +{ + poll_wait(file, &queue->proc_list, wait); + if (!queue_empty()) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations psaux_fops = { + NULL, /* seek */ + read_aux, + write_aux, + NULL, /* readdir */ + aux_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + open_aux, + NULL, /* flush */ + release_aux, + NULL, + fasync_aux, +}; + +/* + * Initialize driver. + */ +static struct miscdevice psaux_mouse = { + PSMOUSE_MINOR, "psaux", &psaux_fops +}; + +static int __init psaux_init(void) +{ +#if 0 + /* + * Don't bother with the BIOS flag: even if we don't have + * a mouse connected at bootup we may still want to connect + * one later, and we don't want to just let the BIOS tell + * us that it has no mouse.. + */ + if (aux_device_present != 0xaa) + return -EIO; + + printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n"); +#endif + misc_register(&psaux_mouse); + queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + memset(queue, 0, sizeof(*queue)); + queue->head = queue->tail = 0; + queue->proc_list = NULL; + +#ifdef INITIALIZE_MOUSE + kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */ + aux_write_dev(AUX_SET_SAMPLE); + aux_write_dev(100); /* 100 samples/sec */ + aux_write_dev(AUX_SET_RES); + aux_write_dev(3); /* 8 counts per mm */ + aux_write_dev(AUX_SET_SCALE21); /* 2:1 scaling */ +#endif /* INITIALIZE_MOUSE */ + kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */ + kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */ + + return 0; +} + +#endif /* CONFIG_PSMOUSE */ diff --git a/drivers/char/pc_keyb.h b/drivers/char/pc_keyb.h deleted file mode 100644 index 0a6df026c..000000000 --- a/drivers/char/pc_keyb.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * linux/drivers/char/pc_keyb.h - * - * PC Keyboard And Keyboard Controller - * - * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> - */ - -/* - * Configuration Switches - */ - -#undef KBD_REPORT_ERR /* Report keyboard errors */ -#define KBD_REPORT_UNKN /* Report unknown scan codes */ -#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ -#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ - -#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */ -#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ -#define KBD_TIMEOUT 1000 /* Timeout in ms for keyboard command acknowledge */ - -/* - * Internal variables of the driver - */ - -extern unsigned char pckbd_read_mask; -extern unsigned char aux_device_present; - -/* - * Keyboard Controller Registers - */ - -#define KBD_STATUS_REG 0x64 /* Status register (R) */ -#define KBD_CNTL_REG 0x64 /* Controller command register (W) */ -#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ - -/* - * Keyboard Controller Commands - */ - -#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ -#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ -#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ -#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ -#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ -#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ -#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ -#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ -#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ -#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ -#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ - -/* - * Keyboard Commands - */ - -#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ -#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ -#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ -#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */ -#define KBD_CMD_RESET 0xFF /* Reset */ - -/* - * Keyboard Replies - */ - -#define KBD_REPLY_POR 0xAA /* Power on reset */ -#define KBD_REPLY_ACK 0xFA /* Command ACK */ -#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ - -/* - * Status Register Bits - */ - -#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ -#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ -#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ -#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ -#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ -#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ -#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ -#define KBD_STAT_PERR 0x80 /* Parity error */ - -#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF) - -/* - * Controller Mode Register Bits - */ - -#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ -#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ -#define KBD_MODE_SYS 0x04 /* The system flag (?) */ -#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ -#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ -#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ -#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ -#define KBD_MODE_RFU 0x80 - -/* - * Mouse Commands - */ - -#define AUX_SET_RES 0xE8 /* Set resolution */ -#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ -#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ -#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ -#define AUX_SET_STREAM 0xEA /* Set stream mode */ -#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ -#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ -#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ -#define AUX_RESET 0xFF /* Reset aux device */ diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index 9fe8bcc23..b1c1bb3e3 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -633,8 +633,7 @@ static void pcxe_close(struct tty_struct * tty, struct file * filp) if(info->blocked_open) { if(info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } diff --git a/drivers/char/pms.c b/drivers/char/pms.c index 1b2da0bb7..8886b59be 100644 --- a/drivers/char/pms.c +++ b/drivers/char/pms.c @@ -77,7 +77,7 @@ extern __inline__ u8 mvv_read(u8 index) return inb(data_port); } -extern int i2c_stat(u8 slave) +static int pms_i2c_stat(u8 slave) { int counter; int i; @@ -116,7 +116,7 @@ extern int i2c_stat(u8 slave) return inb(data_port); } -int i2c_write(u16 slave, u16 sub, u16 data) +static int pms_i2c_write(u16 slave, u16 sub, u16 data) { int skip=0; int count; @@ -166,7 +166,7 @@ int i2c_write(u16 slave, u16 sub, u16 data) return count; } -int i2c_read(int slave, int sub) +static int pms_i2c_read(int slave, int sub) { int i=0; for(i=0;i<i2c_count;i++) @@ -178,13 +178,13 @@ int i2c_read(int slave, int sub) } -void i2c_andor(int slave, int sub, int and, int or) +static void pms_i2c_andor(int slave, int sub, int and, int or) { u8 tmp; - tmp=i2c_read(slave, sub); + tmp=pms_i2c_read(slave, sub); tmp = (tmp&and)|or; - i2c_write(slave, sub, tmp); + pms_i2c_write(slave, sub, tmp); } /* @@ -202,13 +202,13 @@ static void pms_hue(short hue) switch(decoder) { case MOTOROLA: - i2c_write(0x8A, 0x00, hue); + pms_i2c_write(0x8A, 0x00, hue); break; case PHILIPS2: - i2c_write(0x8A, 0x07, hue); + pms_i2c_write(0x8A, 0x07, hue); break; case PHILIPS1: - i2c_write(0x42, 0x07, hue); + pms_i2c_write(0x42, 0x07, hue); break; } } @@ -218,10 +218,10 @@ static void pms_colour(short colour) switch(decoder) { case MOTOROLA: - i2c_write(0x8A, 0x00, colour); + pms_i2c_write(0x8A, 0x00, colour); break; case PHILIPS1: - i2c_write(0x42, 012, colour); + pms_i2c_write(0x42, 012, colour); break; } } @@ -232,10 +232,10 @@ static void pms_contrast(short contrast) switch(decoder) { case MOTOROLA: - i2c_write(0x8A, 0x00, contrast); + pms_i2c_write(0x8A, 0x00, contrast); break; case PHILIPS1: - i2c_write(0x42, 0x13, contrast); + pms_i2c_write(0x42, 0x13, contrast); break; } } @@ -245,12 +245,12 @@ static void pms_brightness(short brightness) switch(decoder) { case MOTOROLA: - i2c_write(0x8A, 0x00, brightness); - i2c_write(0x8A, 0x00, brightness); - i2c_write(0x8A, 0x00, brightness); + pms_i2c_write(0x8A, 0x00, brightness); + pms_i2c_write(0x8A, 0x00, brightness); + pms_i2c_write(0x8A, 0x00, brightness); break; case PHILIPS1: - i2c_write(0x42, 0x19, brightness); + pms_i2c_write(0x42, 0x19, brightness); break; } } @@ -271,20 +271,20 @@ static void pms_format(short format) switch(format) { case 0: /* Auto */ - i2c_andor(target, 0x0D, 0xFE,0x00); - i2c_andor(target, 0x0F, 0x3F,0x80); + pms_i2c_andor(target, 0x0D, 0xFE,0x00); + pms_i2c_andor(target, 0x0F, 0x3F,0x80); break; case 1: /* NTSC */ - i2c_andor(target, 0x0D, 0xFE, 0x00); - i2c_andor(target, 0x0F, 0x3F, 0x40); + pms_i2c_andor(target, 0x0D, 0xFE, 0x00); + pms_i2c_andor(target, 0x0F, 0x3F, 0x40); break; case 2: /* PAL */ - i2c_andor(target, 0x0D, 0xFE, 0x00); - i2c_andor(target, 0x0F, 0x3F, 0x00); + pms_i2c_andor(target, 0x0D, 0xFE, 0x00); + pms_i2c_andor(target, 0x0F, 0x3F, 0x00); break; case 3: /* SECAM */ - i2c_andor(target, 0x0D, 0xFE, 0x01); - i2c_andor(target, 0x0F, 0x3F, 0x00); + pms_i2c_andor(target, 0x0D, 0xFE, 0x01); + pms_i2c_andor(target, 0x0F, 0x3F, 0x00); break; } } @@ -302,12 +302,12 @@ static void pms_hstart(short start) switch(decoder) { case PHILIPS1: - i2c_write(0x8A, 0x05, start); - i2c_write(0x8A, 0x18, start); + pms_i2c_write(0x8A, 0x05, start); + pms_i2c_write(0x8A, 0x18, start); break; case PHILIPS2: - i2c_write(0x42, 0x05, start); - i2c_write(0x42, 0x18, start); + pms_i2c_write(0x42, 0x05, start); + pms_i2c_write(0x42, 0x18, start); break; } } @@ -319,94 +319,94 @@ static void pms_hstart(short start) static void pms_bandpass(short pass) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4); + pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4); + pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4); } static void pms_antisnow(short snow) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2); + pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2); + pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2); } static void pms_sharpness(short sharp) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03); + pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x06, 0xFC, sharp&0x03); + pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03); } static void pms_chromaagc(short agc) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5); + pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5); + pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5); } static void pms_vertnoise(short noise) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x10, 0xFC, noise&3); + pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x10, 0xFC, noise&3); + pms_i2c_andor(0x42, 0x10, 0xFC, noise&3); } static void pms_forcecolour(short colour) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7); + pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7); + pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7); } static void pms_antigamma(short gamma) { if(decoder==PHILIPS2) - i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7); + pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7); + pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7); } static void pms_prefilter(short filter) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6); + pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6); + pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6); } static void pms_hfilter(short filter) { if(decoder==PHILIPS2) - i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5); + pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5); + pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5); } static void pms_vfilter(short filter) { if(decoder==PHILIPS2) - i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5); + pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5); + pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5); } static void pms_killcolour(short colour) { if(decoder==PHILIPS2) { - i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3); - i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3); + pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3); + pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3); } else if(decoder==PHILIPS1) { - i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3); - i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3); + pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3); + pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3); } } @@ -414,11 +414,11 @@ static void pms_chromagain(short chroma) { if(decoder==PHILIPS2) { - i2c_write(0x8A, 0x11, chroma); + pms_i2c_write(0x8A, 0x11, chroma); } else if(decoder==PHILIPS1) { - i2c_write(0x42, 0x11, chroma); + pms_i2c_write(0x42, 0x11, chroma); } } @@ -444,9 +444,9 @@ static void pms_vstart(short start) static void pms_secamcross(short cross) { if(decoder==PHILIPS2) - i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); + pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); else if(decoder==PHILIPS1) - i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); + pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); } @@ -454,13 +454,13 @@ static void pms_swsense(short sense) { if(decoder==PHILIPS2) { - i2c_write(0x8A, 0x0A, sense); - i2c_write(0x8A, 0x0B, sense); + pms_i2c_write(0x8A, 0x0A, sense); + pms_i2c_write(0x8A, 0x0B, sense); } else if(decoder==PHILIPS1) { - i2c_write(0x42, 0x0A, sense); - i2c_write(0x42, 0x0B, sense); + pms_i2c_write(0x42, 0x0A, sense); + pms_i2c_write(0x42, 0x0B, sense); } } @@ -613,9 +613,9 @@ static void pms_resolution(short width, short height) static void pms_vcrinput(short input) { if(decoder==PHILIPS2) - i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); + pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); else if(decoder==PHILIPS1) - i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); + pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); } @@ -954,13 +954,13 @@ static int init_mediavision(void) id=mvv_read(3); - decst=i2c_stat(0x43); + decst=pms_i2c_stat(0x43); if(decst!=-1) idec=2; - else if(i2c_stat(0xb9)!=-1) + else if(pms_i2c_stat(0xb9)!=-1) idec=3; - else if(i2c_stat(0x8b)!=-1) + else if(pms_i2c_stat(0x8b)!=-1) idec=1; else idec=0; @@ -983,19 +983,19 @@ static int init_mediavision(void) for(i=0;i<0x19;i++) { if(i2c_defs[i]==0xFF) - i2c_andor(0x8A, i, 0x07,0x00); + pms_i2c_andor(0x8A, i, 0x07,0x00); else - i2c_write(0x8A, i, i2c_defs[i]); + pms_i2c_write(0x8A, i, i2c_defs[i]); } - i2c_write(0xB8,0x00,0x12); - i2c_write(0xB8,0x04,0x00); - i2c_write(0xB8,0x07,0x00); - i2c_write(0xB8,0x08,0x00); - i2c_write(0xB8,0x09,0xFF); - i2c_write(0xB8,0x0A,0x00); - i2c_write(0xB8,0x0B,0x10); - i2c_write(0xB8,0x10,0x03); + pms_i2c_write(0xB8,0x00,0x12); + pms_i2c_write(0xB8,0x04,0x00); + pms_i2c_write(0xB8,0x07,0x00); + pms_i2c_write(0xB8,0x08,0x00); + pms_i2c_write(0xB8,0x09,0xFF); + pms_i2c_write(0xB8,0x0A,0x00); + pms_i2c_write(0xB8,0x0B,0x10); + pms_i2c_write(0xB8,0x10,0x03); mvv_write(0x01, 0x00); mvv_write(0x05, 0xA0); @@ -1037,7 +1037,7 @@ static void shutdown_mediavision(void) #ifdef MODULE int init_module(void) #else -void init_pms_cards(void) +int init_pms_cards(struct video_init *v) #endif { printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c deleted file mode 100644 index f401a0f88..000000000 --- a/drivers/char/psaux.c +++ /dev/null @@ -1,693 +0,0 @@ -/* - * linux/drivers/char/psaux.c - * - * Driver for PS/2 type mouse by Johan Myreen. - * - * Supports pointing devices attached to a PS/2 type - * Keyboard and Auxiliary Device Controller. - * - * Corrections in device setup for some laptop mice & trackballs. - * 02Feb93 (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca) - * - * Changed to prevent keyboard lockups on AST Power Exec. - * 28Jul93 Brad Bosch - brad@lachman.com - * - * Modified by Johan Myreen (jem@pandora.pp.fi) 04Aug93 - * to include support for QuickPort mouse. - * - * Changed references to "QuickPort" with "82C710" since "QuickPort" - * is not what this driver is all about -- QuickPort is just a - * connector type, and this driver is for the mouse port on the Chips - * & Technologies 82C710 interface chip. 15Nov93 jem@pandora.pp.fi - * - * Added support for SIGIO. 28Jul95 jem@pandora.pp.fi - * - * Rearranged SIGIO support to use code from tty_io. 9Sept95 ctm@ardi.com - * - * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> - * - * Fixed keyboard lockups at open time - * 3-Jul-96, 22-Aug-96 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> - * - * Cleanup by Martin Mares, 01-Jun-97 (now uses the new PC kbd include) - * - * Renamed misc. name to "psaux",more in keeping with Documentation/devices.txt - * 13-Jan-1998, Richard Gooch <rgooch@atnf.csiro.au> - */ - -/* Uncomment the following line if your mouse needs initialization. */ - -/* #define INITIALIZE_DEVICE */ - -#include <linux/module.h> - -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/fcntl.h> -#include <linux/errno.h> -#include <linux/timer.h> -#include <linux/malloc.h> -#include <linux/miscdevice.h> -#include <linux/random.h> -#include <linux/poll.h> -#include <linux/init.h> - -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <asm/semaphore.h> - -#include <linux/config.h> - -#include "pc_keyb.h" - -#include <asm/keyboard.h> - -/* - * Generic declarations for both PS2 and 82C710 - */ -#define PSMOUSE_MINOR 1 /* minor device # for this mouse */ - -#define AUX_BUF_SIZE 2048 - -struct aux_queue { - unsigned long head; - unsigned long tail; - struct wait_queue *proc_list; - struct fasync_struct *fasync; - unsigned char buf[AUX_BUF_SIZE]; -}; - -static struct aux_queue *queue; -static int aux_ready = 0; -static int aux_count = 0; -static int aux_present = 0; - -/* - * Shared subroutines - */ - -static unsigned int get_from_queue(void) -{ - unsigned int result; - unsigned long flags; - - save_flags(flags); - cli(); - result = queue->buf[queue->tail]; - queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); - restore_flags(flags); - return result; -} - - -static inline int queue_empty(void) -{ - return queue->head == queue->tail; -} - -static int fasync_aux(int fd, struct file *filp, int on) -{ - int retval; - - retval = fasync_helper(fd, filp, on, &queue->fasync); - if (retval < 0) - return retval; - return 0; -} - -/* - * PS/2 Aux Device - */ - -#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) -#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) - -#define MAX_RETRIES 60 /* some aux operations take long time*/ - -/* - * Status polling - */ - -static int poll_aux_status(void) -{ - int retries=0; - unsigned char status; - - while ((kbd_read_status() & 0x03) && retries < MAX_RETRIES) { - status = kbd_read_status(); - kbd_pause(); - if ((status & AUX_STAT_OBF) == AUX_STAT_OBF) { - kbd_read_input(); - kbd_pause(); - } - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (5*HZ + 99) / 100; - schedule(); - retries++; - } - return (retries < MAX_RETRIES); -} - -/* - * Write to aux device - */ - -static void aux_write_dev(int val) -{ - poll_aux_status(); - kbd_write_command(KBD_CCMD_WRITE_MOUSE); /* Write magic cookie */ - poll_aux_status(); - kbd_write_output(val); /* Write data */ -} - -/* - * Write to device & handle returned ack - */ - -#ifdef INITIALIZE_DEVICE -__initfunc(static int aux_write_ack(int val)) -{ - aux_write_dev(val); - poll_aux_status(); - - if ((kbd_read_status() & AUX_STAT_OBF) == AUX_STAT_OBF) - { - return kbd_read_input(); - } - return 0; -} -#endif /* INITIALIZE_DEVICE */ - -/* - * Write aux device command - */ - -static void aux_write_cmd(int val) -{ - poll_aux_status(); - kbd_write_command(KBD_CCMD_WRITE_MODE); - poll_aux_status(); - kbd_write_output(val); -} - -/* - * AUX handler critical section start and end. - * - * Only one process can be in the critical section and all keyboard sends are - * deferred as long as we're inside. This is necessary as we may sleep when - * waiting for the keyboard controller and other processes / BH's can - * preempt us. Please note that the input buffer must be flushed when - * aux_end_atomic() is called and the interrupt is no longer enabled as not - * doing so might cause the keyboard driver to ignore all incoming keystrokes. - */ - -static struct semaphore aux_sema4 = MUTEX; - -static inline void aux_start_atomic(void) -{ - down(&aux_sema4); - disable_bh(KEYBOARD_BH); -} - -static inline void aux_end_atomic(void) -{ - enable_bh(KEYBOARD_BH); - up(&aux_sema4); -} - -/* - * Interrupt from the auxiliary device: a character - * is waiting in the keyboard/aux controller. - */ -#ifdef CONFIG_SGI -/* On the SGI we export this routine because the keyboard chirps at - * the same interrupt level. The status and data bytes are passed - * directly to us if the keyboard interrupt service routine detects - * that the keyboard is not the cause of the interrupt, see keyboard.c - * for details. - */ -void aux_interrupt(unsigned char status, unsigned char data) -{ - int head = queue->head; - int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); - - if ((status & AUX_STAT_OBF) != AUX_STAT_OBF) - return; - - add_mouse_randomness(queue->buf[head] = data); - if (head != maxhead) { - head++; - head &= AUX_BUF_SIZE-1; - } - queue->head = head; - aux_ready = 1; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); - wake_up_interruptible(&queue->proc_list); -} - - -#else /* !defined(CONFIG_SGI) */ -#ifdef CONFIG_MIPS_JAZZ -void aux_interrupt(int cpl, void *dev_id, struct pt_regs * regs) -#else -static void aux_interrupt(int cpl, void *dev_id, struct pt_regs * regs) -#endif -{ - int head = queue->head; - int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); - - if ((kbd_read_status() & AUX_STAT_OBF) != AUX_STAT_OBF) - return; - - add_mouse_randomness(queue->buf[head] = kbd_read_input()); - if (head != maxhead) { - head++; - head &= AUX_BUF_SIZE-1; - } - queue->head = head; - aux_ready = 1; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); - wake_up_interruptible(&queue->proc_list); -} -#endif /* !defined(CONFIG_SGI) */ - -static int release_aux(struct inode * inode, struct file * file) -{ - fasync_aux(-1, file, 0); - if (--aux_count) - return 0; -#ifdef CONFIG_VT - pckbd_read_mask = KBD_STAT_OBF; -#endif - aux_start_atomic(); - aux_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ - poll_aux_status(); - kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable Aux device */ - poll_aux_status(); - aux_end_atomic(); - ps2_free_irq(inode); - MOD_DEC_USE_COUNT; - return 0; -} - - -static int open_aux(struct inode * inode, struct file * file) -{ - if (!aux_present) - return -ENODEV; - aux_start_atomic(); - if (aux_count++) { - aux_end_atomic(); - return 0; - } - if (!poll_aux_status()) { /* FIXME: Race condition */ - aux_count--; - aux_end_atomic(); - return -EBUSY; - } - queue->head = queue->tail = 0; /* Flush input queue */ - if(ps2_request_irq()) { - aux_count--; - aux_end_atomic(); - return -EBUSY; - } - MOD_INC_USE_COUNT; - poll_aux_status(); - kbd_write_command(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux */ - aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ - aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */ - poll_aux_status(); - aux_end_atomic(); - -#ifdef CONFIG_VT - pckbd_read_mask = AUX_STAT_OBF; -#endif - - aux_ready = 0; - return 0; -} - -/* - * Write to the aux device. - */ - -static ssize_t write_aux(struct file * file, const char * buffer, - size_t count, loff_t *ppos) -{ - ssize_t retval = 0; - - if (count) { - ssize_t written = 0; - - aux_start_atomic(); - do { - char c; - if (!poll_aux_status()) - break; - kbd_write_command(KBD_CCMD_WRITE_MOUSE); - if (!poll_aux_status()) - break; - get_user(c, buffer++); - kbd_write_output(c); - written++; - } while (--count); - aux_end_atomic(); - retval = -EIO; - if (written) { - retval = written; - file->f_dentry->d_inode->i_mtime = CURRENT_TIME; - } - } - - return retval; -} - -/* - * 82C710 Interface - */ - -#ifdef CONFIG_82C710_MOUSE - -#define QP_DATA 0x310 /* Data Port I/O Address */ -#define QP_STATUS 0x311 /* Status Port I/O Address */ - -#define QP_DEV_IDLE 0x01 /* Device Idle */ -#define QP_RX_FULL 0x02 /* Device Char received */ -#define QP_TX_IDLE 0x04 /* Device XMIT Idle */ -#define QP_RESET 0x08 /* Device Reset */ -#define QP_INTS_ON 0x10 /* Device Interrupt On */ -#define QP_ERROR_FLAG 0x20 /* Device Error */ -#define QP_CLEAR 0x40 /* Device Clear */ -#define QP_ENABLE 0x80 /* Device Enable */ - -#define QP_IRQ 12 - -static int qp_present = 0; -static int qp_count = 0; -static int qp_data = QP_DATA; -static int qp_status = QP_STATUS; - -static int poll_qp_status(void); -static int probe_qp(void); - -/* - * Interrupt handler for the 82C710 mouse port. A character - * is waiting in the 82C710. - */ - -static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) -{ - int head = queue->head; - int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); - - add_mouse_randomness(queue->buf[head] = inb(qp_data)); - if (head != maxhead) { - head++; - head &= AUX_BUF_SIZE-1; - } - queue->head = head; - aux_ready = 1; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); - wake_up_interruptible(&queue->proc_list); -} - -static int release_qp(struct inode * inode, struct file * file) -{ - unsigned char status; - - fasync_aux(-1, file, 0); - if (!--qp_count) { - if (!poll_qp_status()) - printk("Warning: Mouse device busy in release_qp()\n"); - status = inb_p(qp_status); - outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status); - if (!poll_qp_status()) - printk("Warning: Mouse device busy in release_qp()\n"); - free_irq(QP_IRQ, NULL); - MOD_DEC_USE_COUNT; - } - return 0; -} - -/* - * Install interrupt handler. - * Enable the device, enable interrupts. - */ - -static int open_qp(struct inode * inode, struct file * file) -{ - unsigned char status; - - if (!qp_present) - return -EINVAL; - - if (qp_count++) - return 0; - - if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) { - qp_count--; - return -EBUSY; - } - - status = inb_p(qp_status); - status |= (QP_ENABLE|QP_RESET); - outb_p(status, qp_status); - status &= ~(QP_RESET); - outb_p(status, qp_status); - - queue->head = queue->tail = 0; /* Flush input queue */ - status |= QP_INTS_ON; - outb_p(status, qp_status); /* Enable interrupts */ - - while (!poll_qp_status()) { - printk("Error: Mouse device busy in open_qp()\n"); - qp_count--; - status &= ~(QP_ENABLE|QP_INTS_ON); - outb_p(status, qp_status); - free_irq(QP_IRQ, NULL); - return -EBUSY; - } - - outb_p(AUX_ENABLE_DEV, qp_data); /* Wake up mouse */ - MOD_INC_USE_COUNT; - return 0; -} - -/* - * Write to the 82C710 mouse device. - */ - -static ssize_t write_qp(struct file * file, const char * buffer, - size_t count, loff_t *ppos) -{ - ssize_t i = count; - - while (i--) { - char c; - if (!poll_qp_status()) - return -EIO; - get_user(c, buffer++); - outb_p(c, qp_data); - } - file->f_dentry->d_inode->i_mtime = CURRENT_TIME; - return count; -} - -/* - * Wait for device to send output char and flush any input char. - */ - -static int poll_qp_status(void) -{ - int retries=0; - - while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE)) - != (QP_DEV_IDLE|QP_TX_IDLE) - && retries < MAX_RETRIES) { - - if (inb_p(qp_status)&(QP_RX_FULL)) - inb_p(qp_data); - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (5*HZ + 99) / 100; - schedule(); - retries++; - } - return !(retries==MAX_RETRIES); -} - -/* - * Function to read register in 82C710. - */ - -static inline unsigned char read_710(unsigned char index) -{ - outb_p(index, 0x390); /* Write index */ - return inb_p(0x391); /* Read the data */ -} - -/* - * See if we can find a 82C710 device. Read mouse address. - */ - -__initfunc(static int probe_qp(void)) -{ - outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ - outb_p(0xaa, 0x3fa); /* Inverse of 55 */ - outb_p(0x36, 0x3fa); /* Address the chip */ - outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ - outb_p(0x1b, 0x2fa); /* Inverse of e4 */ - if (read_710(0x0f) != 0xe4) /* Config address found? */ - return 0; /* No: no 82C710 here */ - qp_data = read_710(0x0d)*4; /* Get mouse I/O address */ - qp_status = qp_data+1; - outb_p(0x0f, 0x390); - outb_p(0x0f, 0x391); /* Close config mode */ - return 1; -} - -#endif - -/* - * Generic part continues... - */ - -/* - * Put bytes from input queue to buffer. - */ - -static ssize_t read_aux(struct file * file, char * buffer, - size_t count, loff_t *ppos) -{ - struct wait_queue wait = { current, NULL }; - ssize_t i = count; - unsigned char c; - - if (queue_empty()) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - add_wait_queue(&queue->proc_list, &wait); -repeat: - current->state = TASK_INTERRUPTIBLE; - if (queue_empty() && !signal_pending(current)) { - schedule(); - goto repeat; - } - current->state = TASK_RUNNING; - remove_wait_queue(&queue->proc_list, &wait); - } - while (i > 0 && !queue_empty()) { - c = get_from_queue(); - put_user(c, buffer++); - i--; - } - aux_ready = !queue_empty(); - if (count-i) { - file->f_dentry->d_inode->i_atime = CURRENT_TIME; - return count-i; - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -static unsigned int aux_poll(struct file *file, poll_table * wait) -{ - poll_wait(file, &queue->proc_list, wait); - if (aux_ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations psaux_fops = { - NULL, /* seek */ - read_aux, - write_aux, - NULL, /* readdir */ - aux_poll, - NULL, /* ioctl */ - NULL, /* mmap */ - open_aux, - NULL, /* flush */ - release_aux, - NULL, - fasync_aux, -}; - -/* - * Initialize driver. First check for a 82C710 chip; if found - * forget about the Aux port and use the *_qp functions. - */ -static struct miscdevice psaux_mouse = { - PSMOUSE_MINOR, "psaux", &psaux_fops -}; - -__initfunc(int psaux_init(void)) -{ - int qp_found = 0; - -#ifdef CONFIG_82C710_MOUSE - if ((qp_found = probe_qp())) { - printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n"); -/* printk("82C710 address = %x (should be 0x310)\n", qp_data); */ - qp_present = 1; - psaux_fops.write = write_qp; - psaux_fops.open = open_qp; - psaux_fops.release = release_qp; - } else -#endif -#if (defined(CONFIG_SGI) || defined(CONFIG_MIPS_JAZZ)) && defined(CONFIG_PSMOUSE) - if (1) { -#else - if (aux_device_present == 0xaa) { -#endif - printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n"); - aux_present = 1; - } else { - return -EIO; - } - misc_register(&psaux_mouse); - queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); - memset(queue, 0, sizeof(*queue)); - queue->head = queue->tail = 0; - queue->proc_list = NULL; - if (!qp_found) { - aux_start_atomic(); -#ifdef INITIALIZE_DEVICE - kbd_write_command(AUX_ENABLE); /* Enable Aux */ - aux_write_ack(AUX_SET_SAMPLE); - aux_write_ack(100); /* 100 samples/sec */ - aux_write_ack(AUX_SET_RES); - aux_write_ack(3); /* 8 counts per mm */ - aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ - poll_aux_status(); -#endif /* INITIALIZE_DEVICE */ - kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable Aux device */ - poll_aux_status(); - kbd_write_command(KBD_CCMD_WRITE_MODE); /* Disable controller interrupts */ - poll_aux_status(); - kbd_write_output(AUX_INTS_OFF); - kbd_pause(); - poll_aux_status(); - aux_end_atomic(); - } - - return 0; -} - -#ifdef MODULE -int init_module(void) -{ - return psaux_init(); -} - -void cleanup_module(void) -{ - misc_deregister(&psaux_mouse); - kfree(queue); -} -#endif diff --git a/drivers/char/pty.c b/drivers/char/pty.c index a7ea98497..0b27057a1 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -336,7 +336,9 @@ static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios) __initfunc(int pty_init(void)) { +#ifdef CONFIG_UNIX98_PTYS int i; +#endif /* Traditional BSD devices */ diff --git a/drivers/char/qpmouse.c b/drivers/char/qpmouse.c new file mode 100644 index 000000000..12b134719 --- /dev/null +++ b/drivers/char/qpmouse.c @@ -0,0 +1,373 @@ +/* + * linux/drivers/char/qpmouse.c + * + * Driver for a 82C710 C&T mouse interface chip. + * + * Based on the PS/2 driver by Johan Myreen. + * + * Corrections in device setup for some laptop mice & trackballs. + * 02Feb93 (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca) + * + * Modified by Johan Myreen (jem@iki.fi) 04Aug93 + * to include support for QuickPort mouse. + * + * Changed references to "QuickPort" with "82C710" since "QuickPort" + * is not what this driver is all about -- QuickPort is just a + * connector type, and this driver is for the mouse port on the Chips + * & Technologies 82C710 interface chip. 15Nov93 jem@iki.fi + * + * Added support for SIGIO. 28Jul95 jem@iki.fi + * + * Rearranged SIGIO support to use code from tty_io. 9Sept95 ctm@ardi.com + * + * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk> + */ + +#include <linux/module.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/fcntl.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/malloc.h> +#include <linux/miscdevice.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/semaphore.h> + +#include <linux/pc_keyb.h> /* mouse enable command.. */ + + +/* + * We use the same minor number as the PS/2 mouse for (bad) historical + * reasons.. + */ +#define PSMOUSE_MINOR 1 /* Minor device # for this mouse */ +#define QP_BUF_SIZE 2048 + +struct qp_queue { + unsigned long head; + unsigned long tail; + struct wait_queue *proc_list; + struct fasync_struct *fasync; + unsigned char buf[QP_BUF_SIZE]; +}; + +static struct qp_queue *queue; + +static unsigned int get_from_queue(void) +{ + unsigned int result; + unsigned long flags; + + save_flags(flags); + cli(); + result = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (QP_BUF_SIZE-1); + restore_flags(flags); + return result; +} + + +static inline int queue_empty(void) +{ + return queue->head == queue->tail; +} + +static int fasync_qp(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &queue->fasync); + if (retval < 0) + return retval; + return 0; +} + +/* + * 82C710 Interface + */ + +#define QP_DATA 0x310 /* Data Port I/O Address */ +#define QP_STATUS 0x311 /* Status Port I/O Address */ + +#define QP_DEV_IDLE 0x01 /* Device Idle */ +#define QP_RX_FULL 0x02 /* Device Char received */ +#define QP_TX_IDLE 0x04 /* Device XMIT Idle */ +#define QP_RESET 0x08 /* Device Reset */ +#define QP_INTS_ON 0x10 /* Device Interrupt On */ +#define QP_ERROR_FLAG 0x20 /* Device Error */ +#define QP_CLEAR 0x40 /* Device Clear */ +#define QP_ENABLE 0x80 /* Device Enable */ + +#define QP_IRQ 12 + +static int qp_present = 0; +static int qp_count = 0; +static int qp_data = QP_DATA; +static int qp_status = QP_STATUS; + +static int poll_qp_status(void); +static int probe_qp(void); + +/* + * Interrupt handler for the 82C710 mouse port. A character + * is waiting in the 82C710. + */ + +static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs) +{ + int head = queue->head; + int maxhead = (queue->tail-1) & (QP_BUF_SIZE-1); + + add_mouse_randomness(queue->buf[head] = inb(qp_data)); + if (head != maxhead) { + head++; + head &= QP_BUF_SIZE-1; + } + queue->head = head; + if (queue->fasync) + kill_fasync(queue->fasync, SIGIO); + wake_up_interruptible(&queue->proc_list); +} + +static int release_qp(struct inode * inode, struct file * file) +{ + unsigned char status; + + fasync_qp(-1, file, 0); + if (!--qp_count) { + if (!poll_qp_status()) + printk("Warning: Mouse device busy in release_qp()\n"); + status = inb_p(qp_status); + outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status); + if (!poll_qp_status()) + printk("Warning: Mouse device busy in release_qp()\n"); + free_irq(QP_IRQ, NULL); + MOD_DEC_USE_COUNT; + } + return 0; +} + +/* + * Install interrupt handler. + * Enable the device, enable interrupts. + */ + +static int open_qp(struct inode * inode, struct file * file) +{ + unsigned char status; + + if (!qp_present) + return -EINVAL; + + if (qp_count++) + return 0; + + if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) { + qp_count--; + return -EBUSY; + } + + status = inb_p(qp_status); + status |= (QP_ENABLE|QP_RESET); + outb_p(status, qp_status); + status &= ~(QP_RESET); + outb_p(status, qp_status); + + queue->head = queue->tail = 0; /* Flush input queue */ + status |= QP_INTS_ON; + outb_p(status, qp_status); /* Enable interrupts */ + + while (!poll_qp_status()) { + printk("Error: Mouse device busy in open_qp()\n"); + qp_count--; + status &= ~(QP_ENABLE|QP_INTS_ON); + outb_p(status, qp_status); + free_irq(QP_IRQ, NULL); + return -EBUSY; + } + + outb_p(AUX_ENABLE_DEV, qp_data); /* Wake up mouse */ + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Write to the 82C710 mouse device. + */ + +static ssize_t write_qp(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + ssize_t i = count; + + while (i--) { + char c; + if (!poll_qp_status()) + return -EIO; + get_user(c, buffer++); + outb_p(c, qp_data); + } + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + return count; +} + +static unsigned int poll_qp(struct file *file, poll_table * wait) +{ + poll_wait(file, &queue->proc_list, wait); + if (!queue_empty()) + return POLLIN | POLLRDNORM; + return 0; +} + +/* + * Wait for device to send output char and flush any input char. + */ + +#define MAX_RETRIES (60) + +static int poll_qp_status(void) +{ + int retries=0; + + while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE)) + != (QP_DEV_IDLE|QP_TX_IDLE) + && retries < MAX_RETRIES) { + + if (inb_p(qp_status)&(QP_RX_FULL)) + inb_p(qp_data); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((5*HZ + 99) / 100); + retries++; + } + return !(retries==MAX_RETRIES); +} + +/* + * Put bytes from input queue to buffer. + */ + +static ssize_t read_qp(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + struct wait_queue wait = { current, NULL }; + ssize_t i = count; + unsigned char c; + + if (queue_empty()) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + add_wait_queue(&queue->proc_list, &wait); +repeat: + current->state = TASK_INTERRUPTIBLE; + if (queue_empty() && !signal_pending(current)) { + schedule(); + goto repeat; + } + current->state = TASK_RUNNING; + remove_wait_queue(&queue->proc_list, &wait); + } + while (i > 0 && !queue_empty()) { + c = get_from_queue(); + put_user(c, buffer++); + i--; + } + if (count-i) { + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + return count-i; + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +struct file_operations qp_fops = { + NULL, /* seek */ + read_qp, + write_qp, + NULL, /* readdir */ + poll_qp, + NULL, /* ioctl */ + NULL, /* mmap */ + open_qp, + NULL, /* flush */ + release_qp, + NULL, + fasync_qp, +}; + +/* + * Initialize driver. + */ +static struct miscdevice qp_mouse = { + PSMOUSE_MINOR, "QPmouse", &qp_fops +}; + +/* + * Function to read register in 82C710. + */ + +static inline unsigned char read_710(unsigned char index) +{ + outb_p(index, 0x390); /* Write index */ + return inb_p(0x391); /* Read the data */ +} + + +/* + * See if we can find a 82C710 device. Read mouse address. + */ + +static int __init probe_qp(void) +{ + outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */ + outb_p(0xaa, 0x3fa); /* Inverse of 55 */ + outb_p(0x36, 0x3fa); /* Address the chip */ + outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */ + outb_p(0x1b, 0x2fa); /* Inverse of e4 */ + if (read_710(0x0f) != 0xe4) /* Config address found? */ + return 0; /* No: no 82C710 here */ + qp_data = read_710(0x0d)*4; /* Get mouse I/O address */ + qp_status = qp_data+1; + outb_p(0x0f, 0x390); + outb_p(0x0f, 0x391); /* Close config mode */ + return 1; +} + +int __init qpmouse_init(void) +{ + if (!probe_qp()) + return -EIO; + + printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n"); +/* printk("82C710 address = %x (should be 0x310)\n", qp_data); */ + qp_present = 1; + misc_register(&qp_mouse); + queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + memset(queue, 0, sizeof(*queue)); + queue->head = queue->tail = 0; + queue->proc_list = NULL; + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return qpmouse_init(); +} + +void cleanup_module(void) +{ + misc_deregister(&qp_mouse); + kfree(queue); +} +#endif diff --git a/drivers/char/radio-sf16fmi.c b/drivers/char/radio-sf16fmi.c index 192fa5236..8840bbc20 100644 --- a/drivers/char/radio-sf16fmi.c +++ b/drivers/char/radio-sf16fmi.c @@ -26,7 +26,7 @@ struct fmi_device { int port; int curvol; /* 1 or 0 */ - unsigned long curfreq; /* RSF16_PREC * freq in MHz */ + unsigned long curfreq; /* freq in kHz */ __u32 flags; }; @@ -43,7 +43,7 @@ static int users = 0; * 92.7400017 -> 92.75 */ #define RSF16_ENCODE(x) ((x)/800+214) -#define RSF16_MINFREQ 88*16000 +#define RSF16_MINFREQ 87*16000 #define RSF16_MAXFREQ 108*16000 static void outbits(int bits, unsigned int data, int port) @@ -64,39 +64,60 @@ static void outbits(int bits, unsigned int data, int port) } } -static void fmi_mute(int port) +static inline void fmi_mute(int port) { outb(0x00, port); } -static void fmi_unmute(int port) +static inline void fmi_unmute(int port) { outb(0x08, port); } -static int fmi_setfreq(struct fmi_device *dev, unsigned long freq) +static inline int fmi_setfreq(struct fmi_device *dev, unsigned long freq) { int myport = dev->port; - + int i; + outbits(16, RSF16_ENCODE(freq), myport); outbits(8, 0xC0, myport); - /* we should wait here... */ + for(i=0; i< 100; i++) + { + udelay(1400); + if(current->need_resched) + schedule(); + } +/* If this becomes allowed use it ... + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/7); +*/ + if (dev->curvol) fmi_unmute(myport); return 0; } -static int fmi_getsigstr(struct fmi_device *dev) +static inline int fmi_getsigstr(struct fmi_device *dev) { int val; int res; int myport = dev->port; - + int i; + val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ outb(val, myport); outb(val | 0x10, myport); - udelay(140000); + for(i=0; i< 100; i++) + { + udelay(1400); + if(current->need_resched) + schedule(); + } +/* If this becomes allowed use it ... + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/7); +*/ res = (int)inb(myport+1); outb(val, myport); - return (res & 2) ? 0 : 1; + return (res & 2) ? 0 : 0xFFFF; } static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) @@ -136,7 +157,7 @@ static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.rangehigh = RSF16_MAXFREQ/mult; v.flags=fmi->flags; v.mode=VIDEO_MODE_AUTO; - v.signal=0xFFFF*fmi_getsigstr(fmi); + v.signal = fmi_getsigstr(fmi); if(copy_to_user(arg,&v, sizeof(v))) return -EFAULT; return 0; diff --git a/drivers/char/random.c b/drivers/char/random.c index 5db97f906..c980d5192 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1250,7 +1250,7 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, } #if HASH_BUFFER_SIZE & 1 /* There's a middle word to deal with */ x = tmp[HASH_BUFFER_SIZE/2]; - add_entropy_words(r, x, (__u32)buf); + add_entropy_words(r, x, (__u32)((unsigned long)buf)); x ^= (x >> 16); /* Fold it in half */ ((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x; #endif @@ -1407,19 +1407,15 @@ random_ioctl(struct inode * inode, struct file * file, switch (cmd) { case RNDGETENTCNT: - retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); - if (retval) - return(retval); ent_count = random_state.entropy_count; - put_user(ent_count, (int *) arg); + if (put_user(ent_count, (int *) arg)) + return -EFAULT; return 0; case RNDADDTOENTCNT: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int)); - if (retval) - return(retval); - get_user(ent_count, (int *) arg); + if (get_user(ent_count, (int *) arg)) + return -EFAULT; /* * Add i to entropy_count, limiting the result to be * between 0 and POOLBITS. @@ -1446,16 +1442,14 @@ random_ioctl(struct inode * inode, struct file * file, if (!capable(CAP_SYS_ADMIN)) return -EPERM; p = (int *) arg; - retval = verify_area(VERIFY_WRITE, (void *) p, sizeof(int)); - if (retval) - return(retval); ent_count = random_state.entropy_count; - put_user(ent_count, p++); - retval = verify_area(VERIFY_WRITE, (void *) p, sizeof(int)); - if (retval) - return(retval); - get_user(size, p); - put_user(POOLWORDS, p++); + if (put_user(ent_count, p++)) + return -EFAULT; + + if (get_user(size, p)) + return -EFAULT; + if (put_user(POOLWORDS, p++)) + return -EFAULT; if (size < 0) return -EINVAL; if (size > POOLWORDS) @@ -1467,16 +1461,12 @@ random_ioctl(struct inode * inode, struct file * file, if (!capable(CAP_SYS_ADMIN)) return -EPERM; p = (int *) arg; - retval = verify_area(VERIFY_READ, (void *) p, 2*sizeof(int)); - if (retval) - return(retval); - get_user(ent_count, p++); + if (get_user(ent_count, p++)) + return -EFAULT; if (ent_count < 0) return -EINVAL; - get_user(size, p++); - retval = verify_area(VERIFY_READ, (void *) p, size); - if (retval) - return retval; + if (get_user(size, p++)) + return -EFAULT; retval = random_write(file, (const char *) p, size, &file->f_pos); if (retval < 0) diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index b854fa338..7da0fa512 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1172,8 +1172,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp) timeout = jiffies+HZ; while(port->IER & IER_TXEMPTY) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + port->timeout; - schedule(); + schedule_timeout(port->timeout); if (jiffies > timeout) break; } @@ -1189,8 +1188,7 @@ static void rc_close(struct tty_struct * tty, struct file * filp) if (port->blocked_open) { if (port->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + port->close_delay; - schedule(); + schedule_timeout(port->close_delay); } wake_up_interruptible(&port->open_wait); } diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index c661f8238..9dff48416 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1181,8 +1181,7 @@ static void rp_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } else { @@ -1269,10 +1268,9 @@ static void rp_set_termios(struct tty_struct *tty, struct termios *old_termios) static void send_break( struct r_port * info, int duration) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; cli(); sSendBreak(&info->channel); - schedule(); + schedule_timeout(duration); sClrBreak(&info->channel); sti(); } @@ -1666,8 +1664,7 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout) #endif current->state = TASK_INTERRUPTIBLE; current->counter = 0; /* make us low-priority */ - current->timeout = jiffies + check_time; - schedule(); + schedule_timeout(check_time); if (signal_pending(current)) break; } diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index da46db987..6a26bb088 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -414,7 +414,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, restore_flags(flags); return 0; } -#ifdef __alpha__ +#if defined(__alpha__) || defined(__mips__) case RTC_EPOCH_READ: /* Read the epoch. */ { return put_user (epoch, (unsigned long *)arg); @@ -521,7 +521,7 @@ static struct miscdevice rtc_dev= __initfunc(int rtc_init(void)) { unsigned long flags; -#ifdef __alpha__ +#if defined(__alpha__) || defined(__mips__) unsigned int year, ctrl; unsigned long uip_watchdog; char *guess = NULL; @@ -536,7 +536,7 @@ __initfunc(int rtc_init(void)) misc_register(&rtc_dev); /* Check region? Naaah! Just snarf it up. */ request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc"); -#ifdef __alpha__ +#if defined(__alpha__) || defined(__mips__) rtc_freq = HZ; /* Each operating system on an Alpha uses its own epoch. diff --git a/drivers/char/saa5249.c b/drivers/char/saa5249.c index f3185252f..bc49cb80b 100644 --- a/drivers/char/saa5249.c +++ b/drivers/char/saa5249.c @@ -149,7 +149,6 @@ static int saa5249_attach(struct i2c_device *device) if(device->bus->id!=I2C_BUSID_BT848) return -EINVAL; - printk(KERN_DEBUG "saa5249_attach: bus %p\n", device->bus); strcpy(device->name, IF_NAME); /* @@ -205,7 +204,6 @@ static int saa5249_attach(struct i2c_device *device) static int saa5249_detach(struct i2c_device *device) { struct video_device *vd=device->data; - printk(KERN_DEBUG "saa5249_detach\n"); video_unregister_device(vd); kfree(vd->priv); kfree(vd); @@ -215,7 +213,6 @@ static int saa5249_detach(struct i2c_device *device) static int saa5249_command(struct i2c_device *device, unsigned int cmd, void *arg) { - printk(KERN_DEBUG "saa5249_command\n"); return -EINVAL; } @@ -245,8 +242,7 @@ static void jdelay(unsigned long delay) recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + delay; - schedule(); + schedule_timeout(delay); spin_lock_irq(¤t->sigmask_lock); current->blocked = oldblocked; @@ -621,11 +617,9 @@ static int saa5249_open(struct video_device *vd, int nb) struct saa5249_device *t=vd->priv; int pgbuf; - printk("t=%p\n",t); if (t->bus==NULL) return -ENODEV; - printk("Do i2c %p\n",t->bus); if (i2c_senddata(t, CCTWR, 0, 0, -1) || /* Select R11 */ /* Turn off parity checks (we do this ourselves) */ i2c_senddata(t, CCTWR, 1, disp_modes[t->disp_mode][0], 0, -1) || @@ -636,7 +630,6 @@ static int saa5249_open(struct video_device *vd, int nb) return -EIO; } - printk("clean\n"); for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); @@ -648,7 +641,6 @@ static int saa5249_open(struct video_device *vd, int nb) t->is_searching[pgbuf] = FALSE; } t->virtual_mode=FALSE; - printk("Go\n"); MOD_INC_USE_COUNT; return 0; } @@ -674,6 +666,15 @@ static long saa5249_read(struct video_device *v, char *buf, unsigned long l, int return -EINVAL; } +int init_saa_5249(struct video_init *v) +{ + printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n", + VTX_VER_MAJ, VTX_VER_MIN); + i2c_register_driver(&i2c_driver_videotext); + + return 0; +} + static struct video_device saa_template= { IF_NAME, @@ -692,15 +693,15 @@ static struct video_device saa_template= 0 }; +#ifdef MODULE + /* * Routines for loadable modules */ int init_module(void) { - printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n", - VTX_VER_MAJ, VTX_VER_MIN); - i2c_register_driver(&i2c_driver_videotext); + init_saa_5249(NULL); return 0; } @@ -709,3 +710,5 @@ void cleanup_module(void) { i2c_unregister_driver(&i2c_driver_videotext); } + +#endif diff --git a/drivers/char/selection.c b/drivers/char/selection.c index 3910d0e30..6d38fcf7e 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -123,7 +123,7 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) int i, ps, pe; unsigned int currcons = fg_console; - do_unblank_screen(); + unblank_screen(); poke_blanked_console(); { unsigned short *args, xs, ys, xe, ye; diff --git a/drivers/char/serial.c b/drivers/char/serial.c index 9c5ecd8ef..e352813cc 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1675,6 +1675,8 @@ static int set_serial_info(struct async_struct * info, return -EPERM; state->flags = ((state->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((state->flags & ~ASYNC_USR_MASK) | + (info->flags & ASYNC_USR_MASK)); state->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } @@ -1682,8 +1684,9 @@ static int set_serial_info(struct async_struct * info, new_serial.irq = irq_cannonicalize(new_serial.irq); if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) || - (new_serial.type < PORT_UNKNOWN) || - (new_serial.type > PORT_MAX)) { + (new_serial.baud_base == 0) || (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { return -EINVAL; } @@ -2313,8 +2316,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } @@ -2368,8 +2370,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) #endif current->state = TASK_INTERRUPTIBLE; current->counter = 0; /* make us low-priority */ - current->timeout = jiffies + char_time; - schedule(); + schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && ((orig_jiffies + timeout) < jiffies)) @@ -2603,7 +2604,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) tty->driver_data = info; info->tty = tty; if (serial_paranoia_check(info, tty->device, "rs_open")) { - MOD_DEC_USE_COUNT; + /* MOD_DEC_USE_COUNT; "info->tty" will cause this */ return -ENODEV; } @@ -2616,7 +2617,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) if (!tmp_buf) { page = get_free_page(GFP_KERNEL); if (!page) { - MOD_DEC_USE_COUNT; + /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ return -ENOMEM; } if (tmp_buf) @@ -2632,7 +2633,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); - MOD_DEC_USE_COUNT; + /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); @@ -2646,13 +2647,13 @@ static int rs_open(struct tty_struct *tty, struct file * filp) */ retval = startup(info); if (retval) { - MOD_DEC_USE_COUNT; + /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ return retval; } retval = block_til_ready(tty, filp, info); if (retval) { - MOD_DEC_USE_COUNT; + /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */ #ifdef SERIAL_DEBUG_OPEN printk("rs_open returning after block_til_ready with %d\n", retval); diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c new file mode 100644 index 000000000..b3ff15257 --- /dev/null +++ b/drivers/char/serial167.c @@ -0,0 +1,2983 @@ +/* + * linux/drivers/char/serial167.c + * + * Driver for MVME166/7 board serial ports, which are via a CD2401. + * Based very much on cyclades.c. + * + * MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk] + * + * ============================================================== + * + * static char rcsid[] = + * "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $"; + * + * linux/kernel/cyclades.c + * + * Maintained by Marcio Saito (cyclades@netcom.com) and + * Randolph Bentson (bentson@grieg.seaslug.org) + * + * Much of the design and some of the code came from serial.c + * which was copyright (C) 1991, 1992 Linus Torvalds. It was + * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92, + * and then fixed as suggested by Michael K. Johnson 12/12/92. + * + * This version does not support shared irq's. + * + * This module exports the following rs232 io functions: + * int cy_init(void); + * int cy_open(struct tty_struct *tty, struct file *filp); + * + * $Log: cyclades.c,v $ + * Revision 1.36.1.4 1995/03/29 06:14:14 bentson + * disambiguate between Cyclom-16Y and Cyclom-32Ye; + * + * 200 lines of changes record removed - RGH 11-10-95, starting work on + * converting this to drive serial ports on mvme166 (cd2401). + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/interrupt.h> +#include <linux/serial.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/serial167.h> +#include <linux/delay.h> +#include <linux/major.h> +#include <linux/mm.h> +#include <linux/console.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/mvme16xhw.h> + +#include <linux/types.h> +#include <linux/kernel.h> + +#include <linux/version.h> +#include <asm/uaccess.h> +#include <linux/init.h> + +#define cy_put_user put_user + +static unsigned long cy_get_user(unsigned long *addr) +{ + unsigned long result = 0; + int error = get_user (result, addr); + if (error) + printk ("serial167: cy_get_user: error == %d\n", error); + return result; +} + +#define SERIAL_PARANOIA_CHECK +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_OTHER +#undef SERIAL_DEBUG_IO +#undef SERIAL_DEBUG_COUNT +#undef SERIAL_DEBUG_DTR +#undef CYCLOM_16Y_HACK +#define CYCLOM_ENABLE_MONITORING + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define WAKEUP_CHARS 256 + +#define STD_COM_FLAGS (0) + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + + +DECLARE_TASK_QUEUE(tq_cyclades); + +struct tty_driver cy_serial_driver, cy_callout_driver; +extern int serial_console; +static struct cyclades_port *serial_console_info = NULL; +static unsigned int serial_console_cflag = 0; +u_char initial_console_speed; + +/* Base address of cd2401 chip on mvme166/7 */ + +#define BASE_ADDR (0xfff45000) +#define pcc2chip ((volatile u_char *)0xfff42000) +#define PccSCCMICR 0x1d +#define PccSCCTICR 0x1e +#define PccSCCRICR 0x1f +#define PccTPIACKR 0x25 +#define PccRPIACKR 0x27 +#define PccIMLR 0x3f + +/* This is the per-port data structure */ +struct cyclades_port cy_port[] = { + /* CARD# */ + {-1 }, /* ttyS0 */ + {-1 }, /* ttyS1 */ + {-1 }, /* ttyS2 */ + {-1 }, /* ttyS3 */ +}; +#define NR_PORTS (sizeof(cy_port)/sizeof(struct cyclades_port)) + +static int serial_refcount; + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf = 0; +static struct semaphore tmp_buf_sem = MUTEX; + +/* + * This is used to look up the divisor speeds and the timeouts + * We're normally limited to 15 distinct baud rates. The extra + * are accessed via settings in info->flags. + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, + 0}; + +#if 0 +static char baud_co[] = { /* 25 MHz clock option table */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static char baud_bpr[] = { /* 25 MHz baud rate period table */ + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; +#endif + +/* I think 166 brd clocks 2401 at 20MHz.... */ + +/* These values are written directly to tcor, and >> 5 for writing to rcor */ +static u_char baud_co[] = { /* 20 MHz clock option table */ + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40, + 0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* These values written directly to tbpr/rbpr */ +static u_char baud_bpr[] = { /* 20 MHz baud rate period table */ + 0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81, + 0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10}; + +static u_char baud_cor4[] = { /* receive threshold */ + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; + + + +static void shutdown(struct cyclades_port *); +static int startup (struct cyclades_port *); +static void cy_throttle(struct tty_struct *); +static void cy_unthrottle(struct tty_struct *); +static void config_setup(struct cyclades_port *); +extern void console_print(const char *); +#ifdef CYCLOM_SHOW_STATUS +static void show_status(int); +#endif + +#ifdef CONFIG_REMOTE_DEBUG +static void debug_setup(void); +void queueDebugChar (int c); +int getDebugChar(void); + +#define DEBUG_PORT 1 +#define DEBUG_LEN 256 + +typedef struct { + int in; + int out; + unsigned char buf[DEBUG_LEN]; +} debugq; + +debugq debugiq; +#endif + +/* + * I have my own version of udelay(), as it is needed when initialising + * the chip, before the delay loop has been calibrated. Should probably + * reference one of the vmechip2 or pccchip2 counter for an accurate + * delay, but this wild guess will do for now. + */ + +void my_udelay (long us) +{ + u_char x; + volatile u_char *p = &x; + int i; + + while (us--) + for (i = 100; i; i--) + x |= *p; +} + +static inline int +serial_paranoia_check(struct cyclades_port *info, + dev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%d, %d) in %s\n"; + static const char *badinfo = + "Warning: null cyclades_port for (%d, %d) in %s\n"; + static const char *badrange = + "Warning: cyclades_port out of range for (%d, %d) in %s\n"; + + if (!info) { + printk(badinfo, MAJOR(device), MINOR(device), routine); + return 1; + } + + if( (long)info < (long)(&cy_port[0]) + || (long)(&cy_port[NR_PORTS]) < (long)info ){ + printk(badrange, MAJOR(device), MINOR(device), routine); + return 1; + } + + if (info->magic != CYCLADES_MAGIC) { + printk(badmagic, MAJOR(device), MINOR(device), routine); + return 1; + } +#endif + return 0; +} /* serial_paranoia_check */ + +#if 0 +/* The following diagnostic routines allow the driver to spew + information on the screen, even (especially!) during interrupts. + */ +void +SP(char *data){ + unsigned long flags; + save_flags(flags); cli(); + console_print(data); + restore_flags(flags); +} +char scrn[2]; +void +CP(char data){ + unsigned long flags; + save_flags(flags); cli(); + scrn[0] = data; + console_print(scrn); + restore_flags(flags); +}/* CP */ + +void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */ +void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */ +void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */ +void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */ +#endif + +/* This routine waits up to 1000 micro-seconds for the previous + command to the Cirrus chip to complete and then issues the + new command. An error is returned if the previous command + didn't finish within the time limit. + */ +u_short +write_cy_cmd(volatile u_char *base_addr, u_char cmd) +{ + unsigned long flags; + volatile int i; + + save_flags(flags); cli(); + /* Check to see that the previous command has completed */ + for(i = 0 ; i < 100 ; i++){ + if (base_addr[CyCCR] == 0){ + break; + } + my_udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if ( i == 10 ) { + restore_flags(flags); + return (-1); + } + + /* Issue the new command */ + base_addr[CyCCR] = cmd; + restore_flags(flags); + return(0); +} /* write_cy_cmd */ + + +/* cy_start and cy_stop provide software output flow control as a + function of XON/XOFF, software CTS, and other such stuff. */ + +static void +cy_stop(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_stop ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_stop")) + return; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)(channel); /* index channel */ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + restore_flags(flags); + + return; +} /* cy_stop */ + +static void +cy_start(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_start ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)(channel); + base_addr[CyIER] |= CyTxMpty; + restore_flags(flags); + + return; +} /* cy_start */ + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver + * (also known as the "bottom half"). This can be called any + * number of times for any channel without harm. + */ +static inline void +cy_sched_event(struct cyclades_port *info, int event) +{ + info->event |= 1 << event; /* remember what kind of event and who */ + queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ + mark_bh(CYCLADES_BH); /* then trigger event */ +} /* cy_sched_event */ + + +/* The real interrupt service routines are called + whenever the card wants its hand held--chars + received, out buffer empty, modem change, etc. + */ +static void +cd2401_rxerr_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct tty_struct *tty; + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + unsigned char err, rfoc; + int channel; + char data; + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + + if ((err = base_addr[CyRISR]) & CyTIMEOUT) { + /* This is a receive timeout interrupt, ignore it */ + base_addr[CyREOIR] = CyNOTRANS; + return; + } + + /* Read a byte of data if there is any - assume the error + * is associated with this character */ + + if ((rfoc = base_addr[CyRFOC]) != 0) + data = base_addr[CyRDR]; + else + data = 0; + + /* if there is nowhere to put the data, discard it */ + if(info->tty == 0) { + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return; + } + else { /* there is an open port for this data */ + tty = info->tty; + if(err & info->ignore_status_mask){ + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; + return; + } + if (tty->flip.count < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + if (err & info->read_status_mask){ + if(err & CyBREAK){ + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = data; + if (info->flags & ASYNC_SAK){ + do_SAK(tty); + } + }else if(err & CyFRAME){ + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + *tty->flip.char_buf_ptr++ = data; + }else if(err & CyPARITY){ + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + *tty->flip.char_buf_ptr++ = data; + }else if(err & CyOVERRUN){ + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + *tty->flip.char_buf_ptr++ = 0; + /* + If the flip buffer itself is + overflowing, we still loose + the next incoming character. + */ + if(tty->flip.count < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + } + /* These two conditions may imply */ + /* a normal read should be done. */ + /* else if(data & CyTIMEOUT) */ + /* else if(data & CySPECHAR) */ + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + /* there was a software buffer overrun + and nothing could be done about it!!! */ + } + } + queue_task(&tty->flip.tqueue, &tq_timer); + /* end of service */ + base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS; +} /* cy_rxerr_interrupt */ + +static void +cd2401_modem_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + int mdm_change; + int mdm_status; + + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + + mdm_change = base_addr[CyMISR]; + mdm_status = base_addr[CyMSVR1]; + + if(info->tty == 0){ /* nowhere to put the data, ignore it */ + ; + }else{ + if((mdm_change & CyDCD) + && (info->flags & ASYNC_CHECK_CD)){ + if(mdm_status & CyDCD){ +/* CP('!'); */ + cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) + &&(info->flags & ASYNC_CALLOUT_NOHUP))){ +/* CP('@'); */ + cy_sched_event(info, Cy_EVENT_HANGUP); + } + } + if((mdm_change & CyCTS) + && (info->flags & ASYNC_CTS_FLOW)){ + if(info->tty->stopped){ + if(mdm_status & CyCTS){ + /* !!! cy_start isn't used because... */ + info->tty->stopped = 0; + base_addr[CyIER] |= CyTxMpty; + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + }else{ + if(!(mdm_status & CyCTS)){ + /* !!! cy_stop isn't used because... */ + info->tty->stopped = 1; + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + } + } + } + if(mdm_status & CyDSR){ + } + } + base_addr[CyMEOIR] = 0; +} /* cy_modem_interrupt */ + +static void +cd2401_tx_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + int char_count, saved_cnt; + int outch; + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + +#ifdef CONFIG_REMOTE_DEBUG + if (channel == DEBUG_PORT) { + panic ("TxInt on debug port!!!"); + } +#endif + + info = &cy_port[channel]; + + /* validate the port number (as configured and open) */ + if( (channel < 0) || (NR_PORTS <= channel) ){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + base_addr[CyTEOIR] = CyNOTRANS; + return; + } + info->last_active = jiffies; + if(info->tty == 0){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + base_addr[CyTEOIR] = CyNOTRANS; + return; + } + + /* load the on-chip space available for outbound data */ + saved_cnt = char_count = base_addr[CyTFTC]; + + if(info->x_char) { /* send special char */ + outch = info->x_char; + base_addr[CyTDR] = outch; + char_count--; + info->x_char = 0; + } + + if (info->x_break){ + /* The Cirrus chip requires the "Embedded Transmit + Commands" of start break, delay, and end break + sequences to be sent. The duration of the + break is given in TICs, which runs at HZ + (typically 100) and the PPR runs at 200 Hz, + so the delay is duration * 200/HZ, and thus a + break can run from 1/100 sec to about 5/4 sec. + Need to check these values - RGH 141095. + */ + base_addr[CyTDR] = 0; /* start break */ + base_addr[CyTDR] = 0x81; + base_addr[CyTDR] = 0; /* delay a bit */ + base_addr[CyTDR] = 0x82; + base_addr[CyTDR] = info->x_break*200/HZ; + base_addr[CyTDR] = 0; /* terminate break */ + base_addr[CyTDR] = 0x83; + char_count -= 7; + info->x_break = 0; + } + + while (char_count > 0){ + if (!info->xmit_cnt){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + break; + } + if (info->xmit_buf == 0){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + break; + } + if (info->tty->stopped || info->tty->hw_stopped){ + base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); + break; + } + /* Because the Embedded Transmit Commands have been + enabled, we must check to see if the escape + character, NULL, is being sent. If it is, we + must ensure that there is room for it to be + doubled in the output stream. Therefore we + no longer advance the pointer when the character + is fetched, but rather wait until after the check + for a NULL output character. (This is necessary + because there may not be room for the two chars + needed to send a NULL. + */ + outch = info->xmit_buf[info->xmit_tail]; + if( outch ){ + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR] = outch; + char_count--; + }else{ + if(char_count > 1){ + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR] = outch; + base_addr[CyTDR] = 0; + char_count--; + char_count--; + }else{ + break; + } + } + } + + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } + base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS; +} /* cy_tx_interrupt */ + +static void +cd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp) +{ + struct tty_struct *tty; + struct cyclades_port *info; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + char data; + int char_count; + int save_cnt; + + /* determine the channel and change to that context */ + channel = (u_short ) (base_addr[CyLICR] >> 2); + info = &cy_port[channel]; + info->last_active = jiffies; + save_cnt = char_count = base_addr[CyRFOC]; + +#ifdef CONFIG_REMOTE_DEBUG + if (channel == DEBUG_PORT) { + while (char_count--) { + data = base_addr[CyRDR]; + queueDebugChar(data); + } + } + else +#endif + /* if there is nowhere to put the data, discard it */ + if(info->tty == 0){ + while(char_count--){ + data = base_addr[CyRDR]; + } + }else{ /* there is an open port for this data */ + tty = info->tty; + /* load # characters available from the chip */ + +#ifdef CYCLOM_ENABLE_MONITORING + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + while(char_count--){ + data = base_addr[CyRDR]; + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + continue; + } + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; +#ifdef CYCLOM_16Y_HACK + udelay(10L); +#endif + } + queue_task(&tty->flip.tqueue, &tq_timer); + } + /* end of service */ + base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS; +} /* cy_rx_interrupt */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using cy_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_cyclades_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the cyclades_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. + */ +static void +do_cyclades_bh(void) +{ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ + +static void +do_softint(void *private_) +{ + struct cyclades_port *info = (struct cyclades_port *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } +} /* do_softint */ + + +/* This is called whenever a port becomes active; + interrupts are enabled and DTR & RTS are turned on. + */ +static int +startup(struct cyclades_port * info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; + int channel; + + if (info->flags & ASYNC_INITIALIZED){ + return 0; + } + + if (!info->type){ + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; + } + if (!info->xmit_buf){ + info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); + if (!info->xmit_buf){ + return -ENOMEM; + } + } + + config_setup(info); + + channel = info->line; + +#ifdef SERIAL_DEBUG_OPEN + printk("startup channel %d\n", channel); +#endif + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR); + + base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */ + base_addr[CyMSVR1] = CyRTS; +/* CP('S');CP('1'); */ + base_addr[CyMSVR2] = CyDTR; + +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + + base_addr[CyIER] |= CyRxData; + info->flags |= ASYNC_INITIALIZED; + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + restore_flags(flags); + +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif + return 0; +} /* startup */ + +void +start_xmit( struct cyclades_port *info ) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + channel = info->line; + save_flags(flags); cli(); + base_addr[CyCAR] = channel; + base_addr[CyIER] |= CyTxMpty; + restore_flags(flags); +} /* start_xmit */ + +/* + * This routine shuts down a serial port; interrupts are disabled, + * and DTR is dropped if the hangup on close termio flag is on. + */ +static void +shutdown(struct cyclades_port * info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + if (!(info->flags & ASYNC_INITIALIZED)){ +/* CP('$'); */ + return; + } + + channel = info->line; + +#ifdef SERIAL_DEBUG_OPEN + printk("shutdown channel %d\n", channel); +#endif + + /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE + SENT BEFORE DROPPING THE LINE !!! (Perhaps + set some flag that is read when XMTY happens.) + Other choices are to delay some fixed interval + or schedule some later processing. + */ + save_flags(flags); cli(); + if (info->xmit_buf){ + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + base_addr[CyCAR] = (u_char)channel; + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + base_addr[CyMSVR1] = 0; +/* CP('C');CP('1'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + } + write_cy_cmd(base_addr,CyDIS_RCVR); + /* it may be appropriate to clear _XMIT at + some later date (after testing)!!! */ + + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + +#ifdef SERIAL_DEBUG_OPEN + printk(" done\n"); +#endif + return; +} /* shutdown */ + +/* + * This routine finds or computes the various line characteristics. + */ +static void +config_setup(struct cyclades_port * info) +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + unsigned cflag; + int i; + unsigned char ti, need_init_chan = 0; + + if (!info->tty || !info->tty->termios){ + return; + } + if (info->line == -1){ + return; + } + cflag = info->tty->termios->c_cflag; + + /* baud rate */ + i = cflag & CBAUD; +#ifdef CBAUDEX +/* Starting with kernel 1.1.65, there is direct support for + higher baud rates. The following code supports those + changes. The conditional aspect allows this driver to be + used for earlier as well as later kernel versions. (The + mapping is slightly different from serial.c because there + is still the possibility of supporting 75 kbit/sec with + the Cyclades board.) + */ + if (i & CBAUDEX) { + if (i == B57600) + i = 16; + else if(i == B115200) + i = 18; +#ifdef B78600 + else if(i == B78600) + i = 17; +#endif + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } +#endif + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + } + /* Don't ever change the speed of the console port. It will + * run at the speed specified in bootinfo, or at 19.2K */ + /* Actually, it should run at whatever speed 166Bug was using */ + /* Note info->timeout isn't used at present */ + if (info != serial_console_info) { + info->tbpr = baud_bpr[i]; /* Tx BPR */ + info->tco = baud_co[i]; /* Tx CO */ + info->rbpr = baud_bpr[i]; /* Rx BPR */ + info->rco = baud_co[i] >> 5; /* Rx CO */ + if (baud_table[i] == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor7 = 0; + info->cor6 = 0; + info->cor5 = 0; + info->cor4 = (info->default_threshold + ? info->default_threshold + : baud_cor4[i]); /* receive threshold */ + /* Following two lines added 101295, RGH. */ + /* It is obviously wrong to access CyCORx, and not info->corx here, + * try and remember to fix it later! */ + channel = info->line; + base_addr[CyCAR] = (u_char)channel; + if (C_CLOCAL(info->tty)) { + if (base_addr[CyIER] & CyMdmCh) + base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */ + /* ignore 1->0 modem transitions */ + if (base_addr[CyCOR4] & (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR4] &= ~(CyDSR|CyCTS|CyDCD); + /* ignore 0->1 modem transitions */ + if (base_addr[CyCOR5] & (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR5] &= ~(CyDSR|CyCTS|CyDCD); + } else { + if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh) + base_addr[CyIER] |= CyMdmCh; /* with modem intr */ + /* act on 1->0 modem transitions */ + if ((base_addr[CyCOR4] & (CyDSR|CyCTS|CyDCD)) != (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR4] |= CyDSR|CyCTS|CyDCD; + /* act on 0->1 modem transitions */ + if ((base_addr[CyCOR5] & (CyDSR|CyCTS|CyDCD)) != (CyDSR|CyCTS|CyDCD)) + base_addr[CyCOR5] |= CyDSR|CyCTS|CyDCD; + } + info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP; + info->cor2 = CyETC; + switch(cflag & CSIZE){ + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + info->cor1 |= CyPARITY_O; + }else{ + info->cor1 |= CyPARITY_E; + } + }else{ + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ +#if 0 + /* Don't complcate matters for now! RGH 141095 */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } +#endif + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + + /* CyCMR set once only in mvme167_init_serial() */ + if (base_addr[CyLICR] != channel << 2) + base_addr[CyLICR] = channel << 2; + if (base_addr[CyLIVR] != 0x5c) + base_addr[CyLIVR] = 0x5c; + + /* tx and rx baud rate */ + + if (base_addr[CyCOR1] != info->cor1) + need_init_chan = 1; + if (base_addr[CyTCOR] != info->tco) + base_addr[CyTCOR] = info->tco; + if (base_addr[CyTBPR] != info->tbpr) + base_addr[CyTBPR] = info->tbpr; + if (base_addr[CyRCOR] != info->rco) + base_addr[CyRCOR] = info->rco; + if (base_addr[CyRBPR] != info->rbpr) + base_addr[CyRBPR] = info->rbpr; + + /* set line characteristics according configuration */ + + if (base_addr[CySCHR1] != START_CHAR(info->tty)) + base_addr[CySCHR1] = START_CHAR(info->tty); + if (base_addr[CySCHR2] != STOP_CHAR(info->tty)) + base_addr[CySCHR2] = STOP_CHAR(info->tty); + if (base_addr[CySCRL] != START_CHAR(info->tty)) + base_addr[CySCRL] = START_CHAR(info->tty); + if (base_addr[CySCRH] != START_CHAR(info->tty)) + base_addr[CySCRH] = START_CHAR(info->tty); + if (base_addr[CyCOR1] != info->cor1) + base_addr[CyCOR1] = info->cor1; + if (base_addr[CyCOR2] != info->cor2) + base_addr[CyCOR2] = info->cor2; + if (base_addr[CyCOR3] != info->cor3) + base_addr[CyCOR3] = info->cor3; + if (base_addr[CyCOR4] != info->cor4) + base_addr[CyCOR4] = info->cor4; + if (base_addr[CyCOR5] != info->cor5) + base_addr[CyCOR5] = info->cor5; + if (base_addr[CyCOR6] != info->cor6) + base_addr[CyCOR6] = info->cor6; + if (base_addr[CyCOR7] != info->cor7) + base_addr[CyCOR7] = info->cor7; + + if (need_init_chan) + write_cy_cmd(base_addr,CyINIT_CHAN); + + base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */ + + /* 2ms default rx timeout */ + ti = info->default_timeout ? info->default_timeout : 0x02; + if (base_addr[CyRTPRL] != ti) + base_addr[CyRTPRL] = ti; + if (base_addr[CyRTPRH] != 0) + base_addr[CyRTPRH] = 0; + + /* Set up RTS here also ????? RGH 141095 */ + if(i == 0){ /* baud rate is zero, turn off line */ + if ((base_addr[CyMSVR2] & CyDTR) == CyDTR) + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + }else{ + if ((base_addr[CyMSVR2] & CyDTR) != CyDTR) + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + } + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + restore_flags(flags); + +} /* config_setup */ + + +static void +cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cy_put_char ttyS%d(0x%02x)\n", info->line, ch); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= PAGE_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= PAGE_SIZE - 1; + info->xmit_cnt++; + restore_flags(flags); +} /* cy_put_char */ + + +static void +cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_IO + printk("cy_flush_chars ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = channel; + base_addr[CyIER] |= CyTxMpty; + restore_flags(flags); +} /* cy_flush_chars */ + + +/* This routine gets called when tty_write has put something into + the write_queue. If the port is not already transmitting stuff, + start it off by enabling interrupts. The interrupt service + routine will then ensure that the characters are sent. If the + port is already active, there is no need to kick it. + */ +static int +cy_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + int c, total = 0; + +#ifdef SERIAL_DEBUG_IO + printk("cy_write ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_write")){ + return 0; + } + + if (!tty || !info->xmit_buf || !tmp_buf){ + return 0; + } + + while (1) { + save_flags(flags); cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0){ + restore_flags(flags); + break; + } + + if (from_user) { + down(&tmp_buf_sem); + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + up(&tmp_buf_sem); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + + + if (info->xmit_cnt + && !tty->stopped + && !tty->hw_stopped ) { + start_xmit(info); + } + return total; +} /* cy_write */ + + +static int +cy_write_room(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int ret; + +#ifdef SERIAL_DEBUG_IO + printk("cy_write_room ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_write_room")) + return 0; + ret = PAGE_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} /* cy_write_room */ + + +static int +cy_chars_in_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_IO + printk("cy_chars_in_buffer ttyS%d %d\n", info->line, info->xmit_cnt); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) + return 0; + + return info->xmit_cnt; +} /* cy_chars_in_buffer */ + + +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cy_flush_buffer ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ + + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled or that the + throttle should be released. + */ +static void +cy_throttle(struct tty_struct * tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); + printk("cy_throttle ttyS%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; + } + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = 0; + restore_flags(flags); + + return; +} /* cy_throttle */ + + +static void +cy_unthrottle(struct tty_struct * tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); + printk("cy_unthrottle ttyS%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; + } + + if (I_IXOFF(tty)) { + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; + restore_flags(flags); + + return; +} /* cy_unthrottle */ + +static int +get_serial_info(struct cyclades_port * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + +/* CP('g'); */ + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->line; + tmp.irq = 0; + tmp.flags = info->flags; + tmp.baud_base = 0; /*!!!*/ + tmp.close_delay = info->close_delay; + tmp.custom_divisor = 0; /*!!!*/ + tmp.hub6 = 0; /*!!!*/ + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); + return 0; +} /* get_serial_info */ + +static int +set_serial_info(struct cyclades_port * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct cyclades_port old_info; + +/* CP('s'); */ + if (!new_info) + return -EFAULT; + copy_from_user(&new_serial,new_info,sizeof(new_serial)); + old_info = *info; + + if (!suser()) { + if ((new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != + (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + goto check_and_exit; + } + + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay; + + +check_and_exit: + if (info->flags & ASYNC_INITIALIZED){ + config_setup(info); + return 0; + }else{ + return startup(info); + } +} /* set_serial_info */ + +static int +get_modem_info(struct cyclades_port * info, unsigned int *value) +{ + int channel; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + unsigned char status; + unsigned int result; + + channel = info->line; + + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + status = base_addr[CyMSVR1] | base_addr[CyMSVR2]; + restore_flags(flags); + + result = ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0) + | ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); + cy_put_user(result,(unsigned long *) value); + return 0; +} /* get_modem_info */ + +static int +set_modem_info(struct cyclades_port * info, unsigned int cmd, + unsigned int *value) +{ + int channel; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + unsigned int arg = cy_get_user((unsigned long *) value); + + channel = info->line; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('S');CP('2'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = 0; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('C');CP('2'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; + restore_flags(flags); + }else{ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = 0; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('S');CP('3'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + }else{ + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('C');CP('3'); */ + base_addr[CyMSVR2] = 0; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: dropping DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + restore_flags(flags); + } + break; + default: + return -EINVAL; + } + return 0; +} /* set_modem_info */ + +static void +send_break( struct cyclades_port * info, int duration) +{ /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt ) { + start_xmit(info); + } +} /* send_break */ + +static int +get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) +{ + + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +} + +static int +set_threshold(struct cyclades_port * info, unsigned long value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + channel = info->line; + + info->cor4 &= ~CyREC_FIFO; + info->cor4 |= value & CyREC_FIFO; + base_addr[CyCOR4] = info->cor4; + return 0; +} + +static int +get_threshold(struct cyclades_port * info, unsigned long *value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + unsigned long tmp; + + channel = info->line; + + tmp = base_addr[CyCOR4] & CyREC_FIFO; + cy_put_user(tmp,value); + return 0; +} + +static int +set_default_threshold(struct cyclades_port * info, unsigned long value) +{ + info->default_threshold = value & 0x0f; + return 0; +} + +static int +get_default_threshold(struct cyclades_port * info, unsigned long *value) +{ + cy_put_user(info->default_threshold,value); + return 0; +} + +static int +set_timeout(struct cyclades_port * info, unsigned long value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + + channel = info->line; + + base_addr[CyRTPRL] = value & 0xff; + base_addr[CyRTPRH] = (value >> 8) & 0xff; + return 0; +} + +static int +get_timeout(struct cyclades_port * info, unsigned long *value) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + unsigned long tmp; + + channel = info->line; + + tmp = base_addr[CyRTPRL]; + cy_put_user(tmp,value); + return 0; +} + +static int +set_default_timeout(struct cyclades_port * info, unsigned long value) +{ + info->default_timeout = value & 0xff; + return 0; +} + +static int +get_default_timeout(struct cyclades_port * info, unsigned long *value) +{ + cy_put_user(info->default_timeout,value); + return 0; +} + +static int +cy_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + int ret_val = 0; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl ttyS%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */ +#endif + + switch (cmd) { + case CYGETMON: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct cyclades_monitor)); + if (error){ + ret_val = error; + break; + } + ret_val = get_mon_info(info, (struct cyclades_monitor *)arg); + break; + case CYGETTHRESH: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_threshold(info, (unsigned long *)arg); + break; + case CYSETTHRESH: + ret_val = set_threshold(info, (unsigned long)arg); + break; + case CYGETDEFTHRESH: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_default_threshold(info, (unsigned long *)arg); + break; + case CYSETDEFTHRESH: + ret_val = set_default_threshold(info, (unsigned long)arg); + break; + case CYGETTIMEOUT: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_timeout(info, (unsigned long *)arg); + break; + case CYSETTIMEOUT: + ret_val = set_timeout(info, (unsigned long)arg); + break; + case CYGETDEFTIMEOUT: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned long)); + if (error){ + ret_val = error; + break; + } + ret_val = get_default_timeout(info, (unsigned long *)arg); + break; + case CYSETDEFTIMEOUT: + ret_val = set_default_timeout(info, (unsigned long)arg); + break; + case TCSBRK: /* SVID version: non-zero arg --> no break */ + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; + tty_wait_until_sent(tty,0); + if (!arg) + send_break(info, HZ/4); /* 1/4 second */ + break; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; + tty_wait_until_sent(tty,0); + send_break(info, arg ? arg*(HZ/10) : HZ/4); + break; + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret_val = set_modem_info(info, cmd, (unsigned int *) arg); + break; + +/* The following commands are incompletely implemented!!! */ + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned int *)); + if (error){ + ret_val = error; + break; + } + cy_put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + break; + case TIOCSSOFTCAR: + error = verify_area(VERIFY_READ, (void *) arg + ,sizeof(unsigned long *)); + if (error){ + ret_val = error; + break; + } + + arg = cy_get_user((unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + break; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned int *)); + if (error){ + ret_val = error; + break; + } + ret_val = get_modem_info(info, (unsigned int *) arg); + break; + case TIOCGSERIAL: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct serial_struct)); + if (error){ + ret_val = error; + break; + } + ret_val = get_serial_info(info, + (struct serial_struct *) arg); + break; + case TIOCSSERIAL: + ret_val = set_serial_info(info, + (struct serial_struct *) arg); + break; + default: + ret_val = -ENOIOCTLCMD; + } + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_ioctl done\n"); +#endif + + return ret_val; +} /* cy_ioctl */ + + + + +static void +cy_set_termios(struct tty_struct *tty, struct termios * old_termios) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_set_termios ttyS%d\n", info->line); +#endif + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + config_setup(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->stopped = 0; + cy_start(tty); + } +#ifdef tytso_patch_94Nov25_1726 + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif + + return; +} /* cy_set_termios */ + + +static void +cy_close(struct tty_struct * tty, struct file * filp) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +/* CP('C'); */ +#ifdef SERIAL_DEBUG_OTHER + printk("cy_close ttyS%d\n", info->line); +#endif + + if (!info + || serial_paranoia_check(info, tty->device, "cy_close")){ + return; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_close ttyS%d, count = %d\n", info->line, info->count); +#endif + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count - 1); +#endif + if (--info->count < 0) { + printk("cy_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->count = 0; + } + if (info->count) + return; + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + if (info->flags & ASYNC_INITIALIZED) + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + info->event = 0; + info->tty = 0; + if (tty->ldisc.num != ldiscs[N_TTY].num) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if (tty->ldisc.open) + (tty->ldisc.open)(tty); + } + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_close done\n"); +#endif + + return; +} /* cy_close */ + +/* + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void +cy_hangup(struct tty_struct *tty) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + +#ifdef SERIAL_DEBUG_OTHER + printk("cy_hangup ttyS%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_hangup")) + return; + + shutdown(info); +#if 0 + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->tty = 0; +#endif + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ + + + +/* + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ + */ + +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct cyclades_port *info) +{ + struct wait_queue wait = { current, NULL }; + unsigned long flags; + int channel; + int retval; + volatile u_char *base_addr = (u_char *)BASE_ADDR; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + if (info->flags & ASYNC_HUP_NOTIFY){ + return -EAGAIN; + }else{ + return -ERESTARTSYS; + } + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)){ + return -EBUSY; + } + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, then make the check up front + * and then exit. + */ + if (filp->f_flags & O_NONBLOCK) { + if (info->flags & ASYNC_CALLOUT_ACTIVE){ + return -EBUSY; + } + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttyS%d, count = %d\n", + info->line, info->count);/**/ +#endif + info->count--; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count); +#endif + info->blocked_open++; + + channel = info->line; + + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ + base_addr[CyCAR] = (u_char)channel; + base_addr[CyMSVR1] = CyRTS; +/* CP('S');CP('4'); */ + base_addr[CyMSVR2] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc: %d: raising DTR\n", __LINE__); + printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]); +#endif + } + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + save_flags(flags); cli(); + base_addr[CyCAR] = (u_char)channel; +/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */ + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (base_addr[CyMSVR1] & CyDCD))) { + restore_flags(flags); + break; + } + restore_flags(flags); + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttyS%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)){ + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count); +#endif + } + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttyS%d, count = %d\n", + info->line, info->count);/**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ + +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int +cy_open(struct tty_struct *tty, struct file * filp) +{ + struct cyclades_port *info; + int retval, line; + +/* CP('O'); */ + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (NR_PORTS <= line)){ + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OTHER + printk("cy_open ttyS%d\n", info->line); /* */ +#endif + if (serial_paranoia_check(info, tty->device, "cy_open")){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open ttyS%d, count = %d\n", info->line, info->count);/**/ +#endif + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count); +#endif + tty->driver_data = info; + info->tty = tty; + + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + } + /* + * Start up serial port + */ + retval = startup(info); + if (retval){ + return retval; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("cy_open done\n");/**/ +#endif + return 0; +} /* cy_open */ + + + +/* + * --------------------------------------------------------------------- + * serial167_init() and friends + * + * serial167_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static void +show_version(void) +{ + printk("MVME166/167 cd2401 driver\n"); +} /* show_version */ + +/* initialize chips on card -- return number of valid + chips (which is number of ports/4) */ + +/* + * This initialises the hardware to a reasonable state. It should + * probe the chip first so as to copy 166-Bug setup as a default for + * port 0. It initialises CMR to CyASYNC; that is never done again, so + * as to limit the number of CyINIT_CHAN commands in normal running. + * + * ... I wonder what I should do if this fails ... + */ + +void +mvme167_serial_console_setup(int cflag) +{ + volatile unsigned char* base_addr = (u_char *)BASE_ADDR; + int ch; + u_char spd; + u_char rcor, rbpr, badspeed = 0; + unsigned long flags; + + save_flags(flags); cli(); + + /* + * First probe channel zero of the chip, to see what speed has + * been selected. + */ + + base_addr[CyCAR] = 0; + + rcor = base_addr[CyRCOR] << 5; + rbpr = base_addr[CyRBPR]; + + for (spd = 0; spd < sizeof(baud_bpr); spd++) + if (rbpr == baud_bpr[spd] && rcor == baud_co[spd]) + break; + if (spd >= sizeof(baud_bpr)) { + spd = 14; /* 19200 */ + badspeed = 1; /* Failed to identify speed */ + } + initial_console_speed = spd; + + /* OK, we have chosen a speed, now reset and reinitialise */ + + my_udelay(20000L); /* Allow time for any active o/p to complete */ + if(base_addr[CyCCR] != 0x00){ + /* printk(" chip is never idle (CCR != 0)\n"); */ + return; + } + + base_addr[CyCCR] = CyCHIP_RESET; /* Reset the chip */ + my_udelay(1000L); + + if(base_addr[CyGFRCR] == 0x00){ + /* printk(" chip is not responding (GFRCR stayed 0)\n"); */ + return; + } + + /* + * System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms + * tick + */ + + base_addr[CyTPR] = 10; + + base_addr[CyPILR1] = 0x01; /* Interrupt level for modem change */ + base_addr[CyPILR2] = 0x02; /* Interrupt level for tx ints */ + base_addr[CyPILR3] = 0x03; /* Interrupt level for rx ints */ + + /* + * Attempt to set up all channels to something reasonable, and + * bang out a INIT_CHAN command. We should then be able to limit + * the ammount of fiddling we have to do in normal running. + */ + + for (ch = 3; ch >= 0 ; ch--) { + base_addr[CyCAR] = (u_char)ch; + base_addr[CyIER] = 0; + base_addr[CyCMR] = CyASYNC; + base_addr[CyLICR] = (u_char)ch << 2; + base_addr[CyLIVR] = 0x5c; + base_addr[CyTCOR] = baud_co[spd]; + base_addr[CyTBPR] = baud_bpr[spd]; + base_addr[CyRCOR] = baud_co[spd] >> 5; + base_addr[CyRBPR] = baud_bpr[spd]; + base_addr[CySCHR1] = 'Q' & 0x1f; + base_addr[CySCHR2] = 'X' & 0x1f; + base_addr[CySCRL] = 0; + base_addr[CySCRH] = 0; + base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; + base_addr[CyCOR2] = 0; + base_addr[CyCOR3] = Cy_1_STOP; + base_addr[CyCOR4] = baud_cor4[spd]; + base_addr[CyCOR5] = 0; + base_addr[CyCOR6] = 0; + base_addr[CyCOR7] = 0; + base_addr[CyRTPRL] = 2; + base_addr[CyRTPRH] = 0; + base_addr[CyMSVR1] = 0; + base_addr[CyMSVR2] = 0; + write_cy_cmd(base_addr,CyINIT_CHAN|CyDIS_RCVR|CyDIS_XMTR); + } + + /* + * Now do specials for channel zero.... + */ + + base_addr[CyMSVR1] = CyRTS; + base_addr[CyMSVR2] = CyDTR; + base_addr[CyIER] = CyRxData; + write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR); + + restore_flags(flags); + + my_udelay(20000L); /* Let it all settle down */ + + printk("CD2401 initialised, chip is rev 0x%02x\n", base_addr[CyGFRCR]); + if (badspeed) + printk(" WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x\n", + rcor >> 5, rbpr); +} /* serial_console_init */ + +/* The serial driver boot-time initialization code! + Hardware I/O ports are mapped to character special devices on a + first found, first allocated manner. That is, this code searches + for Cyclom cards in the system. As each is found, it is probed + to discover how many chips (and thus how many ports) are present. + These ports are mapped to the tty ports 64 and upward in monotonic + fashion. If an 8-port card is replaced with a 16-port card, the + port mapping on a following card will shift. + + This approach is different from what is used in the other serial + device driver because the Cyclom is more properly a multiplexer, + not just an aggregation of serial ports on one card. + + If there are more cards with more ports than have been statically + allocated above, a warning is printed and the extra ports are ignored. + */ +int +serial167_init(void) +{ + struct cyclades_port *info; + int good_ports = 0; + int port_num = 0; + int index; + int DefSpeed; +#ifdef notyet + struct sigaction sa; +#endif + + if (!(mvme16x_config &MVME16x_CONFIG_GOT_CD2401)) + return 0; + +#if 0 +scrn[1] = '\0'; +#endif + + show_version(); + + /* Has "console=0,9600n8" been used in bootinfo to change speed? */ + if (serial_console_cflag) + DefSpeed = serial_console_cflag & 0017; + else { + DefSpeed = initial_console_speed; + serial_console_info = &cy_port[0]; + serial_console_cflag = DefSpeed | CS8; +#if 0 + serial_console = 64; /*callout_driver.minor_start*/ +#endif + } + + /* Initialize the tty_driver structure */ + + memset(&cy_serial_driver, 0, sizeof(struct tty_driver)); + cy_serial_driver.magic = TTY_DRIVER_MAGIC; + cy_serial_driver.name = "ttyS"; + cy_serial_driver.major = TTY_MAJOR; + cy_serial_driver.minor_start = 64; + cy_serial_driver.num = NR_PORTS; + cy_serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + cy_serial_driver.subtype = SERIAL_TYPE_NORMAL; + cy_serial_driver.init_termios = tty_std_termios; + cy_serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + cy_serial_driver.flags = TTY_DRIVER_REAL_RAW; + cy_serial_driver.refcount = &serial_refcount; + cy_serial_driver.table = serial_table; + cy_serial_driver.termios = serial_termios; + cy_serial_driver.termios_locked = serial_termios_locked; + cy_serial_driver.open = cy_open; + cy_serial_driver.close = cy_close; + cy_serial_driver.write = cy_write; + cy_serial_driver.put_char = cy_put_char; + cy_serial_driver.flush_chars = cy_flush_chars; + cy_serial_driver.write_room = cy_write_room; + cy_serial_driver.chars_in_buffer = cy_chars_in_buffer; + cy_serial_driver.flush_buffer = cy_flush_buffer; + cy_serial_driver.ioctl = cy_ioctl; + cy_serial_driver.throttle = cy_throttle; + cy_serial_driver.unthrottle = cy_unthrottle; + cy_serial_driver.set_termios = cy_set_termios; + cy_serial_driver.stop = cy_stop; + cy_serial_driver.start = cy_start; + cy_serial_driver.hangup = cy_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + cy_callout_driver = cy_serial_driver; + cy_callout_driver.name = "cua"; + cy_callout_driver.major = TTYAUX_MAJOR; + cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if (tty_register_driver(&cy_serial_driver)) + panic("Couldn't register Cyclom serial driver\n"); + if (tty_register_driver(&cy_callout_driver)) + panic("Couldn't register Cyclom callout driver\n"); + + init_bh(CYCLADES_BH, do_cyclades_bh); + + port_num = 0; + info = cy_port; + for (index = 0; index < 1; index++) { + + good_ports = 4; + + if(port_num < NR_PORTS){ + while( good_ports-- && port_num < NR_PORTS){ + /*** initialize port ***/ + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = index; + info->line = port_num; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = 12; + info->cor1 = CyPARITY_NONE|Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = Cy_1_STOP; + info->cor4 = 0x08; /* _very_ small receive threshold */ + info->cor5 = 0; + info->cor6 = 0; + info->cor7 = 0; + info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */ + info->tco = baud_co[DefSpeed]; /* Tx CO */ + info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */ + info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */ + info->close_delay = 0; + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc: %d: setting count to 0\n", __LINE__); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios =cy_callout_driver.init_termios; + info->normal_termios = cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + /* info->session */ + /* info->pgrp */ +/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/ + info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK + | CyPARITY| CyFRAME| CyOVERRUN; + /* info->timeout */ + + printk("ttyS%1d ", info->line); + port_num++;info++; + if(!(port_num & 7)){ + printk("\n "); + } + } + } + printk("\n"); + } + while( port_num < NR_PORTS){ + info->line = -1; + port_num++;info++; + } +#ifdef CONFIG_REMOTE_DEBUG + debug_setup(); +#endif + if (request_irq (MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, + "cd2401_errors", cd2401_rxerr_interrupt) || + request_irq (MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0, + "cd2401_modem", cd2401_modem_interrupt) || + request_irq (MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0, + "cd2401_txints", cd2401_tx_interrupt) || + request_irq (MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0, + "cd2401_rxints", cd2401_rx_interrupt)) + { + panic ("Couldn't get serial IRQs"); + } + + /* Now we have registered the interrupt handlers, allow the interrupts */ + + pcc2chip[PccSCCMICR] = 0x15; /* Serial ints are level 5 */ + pcc2chip[PccSCCTICR] = 0x15; + pcc2chip[PccSCCRICR] = 0x15; + + pcc2chip[PccIMLR] = 3; /* Allow PCC2 ints above 3!? */ + + return 0; +} /* serial167_init */ + + +#ifdef CYCLOM_SHOW_STATUS +static void +show_status(int line_num) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int channel; + struct cyclades_port * info; + unsigned long flags; + + info = &cy_port[line_num]; + channel = info->line; + printk(" channel %d\n", channel);/**/ + + printk(" cy_port\n"); + printk(" card line flags = %d %d %x\n", + info->card, info->line, info->flags); + printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + (long)info->tty, info->read_status_mask, + info->timeout, info->xmit_fifo_size); + printk(" cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n", + info->cor1, info->cor2, info->cor3, info->cor4, info->cor5, + info->cor6, info->cor7); + printk(" tbpr,tco,rbpr,rco = %d %d %d %d\n", + info->tbpr, info->tco, info->rbpr, info->rco); + printk(" close_delay event count = %d %d %d\n", + info->close_delay, info->event, info->count); + printk(" x_char blocked_open = %x %x\n", + info->x_char, info->blocked_open); + printk(" session pgrp open_wait = %lx %lx %lx\n", + info->session, info->pgrp, (long)info->open_wait); + + + save_flags(flags); cli(); + +/* Global Registers */ + + printk(" CyGFRCR %x\n", base_addr[CyGFRCR]); + printk(" CyCAR %x\n", base_addr[CyCAR]); + printk(" CyRISR %x\n", base_addr[CyRISR]); + printk(" CyTISR %x\n", base_addr[CyTISR]); + printk(" CyMISR %x\n", base_addr[CyMISR]); + printk(" CyRIR %x\n", base_addr[CyRIR]); + printk(" CyTIR %x\n", base_addr[CyTIR]); + printk(" CyMIR %x\n", base_addr[CyMIR]); + printk(" CyTPR %x\n", base_addr[CyTPR]); + + base_addr[CyCAR] = (u_char)channel; + +/* Virtual Registers */ + +#if 0 + printk(" CyRIVR %x\n", base_addr[CyRIVR]); + printk(" CyTIVR %x\n", base_addr[CyTIVR]); + printk(" CyMIVR %x\n", base_addr[CyMIVR]); + printk(" CyMISR %x\n", base_addr[CyMISR]); +#endif + +/* Channel Registers */ + + printk(" CyCCR %x\n", base_addr[CyCCR]); + printk(" CyIER %x\n", base_addr[CyIER]); + printk(" CyCOR1 %x\n", base_addr[CyCOR1]); + printk(" CyCOR2 %x\n", base_addr[CyCOR2]); + printk(" CyCOR3 %x\n", base_addr[CyCOR3]); + printk(" CyCOR4 %x\n", base_addr[CyCOR4]); + printk(" CyCOR5 %x\n", base_addr[CyCOR5]); +#if 0 + printk(" CyCCSR %x\n", base_addr[CyCCSR]); + printk(" CyRDCR %x\n", base_addr[CyRDCR]); +#endif + printk(" CySCHR1 %x\n", base_addr[CySCHR1]); + printk(" CySCHR2 %x\n", base_addr[CySCHR2]); +#if 0 + printk(" CySCHR3 %x\n", base_addr[CySCHR3]); + printk(" CySCHR4 %x\n", base_addr[CySCHR4]); + printk(" CySCRL %x\n", base_addr[CySCRL]); + printk(" CySCRH %x\n", base_addr[CySCRH]); + printk(" CyLNC %x\n", base_addr[CyLNC]); + printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]); + printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]); +#endif + printk(" CyRTPRL %x\n", base_addr[CyRTPRL]); + printk(" CyRTPRH %x\n", base_addr[CyRTPRH]); + printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]); + printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]); + printk(" CyRBPR %x\n", base_addr[CyRBPR]); + printk(" CyRCOR %x\n", base_addr[CyRCOR]); + printk(" CyTBPR %x\n", base_addr[CyTBPR]); + printk(" CyTCOR %x\n", base_addr[CyTCOR]); + + restore_flags(flags); +} /* show_status */ +#endif + + +#if 0 +/* Dummy routine in mvme16x/config.c for now */ + +/* Serial console setup. Called from linux/init/main.c */ + +void console_setup(char *str, int *ints) +{ + char *s; + int baud, bits, parity; + int cflag = 0; + + /* Sanity check. */ + if (ints[0] > 3 || ints[1] > 3) return; + + /* Get baud, bits and parity */ + baud = 2400; + bits = 8; + parity = 'n'; + if (ints[2]) baud = ints[2]; + if ((s = strchr(str, ','))) { + do { + s++; + } while(*s >= '0' && *s <= '9'); + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* Now construct a cflag setting. */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 9600: + cflag |= B9600; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 2400: + default: + cflag |= B2400; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + + serial_console_info = &cy_port[ints[1]]; + serial_console_cflag = cflag; + serial_console = ints[1] + 64; /*callout_driver.minor_start*/ +} +#endif + +/* + * The following is probably out of date for 2.1.x serial console stuff. + * + * The console is registered early on from arch/m68k/kernel/setup.c, and + * it therefore relies on the chip being setup correctly by 166-Bug. This + * seems reasonable, as the serial port has been used to invoke the system + * boot. It also means that this function must not rely on any data + * initialisation performed by serial167_init() etc. + * + * Of course, once the console has been registered, we had better ensure + * that serial167_init() doesn't leave the chip non-functional. + */ + +void serial167_write(struct console *co, const char *str, unsigned count) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + u_char do_lf = 0; + int i = 0; + + save_flags(flags); cli(); + + /* Ensure transmitter is enabled! */ + + port = 0; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_XMTR; + + ier = base_addr[CyIER]; + base_addr[CyIER] = CyTxMpty; + + while (1) { + if (pcc2chip[PccSCCTICR] & 0x20) + { + /* We have a Tx int. Acknowledge it */ + sink = pcc2chip[PccTPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + if (i == count) { + /* Last char of string is now output */ + base_addr[CyTEOIR] = CyNOTRANS; + break; + } + if (do_lf) { + base_addr[CyTDR] = '\n'; + str++; + i++; + do_lf = 0; + } + else if (*str == '\n') { + base_addr[CyTDR] = '\r'; + do_lf = 1; + } + else { + base_addr[CyTDR] = *str++; + i++; + } + base_addr[CyTEOIR] = 0; + } + else + base_addr[CyTEOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); +} + +#ifdef CONFIG_REMOTE_DEBUG +void putDebugChar (int c) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + + save_flags(flags); cli(); + + /* Ensure transmitter is enabled! */ + + port = DEBUG_PORT; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_XMTR; + + ier = base_addr[CyIER]; + base_addr[CyIER] = CyTxMpty; + + while (1) { + if (pcc2chip[PccSCCTICR] & 0x20) + { + /* We have a Tx int. Acknowledge it */ + sink = pcc2chip[PccTPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + base_addr[CyTDR] = c; + base_addr[CyTEOIR] = 0; + break; + } + else + base_addr[CyTEOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); +} + +int getDebugChar() +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + int i, c; + + i = debugiq.out; + if (i != debugiq.in) { + c = debugiq.buf[i]; + if (++i == DEBUG_LEN) + i = 0; + debugiq.out = i; + return c; + } + /* OK, nothing in queue, wait in poll loop */ + + save_flags(flags); cli(); + + /* Ensure receiver is enabled! */ + + port = DEBUG_PORT; + base_addr[CyCAR] = (u_char)port; +#if 0 + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_RCVR; +#endif + ier = base_addr[CyIER]; + base_addr[CyIER] = CyRxData; + + while (1) { + if (pcc2chip[PccSCCRICR] & 0x20) + { + /* We have a Rx int. Acknowledge it */ + sink = pcc2chip[PccRPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + int cnt = base_addr[CyRFOC]; + while (cnt-- > 0) + { + c = base_addr[CyRDR]; + if (c == 0) + printk ("!! debug char is null (cnt=%d) !!", cnt); + else + queueDebugChar (c); + } + base_addr[CyREOIR] = 0; + i = debugiq.out; + if (i == debugiq.in) + panic ("Debug input queue empty!"); + c = debugiq.buf[i]; + if (++i == DEBUG_LEN) + i = 0; + debugiq.out = i; + break; + } + else + base_addr[CyREOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); + + return (c); +} + +void queueDebugChar (int c) +{ + int i; + + i = debugiq.in; + debugiq.buf[i] = c; + if (++i == DEBUG_LEN) + i = 0; + if (i != debugiq.out) + debugiq.in = i; +} + +static void +debug_setup() +{ + unsigned long flags; + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + int i, cflag; + + cflag = B19200; + + save_flags(flags); cli(); + + for (i = 0; i < 4; i++) + { + base_addr[CyCAR] = i; + base_addr[CyLICR] = i << 2; + } + + debugiq.in = debugiq.out = 0; + + base_addr[CyCAR] = DEBUG_PORT; + + /* baud rate */ + i = cflag & CBAUD; + + base_addr[CyIER] = 0; + + base_addr[CyCMR] = CyASYNC; + base_addr[CyLICR] = DEBUG_PORT << 2; + base_addr[CyLIVR] = 0x5c; + + /* tx and rx baud rate */ + + base_addr[CyTCOR] = baud_co[i]; + base_addr[CyTBPR] = baud_bpr[i]; + base_addr[CyRCOR] = baud_co[i] >> 5; + base_addr[CyRBPR] = baud_bpr[i]; + + /* set line characteristics according configuration */ + + base_addr[CySCHR1] = 0; + base_addr[CySCHR2] = 0; + base_addr[CySCRL] = 0; + base_addr[CySCRH] = 0; + base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; + base_addr[CyCOR2] = 0; + base_addr[CyCOR3] = Cy_1_STOP; + base_addr[CyCOR4] = baud_cor4[i]; + base_addr[CyCOR5] = 0; + base_addr[CyCOR6] = 0; + base_addr[CyCOR7] = 0; + + write_cy_cmd(base_addr,CyINIT_CHAN); + write_cy_cmd(base_addr,CyENB_RCVR); + + base_addr[CyCAR] = DEBUG_PORT; /* !!! Is this needed? */ + + base_addr[CyRTPRL] = 2; + base_addr[CyRTPRH] = 0; + + base_addr[CyMSVR1] = CyRTS; + base_addr[CyMSVR2] = CyDTR; + + base_addr[CyIER] = CyRxData; + + restore_flags(flags); + +} /* debug_setup */ + +#endif diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index a5d66bbd8..72d1f6276 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -58,6 +58,8 @@ * Made many more debug printk's a compile time option. * Revision 1.8: Jul 1 1997 * port to linux-2.1.43 kernel. + * Revision 1.9: Oct 9 1998 + * Added stuff for the IO8+/PCI version. . * */ @@ -87,6 +89,7 @@ #include <linux/delay.h> #include <linux/tqueue.h> #include <linux/version.h> +#include <linux/pci.h> /* ************************************************************** */ @@ -173,7 +176,6 @@ DECLARE_TASK_QUEUE(tq_specialix); #define SPECIALIX_TYPE_NORMAL 1 #define SPECIALIX_TYPE_CALLOUT 2 -static struct specialix_board * IRQ_to_board[16] = { NULL, } ; static struct tty_driver specialix_driver, specialix_callout_driver; static int specialix_refcount = 0; static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, }; @@ -331,17 +333,22 @@ extern inline int sx_check_io_range(struct specialix_board * bp) extern inline void sx_request_io_range(struct specialix_board * bp) { - request_region(bp->base, SX_IO_SPACE, "specialix IO8+" ); + request_region(bp->base, + bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE, + "specialix IO8+" ); } extern inline void sx_release_io_range(struct specialix_board * bp) { - release_region(bp->base, SX_IO_SPACE); + release_region(bp->base, + bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE); } /* Must be called with enabled interrupts */ +/* Ugly. Very ugly. Don't use this for anything else than initialization + code */ extern inline void sx_long_delay(unsigned long delay) { unsigned long i; @@ -357,6 +364,8 @@ int sx_set_irq ( struct specialix_board *bp) int virq; int i; + if (bp->flags & SX_BOARD_IS_PCI) + return 1; switch (bp->irq) { /* In the same order as in the docs... */ case 15: virq = 0;break; @@ -484,9 +493,14 @@ static int sx_probe(struct specialix_board *bp) printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", board_No(bp), val1, val2); #endif - if (val1 != 0xb2) { - printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n", - board_No(bp), bp->base); + /* They managed to switch the bit order between the docs and + the IO8+ card. The new PCI card now conforms to old docs. + They changed the PCI docs to reflect the situation on the + old card. */ + val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2; + if (val1 != val2) { + printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n", + board_No(bp), val2, bp->base, val1); return 1; } @@ -868,7 +882,7 @@ static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs) unsigned long loop = 0; int saved_reg; - bp = IRQ_to_board[irq]; + bp = dev_id; if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) { #ifdef SPECIALIX_DEBUG @@ -924,6 +938,25 @@ static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs) * Routines for open & close processing. */ +void turn_ints_off (struct specialix_board *bp) +{ + if (bp->flags & SX_BOARD_IS_PCI) { + /* This was intended for enabeling the interrupt on the + * PCI card. However it seems that it's already enabled + * and as PCI interrupts can be shared, there is no real + * reason to have to turn it off. */ + } + (void) sx_in_off (bp, 0); /* Turn off interrupts. */ +} + +void turn_ints_on (struct specialix_board *bp) +{ + if (bp->flags & SX_BOARD_IS_PCI) { + /* play with the PCI chip. See comment above. */ + } + (void) sx_in (bp, 0); /* Turn ON interrupts. */ +} + /* Called with disabled interrupts */ extern inline int sx_setup_board(struct specialix_board * bp) @@ -933,14 +966,12 @@ extern inline int sx_setup_board(struct specialix_board * bp) if (bp->flags & SX_BOARD_ACTIVE) return 0; - error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL); + error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp); if (error) return error; - IRQ_to_board[bp->irq] = bp; - (void) sx_in (bp, 0); /* Turn ON interrupts. */ - + turn_ints_on (bp); bp->flags |= SX_BOARD_ACTIVE; MOD_INC_USE_COUNT; @@ -956,11 +987,13 @@ extern inline void sx_shutdown_board(struct specialix_board *bp) bp->flags &= ~SX_BOARD_ACTIVE; - free_irq(bp->irq, NULL); - (void) sx_in_off (bp, 0); /* Turn off interrupts. */ +#if SPECIALIX_DEBUG > 2 + printk ("Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp)); +#endif + free_irq(bp->irq, bp); + + turn_ints_off (bp); - IRQ_to_board[bp->irq] = NULL; - MOD_DEC_USE_COUNT; } @@ -1045,12 +1078,14 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p /* Page 48 of version 2.0 of the CL-CD1865 databook */ if (tmp >= 12) { printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" - "Performance degradation is possible.\n", + "Performance degradation is possible.\n" + "Read specialix.txt for more info.\n", port_No (port), tmp); } else { printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" "Warning: overstressing Cirrus chip. " - "This might not work.\n", + "This might not work.\n" + "Read specialix.txt for more info.\n", port_No (port), tmp); } } @@ -1516,8 +1551,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) timeout = jiffies+HZ; while(port->IER & IER_TXEMPTY) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + port->timeout; - schedule(); + schedule_timeout(port->timeout); if (jiffies > timeout) { printk (KERN_INFO "Timeout waiting for close\n"); break; @@ -1536,8 +1570,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) if (port->blocked_open) { if (port->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + port->close_delay; - schedule(); + schedule_timeout(port->close_delay); } wake_up_interruptible(&port->open_wait); } @@ -2153,7 +2186,6 @@ static int sx_init_drivers(void) return 1; } init_bh(SPECIALIX_BH, do_specialix_bh); - memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); memset(&specialix_driver, 0, sizeof(specialix_driver)); specialix_driver.magic = TTY_DRIVER_MAGIC; specialix_driver.name = "ttyW"; @@ -2265,7 +2297,7 @@ int specialix_init(void) int i; int found = 0; - printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n"); + printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n"); printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n"); #ifdef CONFIG_SPECIALIX_RTSCTS printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n"); @@ -2280,6 +2312,35 @@ int specialix_init(void) if (sx_board[i].base && !sx_probe(&sx_board[i])) found++; +#ifdef CONFIG_PCI + if (pci_present()) { + struct pci_dev *pdev = NULL; + unsigned int tint; + + i=0; + while (i <= SX_NBOARD) { + if (sx_board[i].flags & SX_BOARD_PRESENT) { + i++; + continue; + } + pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX, + PCI_DEVICE_ID_SPECIALIX_IO8, + pdev); + if (!pdev) break; + + sx_board[i].irq = pdev->irq; + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint); + /* Mask out the fact that it's IO-space */ + sx_board[i].base = tint & PCI_BASE_ADDRESS_IO_MASK; + + sx_board[i].flags |= SX_BOARD_IS_PCI; + if (!sx_probe(&sx_board[i])) + found ++; + } + } +#endif + if (!found) { sx_release_drivers(); printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n"); @@ -2296,7 +2357,8 @@ int irq [SX_NBOARD] = {0,}; /* * You can setup up to 4 boards. - * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter. + * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter. + * You should specify the IRQs too in that case "irq=....,...". * * More than 4 boards in one computer is not possible, as the card can * only use 4 different interrupts. diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h index e8fc54658..f4ca99ea6 100644 --- a/drivers/char/specialix_io8.h +++ b/drivers/char/specialix_io8.h @@ -43,9 +43,16 @@ #ifdef __KERNEL__ -#define SX_NBOARD 4 +/* You can have max 4 ISA cards in one PC, and I recommend not much +more than a few PCI versions of the card. */ + +#define SX_NBOARD 8 + /* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ #define SX_IO_SPACE 4 +/* The PCI version decodes 8 addresses, but still only 2 are used. */ +#define SX_PCI_IO_SPACE 8 + /* eight ports per board. */ #define SX_NPORT 8 #define SX_BOARD(line) ((line) / SX_NPORT) @@ -93,6 +100,7 @@ struct specialix_board { #define SX_BOARD_PRESENT 0x00000001 #define SX_BOARD_ACTIVE 0x00000002 +#define SX_BOARD_IS_PCI 0x00000004 struct specialix_port { diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 81d389cad..0b2f495b1 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -144,7 +144,7 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); */ static char *stl_drvtitle = "Stallion Multiport Serial Driver"; static char *stl_drvname = "stallion"; -static char *stl_drvversion = "5.4.6"; +static char *stl_drvversion = "5.4.7"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -664,17 +664,21 @@ static unsigned int sc26198_baudtable[] = { * to get at port stats - only not using the port device itself. */ static struct file_operations stl_fsiomem = { - NULL, - NULL, - NULL, - NULL, - NULL, - stl_memioctl, - NULL, - stl_memopen, + NULL, /* llseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + stl_memioctl, /* ioctl */ + NULL, /* mmap */ + stl_memopen, /* open */ NULL, /* flush */ - stl_memclose, - NULL + stl_memclose, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ }; /*****************************************************************************/ @@ -1068,8 +1072,7 @@ static void stl_delay(int len) #endif if (len > 0) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + len; - schedule(); + schedule_timeout(len); current->state = TASK_RUNNING; } } diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index c70ec0e1f..5a4ad1c6f 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.7 1997/11/06 15:57:09 mj Exp $ + * $Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $ * * Linux Magic System Request Key Hacks * @@ -70,12 +70,14 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, printk("Keyboard mode set to XLATE\n"); } break; +#ifdef CONFIG_VT case 'k': /* K -- SAK */ printk("SAK\n"); if (tty) do_SAK(tty); reset_vc(fg_console); break; +#endif case 'b': /* B -- boot immediately */ printk("Resetting\n"); machine_restart(NULL); @@ -83,7 +85,7 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, #ifdef CONFIG_APM case 'o': /* O -- power off */ printk("Power off\n"); - apm_set_power_state(APM_STATE_OFF); + apm_power_off(); break; #endif case 's': /* S -- emergency sync */ @@ -131,8 +133,10 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, default: /* Unknown: help */ if (kbd) printk("unRaw "); +#ifdef CONFIG_VT if (tty) printk("saK "); +#endif printk("Boot " #ifdef CONFIG_APM "Off " @@ -164,7 +168,14 @@ static int is_local_disk(kdev_t dev) /* Guess if the device is a local hard case IDE1_MAJOR: case IDE2_MAJOR: case IDE3_MAJOR: - case SCSI_DISK_MAJOR: + case SCSI_DISK0_MAJOR: + case SCSI_DISK1_MAJOR: + case SCSI_DISK2_MAJOR: + case SCSI_DISK3_MAJOR: + case SCSI_DISK4_MAJOR: + case SCSI_DISK5_MAJOR: + case SCSI_DISK6_MAJOR: + case SCSI_DISK7_MAJOR: return 1; default: return 0; diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 56e2099ba..4878d877e 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -588,9 +588,10 @@ static int wait_for_ready(time_t timeout) /* not ready and no exception && timeout not expired yet */ while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) { /* be `nice` to other processes on long operations... */ - current->timeout = jiffies + 3*HZ/10; /* nap 0.30 sec between checks, */ current->state = TASK_INTERRUPTIBLE; - schedule(); /* but could be woken up earlier by signals... */ + /* nap 0.30 sec between checks, */ + /* but could be woken up earlier by signals... */ + schedule_timeout(3*HZ/10); } /* don't use jiffies for this test because it may have changed by now */ @@ -1360,6 +1361,7 @@ static int do_ioctl_cmd(int cmd) */ static inline void dma_transfer(void) { + unsigned long flags; if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */ outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */ @@ -1369,6 +1371,7 @@ static inline void dma_transfer(void) outb_p(ctlbits, QIC02_CTL_PORT); + flags=claim_dma_lock(); clear_dma_ff(QIC02_TAPE_DMA); set_dma_mode(QIC02_TAPE_DMA, dma_mode); set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done); /* full address */ @@ -1393,6 +1396,9 @@ static inline void dma_transfer(void) /* start computer DMA controller */ enable_dma(QIC02_TAPE_DMA); + + release_dma_lock(flags); + /* block transfer should start now, jumping to the * interrupt routine when done or an exception was detected. */ @@ -1410,6 +1416,7 @@ static int start_dma(short mode, unsigned long bytes_todo) /* assume 'bytes_todo'>0 */ { int stat; + unsigned long flags; tpqputs(TPQD_DEBUG, "start_dma() enter"); TPQDEB({printk(TPQIC02_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);}) @@ -1506,9 +1513,10 @@ static int start_dma(short mode, unsigned long bytes_todo) /* initiate first data block read from/write to the tape controller */ + save_flags(flags); cli(); dma_transfer(); - sti(); + restore_flags(flags); TPQPUTS("start_dma() end"); return TE_OK; @@ -1524,13 +1532,18 @@ static int start_dma(short mode, unsigned long bytes_todo) static void end_dma(unsigned long * bytes_done) { int stat = TE_OK; + unsigned long flags; TIMEROFF; TPQPUTS("end_dma() enter"); + flags=claim_dma_lock(); + disable_dma(QIC02_TAPE_DMA); clear_dma_ff(QIC02_TAPE_DMA); + + release_dma_lock(flags); if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */ outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT); /* back to normal */ @@ -1633,6 +1646,7 @@ static void qic02_tape_times_out(void) static void qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int stat, r, i; + unsigned long flags; TIMEROFF; @@ -1682,10 +1696,14 @@ static void qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs) r = 1; } + flags=claim_dma_lock(); + if ( (i = get_dma_residue(QIC02_TAPE_DMA)) != 0 ) { printk(TPQIC02_NAME ": dma_residue == %x !!!\n", i); r = 1; /* big trouble, but can't do much about it... */ } + + release_dma_lock(flags); if (r) return; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index d2ecd8183..192e5350c 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -126,11 +126,11 @@ static ssize_t tty_write(struct file *, const char *, size_t, loff_t *); static unsigned int tty_poll(struct file *, poll_table *); static int tty_open(struct inode *, struct file *); static int tty_release(struct inode *, struct file *); -static int tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg); +int tty_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); static int tty_fasync(int fd, struct file * filp, int on); #ifdef CONFIG_8xx -extern long console_8xx_init(void); +extern long console_8xx_init(long, long); extern int rs_8xx_init(void); #endif /* CONFIG_8xx */ @@ -390,7 +390,9 @@ void do_tty_hangup(void *data) { struct tty_struct *tty = (struct tty_struct *) data; struct file * filp; + struct file * cons_filp = NULL; struct task_struct *p; + int closecount = 0, n; if (!tty) return; @@ -407,10 +409,13 @@ void do_tty_hangup(void *data) if (!filp->f_dentry->d_inode) continue; if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV || - filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) + filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) { + cons_filp = filp; continue; + } if (filp->f_op != &tty_fops) continue; + closecount++; tty_fasync(-1, filp, 0); filp->f_op = &hung_up_tty_fops; } @@ -470,7 +475,17 @@ void do_tty_hangup(void *data) tty->session = 0; tty->pgrp = -1; tty->ctrl_status = 0; - if (tty->driver.hangup) + /* + * If one of the devices matches a console pointer, we + * cannot just call hangup() because that will cause + * tty->count and state->count to go out of sync. + * So we just call close() the right number of times. + */ + if (cons_filp) { + if (tty->driver.close) + for (n = 0; n < closecount; n++) + tty->driver.close(tty, cons_filp); + } else if (tty->driver.hangup) (tty->driver.hangup)(tty); unlock_kernel(); } @@ -640,7 +655,13 @@ static inline ssize_t do_tty_write( size_t count) { ssize_t ret = 0, written = 0; - + struct inode *inode = file->f_dentry->d_inode; + + up(&inode->i_sem); + if (down_interruptible(&inode->i_atomic_write)) { + down(&inode->i_sem); + return -ERESTARTSYS; + } for (;;) { unsigned long size = PAGE_SIZE*2; if (size > count) @@ -663,6 +684,8 @@ static inline ssize_t do_tty_write( file->f_dentry->d_inode->i_mtime = CURRENT_TIME; ret = written; } + up(&inode->i_atomic_write); + down(&inode->i_sem); return ret; } @@ -845,7 +868,13 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) * Failures after this point use release_mem to clean up, so * there's no need to null out the local pointers. */ - driver->table[idx] = tty; + driver->table[idx] = tty; /* FIXME: this is broken and + probably causes ^D bug. tty->private_date does not (yet) point + to a console, if keypress comes now, await armagedon. + + also, driver->table is accessed from interrupt for vt case, + and this does not look like atomic access at all. */ + if (!*tp_loc) *tp_loc = tp; if (!*ltp_loc) @@ -1109,7 +1138,6 @@ static void release_dev(struct file * filp) * both sides, and we've completed the last operation that could * block, so it's safe to proceed with closing. */ - if (pty_master) { if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", @@ -1124,6 +1152,16 @@ static void release_dev(struct file * filp) } /* + * We've decremented tty->count, so we should zero out + * filp->private_data, to break the link between the tty and + * the file descriptor. Otherwise if close_fp() blocks before + * the the file descriptor is removed from the inuse_filp + * list, check_tty_count() could observe a discrepancy and + * printk a warning message to the user. + */ + filp->private_data = 0; + + /* * Perform some housekeeping before deciding whether to return. * * Set the TTY_CLOSING flag if this was the last open. In the @@ -1157,7 +1195,6 @@ static void release_dev(struct file * filp) /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) return; - filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure..."); @@ -1235,10 +1272,13 @@ retry_open: if (!c) return -ENODEV; device = c->device(c); + filp->f_flags |= O_NONBLOCK; /* Don't let /dev/console block */ noctty = 1; } -#ifdef CONFIG_UNIX98_PTYS + if (device == PTMX_DEV) { +#ifdef CONFIG_UNIX98_PTYS + /* find a free pty. */ int major, minor; struct tty_driver *driver; @@ -1261,15 +1301,21 @@ retry_open: devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start)); noctty = 1; goto init_dev_done; + +#else /* CONFIG_UNIX_98_PTYS */ + + return -ENODEV; + +#endif /* CONFIG_UNIX_98_PTYS */ } -#endif - + retval = init_dev(device, &tty); if (retval) return retval; - /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */ +#ifdef CONFIG_UNIX98_PTYS init_dev_done: +#endif filp->private_data = tty; check_tty_count(tty, "tty_open"); if (tty->driver.type == TTY_DRIVER_TYPE_PTY && @@ -1590,11 +1636,10 @@ static int tiocsetd(struct tty_struct *tty, int *arg) static int send_break(struct tty_struct *tty, int duration) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; tty->driver.break_ctl(tty, -1); if (!signal_pending(current)) - schedule(); + schedule_timeout(duration); tty->driver.break_ctl(tty, 0); if (signal_pending(current)) return -EINTR; @@ -1604,8 +1649,8 @@ static int send_break(struct tty_struct *tty, int duration) /* * Split this up, as gcc can choke on it otherwise.. */ -static int tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +int tty_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) { struct tty_struct *tty, *real_tty; int retval; @@ -2008,12 +2053,19 @@ long __init console_init(long kmem_start, long kmem_end) kmem_start = con_init(kmem_start); #endif #ifdef CONFIG_SERIAL_CONSOLE +#ifdef CONFIG_8xx + kmem_start = console_8xx_init(kmem_start, kmem_end); +#else kmem_start = serial_console_init(kmem_start, kmem_end); +#endif /* CONFIG_8xx */ #endif return kmem_start; } -static struct tty_driver dev_tty_driver, dev_syscons_driver, dev_ptmx_driver; +static struct tty_driver dev_tty_driver, dev_syscons_driver; +#ifdef CONFIG_UNIX98_PTYS +static struct tty_driver dev_ptmx_driver; +#endif #ifdef CONFIG_VT static struct tty_driver dev_console_driver; #endif @@ -2058,6 +2110,7 @@ __initfunc(int tty_init(void)) if (tty_register_driver(&dev_syscons_driver)) panic("Couldn't register /dev/console driver\n"); +#ifdef CONFIG_UNIX98_PTYS dev_ptmx_driver = dev_tty_driver; dev_ptmx_driver.driver_name = "/dev/ptmx"; dev_ptmx_driver.name = dev_ptmx_driver.driver_name + 5; @@ -2068,7 +2121,8 @@ __initfunc(int tty_init(void)) if (tty_register_driver(&dev_ptmx_driver)) panic("Couldn't register /dev/ptmx driver\n"); - +#endif + #ifdef CONFIG_VT dev_console_driver = dev_tty_driver; dev_console_driver.driver_name = "/dev/tty0"; @@ -2096,6 +2150,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_ROCKETPORT rp_init(); #endif +#ifdef CONFIG_MVME16x + serial167_init(); +#endif #ifdef CONFIG_CYCLADES cy_init(); #endif diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 735a955fc..6a1d1c649 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -40,7 +40,7 @@ #define TERMIOS_WAIT 2 #define TERMIOS_TERMIO 4 -void tty_wait_until_sent(struct tty_struct * tty, int timeout) +void tty_wait_until_sent(struct tty_struct * tty, long timeout) { struct wait_queue wait = { current, NULL }; @@ -53,10 +53,8 @@ void tty_wait_until_sent(struct tty_struct * tty, int timeout) return; add_wait_queue(&tty->write_wait, &wait); current->counter = 0; /* make us low-priority */ - if (timeout) - current->timeout = timeout + jiffies; - else - current->timeout = (unsigned) -1; + if (!timeout) + timeout = MAX_SCHEDULE_TIMEOUT; do { #ifdef TTY_DEBUG_WAIT_UNTIL_SENT printk("waiting %s...(%d)\n", tty_name(tty, buf), @@ -67,15 +65,10 @@ void tty_wait_until_sent(struct tty_struct * tty, int timeout) goto stop_waiting; if (!tty->driver.chars_in_buffer(tty)) break; - schedule(); - } while (current->timeout); - if (tty->driver.wait_until_sent) { - if (current->timeout == -1) - timeout = 0; - else - timeout = current->timeout - jiffies; + timeout = schedule_timeout(timeout); + } while (timeout); + if (tty->driver.wait_until_sent) tty->driver.wait_until_sent(tty, timeout); - } stop_waiting: current->state = TASK_RUNNING; remove_wait_queue(&tty->write_wait, &wait); diff --git a/drivers/char/tuner.c b/drivers/char/tuner.c index c640ff321..7f7471e19 100644 --- a/drivers/char/tuner.c +++ b/drivers/char/tuner.c @@ -70,6 +70,8 @@ static struct tunertype tuners[] = { 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,732}, {"TEMIC PAL_I", TEMIC, PAL_I, 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623}, + {"Temic 4036 FY5 NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732}, }; /* ---------------------------------------------------------------------- */ @@ -142,8 +144,7 @@ static void set_radio_freq(struct tuner *t, int freq) if (debug) { UNLOCK_I2C_BUS(t->bus); current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ/10; - schedule(); + schedule_timeout(HZ/10); LOCK_I2C_BUS(t->bus); if (tuner_islocked (t)) diff --git a/drivers/char/tuner.h b/drivers/char/tuner.h index 3fb77de47..439cc530d 100644 --- a/drivers/char/tuner.h +++ b/drivers/char/tuner.h @@ -30,6 +30,7 @@ #define TUNER_PHILIPS_PAL 5 #define TUNER_TEMIC_NTSC 6 #define TUNER_TEMIC_PAL_I 7 +#define TUNER_TEMIC_4036FY5_NTSC 8 #define NOTUNER 0 #define PAL 1 diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 2ba0bd030..def6e7039 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -53,6 +53,9 @@ extern int init_colour_qcams(struct video_init *); #ifdef CONFIG_VIDEO_BWQCAM extern int init_bw_qcams(struct video_init *); #endif +#ifdef CONFIG_VIDEO_PLANB +extern int init_planbs(struct video_init *); +#endif #ifdef CONFIG_RADIO_AZTECH extern int aztech_init(struct video_init *); #endif @@ -65,6 +68,9 @@ extern int fmi_init(struct video_init *); #ifdef CONFIG_RADIO_MIROPCM20 extern int pcm20_init(struct video_init *); #endif +#ifdef CONFIG_VIDEO_PMS +extern int init_pms_cards(struct video_init *); +#endif static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BT848 @@ -83,6 +89,9 @@ static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_PMS {"PMS", init_pms_cards}, #endif +#ifdef CONFIG_VIDEO_PLANB + {"planb", init_planbs}, +#endif #ifdef CONFIG_RADIO_AZTECH {"Aztech", aztech_init}, #endif @@ -333,7 +342,7 @@ int videodev_init(void) { struct video_init *vfli = video_init_list; - printk(KERN_INFO "Linux video capture interface: v0.01 ALPHA\n"); + printk(KERN_INFO "Linux video capture interface: v1.00\n"); if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops)) { printk("video_dev: unable to get major %d\n", VIDEO_MAJOR); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 01c973576..d365e30a2 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -57,6 +57,11 @@ extern struct tty_driver console_driver; struct vt_struct *vt_cons[MAX_NR_CONSOLES]; +/* Keyboard type: Default is KB_101, but can be set by machine + * specific code. + */ +unsigned char keyboard_type = KB_101; + #ifndef __alpha__ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); #endif @@ -74,45 +79,6 @@ unsigned int video_scan_lines; #define GPNUM (GPLAST - GPFIRST + 1) /* - * This function is called when the size of the physical screen has been - * changed. If either the row or col argument is nonzero, set the appropriate - * entry in each winsize structure for all the virtual consoles, then - * send SIGWINCH to all processes with a virtual console as controlling - * tty. - */ - -static int -kd_size_changed(int row, int col) -{ - struct task_struct *p; - int i; - - if ( !row && !col ) return 0; - - for ( i = 0 ; i < MAX_NR_CONSOLES ; i++ ) - { - if ( console_driver.table[i] ) - { - if ( row ) console_driver.table[i]->winsize.ws_row = row; - if ( col ) console_driver.table[i]->winsize.ws_col = col; - } - } - - read_lock(&tasklist_lock); - for_each_task(p) - { - if ( p->tty && MAJOR(p->tty->device) == TTY_MAJOR && - MINOR(p->tty->device) <= MAX_NR_CONSOLES && MINOR(p->tty->device) ) - { - send_sig(SIGWINCH, p, 1); - } - } - read_unlock(&tasklist_lock); - - return 0; -} - -/* * Generates sound of some frequency for some number of clock ticks * * If freq is 0, will turn off sound, else will turn it on for that time. @@ -502,7 +468,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, /* * this is naive. */ - ucval = KB_101; + ucval = keyboard_type; goto setchar; #ifndef __alpha__ @@ -554,7 +520,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, * explicitly blank/unblank the screen if switching modes */ if (arg == KD_TEXT) - do_unblank_screen(); + unblank_screen(); else do_blank_screen(1); return 0; @@ -782,7 +748,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (arg == 0 || arg > MAX_NR_CONSOLES) return -ENXIO; arg--; - i = vc_allocate(arg, 0); + i = vc_allocate(arg); if (i) return i; set_console(arg); @@ -834,7 +800,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, */ int newvt = vt_cons[console]->vt_newvt; vt_cons[console]->vt_newvt = -1; - i = vc_allocate(newvt, 0); + i = vc_allocate(newvt); if (i) return i; /* @@ -894,8 +860,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return i; __get_user(ll, &vtsizes->v_rows); __get_user(cc, &vtsizes->v_cols); - i = vc_resize_all(ll, cc); - return i ? i : kd_size_changed(ll, cc); + return vc_resize_all(ll, cc); } case VT_RESIZEX: @@ -943,12 +908,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if ( clin ) video_font_height = clin; - i = vc_resize_all(ll, cc); - if (i) - return i; - - kd_size_changed(ll, cc); - return 0; + return vc_resize_all(ll, cc); } case PIO_FONT: { @@ -1202,7 +1162,7 @@ void complete_change_console(unsigned int new_console) * unblank the screen later. */ old_vc_mode = vt_cons[fg_console]->vc_mode; - update_screen(new_console); + switch_screen(new_console); /* * If this new console is under process control, send it a signal @@ -1240,15 +1200,11 @@ void complete_change_console(unsigned int new_console) if (old_vc_mode != vt_cons[new_console]->vc_mode) { if (vt_cons[new_console]->vc_mode == KD_TEXT) - do_unblank_screen(); + unblank_screen(); else do_blank_screen(1); } - /* Set the colour palette for this VT */ - if (vt_cons[new_console]->vc_mode == KD_TEXT) - set_palette() ; - /* * Wake anyone waiting for their VT to activate */ diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c index a42c35fe3..a5e0fac4e 100644 --- a/drivers/char/wdt.c +++ b/drivers/char/wdt.c @@ -23,6 +23,7 @@ * Alan Cox : Fixed the reboot problem (as noted by * Matt Crocker). * Alan Cox : Added wdt= boot option + * Alan Cox : Cleaned up copy/user stuff */ #include <linux/config.h> @@ -191,7 +192,6 @@ static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr) { unsigned short c=inb_p(WDT_RT); unsigned char cp; - int err; /* Can't seek (pread) on this device */ if (ptr != &file->f_pos) @@ -200,13 +200,11 @@ static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr) switch(MINOR(file->f_dentry->d_inode->i_rdev)) { case TEMP_MINOR: - err=verify_area(VERIFY_WRITE, buf, 1); - if(err) - return err; c*=11; c/=15; cp=c+7; - copy_to_user(buf,&cp,1); + if(copy_to_user(buf,&cp,1)) + return -EFAULT; return 1; default: return -EINVAL; @@ -216,7 +214,6 @@ static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr) static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int i; static struct watchdog_info ident= { WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER @@ -231,20 +228,10 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: return -ENOIOCTLCMD; case WDIOC_GETSUPPORT: - i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info)); - if (i) - return i; - else - return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); + return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_GETSTATUS: - i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int)); - if (i) - return i; - else - { - return put_user(wdt_status(),(int *)arg); - } + return put_user(wdt_status(),(int *)arg); case WDIOC_GETBOOTSTATUS: return put_user(0, (int *)arg); case WDIOC_KEEPALIVE: |