summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Config.in27
-rw-r--r--drivers/char/Makefile101
-rw-r--r--drivers/char/adbmouse.c187
-rw-r--r--drivers/char/apm_bios.c1346
-rw-r--r--drivers/char/bttv.c625
-rw-r--r--drivers/char/bttv.h20
-rw-r--r--drivers/char/bw-qcam.c11
-rw-r--r--drivers/char/c-qcam.c6
-rw-r--r--drivers/char/console.c344
-rw-r--r--drivers/char/console_macros.h3
-rw-r--r--drivers/char/consolemap.c18
-rw-r--r--drivers/char/cyclades.c18
-rw-r--r--drivers/char/dsp56k.c3
-rw-r--r--drivers/char/epca.c3
-rw-r--r--drivers/char/esp.c47
-rw-r--r--drivers/char/fbmem.c661
-rw-r--r--drivers/char/ftape/lowlevel/fdc-io.c11
-rw-r--r--drivers/char/ftape/lowlevel/ftape-io.c7
-rw-r--r--drivers/char/ftape/zftape/zftape-buffers.c3
-rw-r--r--drivers/char/hfmodem/main.c12
-rw-r--r--drivers/char/i2c.c2
-rw-r--r--drivers/char/istallion.c38
-rw-r--r--drivers/char/joystick.c861
-rw-r--r--drivers/char/joystick/Config.in19
-rw-r--r--drivers/char/joystick/Makefile112
-rw-r--r--drivers/char/joystick/joy-amiga.c157
-rw-r--r--drivers/char/joystick/joy-analog.c208
-rw-r--r--drivers/char/joystick/joy-analog.h297
-rw-r--r--drivers/char/joystick/joy-assasin.c423
-rw-r--r--drivers/char/joystick/joy-console.c596
-rw-r--r--drivers/char/joystick/joy-db9.c406
-rw-r--r--drivers/char/joystick/joy-gravis.c401
-rw-r--r--drivers/char/joystick/joy-lightning.c366
-rw-r--r--drivers/char/joystick/joy-logitech.c371
-rw-r--r--drivers/char/joystick/joy-sidewinder.c476
-rw-r--r--drivers/char/joystick/joy-thrustmaster.c331
-rw-r--r--drivers/char/joystick/joy-turbografx.c281
-rw-r--r--drivers/char/joystick/joystick.c1231
-rw-r--r--drivers/char/keyboard.c7
-rw-r--r--drivers/char/lp.c402
-rw-r--r--drivers/char/lp_m68k.c6
-rw-r--r--drivers/char/mac_SCC.c13
-rw-r--r--drivers/char/mem.c38
-rw-r--r--drivers/char/misc.c54
-rw-r--r--drivers/char/msp3400.c9
-rw-r--r--drivers/char/n_tty.c16
-rw-r--r--drivers/char/pc_keyb.c386
-rw-r--r--drivers/char/pc_keyb.h111
-rw-r--r--drivers/char/pcxx.c3
-rw-r--r--drivers/char/pms.c154
-rw-r--r--drivers/char/psaux.c693
-rw-r--r--drivers/char/pty.c2
-rw-r--r--drivers/char/qpmouse.c373
-rw-r--r--drivers/char/radio-sf16fmi.c45
-rw-r--r--drivers/char/random.c42
-rw-r--r--drivers/char/riscom8.c6
-rw-r--r--drivers/char/rocket.c9
-rw-r--r--drivers/char/rtc.c6
-rw-r--r--drivers/char/saa5249.c27
-rw-r--r--drivers/char/selection.c2
-rw-r--r--drivers/char/serial.c23
-rw-r--r--drivers/char/serial167.c2983
-rw-r--r--drivers/char/specialix.c110
-rw-r--r--drivers/char/specialix_io8.h10
-rw-r--r--drivers/char/stallion.c29
-rw-r--r--drivers/char/sysrq.c17
-rw-r--r--drivers/char/tpqic02.c24
-rw-r--r--drivers/char/tty_io.c95
-rw-r--r--drivers/char/tty_ioctl.c19
-rw-r--r--drivers/char/tuner.c5
-rw-r--r--drivers/char/tuner.h1
-rw-r--r--drivers/char/videodev.c11
-rw-r--r--drivers/char/vt.c70
-rw-r--r--drivers/char/wdt.c23
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 < &registered_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(&current->sigmask_lock);
old_sigmask = current->blocked;
@@ -409,9 +410,10 @@ int fdc_interrupt_wait(unsigned int time)
recalc_sigpending(current);
spin_unlock_irq(&current->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(&current->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(&current->sigmask_lock);
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + delay;
- schedule();
+ schedule_timeout(delay);
spin_lock_irq(&current->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: