diff options
Diffstat (limited to 'drivers/char')
82 files changed, 11192 insertions, 7347 deletions
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog index 95d6741c5..eddd9e674 100644 --- a/drivers/char/ChangeLog +++ b/drivers/char/ChangeLog @@ -33,13 +33,13 @@ Sat Nov 22 07:53:36 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> it. * serial.c (autoconfig): Change 16750 test to hopefully eliminate - false results by people with strange 16550A's being - detected as 16750's. Hopefully 16750's will still be - detected as 16750, and other wierd UART's won't get poorly + false results by people with strange 16550As being + detected as 16750s. Hopefully 16750s will still be + detected as 16750, and other weird UARTs won't get poorly autodetected. If this doesn't work, I'll have to disable - the auto identification for the 16750.... + the auto identification for the 16750. - * tty_io.c (tty_hangup): Now do actually do the tty hangup + * tty_io.c (tty_hangup): Now actually do the tty hangup processing during the timer processing, and disable interrupts while doing the hangup processing. This avoids several nasty race conditions which happened when the @@ -84,7 +84,7 @@ Thu Feb 27 01:53:08 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> * serial.c (change_speed): Add support for the termios flag CMSPAR, which allows the user to select stick parity. - (i.e, if PARODD is set, the the parity bit is always 1; if + (i.e, if PARODD is set, the parity bit is always 1; if PARRODD is not set, then the parity bit is always 0). Wed Feb 26 19:03:10 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> @@ -129,7 +129,7 @@ Wed Feb 12 14:50:44 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> * serial.c: Update routines to use the new 2.1 memory access routines. -Wed Dec 4 07:51:52 1996 Theodre Ts'o <tytso@localhost.mit.edu> +Wed Dec 4 07:51:52 1996 Theodore Ts'o <tytso@localhost.mit.edu> * serial.c (change_speed): Use save_flags(); cli() and restore_flags() in order to ensure we don't accidentally @@ -139,7 +139,7 @@ Wed Dec 4 07:51:52 1996 Theodre Ts'o <tytso@localhost.mit.edu> should be off this whole time, but we eventually will want to reduce this window. -Thu Nov 21 10:05:22 1996 Theodre Ts'o <tytso@localhost.mit.edu> +Thu Nov 21 10:05:22 1996 Theodore Ts'o <tytso@localhost.mit.edu> * tty_ioctl.c (tty_wait_until_sent): Always check the driver wait_until_ready routine, even if there are no characters @@ -174,7 +174,7 @@ Thu Nov 21 10:05:22 1996 Theodre Ts'o <tytso@localhost.mit.edu> DTR and RTS. DTR and RTS are only be changed on the transition to or from the B0 state. (rs_close): Wait for the characters to drain based on - info->timeout. At low baud rates (50bps), it may take a + info->timeout. At low baud rates (50 bps), it may take a long time for the FIFO to completely drain out! (rs_wait_until_sent): Fixed timeout handling. Now releases control to the scheduler, but checks frequently @@ -479,7 +479,7 @@ Sat Feb 18 12:13:51 1995 Theodore Y. Ts'o (tytso@rt-11) Fri Feb 17 09:34:09 1995 Theodore Y. Ts'o (tytso@rt-11) * serial.c (rs_interrupt_single, rs_interrupt, rs_interrupt_multi): - Change the the number of passes made from 64 to be 256, + Change the number of passes made from 64 to be 256, configurable with the #define RS_ISR_PASS_LIMIT. * serial.c (rs_init, set_serial_info, get_serial_info, rs_close): diff --git a/drivers/char/Config.in b/drivers/char/Config.in index fad48a260..eef6d8f66 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -39,14 +39,10 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS fi tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL - if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then - int ' DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1 - int ' Receive FIFO trigger level' CONFIG_ESPSERIAL_RX_TRIGGER 768 - int ' Transmit FIFO trigger level' CONFIG_ESPSERIAL_TX_TRIGGER 768 - int ' Hardware flow off level' CONFIG_ESPSERIAL_FLOW_OFF 1016 - int ' Hardware flow on level' CONFIG_ESPSERIAL_FLOW_ON 944 - int ' Receiver timeout' CONFIG_ESPSERIAL_RX_TMOUT 128 - fi +fi +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 fi if [ "$CONFIG_PARPORT" != "n" ]; then dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT @@ -114,31 +110,35 @@ bool 'Enhanced Real Time Clock Support' CONFIG_RTC if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then bool 'Tadpole ANA H8 Support' CONFIG_H8 fi + tristate 'Video For Linux' CONFIG_VIDEO_DEV if [ "$CONFIG_VIDEO_DEV" != "n" ]; then + dep_tristate 'AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK $CONFIG_VIDEO_DEV + if [ "$CONFIG_RADIO_RTRACK" = "y" ]; then + hex ' RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 20f + fi + dep_tristate 'Aztech/Packard Bell Radio' CONFIG_RADIO_AZTECH $CONFIG_VIDEO_DEV + if [ "$CONFIG_RADIO_AZTECH" = "y" ]; then + hex ' Aztech/Packard Bell I/O port (0x350 or 0x358)' CONFIG_RADIO_AZTECH_PORT 350 + fi dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV if [ "$CONFIG_PARPORT" != "n" ]; then dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV fi dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV - #dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV - if [ "$CONFIG_VIDEO_SAA5249" != "n" ]; then - define_bool CONFIG_BUS_I2C $CONFIG_VIDEO_SAA5249 + dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV + dep_tristate 'SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV + if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then + hex ' SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284 fi - if [ "$CONFIG_VIDEO_BT848" != "n" ]; then - define_bool CONFIG_BUS_I2C $CONFIG_VIDEO_BT848 + dep_tristate 'Zoltrix Radio' CONFIG_RADIO_ZOLTRIX $CONFIG_VIDEO_DEV + if [ "$CONFIG_RADIO_ZOLTRIX" != "n" ]; 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 -bool 'Radio Device Support' CONFIG_MISC_RADIO -if [ "$CONFIG_MISC_RADIO" != "n" ]; then - bool ' AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK - if [ "$CONFIG_RADIO_RTRACK" != "n" ]; then - hex ' RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f - fi -fi mainmenu_option next_comment comment 'Ftape, the floppy tape device driver' diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b598d7769..9f2a484e0 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -24,8 +24,11 @@ L_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o LX_OBJS := pty.o ifdef CONFIG_VT -L_OBJS += console.o vt.o vc_screen.o consolemap.o consolemap_deftbl.o -LX_OBJS += selection.o +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) @@ -249,6 +252,16 @@ ifdef CONFIG_SGI_GRAPHICS M = y endif +ifeq ($(CONFIG_MACMOUSE),y) +M = y +L_OBJS += macmouse.o +else + ifeq ($(CONFIG_MACMOUSE),m) + M_OBJS += macmouse.o + MM = m + endif +endif + ifdef CONFIG_SUN_MOUSE M = y endif @@ -309,26 +322,30 @@ else endif ifeq ($(CONFIG_BUS_I2C),y) - LX_OBJS += i2c.o + L_I2C=y else ifeq ($(CONFIG_BUS_I2C),m) - MX_OBJS += i2c.o + M_I2C=y endif endif ifeq ($(CONFIG_VIDEO_BT848),y) L_OBJS += bttv.o msp3400.o tuner.o +L_I2C=y else ifeq ($(CONFIG_VIDEO_BT848),m) M_OBJS += bttv.o msp3400.o tuner.o + M_I2C=y endif endif ifeq ($(CONFIG_VIDEO_SAA5249),y) L_OBJS += saa5249.o +L_I2C=y else ifeq ($(CONFIG_VIDEO_SAA5249),m) M_OBJS += saa5249.o + M_I2C=y endif endif @@ -356,12 +373,37 @@ else endif endif -ifeq ($(CONFIG_MISC_RADIO),y) -L_OBJS += radio.o - ifeq ($(CONFIG_RADIO_RTRACK),y) - L_OBJS += rtrack.o +ifeq ($(CONFIG_RADIO_AZTECH),y) +L_OBJS += radio-aztech.o +else + ifeq ($(CONFIG_RADIO_AZTECH),m) + M_OBJS += radio-aztech.o endif -endif +endif + +ifeq ($(CONFIG_RADIO_SF16FMI),y) +L_OBJS += radio-sf16fmi.o +else + ifeq ($(CONFIG_RADIO_SF16FMI),m) + M_OBJS += radio-sf16fmi.o + endif +endif + +ifeq ($(CONFIG_RADIO_RTRACK),y) +L_OBJS += radio-aimslab.o +else + ifeq ($(CONFIG_RADIO_RTRACK),m) + M_OBJS += radio-aimslab.o + endif +endif + +ifeq ($(CONFIG_RADIO_ZOLTRIX),y) +L_OBJS += radio-zoltrix.o +else + ifeq ($(CONFIG_RADIO_ZOLTRIX),m) + M_OBJS += radio-zoltrix.o + endif +endif ifeq ($(CONFIG_QIC02_TAPE),y) L_OBJS += tpqic02.o @@ -401,16 +443,15 @@ else endif endif -ifdef CONFIG_VT - ifdef CONFIG_TGA_CONSOLE - L_OBJS += tga.o - else - ifdef CONFIG_VGA_CONSOLE - L_OBJS += vga.o vesa_blank.o - endif - endif +ifeq ($(L_I2C),y) +LX_OBJS += i2c.o +else + ifeq ($(M_I2C),y) + MX_OBJS += i2c.o + endif endif + ifeq ($(CONFIG_HFMODEM),y) ALL_SUB_DIRS += hfmodem SUB_DIRS += hfmodem @@ -427,7 +468,7 @@ include $(TOPDIR)/Rules.make fastdep: conmakehash: conmakehash.c - $(HOSTCC) -o conmakehash conmakehash.c + $(HOSTCC) $(HOSTCFLAGS) -o conmakehash conmakehash.c consolemap_deftbl.c: $(FONTMAPFILE) conmakehash ./conmakehash $(FONTMAPFILE) > consolemap_deftbl.c diff --git a/drivers/char/README.epca b/drivers/char/README.epca index 3561608f1..0efccae58 100644 --- a/drivers/char/README.epca +++ b/drivers/char/README.epca @@ -44,7 +44,7 @@ The four user programs listed below are described in this document: as it does not require the rebuilding of the kernel. In order to use lilo to configure Digi boards at boot time an appropriate append command should be added to /etc/lilo.conf below the appropriate label decleration. - See footer 4. The append commands format is a string of comma seperated + See footer 4. The append commands format is a string of comma separated identifiers or integers used to configure supported boards. These six values in order are: diff --git a/drivers/char/adbmouse.c b/drivers/char/adbmouse.c new file mode 100644 index 000000000..35e0c833b --- /dev/null +++ b/drivers/char/adbmouse.c @@ -0,0 +1,304 @@ +/* + * Macintosh ADB Mouse driver for Linux + * + * 27 Oct 1997 Michael Schmitz + * logitech fixes by anthony tong + * + * Apple mouse protocol according to: + * + * Device code shamelessly stolen from: + */ +/* + * Atari Mouse Driver for Linux + * by Robert de Vries (robert@and.nl) 19Jul93 + * + * 16 Nov 1994 Andreas Schwab + * Compatibility with busmouse + * Support for three button mouse (shamelessly stolen from MiNT) + * third button wired to one of the joystick directions on joystick 1 + * + * 1996/02/11 Andreas Schwab + * Module support + * Allow multiple open's + */ + +#include <linux/module.h> + +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/init.h> + +#include <asm/adb_mouse.h> +#include <asm/segment.h> +#include <asm/processor.h> + +static struct mouse_status mouse; +static int adb_mouse_x_threshold = 2, adb_mouse_y_threshold = 2; +static int adb_mouse_buttons = 0; + +extern void (*adb_mouse_interrupt_hook) (char *, int); + +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 int buttons = 7; + + /* + Handler 1 -- 100cpi original Apple mouse protocol. + Handler 2 -- 200cpi original Apple mouse protocol. + + For Apple's standard one-button mouse protocol the data array will + 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. + */ + + /* + 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. + 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. + */ + + /* + * '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 + */ + + /* 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; + 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 ); + + if (console_loglevel >= 8) + printk(" %X %X %X buttons %x dx %d dy %d \n", + buf[1], buf[2], buf[3], mouse.buttons, mouse.dx, mouse.dy); + + mouse.ready = 1; + wake_up_interruptible(&mouse.wait); + if (mouse.fasyncptr) + kill_fasync(mouse.fasyncptr, SIGIO); + +} + +static int fasync_mouse(struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(filp, on, &mouse.fasyncptr); + if (retval < 0) + return retval; + return 0; +} + +static int release_mouse(struct inode *inode, struct file *file) +{ + fasync_mouse(file, 0); + if (--mouse.active) + return 0; + + adb_mouse_interrupt_hook = NULL; + MOD_DEC_USE_COUNT; + return 0; +} + +static int open_mouse(struct inode *inode, struct file *file) +{ + if (mouse.active++) + return 0; + + mouse.ready = 0; + + mouse.dx = mouse.dy = 0; + adb_mouse_buttons = 0; + MOD_INC_USE_COUNT; + adb_mouse_interrupt_hook = adb_mouse_interrupt; + return 0; +} + +static ssize_t write_mouse(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t read_mouse(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + int dx, dy, buttons; + + if (count < 3) + return -EINVAL; + if (!mouse.ready) + return -EAGAIN; + dx = mouse.dx; + dy = mouse.dy; + buttons = mouse.buttons; + if (dx > 127) + dx = 127; + else if (dx < -128) + dx = -128; + if (dy > 127) + dy = 127; + else if (dy < -128) + dy = -128; + mouse.dx -= dx; + mouse.dy -= dy; + if (mouse.dx == 0 && mouse.dy == 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; + return count; +} + +static unsigned int mouse_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &mouse.wait, wait); + if (mouse.ready) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations adb_mouse_fops = { + NULL, /* mouse_seek */ + read_mouse, + write_mouse, + NULL, /* mouse_readdir */ + mouse_poll, + NULL, /* mouse_ioctl */ + NULL, /* mouse_mmap */ + open_mouse, + release_mouse, + NULL, + fasync_mouse, +}; + +#define ADB_MOUSE_MINOR 10 + +static struct miscdevice adb_mouse = { + ADB_MOUSE_MINOR, "adbmouse", &adb_mouse_fops +}; + +__initfunc(int adb_mouse_init(void)) +{ + mouse.active = 0; + mouse.ready = 0; + mouse.wait = NULL; + + if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) + return -ENODEV; + printk(KERN_INFO "Macintosh ADB mouse installed.\n"); + misc_register(&adb_mouse); + return 0; +} + + +#define MIN_THRESHOLD 1 +#define MAX_THRESHOLD 20 /* more seems not reasonable... */ + +__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]; + } + } + +} + +#ifdef MODULE +#include <asm/setup.h> + +int init_module(void) +{ + return adb_mouse_init(); +} + +void cleanup_module(void) +{ + misc_deregister(&adb_mouse); +} +#endif diff --git a/drivers/char/amikeyb.c b/drivers/char/amikeyb.c index 9647caf90..164fc5df5 100644 --- a/drivers/char/amikeyb.c +++ b/drivers/char/amikeyb.c @@ -301,13 +301,13 @@ __initfunc(int amiga_keyb_init(void)) return -EIO; /* setup key map */ - memcpy(plain_map, amiplain_map, sizeof(plain_map)); - memcpy(shift_map, amishift_map, sizeof(shift_map)); - memcpy(altgr_map, amialtgr_map, sizeof(altgr_map)); - memcpy(ctrl_map, amictrl_map, sizeof(ctrl_map)); - memcpy(shift_ctrl_map, amishift_ctrl_map, sizeof(shift_ctrl_map)); - memcpy(alt_map, amialt_map, sizeof(alt_map)); - memcpy(ctrl_alt_map, amictrl_alt_map, sizeof(ctrl_alt_map)); + memcpy(key_maps[0], amiplain_map, sizeof(plain_map)); + memcpy(key_maps[1], amishift_map, sizeof(plain_map)); + memcpy(key_maps[2], amialtgr_map, sizeof(plain_map)); + memcpy(key_maps[4], amictrl_map, sizeof(plain_map)); + memcpy(key_maps[5], amishift_ctrl_map, sizeof(plain_map)); + memcpy(key_maps[8], amialt_map, sizeof(plain_map)); + memcpy(key_maps[12], amictrl_alt_map, sizeof(plain_map)); /* * Initialize serial data direction. @@ -341,3 +341,8 @@ int amiga_kbdrate( struct kbd_repeat *k ) return( 0 ); } + +/* for "kbd-reset" cmdline param */ +__initfunc(void amiga_kbd_reset_setup(char *str, int *ints)) +{ +} diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c index c461cf709..e9e964c43 100644 --- a/drivers/char/apm_bios.c +++ b/drivers/char/apm_bios.c @@ -38,7 +38,7 @@ * 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 the time + * 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 @@ -515,6 +515,8 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) 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) { @@ -532,6 +534,7 @@ static int apm_get_battery_status(u_short which, return (error >> 8); return APM_SUCCESS; } +#endif static inline int apm_engage_power_management(u_short device) { diff --git a/drivers/char/atarimouse.c b/drivers/char/atarimouse.c index 707e9d9e2..113395dba 100644 --- a/drivers/char/atarimouse.c +++ b/drivers/char/atarimouse.c @@ -21,10 +21,10 @@ #include <linux/random.h> #include <linux/poll.h> #include <linux/init.h> +#include <linux/busmouse.h> #include <asm/setup.h> #include <asm/atarikb.h> -#include <asm/atari_mouse.h> #include <asm/uaccess.h> static struct mouse_status mouse; @@ -160,15 +160,21 @@ static struct miscdevice atari_mouse = { __initfunc(int atari_mouse_init(void)) { - mouse.active = 0; - mouse.ready = 0; - mouse.wait = NULL; + int r; - if (!MACH_IS_ATARI) - return -ENODEV; - printk(KERN_INFO "Atari mouse installed.\n"); - misc_register(&atari_mouse); - return 0; + if (!MACH_IS_ATARI) + return -ENODEV; + + mouse.active = 0; + mouse.ready = 0; + mouse.wait = NULL; + + r = misc_register(&atari_mouse); + if (r) + return r; + + printk(KERN_INFO "Atari mouse installed.\n"); + return 0; } @@ -201,8 +207,6 @@ __initfunc(void atari_mouse_setup( char *str, int *ints )) } #ifdef MODULE -#include <asm/setup.h> - int init_module(void) { return atari_mouse_init(); diff --git a/drivers/char/bt848.h b/drivers/char/bt848.h index 5a76ea02a..a50e31a5c 100644 --- a/drivers/char/bt848.h +++ b/drivers/char/bt848.h @@ -40,6 +40,7 @@ #define BT848_DSTATUS_FIELD (1<<5) #define BT848_DSTATUS_NUML (1<<4) #define BT848_DSTATUS_CSEL (1<<3) +#define BT848_DSTATUS_PLOCK (1<<2) #define BT848_DSTATUS_LOF (1<<1) #define BT848_DSTATUS_COF (1<<0) @@ -55,10 +56,12 @@ #define BT848_IFORM_XTAUTO (3<<3) #define BT848_IFORM_XTBOTH (3<<3) #define BT848_IFORM_NTSC 1 +#define BT848_IFORM_NTSC_J 2 #define BT848_IFORM_PAL_BDGHI 3 #define BT848_IFORM_PAL_M 4 #define BT848_IFORM_PAL_N 5 #define BT848_IFORM_SECAM 6 +#define BT848_IFORM_PAL_NC 7 #define BT848_IFORM_AUTO 0 #define BT848_IFORM_NORM 7 @@ -114,6 +117,12 @@ #define BT848_SCLOOP_HFILT_QCIF (2<<3) #define BT848_SCLOOP_HFILT_ICON (3<<3) +#define BT848_SCLOOP_PEAK (1<<7) +#define BT848_SCLOOP_HFILT_MINP (1<<3) +#define BT848_SCLOOP_HFILT_MEDP (2<<3) +#define BT848_SCLOOP_HFILT_MAXP (3<<3) + + #define BT848_OFORM 0x048 #define BT848_OFORM_RANGE (1<<7) #define BT848_OFORM_CORE0 (0<<5) @@ -213,6 +222,7 @@ #define BT848_VBI_PACK_DEL_EXT_FRAME 2 #define BT848_VBI_PACK_DEL_VBI_PKT_HI 1 + #define BT848_INT_STAT 0x100 #define BT848_INT_MASK 0x104 @@ -318,4 +328,15 @@ #define BT848_RISC_SKIP123 (0x0a<<28) #define BT848_RISC_WRITE1S23 (0x0b<<28) + + +/* Bt848A and Bt849 only !! */ +#define BT848_TGLB 0x080 +#define BT848_TGCTRL 0x084 +#define BT848_FCAP 0x0E8 +#define BT848_PLL_F_LO 0x0F0 +#define BT848_PLL_F_HI 0x0F4 +#define BT848_PLL_XCI 0x0F8 + + #endif diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index 97d5794f1..8bb3f203e 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -24,22 +24,25 @@ TODO: - * think of some good ioctls for Video4Linux to handle - YUV, planar YUV, ... grabs and sell them to AC :-) * move norm from tuner to channel struct!? 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? + * extra modules for tda9850, tda8425, any volunteers??? + * support 15bpp */ #include <linux/module.h> -#include <linux/bios32.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/malloc.h> +#include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/pci.h> #include <linux/signal.h> @@ -55,7 +58,7 @@ #include <linux/version.h> #include <asm/uaccess.h> -#include "i2c.h" +#include <linux/i2c.h> #include "bttv.h" #include "tuner.h" @@ -153,6 +156,7 @@ static void * rvmalloc(unsigned long size) mem=vmalloc(size); if (mem) { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr=(unsigned long) mem; while (size > 0) { @@ -193,7 +197,7 @@ static void rvfree(void * mem, unsigned long size) static int fbuffer_alloc(struct bttv *btv) { if(!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000); + btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); else printk(KERN_ERR "bttv: Double alloc of fbuffer!\n"); if(!btv->fbuffer) @@ -242,7 +246,7 @@ static int I2CRead(struct i2c_bus *bus, unsigned char addr) stat=btread(BT848_INT_STAT); if (stat & BT848_INT_I2CDONE) break; - udelay(1000); + mdelay(1); } if (!i) @@ -284,7 +288,7 @@ static int I2CWrite(struct i2c_bus *bus, unsigned char addr, unsigned char b1, stat=btread(BT848_INT_STAT); if (stat & BT848_INT_I2CDONE) break; - udelay(1000); + mdelay(1); } if (!i) @@ -349,9 +353,9 @@ void attach_inform(struct i2c_bus *bus, int id) break; case I2C_DRIVERID_TUNER: btv->have_tuner = 1; - if (btv->type == BTTV_MIRO) + if (btv->tuner_type != -1) { - tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7; + tunertype=btv->tuner_type; i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, TUNER_SET_TYPE,&tunertype); } @@ -393,28 +397,50 @@ static struct i2c_bus bttv_i2c_bus_template = /* ----------------------------------------------------------------------- */ + +struct tvcard +{ + int inputs; + int tuner; + int svhs; + u32 gpiomask; + u32 muxsel[8]; + u32 audiomux[6]; /* Tuner, Radio, internal, external, mute, stereo */ +}; + +static struct tvcard tvcards[] = +{ + /* default */ + { 3, 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}}, + /* Hauppauge */ + { 3, 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}}, + /* Intel??? */ + { 3, 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}}, + /* AVerMedia TVPhone */ + { 3, 0, 2,15, { 2, 3, 1, 1}, {12, 0,11,11, 0}}, + /* Matrix Vision MV-Delta */ + { 5,-1, 4, 0, { 2, 3, 1, 0, 0}}, + /* Fly Video II */ + { 3, 0, 2, 0xc00, { 2, 3, 1, 1}, + {0, 0xc00, 0x800, 0x400, 0xc00, 0}}, +}; +#define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) + + /* * Tuner, Radio, internal, external and mute */ -static unsigned char audiomuxs[][5] = -{ - { 0x00, 0x00, 0x00, 0x00, 0x00}, /* unknown */ - { 0x02, 0x00, 0x00, 0x00, 0x0a}, /* MIRO */ - { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Hauppauge */ - { 0x04, 0x00, 0x02, 0x03, 0x01}, /* STB */ - { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Intel??? */ - { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000 */ - { 0x0c, 0x00, 0x0b, 0x0b, 0x00}, /* AVerMedia TVPhone */ -}; - static void audio(struct bttv *btv, int mode) { - /* enable least significant GPIO output nibble */ - btwrite(0x0f, BT848_GPIO_OUT_EN); - - /* select direct input */ - btwrite(0x00, BT848_GPIO_REG_INP); + btaor(tvcards[btv->type].gpiomask, ~tvcards[btv->type].gpiomask, + BT848_GPIO_OUT_EN); switch (mode) { @@ -437,12 +463,17 @@ static void audio(struct bttv *btv, int mode) break; } /* if audio mute or not in H-lock, turn audio off */ - if ((btv->audio&AUDIO_MUTE) || - (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))) + if ((btv->audio&AUDIO_MUTE) +#if 0 + || + (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) +#endif + ) mode=AUDIO_OFF; if ((mode == 0) && (btv->radio)) mode = 1; - btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA); + btaor(tvcards[btv->type].audiomux[mode], + ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA); } @@ -469,14 +500,77 @@ static void bt848_cap(struct bttv *btv, uint state) } } -static void bt848_muxsel(struct bttv *btv, uint input) + +/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/ + +static int set_pll(struct bttv *btv) +{ + int i; + + if (!btv->pll) + return 0; + if ((btread(BT848_IFORM)&BT848_IFORM_XT0)) + { + /* printk ("switching PLL off\n");*/ + btwrite(0x00,BT848_TGCTRL); + btwrite(0x00,BT848_PLL_XCI); + btv->pll&=~2; + return 0; + } + + /* do not set pll again if already active */ + if (btv->pll&2) + return 1; + + /* printk ("setting PLL for PAL/SECAM\n");*/ + + btwrite(0x00,BT848_TGCTRL); + btwrite(0xf9,BT848_PLL_F_LO); + btwrite(0xdc,BT848_PLL_F_HI); + btwrite(0x8e,BT848_PLL_XCI); + + /* Ugh ugh ugh .. schedule ? */ + udelay(100000); + for (i=0; i<100; i++) + { + if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK)) + btwrite(0,BT848_DSTATUS); + else + { + btwrite(0x08,BT848_TGCTRL); + btv->pll|=2; + return 1; + } + udelay(10000); + } + return -1; +} + +static void bt848_muxsel(struct bttv *btv, unsigned int input) { - input&=3; + btwrite(tvcards[btv->type].gpiomask, BT848_GPIO_OUT_EN); /* This seems to get rid of some synchronization problems */ btand(~(3<<5), BT848_IFORM); - udelay(10000); + mdelay(10); + input %= tvcards[btv->type].inputs; + if (input==tvcards[btv->type].svhs) + { + btor(BT848_CONTROL_COMP, BT848_E_CONTROL); + btor(BT848_CONTROL_COMP, BT848_O_CONTROL); + } + else + { + btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); + btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); + } + btaor((tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM); + audio(btv, (input!=tvcards[btv->type].tuner) ? + AUDIO_EXTERN : AUDIO_TUNER); + btaor(tvcards[btv->type].muxsel[input]>>4, + ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA); +#if 0 if (input==3) { btor(BT848_CONTROL_COMP, BT848_E_CONTROL); @@ -491,6 +585,7 @@ static void bt848_muxsel(struct bttv *btv, uint input) input=3; btaor(((input+2)&3)<<5, ~(3<<5), BT848_IFORM); audio(btv, input ? AUDIO_EXTERN : AUDIO_TUNER); +#endif } @@ -540,8 +635,60 @@ int fmtbppx2[16] = { 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 }; -static int make_vrisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, - unsigned int *vbuf, unsigned short width, unsigned short height, unsigned short fmt) +int palette2fmt[] = { + 0, + BT848_COLOR_FMT_Y8, + BT848_COLOR_FMT_RGB8, + BT848_COLOR_FMT_RGB16, + BT848_COLOR_FMT_RGB24, + BT848_COLOR_FMT_RGB32, + BT848_COLOR_FMT_RGB15, + BT848_COLOR_FMT_YUY2, + BT848_COLOR_FMT_BtYUV, + -1, + -1, + -1, + BT848_COLOR_FMT_RAW, + BT848_COLOR_FMT_YCrCb422, + BT848_COLOR_FMT_YCrCb411, +}; +#define PALETTEFMT_MAX 11 + +static int make_rawrisctab(struct bttv *btv, unsigned int *ro, + unsigned int *re, unsigned int *vbuf) +{ + unsigned long line; + unsigned long bpl=1024; + unsigned long vadr=(unsigned long) vbuf; + + *(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0; + *(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0; + + /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY + is 2. We'll have to handle this inside the IRQ handler ... */ + + for (line=0; line < 640; line++) + { + *(ro++)=BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL; + *(ro++)=kvirt_to_bus(vadr); + *(re++)=BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL; + *(re++)=kvirt_to_bus(vadr+BTTV_MAX_FBUF/2); + vadr+=bpl; + } + + *(ro++)=BT848_RISC_JUMP; + *(ro++)=btv->bus_vbi_even; + *(re++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16); + *(re++)=btv->bus_vbi_odd; + + return 0; +} + + +static int make_vrisctab(struct bttv *btv, unsigned int *ro, + unsigned int *re, + unsigned int *vbuf, unsigned short width, + unsigned short height, unsigned short fmt) { unsigned long line; unsigned long bpl; /* bytes per line */ @@ -551,6 +698,9 @@ static int make_vrisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, int inter; unsigned long vadr=(unsigned long) vbuf; + if (btv->gfmt==BT848_COLOR_FMT_RAW) + return make_rawrisctab(btv, ro, re, vbuf); + inter = (height>btv->win.cropheight/2) ? 1 : 0; bpl=width*fmtbppx2[fmt&0xf]/2; @@ -652,6 +802,7 @@ static inline void write_risc_segment(unsigned int **rp, unsigned long line_adr, *x+=dx; } + /* * Set the registers for the size we have specified. Don't bother * trying to understand this without the BT848 manual in front of @@ -683,10 +834,16 @@ static struct tvnorm tvnorms[] = { 944, 186, 922, 0x20}, /* PAL-M */ { 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), - 780, 186, 922, 0x16}, + 780, 135, 754, 0x16}, /* PAL-N */ { 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1), 944, 186, 922, 0x20}, + /* PAL-NC */ + { 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), + 944, 186, 922, 0x20}, + /* NTSC-Japan */ + { 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), + 780, 135, 754, 0x16}, }; #define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) @@ -709,7 +866,7 @@ static inline void bt848_set_eogeo(struct bttv *btv, int odd, u8 vtc, btwrite(vtc, BT848_E_VTC+off); btwrite(hscale>>8, BT848_E_HSCALE_HI+off); btwrite(hscale&0xff, BT848_E_HSCALE_LO+off); - btaor((vscale>>8), 0xc0, BT848_E_VSCALE_HI+off); + btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off); btwrite(vscale&0xff, BT848_E_VSCALE_LO+off); btwrite(hactive&0xff, BT848_E_HACTIVE_LO+off); btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off); @@ -747,6 +904,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); + + set_pll(btv); btwrite(fmt, BT848_COLOR_FMT); hactive=width; @@ -788,30 +947,22 @@ int bpp2fmt[4] = { static void bt848_set_winsize(struct bttv *btv) { unsigned short format; - int bpp; - bpp=fmtbppx2[btv->win.color_fmt&0x0f]/2; - if (btv->win.bpp == 0) - { - btv->win.bpp=bpp; - format=btv->win.color_fmt; - } - else if (btv->win.bpp!=bpp) - btv->win.color_fmt=format=bpp2fmt[(btv->win.bpp-1)&3]; - else - format=btv->win.color_fmt; + btv->win.color_fmt=format= (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : + bpp2fmt[(btv->win.bpp-1)&3]; /* RGB8 seems to be a 9x5x5 GRB color cube starting at * color 16. Why the h... can't they even mention this in the - * datasheet??? [AC - because its a standard format so I guess - * it never occured them] - * Enable dithering in this mode + * data sheet? [AC - because it's a standard format so I guess + * it never occurred to them] + * Enable dithering in this mode. */ +#if 0 if (format==BT848_COLOR_FMT_RGB8) btand(~0x10, BT848_CAP_CTL); else btor(0x10, BT848_CAP_CTL); - +#endif bt848_set_geo(btv,btv->win.width, btv->win.height, format); } @@ -876,6 +1027,8 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) if(fbuffer_alloc(btv)) return -ENOBUFS; } + if(btv->grabbing>1) + return -ENOBUFS; /* * No grabbing past the end of the buffer! @@ -887,7 +1040,7 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) if(mp->height <0 || mp->width <0) return -EINVAL; - if(mp->height>480 || mp->width>640) + if(mp->height>576 || mp->width>768) return -EINVAL; /* @@ -900,20 +1053,30 @@ static int vgrab(struct bttv *btv, struct video_mmap *mp) * Ok load up the BT848 */ - vbuf=(unsigned int *)(btv->fbuffer+0x144000*mp->frame); - if (!(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) - return -EAGAIN; - ro=btv->grisc+(((btv->grabcount++)&1) ? 2048 :0); - re=ro+1024; + vbuf=(unsigned int *)(btv->fbuffer+BTTV_MAX_FBUF*mp->frame); +/* if (!(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) + return -EAGAIN; */ + ro=btv->grisc+(((btv->grabcount++)&1) ? 4096 :0); + re=ro+2048; btv->gwidth=mp->width; btv->gheight=mp->height; - btv->gfmt=mp->format; + if (mp->format > PALETTEFMT_MAX) + return -EINVAL; + btv->gfmt=palette2fmt[mp->format]; + if(btv->gfmt==-1) + return -EINVAL; + make_vrisctab(btv, ro, re, vbuf, btv->gwidth, btv->gheight, btv->gfmt); /* bt848_set_risc_jmps(btv); */ btor(3, BT848_CAP_CTL); btor(3, BT848_GPIO_DMA_CTL); - btv->gro=virt_to_bus(ro); - btv->gre=virt_to_bus(re); + if (btv->grabbing) { + btv->gro_next=virt_to_bus(ro); + btv->gre_next=virt_to_bus(re); + } else { + btv->gro=virt_to_bus(ro); + btv->gre=virt_to_bus(re); + } if (!(btv->grabbing++)) btv->risc_jmp[12]=BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ; /* interruptible_sleep_on(&btv->capq); */ @@ -990,7 +1153,7 @@ static int bttv_open(struct video_device *dev, int flags) find_vga(); btv->fbuffer=NULL; if (!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000); + btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); if (!btv->fbuffer) { btv->user--; @@ -1014,7 +1177,7 @@ static void bttv_close(struct video_device *dev) bt848_set_risc_jmps(btv); if(btv->fbuffer) - rvfree((void *) btv->fbuffer, 2*0x144000); + rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF); btv->fbuffer=0; MOD_DEC_USE_COUNT; } @@ -1411,8 +1574,9 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) strcpy(v.name, "Television"); v.rangelow=0; v.rangehigh=0xFFFFFFFF; - v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC; + v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; v.mode = btv->win.norm; + v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; @@ -1439,13 +1603,15 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case VIDIOCGPICT: { struct video_picture p=btv->picture; - if(btv->win.bpp==1) + if(btv->win.depth==8) p.palette=VIDEO_PALETTE_HI240; - if(btv->win.bpp==2) + if(btv->win.depth==15) + p.palette=VIDEO_PALETTE_RGB555; + if(btv->win.depth==16) p.palette=VIDEO_PALETTE_RGB565; - if(btv->win.bpp==3) + if(btv->win.depth==24) p.palette=VIDEO_PALETTE_RGB24; - if(btv->win.bpp==4) + if(btv->win.depth==32) p.palette=VIDEO_PALETTE_RGB32; if(copy_to_user(arg, &p, sizeof(p))) @@ -1455,6 +1621,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case VIDIOCSPICT: { struct video_picture p; + int format; if(copy_from_user(&p, arg,sizeof(p))) return -EFAULT; /* We want -128 to 127 we get 0-65535 */ @@ -1466,8 +1633,15 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) bt848_hue(btv, (p.hue>>8)-128); /* 0-511 */ bt848_contrast(btv, p.contrast>>7); - btv->picture=p; - return 0; + btv->picture = p; + + /* set palette if bpp matches */ + if (p.palette < sizeof(palette2fmt)/sizeof(int)) { + format = palette2fmt[p.palette]; + if (fmtbppx2[format&0x0f]/2 == btv->win.bpp) + btv->win.color_fmt = format; + } + return 0; } case VIDIOCSWIN: { @@ -1571,7 +1745,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.base=(void *)btv->win.vidadr; v.height=btv->win.sheight; v.width=btv->win.swidth; - v.depth=btv->win.bpp*8; + v.depth=btv->win.depth; v.bytesperline=btv->win.bpl; if(copy_to_user(arg, &v,sizeof(v))) return -EFAULT; @@ -1585,18 +1759,13 @@ 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!=16 && v.depth!=24 && v.depth!=32) + if(v.depth!=8 && v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32) return -EINVAL; - if (v.base) - /* also handle virtual base addresses */ - if ((unsigned int)v.base>=0xe0000000UL) - btv->win.vidadr=(uint)v.base; - else - btv->win.vidadr=PAGE_OFFSET| - uvirt_to_bus((uint)v.base); + btv->win.vidadr=(unsigned long)v.base; btv->win.sheight=v.height; btv->win.swidth=v.width; - btv->win.bpp=v.depth/8; + btv->win.bpp=((v.depth+1)&0x38)/8; + btv->win.depth=v.depth; btv->win.bpl=v.bytesperline; DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", @@ -1635,13 +1804,21 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) strcpy(v.name,"TV"); if (btv->have_msp3400) { - v.flags|=VIDEO_AUDIO_VOLUME; - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_VOLUME,&(v.volume)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_STEREO,&(v.mode)); + v.flags|=VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_GET_VOLUME,&(v.volume)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_GET_BASS,&(v.bass)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_GET_TREBLE,&(v.treble)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_GET_STEREO,&(v.mode)); } else v.mode = VIDEO_SOUND_MONO; if(copy_to_user(arg,&v,sizeof(v))) @@ -1655,28 +1832,37 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; if(v.flags&VIDEO_AUDIO_MUTE) audio(btv, AUDIO_MUTE); - if(v.audio<0||v.audio>2) + /* One audio source per tuner */ + if(v.audio!=0) return -EINVAL; bt848_muxsel(btv,v.audio); if(!(v.flags&VIDEO_AUDIO_MUTE)) audio(btv, AUDIO_UNMUTE); if (btv->have_msp3400) { - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_VOLUME,&(v.volume)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_STEREO,&(v.mode)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_SET_VOLUME,&(v.volume)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_SET_BASS,&(v.bass)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_SET_TREBLE,&(v.treble)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_SET_STEREO,&(v.mode)); } btv->audio_dev=v; return 0; } case VIDIOCSYNC: - if (btv->grabbing && btv->grab==btv->lastgrab) + if (!btv->grabbing) + return -EAGAIN; + if (btv->grab==btv->lastgrab) interruptible_sleep_on(&btv->capq); - btv->lastgrab=btv->grab; + btv->lastgrab++; return 0; case BTTV_WRITEE: @@ -1728,7 +1914,7 @@ static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long si unsigned long start=(unsigned long) adr; unsigned long page,pos; - if (size>2*0x144000) + if (size>2*BTTV_MAX_FBUF) return -EINVAL; if (!btv->fbuffer) { @@ -1757,6 +1943,7 @@ static struct video_device bttv_template= bttv_close, bttv_read, bttv_write, + NULL, /* poll */ bttv_ioctl, bttv_mmap, bttv_init_done, @@ -1810,6 +1997,20 @@ static long vbi_read(struct video_device *v, char *buf, unsigned long count, return count; } +static unsigned int vbi_poll(struct video_device *dev, struct file *file, + poll_table *wait) +{ + struct bttv *btv=(struct bttv *)(dev-2); + unsigned int mask = 0; + + poll_wait(file, &btv->vbiq, wait); + + if (btv->vbip < VBIBUF_SIZE) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + static int vbi_open(struct video_device *dev, int flags) { struct bttv *btv=(struct bttv *)(dev-2); @@ -1847,6 +2048,7 @@ static struct video_device vbi_template= vbi_close, vbi_read, bttv_write, + vbi_poll, vbi_ioctl, NULL, /* no mmap yet */ bttv_init_done, @@ -1943,6 +2145,7 @@ static struct video_device radio_template= radio_close, radio_read, /* just returns -EINVAL */ bttv_write, /* just returns -EINVAL */ + NULL, /* no poll */ radio_ioctl, NULL, /* no mmap */ bttv_init_done, /* just returns 0 */ @@ -1960,18 +2163,24 @@ struct vidbases }; static struct vidbases vbs[] = { - { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, - "Matrox Millennium", PCI_BASE_ADDRESS_1}, - { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1}, - { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX, "ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT, + "ATI MACH64 GT", PCI_BASE_ADDRESS_0}, { PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, - "Number Nine Imagine 128", PCI_BASE_ADDRESS_0}, { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, "DEC DC21030", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, + "Matrox Millennium", PCI_BASE_ADDRESS_1}, + { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, + "Matrox Millennium II", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, + "Matrox Millennium II AGP", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1}, + { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, + "Number Nine Imagine 128", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0}, }; @@ -1991,77 +2200,69 @@ static uint dec_offsets[4] = { static int find_vga(void) { - unsigned int devfn, class, vendev; - unsigned short vendor, device, badr; - int found=0, bus=0, i, tga_type; + unsigned short badr; + int found = 0, i, tga_type; unsigned int vidadr=0; + struct pci_dev *dev; - for (devfn = 0; devfn < 0xff; devfn++) + for (dev = pci_devices; dev != NULL; dev = dev->next) { - if (PCI_FUNC(devfn) != 0) + if (dev->class != PCI_CLASS_NOT_DEFINED_VGA && + ((dev->class) >> 16 != PCI_BASE_CLASS_DISPLAY)) + { continue; - pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev); - if (vendev == 0xffffffff || vendev == 0x00000000) + } + if (PCI_FUNC(dev->devfn) != 0) continue; - pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device); - pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class); - class = class >> 16; -/* if (class == PCI_CLASS_DISPLAY_VGA) {*/ - if ((class>>8) == PCI_BASE_CLASS_DISPLAY || - /* Number 9 GXE64Pro needs this */ - class == PCI_CLASS_NOT_DEFINED_VGA) + + badr=0; + printk(KERN_INFO "bttv: PCI display adapter: "); + for (i=0; i<NR_CARDS; i++) { - badr=0; - printk(KERN_INFO "bttv: PCI display adapter: "); - for (i=0; i<NR_CARDS; i++) - { - if (vendor==vbs[i].vendor) - { - if (vbs[i].device) - if (vbs[i].device!=device) - continue; - printk("%s.\n", vbs[i].name); - badr=vbs[i].badr; - break; - } - } - if (!badr) - { - printk(KERN_ERR "bttv: Unknown video memory base address.\n"); - continue; - } - pcibios_read_config_dword(bus, devfn, badr, &vidadr); - if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) - { - printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n"); - printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n"); - continue; - } - vidadr &= PCI_BASE_ADDRESS_MEM_MASK; - if (!vidadr) + if (dev->vendor == vbs[i].vendor) { - printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!"); - continue; + if (vbs[i].device) + if (vbs[i].device!=dev->device) + continue; + printk("%s.\n", vbs[i].name); + badr=vbs[i].badr; + break; } - - if (vendor==PCI_VENDOR_ID_DEC) - if (device==PCI_DEVICE_ID_DEC_TGA) + } + if (!badr) + { + printk(KERN_ERR "bttv: Unknown video memory base address.\n"); + continue; + } + pci_read_config_dword(dev, badr, &vidadr); + if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) + { + printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n"); + printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n"); + continue; + } + vidadr &= PCI_BASE_ADDRESS_MEM_MASK; + if (!vidadr) + { + printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!"); + continue; + } + + if (dev->vendor == PCI_VENDOR_ID_DEC && + dev->device == PCI_DEVICE_ID_DEC_TGA) + { + tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f; + if (tga_type != 0 && tga_type != 1 && tga_type != 3) { - tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f; - if (tga_type != 0 && tga_type != 1 && tga_type != 3) - { - printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); - found--; - } - vidadr+=dec_offsets[tga_type]; + printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); + found--; } - - DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); - DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn)); - found++; + vidadr+=dec_offsets[tga_type]; } + DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); + DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", dev->devfn)); + found++; } if (vidmem) @@ -2086,79 +2287,77 @@ static int find_vga(void) static void handle_chipset(void) { - int index; + struct pci_dev *dev = NULL; /* Just in case some nut set this to something dangerous */ if (triton1) triton1=BT848_INT_ETBF; - for (index = 0; index < 8; index++) + while ((dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, dev))) { - unsigned char bus, devfn; - unsigned char b; - /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ - - if (!pcibios_find_device(PCI_VENDOR_ID_SI, - PCI_DEVICE_ID_SI_496, - index, &bus, &devfn)) - { - printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); - } + printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); + } - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82441, - index, &bus, &devfn)) - { - pcibios_read_config_byte(bus, devfn, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } + /* dev == NULL */ - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, - index, &bus, &devfn)) - { - printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); - triton1=BT848_INT_ETBF; - -#if 0 - /* The ETBF bit SHOULD make all this unnecessary */ - /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - { - unsigned char bo; + while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, dev))) + { + unsigned char b; + pci_read_config_byte(dev, 0x53, &b); + DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); + DEBUG(printk("bufcon=0x%02x\n",b)); + } - pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b); - bo=b; - DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); + while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, dev))) + { +/* unsigned char b; + unsigned char bo;*/ - if(!(b & TRITON_BUS_CONCURRENCY)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); - b |= TRITON_BUS_CONCURRENCY; - } + printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); + triton1=BT848_INT_ETBF; + +#if 0 + /* The ETBF bit SHOULD make all this unnecessary */ + /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - if(b & TRITON_PEER_CONCURRENCY) - { - printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); - b &= ~TRITON_PEER_CONCURRENCY; - } - if(!(b & TRITON_STREAMING)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); - b |= TRITON_STREAMING; - } + pci_read_config_byte(dev, TRITON_PCON, &b); + bo=b; + DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); + if(!(b & TRITON_BUS_CONCURRENCY)) + { + printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); + b |= TRITON_BUS_CONCURRENCY; + } + if(b & TRITON_PEER_CONCURRENCY) + { + printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); + b &= ~TRITON_PEER_CONCURRENCY; + } + if(!(b & TRITON_STREAMING)) + { + printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); + b |= TRITON_STREAMING; + } - if (b!=bo) - { - pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); - printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); - } - } -#endif + if (b!=bo) + { + pci_write_config_byte(dev, TRITON_PCON, b); + printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); } +#endif } } +static void init_tda8425(struct i2c_bus *bus) +{ + I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB */ + I2CWrite(bus, I2C_TDA8425, TDA8425_VR, 0xFC, 1); /* volume right 0dB */ + I2CWrite(bus, I2C_TDA8425, TDA8425_BA, 0xF6, 1); /* bass 0dB */ + I2CWrite(bus, I2C_TDA8425, TDA8425_TR, 0xF6, 1); /* treble 0dB */ + I2CWrite(bus, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off */ +} + static void init_tda9850(struct i2c_bus *bus) { @@ -2177,13 +2376,13 @@ static void init_tda9850(struct i2c_bus *bus) static void idcard(struct bttv *btv) { - int tunertype; btwrite(0, BT848_GPIO_OUT_EN); DEBUG(printk(KERN_DEBUG "bttv: GPIO: 0x%08x\n", btread(BT848_GPIO_DATA))); /* Default the card to the user-selected one. */ btv->type=card; - + btv->tuner_type=-1; /* use default tuner type */ + /* If we were asked to auto-detect, then do so! Right now this will only recognize Miro, Hauppauge or STB */ @@ -2196,12 +2395,34 @@ static void idcard(struct bttv *btv) else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) btv->type=BTTV_STB; + + if (btv->type == BTTV_MIRO) { + /* auto detect tuner for MIRO cards */ + btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7; + } } - btv->dbx = I2CRead(&(btv->i2c), I2C_TDA9850) ? 0 : 1; + if (I2CRead(&(btv->i2c), I2C_TDA9850) >=0) + { + btv->audio_chip = TDA9850; + printk(KERN_INFO "bttv: audio chip: TDA9850\n"); + } - if (btv->dbx) - init_tda9850(&(btv->i2c)); + if (I2CRead(&(btv->i2c), I2C_TDA8425) >=0) + { + btv->audio_chip = TDA8425; + printk("bttv: audio chip: TDA8425\n"); + } + + switch(btv->audio_chip) + { + case TDA9850: + init_tda9850(&(btv->i2c)); + break; + case TDA8425: + init_tda8425(&(btv->i2c)); + break; + } /* How do I detect the tuner type for other cards but Miro ??? */ printk(KERN_INFO "bttv: model: "); @@ -2209,13 +2430,6 @@ static void idcard(struct bttv *btv) { case BTTV_MIRO: printk("MIRO\n"); - if (btv->have_tuner) - { - tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7; - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_TUNER, - TUNER_SET_TYPE,&tunertype); - } strcpy(btv->video_dev.name,"BT848(Miro)"); break; case BTTV_HAUPPAUGE: @@ -2238,6 +2452,10 @@ static void idcard(struct bttv *btv) printk("AVerMedia\n"); strcpy(btv->video_dev.name,"BT848(AVerMedia)"); break; + case BTTV_MATRIX_VISION: + printk("MATRIX-Vision\n"); + strcpy(btv->video_dev.name,"BT848(MATRIX-Vision)"); + break; } audio(btv, AUDIO_MUTE); } @@ -2288,7 +2506,7 @@ static void bt848_set_risc_jmps(struct bttv *btv) btv->risc_jmp[12]=BT848_RISC_JUMP; btv->risc_jmp[13]=virt_to_bus(btv->risc_jmp); - /* enable cpaturing and DMA */ + /* enable capturing and DMA */ btaor(flags, ~0x0f, BT848_CAP_CTL); if (flags&0x0f) bt848_dma(btv, 3); @@ -2321,6 +2539,7 @@ static int init_bt848(int i) btv->win.cropx=0; btv->win.cropy=0; btv->win.bpp=2; + btv->win.depth=16; btv->win.color_fmt=BT848_COLOR_FMT_RGB16; btv->win.bpl=1024*btv->win.bpp; btv->win.swidth=1024; @@ -2359,9 +2578,11 @@ static int init_bt848(int i) btv->vbibuf=(unsigned char *) vmalloc(VBIBUF_SIZE); if (!btv->vbibuf) return -1; - if (!(btv->grisc=(unsigned int *) kmalloc(16384, GFP_KERNEL))) + if (!(btv->grisc=(unsigned int *) kmalloc(32768, GFP_KERNEL))) return -1; + memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random + memory to the user */ btv->fbuffer=NULL; bt848_muxsel(btv, 1); @@ -2372,7 +2593,11 @@ static int init_bt848(int i) btwrite(0x00, BT848_CAP_CTL); btwrite(0xfc, BT848_GPIO_DMA_CTL); - btwrite(0x0ff, BT848_VBI_PACK_SIZE); + /* select direct input */ + btwrite(0x00, BT848_GPIO_REG_INP); + + + btwrite(0xff, BT848_VBI_PACK_SIZE); btwrite(1, BT848_VBI_PACK_DEL); @@ -2502,10 +2727,11 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) /* captured full frame */ if (stat&(2<<28)) { - btv->grab++; wake_up_interruptible(&btv->capq); if ((--btv->grabbing)) { + btv->gro = btv->gro_next; + btv->gre = btv->gre_next; btv->risc_jmp[5]=btv->gro; btv->risc_jmp[11]=btv->gre; bt848_set_geo(btv, btv->gwidth, @@ -2517,6 +2743,7 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) btv->win.height, btv->win.color_fmt); } + wake_up_interruptible(&btv->capq); break; } if (stat&(8<<28)) @@ -2588,30 +2815,29 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) static int find_bt848(void) { - short pci_index; unsigned char command, latency; int result; - unsigned char bus, devfn; struct bttv *btv; + struct pci_dev *dev; bttv_num=0; if (!pcibios_present()) { - DEBUG(printk(KERN_DEBUG "bttv: PCI-BIOS not present or not accessable!\n")); + DEBUG(printk(KERN_DEBUG "bttv: PCI-BIOS not present or not accessible!\n")); return 0; } - - for (pci_index = 0; - !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, - pci_index, &bus, &devfn); - ++pci_index) + for (dev = pci_devices; dev != NULL; dev = dev->next) { + if (dev->vendor != PCI_VENDOR_ID_BROOKTREE) + continue; + if (dev->device != PCI_DEVICE_ID_BT849 && + dev->device != PCI_DEVICE_ID_BT848) + continue; + + /* Ok, Bt848 or Bt849 found! */ btv=&bttvs[bttv_num]; - btv->bus=bus; - btv->devfn=devfn; + btv->dev=dev; btv->bt848_mem=NULL; btv->vbibuf=NULL; btv->risc_jmp=NULL; @@ -2624,12 +2850,10 @@ static int find_bt848(void) btv->vbip=VBIBUF_SIZE; - pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID, - &btv->id); - pcibios_read_config_byte(btv->bus, btv->devfn, - PCI_INTERRUPT_LINE, &btv->irq); - pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - &btv->bt848_adr); + pci_read_config_word (btv->dev, PCI_DEVICE_ID, &btv->id); + + /* pci_read_config_dword(btv->dev, PCI_BASE_ADDRESS_0, &btv->bt848_adr); */ + btv->bt848_adr = btv->dev->base_address[0]; if (remap&&(!bttv_num)) { @@ -2637,25 +2861,33 @@ static int find_bt848(void) remap&=PCI_BASE_ADDRESS_MEM_MASK; printk(KERN_INFO "Remapping to : 0x%08x.\n", remap); remap|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK); - pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - remap); - pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - &btv->bt848_adr); + pci_write_config_dword(btv->dev, PCI_BASE_ADDRESS_0, remap); + pci_read_config_dword(btv->dev, PCI_BASE_ADDRESS_0, &btv->bt848_adr); + btv->dev->base_address[0] = btv->bt848_adr; } btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION, + pci_read_config_byte(btv->dev, PCI_CLASS_REVISION, &btv->revision); printk(KERN_INFO "bttv: Brooktree Bt%d (rev %d) ", - btv->id, btv->revision); + btv->id, btv->revision); printk("bus: %d, devfn: %d, ", - btv->bus, btv->devfn); - printk("irq: %d, ",btv->irq); + btv->dev->bus->number, btv->dev->devfn); + printk("irq: %d, ",btv->dev->irq); printk("memory: 0x%08x.\n", btv->bt848_adr); + btv->pll = 0; +#ifdef USE_PLL + if (btv->id==849 || (btv->id==848 && btv->revision==0x12)) + { + printk(KERN_INFO "bttv: internal PLL, single crystal operation enabled.\n"); + btv->pll=1; + } +#endif + btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); - result = request_irq(btv->irq, bttv_irq, + result = request_irq(btv->dev->irq, bttv_irq, SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv); if (result==-EINVAL) { @@ -2664,29 +2896,27 @@ static int find_bt848(void) } if (result==-EBUSY) { - printk(KERN_ERR "bttv: IRQ %d busy, change your PnP config in BIOS\n",btv->irq); + printk(KERN_ERR "bttv: IRQ %d busy, change your PnP config in BIOS\n",btv->dev->irq); return result; } if (result < 0) return result; /* Enable bus-mastering */ - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); + pci_read_config_byte(btv->dev, PCI_COMMAND, &command); command|=PCI_COMMAND_MASTER; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); + pci_write_config_byte(btv->dev, PCI_COMMAND, command); + pci_read_config_byte(btv->dev, PCI_COMMAND, &command); if (!(command&PCI_COMMAND_MASTER)) { printk(KERN_ERR "bttv: PCI bus-mastering could not be enabled\n"); return -1; } - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER, - &latency); + pci_read_config_byte(btv->dev, PCI_LATENCY_TIMER, &latency); if (!latency) { latency=32; - pcibios_write_config_byte(btv->bus, btv->devfn, - PCI_LATENCY_TIMER, latency); + pci_write_config_byte(btv->dev, PCI_LATENCY_TIMER, latency); } DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency)); bttv_num++; @@ -2720,9 +2950,10 @@ static void release_bttv(void) i2c_unregister_bus((&btv->i2c)); /* disable PCI bus-mastering */ - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - command|=PCI_COMMAND_MASTER; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); + pci_read_config_byte(btv->dev, PCI_COMMAND, &command); + /* Should this be &=~ ?? */ + command|=PCI_COMMAND_MASTER; + pci_write_config_byte(btv->dev, PCI_COMMAND, command); /* unmap and free memory */ if (btv->grisc) @@ -2743,7 +2974,7 @@ static void release_bttv(void) vfree((void *) btv->vbibuf); - free_irq(btv->irq,btv); + free_irq(btv->dev->irq,btv); DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%08x.\n", btv->bt848_mem)); if (btv->bt848_mem) iounmap(btv->bt848_mem); diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h index ca0fb057c..e1e21f595 100644 --- a/drivers/char/bttv.h +++ b/drivers/char/bttv.h @@ -26,21 +26,14 @@ #include <linux/types.h> #include <linux/wait.h> -#include "i2c.h" +#include <linux/i2c.h> #include "msp3400.h" #include "bt848.h" #include <linux/videodev.h> #define MAX_CLIPRECS 100 #define RISCMEM_LEN (32744*2) -#define MAX_FBUF 0x144000 - -struct riscprog -{ - unsigned int length; - u32 *busadr; - u32 *prog; -}; +#define BTTV_MAX_FBUF 0x144000 /* clipping rectangle */ @@ -51,29 +44,6 @@ struct cliprec }; -/* grab buffer */ -struct gbuffer -{ - struct gbuffer *next; - struct gbuffer *next_active; - void *adr; - int x, y; - int width, height; - unsigned int bpl; - unsigned int fmt; - int flags; -#define GBUF_ODD 1 -#define GBUF_EVEN 2 -#define GBUF_LFB 4 -#define GBUF_INT 8 - unsigned int length; - void *ro; - void *re; - u32 bro; - u32 bre; -}; - - #ifdef __KERNEL__ struct bttv_window @@ -84,11 +54,12 @@ struct bttv_window ushort swidth, sheight; short cropx, cropy; ushort cropwidth, cropheight; - unsigned int vidadr; + unsigned long vidadr; ushort freq; int norm; int interlace; int color_fmt; + ushort depth; }; @@ -103,12 +74,11 @@ struct bttv struct i2c_bus i2c; int have_msp3400; int have_tuner; + int tuner_type; unsigned short id; - unsigned char bus; /* PCI bus the Bt848 is on */ - unsigned char devfn; + struct pci_dev *dev; unsigned char revision; - unsigned char irq; /* IRQ used by Bt848 card */ unsigned int bt848_adr; /* bus address of IO mem returned by PCI BIOS */ unsigned char *bt848_mem; /* pointer to mapped IO memory */ unsigned long busriscmem; @@ -119,7 +89,7 @@ struct bttv int type; /* card type */ int audio; /* audio mode */ int user; - int dbx; + int audio_chip; int radio; u32 *risc_jmp; @@ -145,12 +115,15 @@ struct bttv u32 *grisc; unsigned long gro; unsigned long gre; + unsigned long gro_next; + unsigned long gre_next; char *fbuffer; int gmode; int grabbing; int lastgrab; int grab; int grabcount; + int pll; }; #endif @@ -184,6 +157,8 @@ struct bttv #define BTTV_INTEL 0x04 #define BTTV_DIAMOND 0x05 #define BTTV_AVERMEDIA 0x06 +#define BTTV_MATRIX_VISION 0x07 +#define BTTV_FLYVIDEO 0x08 #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 @@ -194,8 +169,12 @@ struct bttv #define AUDIO_MUTE 0x80 #define AUDIO_UNMUTE 0x81 +#define TDA9850 0x01 +#define TDA8425 0x02 + #define I2C_TSA5522 0xc2 #define I2C_TDA9850 0xb6 +#define I2C_TDA8425 0x82 #define I2C_HAUPEE 0xa0 #define I2C_STBEE 0xae @@ -207,4 +186,12 @@ struct bttv #define TDA9850_ALI2 0x09 #define TDA9850_ALI3 0x0a + +#define TDA8425_VL 0x00 +#define TDA8425_VR 0x01 +#define TDA8425_BA 0x02 +#define TDA8425_TR 0x03 +#define TDA8425_S1 0x08 + + #endif diff --git a/drivers/char/bw-qcam.c b/drivers/char/bw-qcam.c index 2142cf6a1..48de68db3 100644 --- a/drivers/char/bw-qcam.c +++ b/drivers/char/bw-qcam.c @@ -112,7 +112,7 @@ static int qc_calibrate(struct qcam_device *q) do { qc_command(q, 33); value = qc_readparam(q); - udelay(1000); + mdelay(1); schedule(); count++; } while (value == 0xff && count<2048); @@ -296,7 +296,7 @@ static int qc_detect(struct qcam_device *q) if (reg != lastreg) count++; lastreg = reg; - udelay(1000); + mdelay(1); } /* Be liberal in what you accept... */ @@ -842,6 +842,7 @@ static struct video_device qcam_template= qcam_close, qcam_read, qcam_write, + NULL, qcam_ioctl, NULL, qcam_init_done, diff --git a/drivers/char/c-qcam.c b/drivers/char/c-qcam.c index e424266c8..e6d4c16b5 100644 --- a/drivers/char/c-qcam.c +++ b/drivers/char/c-qcam.c @@ -149,7 +149,7 @@ static int qc_detect(struct qcam_device *qcam) ostat = stat = parport_read_status(qcam->pport); for (i=0; i<250; i++) { - udelay(1000); + mdelay(1); stat = parport_read_status(qcam->pport); if (ostat != stat) { @@ -166,9 +166,9 @@ static void qc_reset(struct qcam_device *qcam) { parport_write_control(qcam->pport, 0xc); parport_write_control(qcam->pport, 0x8); - udelay(1000); + mdelay(1); parport_write_control(qcam->pport, 0xc); - udelay(1000); + mdelay(1); } /* Reset the QuickCam and program for brightness, contrast, @@ -325,7 +325,7 @@ static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) { /* Turn the port around */ parport_frob_control(q->pport, 0x20, 0x20); - udelay(3000); + mdelay(3); qcam_set_ack(q, 0); if (qcam_await_ready1(q, 1)) { kfree(tmpbuf); @@ -356,7 +356,7 @@ static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) wantlen -= t; if (t < s) break; - if (need_resched) + if (current->need_resched) schedule(); } @@ -377,7 +377,7 @@ static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) int l; do { l = qcam_read_bytes(q, tmpbuf, 3); - if (need_resched) + if (current->need_resched) schedule(); } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) @@ -392,7 +392,7 @@ static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) return len; } parport_frob_control(q->pport, 0x20, 0); - udelay(3000); + mdelay(3); qcam_set_ack(q, 1); if (qcam_await_ready1(q, 0)) { @@ -407,7 +407,7 @@ static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) int l; do { l = qcam_read_bytes(q, tmpbuf, 1); - if (need_resched) + if (current->need_resched) schedule(); } while (l && tmpbuf[0] == 0x7e); l = qcam_read_bytes(q, tmpbuf+1, 2); @@ -646,6 +646,7 @@ static struct video_device qcam_template= qcam_close, qcam_read, qcam_write, + NULL, qcam_ioctl, NULL, qcam_init_done, diff --git a/drivers/char/conmakehash.c b/drivers/char/conmakehash.c index 4d3958fd9..e0c6891a9 100644 --- a/drivers/char/conmakehash.c +++ b/drivers/char/conmakehash.c @@ -52,7 +52,6 @@ int unicount[MAX_FONTLEN]; void addpair(int fp, int un) { int i; - unicode hu; if ( un <= 0xfffe ) { diff --git a/drivers/char/console.c b/drivers/char/console.c index 9c9e61e8c..d5b315328 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -3,38 +3,8 @@ * * Copyright (C) 1991, 1992 Linus Torvalds */ + /* - * console.c - * - * This module exports the console io functions: - * - * 'void do_keyboard_interrupt(void)' - * - * 'int vc_allocate(unsigned int console)' - * 'int vc_cons_allocated(unsigned int console)' - * 'int vc_resize(unsigned long lines, unsigned long cols)' - * 'void vc_disallocate(unsigned int currcons)' - * - * 'unsigned long con_init(unsigned long)' - * 'int con_open(struct tty_struct *tty, struct file * filp)' - * 'void con_write(struct tty_struct * tty)' - * 'void vt_console_print(const char * b)' - * 'void update_screen(int new_console)' - * - * 'void do_blank_screen(int)' - * 'void do_unblank_screen(void)' - * 'void poke_blanked_console(void)' - * - * 'unsigned short *screen_pos(int currcons, int w_offset, int viewed)' - * 'void complement_pos(int currcons, int offset)' - * 'void invert_screen(int currcons, int offset, int count, int shift)' - * - * 'void scrollback(int lines)' - * 'void scrollfront(int lines)' - * - * 'void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)' - * 'int mouse_reporting(void)' - * * Hopefully this will be a rather complete VT102 implementation. * * Beeping thanks to John T Kohl. @@ -63,38 +33,48 @@ * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95 * * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp> + * + * Merge with the abstract console driver by Geert Uytterhoeven + * <Geert.Uytterhoeven@cs.kuleuven.ac.be>, Jan 1997. + * + * Original m68k console driver modifications by + * + * - Arno Griffioen <arno@usn.nl> + * - David Carter <carter@cs.bris.ac.uk> + * + * Note that the abstract console driver allows all consoles to be of + * potentially different sizes, so the following variables depend on the + * current console (currcons): + * + * - 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 + * console. It supports VGA text mode, frame buffer based graphical consoles + * and special graphics processors that are only accessible through some + * registers (e.g. a TMS340x0 GSP). + * + * The interface to the hardware is specified using a special structure + * (struct consw) which contains function pointers to console operations + * (see <linux/console.h> for more information). + * + * Support for changeable cursor shape + * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997 + * + * Ported to i386 and con_scrolldelta fixed + * by Emmanuel Marty <core@ggi-project.org>, April 1998 + * + * Resurrected character buffers in videoram plus lots of other trickery + * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998 */ -#define BLANK 0x0020 - -/* 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 - * glyph unless the disp_ctrl mode is explicitly enabled. - */ -#define CTRL_ACTION 0x0d00ff81 -#define CTRL_ALWAYS 0x0800f501 /* Cannot be overridden by disp_ctrl */ - -/* - * Here is the default bell parameters: 750HZ, 1/8th of a second - */ -#define DEFAULT_BELL_PITCH 750 -#define DEFAULT_BELL_DURATION (HZ/8) - -/* - * NOTE!!! We sometimes disable and enable interrupts for a short while - * (to put a word in video IO), but this will work even for keyboard - * interrupts. We know interrupts aren't enabled when getting a keyboard - * interrupt, as we use trap-gates. Hopefully all is well. - */ - +#include <linux/module.h> #include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/console.h> -#include <linux/config.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> @@ -102,112 +82,76 @@ #include <linux/malloc.h> #include <linux/major.h> #include <linux/mm.h> -#include <linux/ioport.h> +#include <linux/console.h> #include <linux/init.h> +#include <linux/vt_kern.h> +#include <linux/selection.h> +#include <linux/console_struct.h> +#include <linux/kbd_kern.h> +#include <linux/vt_kern.h> +#include <linux/consolemap.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/config.h> +#include <linux/version.h> +#include <linux/tqueue.h> #ifdef CONFIG_APM #include <linux/apm_bios.h> #endif -#ifdef CONFIG_SGI -#include <asm/sgialib.h> -#elif defined(CONFIG_ACER_PICA_61) -#include <asm/bootinfo.h> -/* - * The video control ports are mapped at virtual address - * 0xe0200000 for the onboard S3 card of the Acer; M700 and Magnum 4000 - * don't have ports at all. - */ -#define PORT_BASE video_port_base -unsigned long video_port_base; -#endif #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> -#include <asm/bitops.h> -#include <linux/kbd_kern.h> -#include <linux/vt_kern.h> -#include <linux/consolemap.h> -#include <linux/selection.h> -#include <linux/console_struct.h> +#include <asm/linux_logo.h> -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif +#include "console_macros.h" + + +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 + * glyph unless the disp_ctrl mode is explicitly enabled. + */ +#define CTRL_ACTION 0x0d00ff81 +#define CTRL_ALWAYS 0x0800f501 /* Cannot be overridden by disp_ctrl */ -int serial_console; +/* + * Here is the default bell parameters: 750HZ, 1/8th of a second + */ +#define DEFAULT_BELL_PITCH 750 +#define DEFAULT_BELL_DURATION (HZ/8) -#ifdef __sparc__ -int serial_console; +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif -struct tty_driver console_driver; -static int console_refcount; static struct tty_struct *console_table[MAX_NR_CONSOLES]; static struct termios *console_termios[MAX_NR_CONSOLES]; static struct termios *console_termios_locked[MAX_NR_CONSOLES]; -unsigned short *vc_scrbuf[MAX_NR_CONSOLES]; struct vc vc_cons [MAX_NR_CONSOLES]; static int con_open(struct tty_struct *, struct file *); -static void con_setsize(unsigned long rows, unsigned long cols); -static void vc_init(unsigned int console, unsigned long rows, - unsigned long cols, int do_clear); -extern void get_scrmem(int currcons); -extern void set_scrmem(int currcons, long offset); -static void set_origin(int currcons); +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); -extern void change_console(unsigned int); -extern void poke_blanked_console(void); static void gotoxy(int currcons, int new_x, int new_y); static void save_cur(int currcons); -extern void set_cursor(int currcons); -extern void hide_cursor(void); static void reset_terminal(int currcons, int do_clear); -extern void reset_vc(unsigned int new_console); -extern void vt_init(void); -extern void set_vesa_blanking(unsigned long arg); -extern void vesa_blank(void); -extern void vesa_unblank(void); -extern void vesa_powerdown(void); -extern void compute_shiftstate(void); -extern void reset_palette(int currcons); -extern void set_palette(void); -extern int con_is_present(void); -extern unsigned long con_type_init(unsigned long, const char **); -extern void con_type_init_finish(void); -extern int set_get_cmap(unsigned char *, int); -extern int set_get_font(unsigned char *, int, int); -extern void rs_cons_hook(int chip, int out, int channel); - -/* Description of the hardware situation */ -unsigned char video_type; /* Type of display being used */ -unsigned long video_mem_base; /* Base of video memory */ -unsigned long video_mem_term; /* End of video memory */ -unsigned short video_port_reg; /* Video register select port */ -unsigned short video_port_val; /* Video register value port */ -unsigned long video_num_columns; /* Number of text columns */ -unsigned long video_num_lines; /* Number of text lines */ -unsigned long video_size_row; -unsigned long video_screen_size; - -int can_do_color = 0; +static void con_flush_chars(struct tty_struct *tty); + static int printable = 0; /* Is console ready for printing? */ -int video_mode_512ch = 0; /* 512-character mode */ -unsigned long video_font_height; /* Height of current screen font */ -unsigned long video_scan_lines; /* Number of scan lines on screen */ -static unsigned long default_font_height; /* Height of default screen font */ -int video_font_is_default = 1; -static unsigned short console_charmask = 0x0ff; +int do_poke_blanked_console = 0; +int console_blanked = 0; -/* used by kbd_bh - set by keyboard_interrupt */ - int do_poke_blanked_console = 0; - int console_blanked = 0; static int blankinterval = 10*60*HZ; static int vesa_off_interval = 0; -static long blank_origin, blank__origin, unblank_origin; /* * fg_console is the current virtual console, @@ -220,270 +164,587 @@ int last_console = 0; int want_console = -1; int kmsg_redirect = 0; -#ifdef CONFIG_SERIAL_ECHO - -#include <linux/serial_reg.h> +/* + * For each existing display, we have a pointer to console currently visible + * on that display, allowing consoles other than fg_console to be refreshed + * appropriately. Unless the low-level driver supplies its own display_fg + * variable, we use this one for the "master display". + */ +static struct vc_data *master_display_fg = NULL; -extern int serial_echo_init (int base); -extern int serial_echo_print (const char *s); +/* + * Unfortunately, we need to delay tty echo when we're currently writing to the + * console since the code is (and always was) not re-entrant, so we insert + * all filp requests to con_task_queue instead of tq_timer and run it from + * the console_bh. + */ +DECLARE_TASK_QUEUE(con_task_queue); /* - * this defines the address for the port to which printk echoing is done - * when CONFIG_SERIAL_ECHO is defined + * Low-Level Functions */ -#define SERIAL_ECHO_PORT 0x3f8 /* COM1 */ -static int serial_echo_port = 0; +#define IS_FG (currcons == fg_console) +#define IS_VISIBLE (*display_fg == vc_cons[currcons].d) -#define serial_echo_outb(v,a) outb((v),(a)+serial_echo_port) -#define serial_echo_inb(a) inb((a)+serial_echo_port) +#ifdef VT_BUF_VRAM_ONLY +#define DO_UPDATE 0 +#else +#define DO_UPDATE IS_VISIBLE +#endif -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) +static inline unsigned short *screenpos(int currcons, int offset, int viewed) +{ + unsigned short *p = (unsigned short *)(visible_origin + offset); + return p; +} -/* Wait for transmitter & holding register to empty */ -#define WAIT_FOR_XMITR \ - do { \ - lsr = serial_echo_inb(UART_LSR); \ - } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY) +static void scrolldelta(int lines) +{ + int currcons = fg_console; -/* These two functions abstract the actual communications with the - * debug port. This is so we can change the underlying communications - * mechanism without modifying the rest of the code. + clear_selection(); + if (vcmode == KD_TEXT) + sw->con_scrolldelta(vc_cons[currcons].d, lines); +} + +static void scrup(int currcons, unsigned int t, unsigned int b, int nr) +{ + unsigned short *d, *s; + + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) + return; + if (IS_VISIBLE && sw->con_scroll(vc_cons[currcons].d, t, b, SM_UP, nr)) + return; + d = (unsigned short *) (origin+video_size_row*t); + s = (unsigned short *) (origin+video_size_row*(t+nr)); + scr_memcpyw(d, s, (b-t-nr) * video_size_row); + scr_memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr); +} + +static void +scrdown(int currcons, unsigned int t, unsigned int b, int nr) +{ + unsigned short *s; + unsigned int step; + + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) + return; + if (IS_VISIBLE && sw->con_scroll(vc_cons[currcons].d, t, b, SM_DOWN, nr)) + return; + s = (unsigned short *) (origin+video_size_row*t); + step = video_num_columns * nr; + scr_memmovew(s + step, s, (b-t-nr)*video_size_row); + scr_memsetw(s, video_erase_char, 2*step); +} + +static void do_update_region(int currcons, unsigned long start, int count) +{ +#ifndef VT_BUF_VRAM_ONLY + unsigned int xx, yy, offset; + u16 *p; + + if (start < origin) { + count -= origin - start; + start = origin; + } + if (count <= 0) + return; + offset = (start - origin) / 2; + xx = offset % video_num_columns; + yy = offset / video_num_columns; + p = (u16 *) start; + for(;;) { + u16 attrib = scr_readw(p) & 0xff00; + int startx = xx; + u16 *q = p; + while (xx < video_num_columns && count) { + if (attrib != (scr_readw(p) & 0xff00)) { + if (p > q) + sw->con_putcs(vc_cons[currcons].d, q, p-q, yy, startx); + startx = xx; + q = p; + attrib = scr_readw(p) & 0xff00; + } + p++; + xx++; + count--; + } + if (p > q) + sw->con_putcs(vc_cons[currcons].d, q, p-q, yy, startx); + if (!count) + break; + xx = 0; + yy++; + } +#endif +} + +void update_region(int currcons, unsigned long start, int count) +{ + if (DO_UPDATE) + do_update_region(currcons, start, count); +} + +/* Structure of attributes is hardware-dependent */ + +static u8 build_attr(int currcons, u8 _color, u8 _intensity, u8 _blink, u8 _underline, u8 _reverse) +{ + if (sw->con_build_attr) + return sw->con_build_attr(vc_cons[currcons].d, _color, _intensity, _blink, _underline, _reverse); + +#ifndef VT_BUF_VRAM_ONLY +/* + * ++roman: I completely changed the attribute format for monochrome + * mode (!can_do_color). The formerly used MDA (monochrome display + * adapter) format didn't allow the combination of certain effects. + * Now the attribute is just a bit vector: + * Bit 0..1: intensity (0..2) + * Bit 2 : underline + * Bit 3 : reverse + * Bit 7 : blink */ -int -serial_echo_print(const char *s) + { + u8 a = color; + if (!can_do_color) + return _intensity | + (_underline ? 4 : 0) | + (_reverse ? 8 : 0) | + (_blink ? 0x80 : 0); + if (_underline) + a = (a & 0xf0) | ulcolor; + else if (_intensity == 0) + a = (a & 0xf0) | halfcolor; + if (_reverse) + a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77); + if (_blink) + a ^= 0x80; + if (_intensity == 2) + a ^= 0x08; + if (hi_font_mask == 0x100) + a <<= 1; + return a; + } +#else + return 0; +#endif +} + +static void update_attr(int currcons) { - int lsr, ier; - int i; + attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm); + video_erase_char = (build_attr(currcons, color, 1, 0, 0, decscnm) << 8) | ' '; +} - if (!serial_echo_port) return (0); +/* Note: inverting the screen twice should revert to the original state */ - /* - * First save the IER then disable the interrupts - */ - ier = serial_echo_inb(UART_IER); - serial_echo_outb(0x00, UART_IER); +void invert_screen(int currcons, int offset, int count, int viewed) +{ + unsigned short *p; - /* - * Now, do each character - */ - for (i = 0; *s; i++, s++) { - WAIT_FOR_XMITR; + count /= 2; + p = screenpos(currcons, offset, viewed); + if (sw->con_invert_region) + sw->con_invert_region(vc_cons[currcons].d, p, count); +#ifndef VT_BUF_VRAM_ONLY + else { + u16 *q = p; + int cnt = count; + + if (!can_do_color) { + while (cnt--) *q++ ^= 0x0800; + } else if (hi_font_mask == 0x100) { + while (cnt--) { + u16 a = *q; + a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4); + *q++ = a; + } + } else { + while (cnt--) { + u16 a = *q; + a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); + *q++ = a; + } + } + } +#endif + update_region(currcons, (unsigned long) p, count); +} - /* Send the character out. */ - serial_echo_outb(*s, UART_TX); +/* used by selection: complement pointer position */ +void complement_pos(int currcons, int offset) +{ + static unsigned short *p = NULL; + static unsigned short old = 0; + static unsigned short oldx = 0, oldy = 0; - /* if a LF, also do CR... */ - if (*s == 10) { - WAIT_FOR_XMITR; - serial_echo_outb(13, UART_TX); + if (p) { + scr_writew(old, p); + if (DO_UPDATE) + sw->con_putc(vc_cons[currcons].d, old, oldy, oldx); + } + if (offset == -1) + p = NULL; + else { + unsigned short new; + p = screenpos(currcons, offset, 1); + old = scr_readw(p); + new = old ^ complement_mask; + scr_writew(new, p); + if (DO_UPDATE) { + oldx = (offset >> 1) % video_num_columns; + oldy = (offset >> 1) / video_num_columns; + sw->con_putc(vc_cons[currcons].d, new, oldy, oldx); } } +} - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - do { - lsr = serial_echo_inb(UART_LSR); - } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY); - serial_echo_outb(ier, UART_IER); +static void insert_char(int currcons, unsigned int nr) +{ + unsigned short *p, *q = (unsigned short *) pos; - return (0); + p = q + video_num_columns - nr - x; + while (--p >= q) + scr_writew(scr_readw(p), p + nr); + scr_memsetw(q, video_erase_char, nr*2); + need_wrap = 0; + if (DO_UPDATE) { + unsigned short oldattr = attr; + sw->con_bmove(vc_cons[currcons].d,y,x,y,x+nr,1, + video_num_columns-x-nr); + attr = video_erase_char >> 8; + while (nr--) + sw->con_putc(vc_cons[currcons].d, + video_erase_char,y,x+nr); + attr = oldattr; + } +} + +static void delete_char(int currcons, unsigned int nr) +{ + unsigned int i = x; + unsigned short *p = (unsigned short *) pos; + + while (++i <= video_num_columns - nr) { + scr_writew(scr_readw(p+nr), p); + p++; + } + scr_memsetw(p, video_erase_char, nr*2); + need_wrap = 0; + if (DO_UPDATE) { + unsigned short oldattr = attr; + sw->con_bmove(vc_cons[currcons].d, y, x+nr, y, x, 1, + video_num_columns-x-nr); + attr = video_erase_char >> 8; + while (nr--) + sw->con_putc(vc_cons[currcons].d, + video_erase_char, y, + video_num_columns-1-nr); + attr = oldattr; + } } +static int softcursor_original; -int -serial_echo_init(int base) +static void add_softcursor(int currcons) { - int comstat, hi, lo; - - if (base != 0x2f8 && base != 0x3f8) { - serial_echo_port = 0; - return (0); - } else - serial_echo_port = base; + int i = scr_readw((u16 *) pos); + u32 type = cursor_type; - /* - * read the Divisor Latch - */ - comstat = serial_echo_inb(UART_LCR); - serial_echo_outb(comstat | UART_LCR_DLAB, UART_LCR); - hi = serial_echo_inb(UART_DLM); - lo = serial_echo_inb(UART_DLL); - serial_echo_outb(comstat, UART_LCR); + if (! (type & 0x10)) return; + if (softcursor_original != -1) return; + softcursor_original = i; + i |= ((type >> 8) & 0xff00 ); + i ^= ((type) & 0xff00 ); + if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000; + if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700; + scr_writew(i, (u16 *) pos); + if (DO_UPDATE) + sw->con_putc(vc_cons[currcons].d, i, y, x); +} - /* - * now do hardwired init - */ - serial_echo_outb(0x03, UART_LCR); /* No parity, 8 data bits, 1 stop */ - serial_echo_outb(0x83, UART_LCR); /* Access divisor latch */ - serial_echo_outb(0x00, UART_DLM); /* 9600 baud */ - serial_echo_outb(0x0c, UART_DLL); - serial_echo_outb(0x03, UART_LCR); /* Done with divisor */ - - /* Prior to disabling interrupts, read the LSR and RBR - * registers - */ - comstat = serial_echo_inb(UART_LSR); /* COM? LSR */ - comstat = serial_echo_inb(UART_RX); /* COM? RBR */ - serial_echo_outb(0x00, UART_IER); /* Disable all interrupts */ +static void hide_cursor(int currcons) +{ + if (currcons == sel_cons) + clear_selection(); + if (softcursor_original != -1) { + scr_writew(softcursor_original,(u16 *) pos); + if (DO_UPDATE) + sw->con_putc(vc_cons[currcons].d, softcursor_original, y, x); + softcursor_original = -1; + } + sw->con_cursor(vc_cons[currcons].d,CM_ERASE); +} - return(0); +void set_cursor(int currcons) +{ + if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS) + return; + if (deccm) { + if (currcons == sel_cons) + clear_selection(); + add_softcursor(currcons); + if ((cursor_type & 0x0f) != 1) + sw->con_cursor(vc_cons[currcons].d,CM_DRAW); + } else + hide_cursor(currcons); +} + +static void set_origin(int currcons) +{ + if (!IS_VISIBLE || + !sw->con_set_origin || + !sw->con_set_origin(vc_cons[currcons].d)) + origin = (unsigned long) screenbuf; + visible_origin = origin; + scr_end = origin + screenbuf_size; + pos = origin + video_size_row*y + 2*x; +} + +static inline void save_screen(void) +{ + int currcons = fg_console; + if (sw->con_save_screen) + sw->con_save_screen(vc_cons[currcons].d); +} + +/* + * Redrawing of screen + */ + +void update_screen(int new_console) +{ + int currcons = fg_console; + int redraw = 1; + int 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); + 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); + currcons = new_console; + if (old_console == new_console) + redraw = 0; + } + if (redraw) { + set_origin(currcons); + if (sw->con_switch(vc_cons[currcons].d)) + /* Update the screen contents */ + do_update_region(currcons, origin, screenbuf_size/2); + } + set_cursor(currcons); + set_leds(); + compute_shiftstate(); + lock = 0; } -#endif /* CONFIG_SERIAL_ECHO */ +/* + * Allocation, freeing and resizing of VTs. + */ int vc_cons_allocated(unsigned int i) { return (i < MAX_NR_CONSOLES && vc_cons[i].d); } -int vc_allocate(unsigned int i) /* return 0 on success */ +void visual_init(int currcons) +{ + /* ++Geert: sw->con_init determines console size */ + sw = conswitchp; + 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); + if (!complement_mask) + complement_mask = can_do_color ? 0x7700 : 0x0800; + video_size_row = video_num_columns<<1; + video_screen_size = video_num_lines*video_size_row; +} + +int vc_allocate(unsigned int currcons, int init) /* return 0 on success */ { - if (i >= MAX_NR_CONSOLES) + if (currcons >= MAX_NR_CONSOLES) return -ENXIO; - if (!vc_cons[i].d) { + if (!vc_cons[currcons].d) { long p, q; /* prevent users from taking too much memory */ - if (i >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) + if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) return -EPERM; /* 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) */ - q = (long) kmalloc(video_screen_size, GFP_KERNEL); - if (!q) - return -ENOMEM; + /* 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 */ p = (long) kmalloc(structsize, GFP_KERNEL); - if (!p) { - kfree_s((char *) q, video_screen_size); + if (!p) + 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); + if (!q) { + kfree_s((char *) p, structsize); + vc_cons[currcons].d = NULL; + vt_cons[currcons] = NULL; return -ENOMEM; } - - vc_cons[i].d = (struct vc_data *) p; - p += sizeof(struct vc_data); - vt_cons[i] = (struct vt_struct *) p; - vc_scrbuf[i] = (unsigned short *) q; - vc_cons[i].d->vc_kmalloced = 1; - vc_cons[i].d->vc_screenbuf_size = video_screen_size; - vc_init (i, video_num_lines, video_num_columns, 1); + 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); } return 0; } /* - * Change # of rows and columns (0 means unchanged) + * Change # of rows and columns (0 means unchanged/the size of fg_console) * [this is to be used together with some user program * like resize that changes the hardware videomode] */ -int vc_resize(unsigned long lines, unsigned long cols) +int vc_resize(unsigned int lines, unsigned int cols, + unsigned int first, unsigned int last) { - unsigned long cc, ll, ss, sr; - unsigned long occ, oll, oss, osr; - unsigned short *p; - unsigned int currcons, i; + unsigned int cc, ll, ss, sr, todo = 0; + unsigned int currcons = fg_console, i; unsigned short *newscreens[MAX_NR_CONSOLES]; - long ol, nl, rlth, rrem; cc = (cols ? cols : video_num_columns); ll = (lines ? lines : video_num_lines); sr = cc << 1; ss = sr * ll; - if (ss > video_mem_term - video_mem_base) - return -ENOMEM; - - /* - * Some earlier version had all consoles of potentially - * different sizes, but that was really messy. - * So now we only change if there is room for all consoles - * of the same size. - */ - for (currcons = 0; currcons < MAX_NR_CONSOLES; currcons++) { - if (!vc_cons_allocated(currcons)) - newscreens[currcons] = 0; - else { - p = (unsigned short *) kmalloc(ss, GFP_USER); - if (!p) { - for (i = 0; i< currcons; i++) - if (newscreens[i]) - kfree_s(newscreens[i], ss); - return -ENOMEM; + for (currcons = first; currcons <= last; currcons++) { + if (!vc_cons_allocated(currcons) || + (cc == video_num_columns && ll == video_num_lines)) + newscreens[currcons] = NULL; + else { + unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER); + if (!p) { + for (i = 0; i< currcons; i++) + if (newscreens[i]) + kfree_s(newscreens[i], ss); + return -ENOMEM; + } + newscreens[currcons] = p; + todo++; } - newscreens[currcons] = p; - } } + if (!todo) + return 0; - get_scrmem(fg_console); - - oll = video_num_lines; - occ = video_num_columns; - osr = video_size_row; - oss = video_screen_size; - - video_num_lines = ll; - video_num_columns = cc; - video_size_row = sr; - video_screen_size = ss; - - for (currcons = 0; currcons < MAX_NR_CONSOLES; currcons++) { - if (!vc_cons_allocated(currcons)) - continue; - - rlth = MIN(osr, sr); - rrem = sr - rlth; - ol = origin; - nl = (long) newscreens[currcons]; - if (ll < oll) - ol += (oll - ll) * osr; - - while (ol < scr_end) { - memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth); - if (rrem) - memsetw((void *)(nl + rlth), video_erase_char, rrem); - ol += osr; - nl += sr; - } - - if (kmalloced) - kfree_s(vc_scrbuf[currcons], screenbuf_size); - vc_scrbuf[currcons] = newscreens[currcons]; - kmalloced = 1; - screenbuf_size = ss; + for (currcons = first; currcons <= last; currcons++) { + unsigned int occ, oll, oss, osr; + unsigned long ol, nl, nlend, rlth, rrem; + if (!newscreens[currcons] || !vc_cons_allocated(currcons)) + continue; - origin = video_mem_start = (long) vc_scrbuf[currcons]; - scr_end = video_mem_end = video_mem_start + ss; + oll = video_num_lines; + occ = video_num_columns; + osr = video_size_row; + oss = video_screen_size; + + video_num_lines = ll; + video_num_columns = cc; + video_size_row = sr; + video_screen_size = ss; + + rlth = MIN(osr, sr); + rrem = sr - rlth; + ol = origin; + nl = (long) newscreens[currcons]; + nlend = nl + ss; + if (ll < oll) + ol += (oll - ll) * osr; + + update_attr(currcons); + + while (ol < scr_end) { + scr_memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth); + if (rrem) + scr_memsetw((void *)(nl + rlth), video_erase_char, rrem); + ol += osr; + nl += sr; + } + if (nlend > nl) + scr_memsetw((void *) nl, video_erase_char, nlend - nl); + if (kmalloced) + kfree_s(screenbuf, oss); + screenbuf = newscreens[currcons]; + kmalloced = 1; + screenbuf_size = ss; + set_origin(currcons); - if (scr_end > nl) - memsetw((void *) nl, video_erase_char, scr_end - nl); + /* do part of a reset_terminal() */ + top = 0; + bottom = video_num_lines; + gotoxy(currcons, x, y); + save_cur(currcons); + + if (console_table[currcons]) { + struct winsize ws, *cws = &console_table[currcons]->winsize; + memset(&ws, 0, sizeof(ws)); + ws.ws_row = video_num_lines; + ws.ws_col = video_num_columns; + if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) && + console_table[currcons]->pgrp > 0) + kill_pg(console_table[currcons]->pgrp, SIGWINCH, 1); + *cws = ws; + } - /* do part of a reset_terminal() */ - top = 0; - bottom = video_num_lines; - gotoxy(currcons, x, y); - save_cur(currcons); + if (IS_FG && vt_cons[fg_console]->vc_mode == KD_TEXT) + update_screen(fg_console); } - set_scrmem(fg_console, 0); - set_origin(fg_console); set_cursor(fg_console); - return 0; } + void vc_disallocate(unsigned int currcons) { if (vc_cons_allocated(currcons)) { + sw->con_deinit(vc_cons[currcons].d); if (kmalloced) - kfree_s(vc_scrbuf[currcons], screenbuf_size); + kfree_s(screenbuf, screenbuf_size); if (currcons >= MIN_NR_CONSOLES) - kfree_s(vc_cons[currcons].d, structsize); - vc_cons[currcons].d = 0; + kfree_s(vc_cons[currcons].d, structsize); + vc_cons[currcons].d = NULL; } } +/* + * VT102 emulator + */ #define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x) #define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x) @@ -550,61 +811,10 @@ static void gotoxay(int currcons, int new_x, int new_y) gotoxy(currcons, new_x, decom ? (top+new_y) : new_y); } -/* - * Hardware scrollback support - */ -extern void __set_origin(unsigned short); -unsigned short __real_origin; /* offset of non-scrolled screen */ -unsigned short __origin; /* offset of currently displayed screen */ -unsigned char has_wrapped; /* all of videomem is data of fg_console */ -static unsigned char hardscroll_enabled; -static unsigned char hardscroll_disabled_by_init = 0; - -void no_scroll(char *str, int *ints) -{ - /* - * Disabling scrollback is required for the Braillex ib80-piezo - * Braille reader made by F.H. Papenmeier (Germany). - * Use the "no-scroll" bootflag. - */ - hardscroll_disabled_by_init = 1; - hardscroll_enabled = 0; -} - -static void scrolldelta(int lines) -{ - int new_origin; - int last_origin_rel = (((video_mem_term - video_mem_base) - / video_num_columns / 2) - (video_num_lines - 1)) * video_num_columns; - - new_origin = __origin + lines * video_num_columns; - if (__origin > __real_origin) - new_origin -= last_origin_rel; - if (new_origin < 0) { - int s_top = __real_origin + video_num_lines*video_num_columns; - new_origin += last_origin_rel; - if (new_origin < s_top) - new_origin = s_top; - if (new_origin > last_origin_rel - video_num_columns - || has_wrapped == 0) - new_origin = 0; - else { - unsigned short * d = (unsigned short *) video_mem_base; - unsigned short * s = d + last_origin_rel; - int count = (video_num_lines-1)*video_num_columns; - while (count) { - count--; - scr_writew(scr_readw(d++),s++); - } - } - } else if (new_origin > __real_origin) - new_origin = __real_origin; - - __set_origin(new_origin); -} - void scrollback(int lines) { + int currcons = fg_console; + if (!lines) lines = video_num_lines/2; scrolldelta(-lines); @@ -612,156 +822,20 @@ void scrollback(int lines) void scrollfront(int lines) { + int currcons = fg_console; + if (!lines) lines = video_num_lines/2; scrolldelta(lines); } -static void set_origin(int currcons) -{ - if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_VGAC && - video_type != VIDEO_TYPE_EGAM && video_type != VIDEO_TYPE_PICA_S3 && - video_type != VIDEO_TYPE_SNI_RM && video_type != VIDEO_TYPE_SGI) - return; - if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) - return; - __real_origin = (origin-video_mem_base) >> 1; - __set_origin(__real_origin); -} - -static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr) -{ - int hardscroll = hardscroll_enabled; - - if (t+nr >= b) - nr = b - t - 1; - if (b > video_num_lines || t >= b || nr < 1) - return; - if (t || b != video_num_lines || nr > 1) - hardscroll = 0; - if (hardscroll) { - origin += video_size_row; - pos += video_size_row; - scr_end += video_size_row; -#if 0 - /* - * Aiiiieee. This check works for Alpha and Intel, but - * not for MIPS boxes ... The #ifdef is a temporary fix - * for MIPSes that is slooow. - */ - if (origin >= last_origin || origin < video_mem_base) { /*}*/ - if (scr_end > video_mem_end) { /*}*/ -#endif - /* - * Is the end of the area to scroll outside of the video RAM? - * If so, just do normal softscroll. The second part of the - * condition is important for some non-Intel architectures. - */ - if (scr_end > video_mem_end || scr_end < video_mem_base) { - unsigned short * d = (unsigned short *) video_mem_start; - unsigned short * s = (unsigned short *) origin; - unsigned int count; - - count = (video_num_lines-1)*video_num_columns; - while (count) { - count--; - scr_writew(scr_readw(s++),d++); - } - count = video_num_columns; - while (count) { - count--; - scr_writew(video_erase_char, d++); - } - scr_end -= origin-video_mem_start; - pos -= origin-video_mem_start; - origin = video_mem_start; - has_scrolled = 1; - if (currcons == fg_console) - has_wrapped = 1; - } else { - unsigned short * d; - unsigned int count; - - d = (unsigned short *) (scr_end - video_size_row); - count = video_num_columns; - while (count) { - count--; - scr_writew(video_erase_char, d++); - } - } - set_origin(currcons); - } else { - unsigned short * d = (unsigned short *) (origin+video_size_row*t); - unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr)); - - memcpyw(d, s, (b-t-nr) * video_size_row); - memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr); - } -} - -static void -scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr) -{ - unsigned short *s; - unsigned int count; - unsigned int step; - - if (t+nr >= b) - nr = b - t - 1; - if (b > video_num_lines || t >= b || nr < 1) - return; - s = (unsigned short *) (origin+video_size_row*(b-nr-1)); - step = video_num_columns * nr; - count = b - t - nr; - while (count--) { - memcpyw(s + step, s, video_size_row); - s -= video_num_columns; - } - while (nr--) { - s += video_num_columns; - memsetw(s, video_erase_char, video_size_row); - } - has_scrolled = 1; -} - -/* - * Routine to reset the visible "screen" to the top of video memory. - * This is necessary when exiting from the kernel back to a console - * which expects only the top of video memory to be used for the visible - * screen (with scrolling down by moving the memory contents). - * The normal action of the LINUX console is to scroll using all of the - * video memory and diddling the hardware top-of-video register as needed. - */ -void -scrreset(void) -{ - int currcons = fg_console; - unsigned short * d = (unsigned short *) video_mem_start; - unsigned short * s = (unsigned short *) origin; - unsigned int count; - - count = (video_num_lines-1)*video_num_columns; - memcpyw(d, s, 2*count); - memsetw(d + count, video_erase_char, - 2*video_num_columns); - scr_end -= origin-video_mem_start; - pos -= origin-video_mem_start; - origin = video_mem_start; - - has_scrolled = 1; - has_wrapped = 1; - - set_origin(currcons); - set_cursor(currcons); -} - static void lf(int currcons) { /* don't scroll if above bottom of scrolling region, or * if below scrolling region */ if (y+1 == bottom) - scrup(currcons,top,bottom, 1); + scrup(currcons,top,bottom,1); else if (y < video_num_lines-1) { y++; pos += video_size_row; @@ -805,91 +879,96 @@ static inline void del(int currcons) static void csi_J(int currcons, int vpar) { - unsigned long count; + unsigned int count; unsigned short * start; switch (vpar) { case 0: /* erase from cursor to end of display */ count = (scr_end-pos)>>1; start = (unsigned short *) pos; + if (DO_UPDATE) { + /* do in two stages */ + sw->con_clear(vc_cons[currcons].d, y, x, 1, + video_num_columns-x); + sw->con_clear(vc_cons[currcons].d, y+1, 0, + video_num_lines-y-1, + video_num_columns); + } break; case 1: /* erase from start to cursor */ count = ((pos-origin)>>1)+1; start = (unsigned short *) origin; + if (DO_UPDATE) { + /* do in two stages */ + sw->con_clear(vc_cons[currcons].d, 0, 0, y, + video_num_columns); + sw->con_clear(vc_cons[currcons].d, y, 0, 1, + x + 1); + } break; case 2: /* erase whole display */ count = video_num_columns * video_num_lines; start = (unsigned short *) origin; + if (DO_UPDATE) + sw->con_clear(vc_cons[currcons].d, 0, 0, + video_num_lines, + video_num_columns); break; default: return; } - memsetw(start, video_erase_char, 2*count); + scr_memsetw(start, video_erase_char, 2*count); need_wrap = 0; } static void csi_K(int currcons, int vpar) { - unsigned long count; + unsigned int count; unsigned short * start; switch (vpar) { case 0: /* erase from cursor to end of line */ count = video_num_columns-x; start = (unsigned short *) pos; + if (DO_UPDATE) + sw->con_clear(vc_cons[currcons].d, y, x, 1, + video_num_columns-x); break; case 1: /* erase from start of line to cursor */ start = (unsigned short *) (pos - (x<<1)); count = x+1; + if (DO_UPDATE) + sw->con_clear(vc_cons[currcons].d, y, 0, 1, + x + 1); break; case 2: /* erase whole line */ start = (unsigned short *) (pos - (x<<1)); count = video_num_columns; + if (DO_UPDATE) + sw->con_clear(vc_cons[currcons].d, y, 0, 1, + video_num_columns); break; default: return; } - memsetw(start, video_erase_char, 2 * count); + scr_memsetw(start, video_erase_char, 2 * count); need_wrap = 0; } static void csi_X(int currcons, int vpar) /* erase the following vpar positions */ { /* not vt100? */ + int count; + if (!vpar) vpar++; + count = (vpar > video_num_columns-x) ? (video_num_columns-x) : vpar; - memsetw((unsigned short *) pos, video_erase_char, - (vpar > video_num_columns-x) ? 2 * (video_num_columns-x) : 2 * vpar); + scr_memsetw((unsigned short *) pos, video_erase_char, 2 * count); + if (DO_UPDATE) + sw->con_clear(vc_cons[currcons].d, y, x, 1, count); need_wrap = 0; } -static void update_attr(int currcons) -{ - attr = color; - if (can_do_color) { - if (underline) - attr = (attr & 0xf0) | ulcolor; - else if (intensity == 0) - attr = (attr & 0xf0) | halfcolor; - } - if (reverse ^ decscnm) - attr = reverse_video_char(attr); - if (blink) - attr ^= 0x80; - if (intensity == 2) - attr ^= 0x08; - if (!can_do_color) { - if (underline) - attr = (attr & 0xf8) | 0x01; - else if (intensity == 0) - attr = (attr & 0xf0) | 0x08; - } - if (decscnm) - video_erase_char = (reverse_video_char(color) << 8) | ' '; - else - video_erase_char = (color << 8) | ' '; -} - static void default_attr(int currcons) { intensity = 1; @@ -1000,14 +1079,14 @@ static void respond_string(const char * p, struct tty_struct * tty) tty_insert_flip_char(tty, *p, 0); p++; } - tty_schedule_flip(tty); + con_schedule_flip(tty); } static void cursor_report(int currcons, struct tty_struct * tty) { char buf[40]; - sprintf(buf, "\033[%ld;%ldR", y + (decom ? top+1 : 1), x+1); + sprintf(buf, "\033[%d;%dR", y + (decom ? top+1 : 1), x+1); respond_string(buf, tty); } @@ -1038,135 +1117,6 @@ int mouse_reporting(void) return report_mouse; } -int tioclinux(struct tty_struct *tty, unsigned long arg) -{ - char type, data; - - if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) - return -EINVAL; - if (current->tty != tty && !suser()) - return -EPERM; - if (get_user(type, (char *)arg)) - return -EFAULT; - switch (type) - { - case 2: - return set_selection(arg, tty, 1); - case 3: - return paste_selection(tty); - case 4: - do_unblank_screen(); - return 0; - case 5: - return sel_loadlut(arg); - case 6: - - /* - * Make it possible to react to Shift+Mousebutton. - * Note that 'shift_state' is an undocumented - * kernel-internal variable; programs not closely - * related to the kernel should not use this. - */ - data = shift_state; - return __put_user(data, (char *) arg); - case 7: - data = mouse_reporting(); - return __put_user(data, (char *) arg); - case 10: - set_vesa_blanking(arg); - return 0; - case 11: /* set kmsg redirect */ - if (!suser()) - return -EPERM; - if (get_user(data, (char *)arg+1)) - return -EFAULT; - kmsg_redirect = data; - return 0; - case 12: /* get fg_console */ - return fg_console; - } - return -EINVAL; -} - -static inline unsigned short *screenpos(int currcons, int offset, int viewed) -{ - unsigned short *p = (unsigned short *)(origin + offset); - if (viewed && currcons == fg_console) - p -= (__real_origin - __origin); - return p; -} - -/* Note: inverting the screen twice should revert to the original state */ -void invert_screen(int currcons, int offset, int count, int viewed) -{ - unsigned short *p; - - count /= 2; - p = screenpos(currcons, offset, viewed); - if (can_do_color) - while (count--) { - unsigned short old = scr_readw(p); - scr_writew(reverse_video_short(old), p); - p++; - } - else - while (count--) { - unsigned short old = scr_readw(p); - scr_writew(old ^ (((old & 0x0700) == 0x0100) - ? 0x7000 : 0x7700), p); - p++; - } -} - -/* used by selection: complement pointer position */ -void complement_pos(int currcons, int offset) -{ - static unsigned short *p = NULL; - static unsigned short old = 0; - - if (p) - scr_writew(old, p); - if (offset == -1) - p = NULL; - else { - p = screenpos(currcons, offset, 1); - old = scr_readw(p); - scr_writew(old ^ 0x7700, p); - } -} - -/* used by selection */ -unsigned short screen_word(int currcons, int offset, int viewed) -{ - return scr_readw(screenpos(currcons, offset, viewed)); -} - -/* used by selection - convert a screen word to a glyph number */ -int scrw2glyph(unsigned short scr_word) -{ - return ( video_mode_512ch ) - ? ((scr_word & 0x0800) >> 3) + (scr_word & 0x00ff) - : scr_word & 0x00ff; -} - -/* used by vcs - note the word offset */ -unsigned short *screen_pos(int currcons, int w_offset, int viewed) -{ - return screenpos(currcons, 2 * w_offset, viewed); -} - -void getconsxy(int currcons, char *p) -{ - p[0] = x; - p[1] = y; -} - -void putconsxy(int currcons, char *p) -{ - gotoxy(currcons, p[0], p[1]); - set_cursor(currcons); -} - static void set_mode(int currcons, int on_off) { int i; @@ -1212,7 +1162,6 @@ static void set_mode(int currcons, int on_off) break; case 25: /* Cursor on/off */ deccm = on_off; - set_cursor(currcons); break; case 1000: report_mouse = on_off ? 2 : 0; @@ -1274,10 +1223,10 @@ static void setterm_command(int currcons) break; case 12: /* bring specified console to the front */ if (par[1] >= 1 && vc_cons_allocated(par[1]-1)) - update_screen(par[1]-1); + set_console(par[1] - 1); break; case 13: /* unblank the screen */ - unblank_screen(); + poke_blanked_console(); break; case 14: /* set vesa powerdown interval */ vesa_off_interval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; @@ -1285,39 +1234,12 @@ static void setterm_command(int currcons) } } -static void insert_char(int currcons) -{ - unsigned int i = x; - unsigned short tmp, old = video_erase_char; - unsigned short * p = (unsigned short *) pos; - - while (i++ < video_num_columns) { - tmp = scr_readw(p); - scr_writew(old, p); - old = tmp; - p++; - } - need_wrap = 0; -} - static void insert_line(int currcons, unsigned int nr) { scrdown(currcons,y,bottom,nr); need_wrap = 0; } -static void delete_char(int currcons) -{ - unsigned int i = x; - unsigned short * p = (unsigned short *) pos; - - while (++i < video_num_columns) { - scr_writew(scr_readw(p+1), p); - p++; - } - scr_writew(video_erase_char, p); - need_wrap = 0; -} static void delete_line(int currcons, unsigned int nr) { @@ -1327,18 +1249,17 @@ static void delete_line(int currcons, unsigned int nr) static void csi_at(int currcons, unsigned int nr) { - if (nr > video_num_columns) - nr = video_num_columns; + if (nr > video_num_columns - x) + nr = video_num_columns - x; else if (!nr) nr = 1; - while (nr--) - insert_char(currcons); + insert_char(currcons, nr); } static void csi_L(int currcons, unsigned int nr) { - if (nr > video_num_lines) - nr = video_num_lines; + if (nr > video_num_lines - y) + nr = video_num_lines - y; else if (!nr) nr = 1; insert_line(currcons, nr); @@ -1346,18 +1267,17 @@ static void csi_L(int currcons, unsigned int nr) static void csi_P(int currcons, unsigned int nr) { - if (nr > video_num_columns) - nr = video_num_columns; + if (nr > video_num_columns - x) + nr = video_num_columns - x; else if (!nr) nr = 1; - while (nr--) - delete_char(currcons); + delete_char(currcons, nr); } static void csi_M(int currcons, unsigned int nr) { - if (nr > video_num_lines) - nr = video_num_lines; + if (nr > video_num_lines - y) + nr = video_num_lines - y; else if (!nr) nr=1; delete_line(currcons, nr); @@ -1431,6 +1351,8 @@ static void reset_terminal(int currcons, int do_clear) kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate; set_leds(); + cursor_type = CUR_DEFAULT; + default_attr(currcons); update_attr(currcons); @@ -1449,61 +1371,380 @@ static void reset_terminal(int currcons, int do_clear) csi_J(currcons,2); } -/* - * Turn the Scroll-Lock LED on when the tty is stopped - */ -static void con_stop(struct tty_struct *tty) +static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c) { - int console_num; - if (!tty) + /* + * Control characters can be used in the _middle_ + * of an escape sequence. + */ + switch (c) { + case 0: return; - console_num = MINOR(tty->device) - (tty->driver.minor_start); - if (!vc_cons_allocated(console_num)) + case 7: + if (bell_duration) + kd_mksound(bell_pitch, bell_duration); return; -#if !CONFIG_AP1000 - set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); - set_leds(); -#endif -} - -/* - * Turn the Scroll-Lock LED off when the console is started - */ -static void con_start(struct tty_struct *tty) -{ - int console_num; - if (!tty) + case 8: + bs(currcons); return; - console_num = MINOR(tty->device) - (tty->driver.minor_start); - if (!vc_cons_allocated(console_num)) + case 9: + pos -= (x << 1); + while (x < video_num_columns - 1) { + x++; + if (tab_stop[x >> 5] & (1 << (x & 31))) + break; + } + pos += (x << 1); return; -#if !CONFIG_AP1000 - clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); - set_leds(); -#endif + case 10: case 11: case 12: + lf(currcons); + if (!is_kbd(lnm)) + return; + case 13: + cr(currcons); + return; + case 14: + charset = 1; + translate = set_translate(G1_charset); + disp_ctrl = 1; + return; + case 15: + charset = 0; + translate = set_translate(G0_charset); + disp_ctrl = 0; + return; + case 24: case 26: + vc_state = ESnormal; + return; + case 27: + vc_state = ESesc; + return; + case 127: + del(currcons); + return; + case 128+27: + vc_state = ESsquare; + return; + } + switch(vc_state) { + case ESesc: + vc_state = ESnormal; + switch (c) { + case '[': + vc_state = ESsquare; + return; + case ']': + vc_state = ESnonstd; + return; + case '%': + vc_state = ESpercent; + return; + case 'E': + cr(currcons); + lf(currcons); + return; + case 'M': + ri(currcons); + return; + case 'D': + lf(currcons); + return; + case 'H': + tab_stop[x >> 5] |= (1 << (x & 31)); + return; + case 'Z': + respond_ID(tty); + return; + case '7': + save_cur(currcons); + return; + case '8': + restore_cur(currcons); + return; + case '(': + vc_state = ESsetG0; + return; + case ')': + vc_state = ESsetG1; + return; + case '#': + vc_state = EShash; + return; + case 'c': + reset_terminal(currcons,1); + return; + case '>': /* Numeric keypad */ + clr_kbd(kbdapplic); + return; + case '=': /* Appl. keypad */ + set_kbd(kbdapplic); + return; + } + return; + case ESnonstd: + if (c=='P') { /* palette escape sequence */ + for (npar=0; npar<NPAR; npar++) + par[npar] = 0 ; + npar = 0 ; + vc_state = ESpalette; + return; + } else if (c=='R') { /* reset palette */ + reset_palette (currcons); + vc_state = ESnormal; + } else + vc_state = ESnormal; + return; + case ESpalette: + if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { + par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; + if (npar==7) { + int i = par[0]*3, j = 1; + palette[i] = 16*par[j++]; + palette[i++] += par[j++]; + palette[i] = 16*par[j++]; + palette[i++] += par[j++]; + palette[i] = 16*par[j++]; + palette[i] += par[j]; + set_palette() ; + vc_state = ESnormal; + } + } else + vc_state = ESnormal; + return; + case ESsquare: + for(npar = 0 ; npar < NPAR ; npar++) + par[npar] = 0; + npar = 0; + vc_state = ESgetpars; + if (c == '[') { /* Function key */ + vc_state=ESfunckey; + return; + } + ques = (c=='?'); + if (ques) + return; + case ESgetpars: + if (c==';' && npar<NPAR-1) { + npar++; + return; + } else if (c>='0' && c<='9') { + par[npar] *= 10; + par[npar] += c-'0'; + return; + } else vc_state=ESgotpars; + case ESgotpars: + vc_state = ESnormal; + switch(c) { + case 'h': + set_mode(currcons,1); + return; + case 'l': + set_mode(currcons,0); + return; + case 'c': + if (ques) { + if (par[0]) + cursor_type = par[0] | (par[1]<<8) | (par[2]<<16); + else + cursor_type = CUR_DEFAULT; + return; + } + break; + case 'n': + if (!ques) { + if (par[0] == 5) + status_report(tty); + else if (par[0] == 6) + cursor_report(currcons,tty); + } + return; + } + if (ques) { + ques = 0; + return; + } + switch(c) { + case 'G': case '`': + if (par[0]) par[0]--; + gotoxy(currcons,par[0],y); + return; + case 'A': + if (!par[0]) par[0]++; + gotoxy(currcons,x,y-par[0]); + return; + case 'B': case 'e': + if (!par[0]) par[0]++; + gotoxy(currcons,x,y+par[0]); + return; + case 'C': case 'a': + if (!par[0]) par[0]++; + gotoxy(currcons,x+par[0],y); + return; + case 'D': + if (!par[0]) par[0]++; + gotoxy(currcons,x-par[0],y); + return; + case 'E': + if (!par[0]) par[0]++; + gotoxy(currcons,0,y+par[0]); + return; + case 'F': + if (!par[0]) par[0]++; + gotoxy(currcons,0,y-par[0]); + return; + case 'd': + if (par[0]) par[0]--; + gotoxay(currcons,x,par[0]); + return; + case 'H': case 'f': + if (par[0]) par[0]--; + if (par[1]) par[1]--; + gotoxay(currcons,par[1],par[0]); + return; + case 'J': + csi_J(currcons,par[0]); + return; + case 'K': + csi_K(currcons,par[0]); + return; + case 'L': + csi_L(currcons,par[0]); + return; + case 'M': + csi_M(currcons,par[0]); + return; + case 'P': + csi_P(currcons,par[0]); + return; + case 'c': + if (!par[0]) + respond_ID(tty); + return; + case 'g': + if (!par[0]) + tab_stop[x >> 5] &= ~(1 << (x & 31)); + else if (par[0] == 3) { + tab_stop[0] = + tab_stop[1] = + tab_stop[2] = + tab_stop[3] = + tab_stop[4] = 0; + } + return; + case 'm': + csi_m(currcons); + return; + case 'q': /* DECLL - but only 3 leds */ + /* map 0,1,2,3 to 0,1,2,4 */ + if (par[0] < 4) + setledstate(kbd_table + currcons, + (par[0] < 3) ? par[0] : 4); + return; + case 'r': + if (!par[0]) + par[0]++; + if (!par[1]) + par[1] = video_num_lines; + /* Minimum allowed region is 2 lines */ + if (par[0] < par[1] && + par[1] <= video_num_lines) { + top=par[0]-1; + bottom=par[1]; + gotoxay(currcons,0,0); + } + return; + case 's': + save_cur(currcons); + return; + case 'u': + restore_cur(currcons); + return; + case 'X': + csi_X(currcons, par[0]); + return; + case '@': + csi_at(currcons,par[0]); + return; + case ']': /* setterm functions */ + setterm_command(currcons); + return; + } + return; + case ESpercent: + vc_state = ESnormal; + switch (c) { + case '@': /* defined in ISO 2022 */ + utf = 0; + return; + case 'G': /* prelim official escape code */ + case '8': /* retained for compatibility */ + utf = 1; + return; + } + return; + case ESfunckey: + vc_state = ESnormal; + return; + case EShash: + vc_state = ESnormal; + if (c == '8') { + /* DEC screen alignment test. kludge :-) */ + video_erase_char = + (video_erase_char & 0xff00) | 'E'; + csi_J(currcons, 2); + video_erase_char = + (video_erase_char & 0xff00) | ' '; + do_update_region(currcons, origin, screenbuf_size/2); + } + return; + case ESsetG0: + if (c == '0') + G0_charset = GRAF_MAP; + else if (c == 'B') + G0_charset = LAT1_MAP; + else if (c == 'U') + G0_charset = IBMPC_MAP; + else if (c == 'K') + G0_charset = USER_MAP; + if (charset == 0) + translate = set_translate(G0_charset); + vc_state = ESnormal; + return; + case ESsetG1: + if (c == '0') + G1_charset = GRAF_MAP; + else if (c == 'B') + G1_charset = LAT1_MAP; + else if (c == 'U') + G1_charset = IBMPC_MAP; + else if (c == 'K') + G1_charset = USER_MAP; + if (charset == 1) + translate = set_translate(G1_charset); + vc_state = ESnormal; + return; + default: + vc_state = ESnormal; + } } -static void con_flush_chars(struct tty_struct *tty) -{ - unsigned int currcons; - struct vt_struct *vt = (struct vt_struct *)tty->driver_data; - - currcons = vt->vc_num; - if (vcmode != KD_GRAPHICS) - set_cursor(currcons); -} - static int do_con_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { - int c, tc, ok, n = 0; +#ifdef VT_BUF_VRAM_ONLY +#define FLUSH do { } while(0); +#else +#define FLUSH if (draw_x >= 0) { \ + sw->con_putcs(vc_cons[currcons].d, (u16 *)draw_from, (u16 *)draw_to-(u16 *)draw_from, y, draw_x); \ + draw_x = -1; \ + } +#endif + + int c, tc, ok, n = 0, draw_x = -1; unsigned int currcons; + unsigned long draw_from = 0, draw_to = 0; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; - -#if CONFIG_AP1000 - ap_write(1,buf,count); - return(count); -#endif + u16 himask, charmask; currcons = vt->vc_num; if (!vc_cons_allocated(currcons)) { @@ -1516,17 +1757,21 @@ static int do_con_write(struct tty_struct * tty, int from_user, return 0; } - if (currcons == sel_cons) - clear_selection(); - if (from_user) { /* just to make sure that noone lurks at places he shouldn't see. */ if (verify_area(VERIFY_READ, buf, count)) return 0; /* ?? are error codes legal here ?? */ } + himask = hi_font_mask; + charmask = himask ? 0x1ff : 0xff; + + /* undraw cursor first */ + if (IS_FG) + hide_cursor(currcons); + disable_bh(CONSOLE_BH); - while (!tty->stopped && count) { + while (!tty->stopped && count) { enable_bh(CONSOLE_BH); if (from_user) __get_user(c, buf); @@ -1591,11 +1836,11 @@ static int do_con_write(struct tty_struct * tty, int from_user, if (vc_state == ESnormal && ok) { /* Now try to find out how to display it */ - tc = conv_uni_to_pc(tc); + tc = conv_uni_to_pc(vc_cons[currcons].d, tc); if ( tc == -4 ) { /* If we got -4 (not found) then see if we have defined a replacement character (U+FFFD) */ - tc = conv_uni_to_pc(0xfffd); + tc = conv_uni_to_pc(vc_cons[currcons].d, 0xfffd); /* One reason for the -4 can be that we just did a clear_unimap(); @@ -1606,429 +1851,86 @@ static int do_con_write(struct tty_struct * tty, int from_user, /* Bad hash table -- hope for the best */ tc = c; } - if (tc & ~console_charmask) + if (tc & ~charmask) continue; /* Conversion failed */ + if (need_wrap || decim) + FLUSH if (need_wrap) { cr(currcons); lf(currcons); } if (decim) - insert_char(currcons); - scr_writew( video_mode_512ch ? - ((attr & 0xf7) << 8) + ((tc & 0x100) << 3) + - (tc & 0x0ff) : (attr << 8) + tc, - (unsigned short *) pos); - if (x == video_num_columns - 1) + insert_char(currcons, 1); + scr_writew(himask ? + ((attr & ~himask) << 8) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) : + (attr << 8) + tc, + (u16 *) pos); + if (DO_UPDATE && draw_x < 0) { + draw_x = x; + draw_from = pos; + } + if (x == video_num_columns - 1) { need_wrap = decawm; - else { + draw_to = pos+2; + } else { x++; - pos+=2; + draw_to = (pos+=2); } continue; } - - /* - * Control characters can be used in the _middle_ - * of an escape sequence. - */ - switch (c) { - case 0: - continue; - case 7: - if (bell_duration) - kd_mksound(bell_pitch, bell_duration); - continue; - case 8: - bs(currcons); - continue; - case 9: - pos -= (x << 1); - while (x < video_num_columns - 1) { - x++; - if (tab_stop[x >> 5] & (1 << (x & 31))) - break; - } - pos += (x << 1); - continue; - case 10: case 11: case 12: - lf(currcons); - if (!is_kbd(lnm)) - continue; - case 13: - cr(currcons); - continue; - case 14: - charset = 1; - translate = set_translate(G1_charset); - disp_ctrl = 1; - continue; - case 15: - charset = 0; - translate = set_translate(G0_charset); - disp_ctrl = 0; - continue; - case 24: case 26: - vc_state = ESnormal; - continue; - case 27: - vc_state = ESesc; - continue; - case 127: - del(currcons); - continue; - case 128+27: - vc_state = ESsquare; - continue; - } - switch(vc_state) { - case ESesc: - vc_state = ESnormal; - switch (c) { - case '[': - vc_state = ESsquare; - continue; - case ']': - vc_state = ESnonstd; - continue; - case '%': - vc_state = ESpercent; - continue; - case 'E': - cr(currcons); - lf(currcons); - continue; - case 'M': - ri(currcons); - continue; - case 'D': - lf(currcons); - continue; - case 'H': - tab_stop[x >> 5] |= (1 << (x & 31)); - continue; - case 'Z': - respond_ID(tty); - continue; - case '7': - save_cur(currcons); - continue; - case '8': - restore_cur(currcons); - continue; - case '(': - vc_state = ESsetG0; - continue; - case ')': - vc_state = ESsetG1; - continue; - case '#': - vc_state = EShash; - continue; - case 'c': - reset_terminal(currcons,1); - continue; - case '>': /* Numeric keypad */ - clr_kbd(kbdapplic); - continue; - case '=': /* Appl. keypad */ - set_kbd(kbdapplic); - continue; - } - continue; - case ESnonstd: - if (c=='P') { /* palette escape sequence */ - for (npar=0; npar<NPAR; npar++) - par[npar] = 0 ; - npar = 0 ; - vc_state = ESpalette; - continue; - } else if (c=='R') { /* reset palette */ - reset_palette (currcons); - vc_state = ESnormal; - } else - vc_state = ESnormal; - continue; - case ESpalette: - if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { - par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; - if (npar==7) { - int i = par[0]*3, j = 1; - palette[i] = 16*par[j++]; - palette[i++] += par[j++]; - palette[i] = 16*par[j++]; - palette[i++] += par[j++]; - palette[i] = 16*par[j++]; - palette[i] += par[j]; - set_palette() ; - vc_state = ESnormal; - } - } else - vc_state = ESnormal; - continue; - case ESsquare: - for(npar = 0 ; npar < NPAR ; npar++) - par[npar] = 0; - npar = 0; - vc_state = ESgetpars; - if (c == '[') { /* Function key */ - vc_state=ESfunckey; - continue; - } - ques = (c=='?'); - if (ques) - continue; - case ESgetpars: - if (c==';' && npar<NPAR-1) { - npar++; - continue; - } else if (c>='0' && c<='9') { - par[npar] *= 10; - par[npar] += c-'0'; - continue; - } else vc_state=ESgotpars; - case ESgotpars: - vc_state = ESnormal; - switch(c) { - case 'h': - set_mode(currcons,1); - continue; - case 'l': - set_mode(currcons,0); - continue; - case 'n': - if (!ques) - if (par[0] == 5) - status_report(tty); - else if (par[0] == 6) - cursor_report(currcons,tty); - continue; - } - if (ques) { - ques = 0; - continue; - } - switch(c) { - case 'G': case '`': - if (par[0]) par[0]--; - gotoxy(currcons,par[0],y); - continue; - case 'A': - if (!par[0]) par[0]++; - gotoxy(currcons,x,y-par[0]); - continue; - case 'B': case 'e': - if (!par[0]) par[0]++; - gotoxy(currcons,x,y+par[0]); - continue; - case 'C': case 'a': - if (!par[0]) par[0]++; - gotoxy(currcons,x+par[0],y); - continue; - case 'D': - if (!par[0]) par[0]++; - gotoxy(currcons,x-par[0],y); - continue; - case 'E': - if (!par[0]) par[0]++; - gotoxy(currcons,0,y+par[0]); - continue; - case 'F': - if (!par[0]) par[0]++; - gotoxy(currcons,0,y-par[0]); - continue; - case 'd': - if (par[0]) par[0]--; - gotoxay(currcons,x,par[0]); - continue; - case 'H': case 'f': - if (par[0]) par[0]--; - if (par[1]) par[1]--; - gotoxay(currcons,par[1],par[0]); - continue; - case 'J': - csi_J(currcons,par[0]); - continue; - case 'K': - csi_K(currcons,par[0]); - continue; - case 'L': - csi_L(currcons,par[0]); - continue; - case 'M': - csi_M(currcons,par[0]); - continue; - case 'P': - csi_P(currcons,par[0]); - continue; - case 'c': - if (!par[0]) - respond_ID(tty); - continue; - case 'g': - if (!par[0]) - tab_stop[x >> 5] &= ~(1 << (x & 31)); - else if (par[0] == 3) { - tab_stop[0] = - tab_stop[1] = - tab_stop[2] = - tab_stop[3] = - tab_stop[4] = 0; - } - continue; - case 'm': - csi_m(currcons); - continue; - case 'q': /* DECLL - but only 3 leds */ - /* map 0,1,2,3 to 0,1,2,4 */ - if (par[0] < 4) - setledstate(kbd_table + currcons, - (par[0] < 3) ? par[0] : 4); - continue; - case 'r': - if (!par[0]) - par[0]++; - if (!par[1]) - par[1] = video_num_lines; - /* Minimum allowed region is 2 lines */ - if (par[0] < par[1] && - par[1] <= video_num_lines) { - top=par[0]-1; - bottom=par[1]; - gotoxay(currcons,0,0); - } - continue; - case 's': - save_cur(currcons); - continue; - case 'u': - restore_cur(currcons); - continue; - case 'X': - csi_X(currcons, par[0]); - continue; - case '@': - csi_at(currcons,par[0]); - continue; - case ']': /* setterm functions */ - setterm_command(currcons); - continue; - } - continue; - case ESpercent: - vc_state = ESnormal; - switch (c) { - case '@': /* defined in ISO 2022 */ - utf = 0; - continue; - case 'G': /* prelim official escape code */ - case '8': /* retained for compatibility */ - utf = 1; - continue; - } - continue; - case ESfunckey: - vc_state = ESnormal; - continue; - case EShash: - vc_state = ESnormal; - if (c == '8') { - /* DEC screen alignment test. kludge :-) */ - video_erase_char = - (video_erase_char & 0xff00) | 'E'; - csi_J(currcons, 2); - video_erase_char = - (video_erase_char & 0xff00) | ' '; - } - continue; - case ESsetG0: - if (c == '0') - G0_charset = GRAF_MAP; - else if (c == 'B') - G0_charset = LAT1_MAP; - else if (c == 'U') - G0_charset = IBMPC_MAP; - else if (c == 'K') - G0_charset = USER_MAP; - if (charset == 0) - translate = set_translate(G0_charset); - vc_state = ESnormal; - continue; - case ESsetG1: - if (c == '0') - G1_charset = GRAF_MAP; - else if (c == 'B') - G1_charset = LAT1_MAP; - else if (c == 'U') - G1_charset = IBMPC_MAP; - else if (c == 'K') - G1_charset = USER_MAP; - if (charset == 1) - translate = set_translate(G1_charset); - vc_state = ESnormal; - continue; - default: - vc_state = ESnormal; - } + FLUSH + do_con_trol(tty, currcons, c); } + FLUSH enable_bh(CONSOLE_BH); return n; +#undef FLUSH } -static int con_write(struct tty_struct * tty, int from_user, - const unsigned char *buf, int count) -{ - int retval; - - retval = do_con_write(tty, from_user, buf, count); - con_flush_chars(tty); - - return retval; -} - -static void con_put_char(struct tty_struct *tty, unsigned char ch) -{ - do_con_write(tty, 0, &ch, 1); -} - -static int con_write_room(struct tty_struct *tty) -{ - if (tty->stopped) - return 0; - return 4096; /* No limit, really; we're not buffering */ -} - -static int con_chars_in_buffer(struct tty_struct *tty) -{ - return 0; /* we're not buffering */ -} - -void poke_blanked_console(void) +/* + * This is the console switching bottom half handler. + * + * Doing console switching in a bottom half handler allows + * us to do the switches asynchronously (needed when we want + * to switch due to a keyboard interrupt), while still giving + * us the option to easily disable it to avoid races when we + * need to write to the console. + */ +static void console_bh(void) { - timer_active &= ~(1<<BLANK_TIMER); - if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) - return; - if (console_blanked) { - timer_table[BLANK_TIMER].fn = unblank_screen; - timer_table[BLANK_TIMER].expires = 0; - timer_active |= 1<<BLANK_TIMER; - } else if (blankinterval) { - timer_table[BLANK_TIMER].expires = jiffies + blankinterval; - timer_active |= 1<<BLANK_TIMER; + run_task_queue(&con_task_queue); + 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 + in an interrupt routine */ + } + want_console = -1; + } + if (do_poke_blanked_console) { /* do not unblank for a LED change */ + do_poke_blanked_console = 0; + poke_blanked_console(); } } +/* + * Console on virtual terminal + */ + #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; + const ushort *start; + ushort cnt = 0; + ushort myx = x; -#if CONFIG_AP1000 - prom_printf(b); - return; -#endif if (!printable || printing) return; /* console not yet initialized */ printing = 1; @@ -2039,36 +1941,68 @@ void vt_console_print(struct console *co, const char * b, unsigned count) if (!vc_cons_allocated(currcons)) { /* impossible */ printk("vt_console_print: tty %d not allocated ??\n", currcons+1); - return; + goto quit; } -#ifdef CONFIG_SERIAL_ECHO - serial_echo_print(b); -#endif /* CONFIG_SERIAL_ECHO */ + /* undraw cursor first */ + if (IS_FG) + hide_cursor(currcons); + + start = (ushort *)pos; - while (count-- > 0) { - c = *(b++); - if (c == 10 || c == 13 || need_wrap) { + /* 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) + sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x); + x += cnt; + if (need_wrap) + x--; + cnt = 0; + } + if (c == 8) { /* backspace */ + bs(currcons); + start = (ushort *)pos; + myx = x; + continue; + } if (c != 13) lf(currcons); cr(currcons); + start = (ushort *)pos; + myx = x; if (c == 10 || c == 13) continue; } - if (c == 8) { /* backspace */ - bs(currcons); - continue; - } scr_writew((attr << 8) + c, (unsigned short *) pos); - if (x == video_num_columns - 1) { + cnt++; + if (myx == video_num_columns - 1) { need_wrap = 1; continue; } - x++; pos+=2; + myx++; } + if (cnt > 0) { + if (IS_VISIBLE) + sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x); + x += cnt; + if (x == video_num_columns) { + x--; + need_wrap = 1; + } + } + enable_bh(CONSOLE_BH); set_cursor(currcons); poke_blanked_console(); + +quit: printing = 0; } @@ -2077,8 +2011,6 @@ static kdev_t vt_console_device(struct console *c) return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1); } -extern int keyboard_wait_for_keypress(struct console *); - struct console vt_console_driver = { "tty", vt_console_print, @@ -2095,6 +2027,92 @@ struct console vt_console_driver = { #endif /* + * Handling of Linux-specific VC ioctls + */ + +int tioclinux(struct tty_struct *tty, unsigned long arg) +{ + char type, data; + + if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) + return -EINVAL; + if (current->tty != tty && !suser()) + return -EPERM; + if (get_user(type, (char *)arg)) + return -EFAULT; + switch (type) + { + case 2: + return set_selection(arg, tty, 1); + case 3: + return paste_selection(tty); + case 4: + do_unblank_screen(); + return 0; + case 5: + return sel_loadlut(arg); + case 6: + + /* + * Make it possible to react to Shift+Mousebutton. + * Note that 'shift_state' is an undocumented + * kernel-internal variable; programs not closely + * related to the kernel should not use this. + */ + data = shift_state; + return __put_user(data, (char *) arg); + case 7: + data = mouse_reporting(); + return __put_user(data, (char *) arg); + case 10: + set_vesa_blanking(arg); + return 0; + case 11: /* set kmsg redirect */ + if (!suser()) + return -EPERM; + if (get_user(data, (char *)arg+1)) + return -EFAULT; + kmsg_redirect = data; + return 0; + case 12: /* get fg_console */ + return fg_console; + } + return -EINVAL; +} + +/* + * /dev/ttyN handling + */ + +static int con_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int retval; + + retval = do_con_write(tty, from_user, buf, count); + con_flush_chars(tty); + + return retval; +} + +static void con_put_char(struct tty_struct *tty, unsigned char ch) +{ + do_con_write(tty, 0, &ch, 1); +} + +static int con_write_room(struct tty_struct *tty) +{ + if (tty->stopped) + return 0; + return 4096; /* No limit, really; we're not buffering */ +} + +static int con_chars_in_buffer(struct tty_struct *tty) +{ + return 0; /* we're not buffering */ +} + +/* * con_throttle and con_unthrottle are only used for * paste_selection(), which has to stuff in a large number of * characters... @@ -2110,9 +2128,69 @@ static void con_unthrottle(struct tty_struct *tty) wake_up_interruptible(&vt->paste_wait); } -static void vc_init(unsigned int currcons, unsigned long rows, unsigned long cols, int do_clear) +/* + * Turn the Scroll-Lock LED on when the tty is stopped + */ +static void con_stop(struct tty_struct *tty) +{ + int console_num; + if (!tty) + return; + console_num = MINOR(tty->device) - (tty->driver.minor_start); + if (!vc_cons_allocated(console_num)) + return; + set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); + set_leds(); +} + +/* + * Turn the Scroll-Lock LED off when the console is started + */ +static void con_start(struct tty_struct *tty) +{ + int console_num; + if (!tty) + return; + console_num = MINOR(tty->device) - (tty->driver.minor_start); + if (!vc_cons_allocated(console_num)) + return; + clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); + set_leds(); +} + +static void con_flush_chars(struct tty_struct *tty) +{ + struct vt_struct *vt = (struct vt_struct *)tty->driver_data; + + set_cursor(vt->vc_num); +} + +/* + * Allocate the console screen memory. + */ +static int con_open(struct tty_struct *tty, struct file * filp) +{ + unsigned int currcons; + int i; + + currcons = MINOR(tty->device) - tty->driver.minor_start; + + i = vc_allocate(currcons, 0); + if (i) + return i; + + vt_cons[currcons]->vc_num = currcons; + tty->driver_data = vt_cons[currcons]; + + if (!tty->winsize.ws_row && !tty->winsize.ws_col) { + tty->winsize.ws_row = video_num_lines; + tty->winsize.ws_col = video_num_columns; + } + return 0; +} + +static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear) { - long base = (long) vc_scrbuf[currcons]; int j, k ; video_num_columns = cols; @@ -2120,9 +2198,8 @@ static void vc_init(unsigned int currcons, unsigned long rows, unsigned long col video_size_row = cols<<1; video_screen_size = video_num_lines * video_size_row; - pos = origin = video_mem_start = base; - scr_end = base + video_screen_size; - video_mem_end = base + video_screen_size; + set_origin(currcons); + pos = origin; reset_vc(currcons); for (j=k=0; j<16; j++) { vc_cons[currcons].d->vc_palette[k++] = default_red[j] ; @@ -2136,83 +2213,26 @@ static void vc_init(unsigned int currcons, unsigned long rows, unsigned long col reset_terminal(currcons, do_clear); } -static void con_setsize(unsigned long rows, unsigned long cols) -{ - video_num_lines = rows; - video_num_columns = cols; - video_size_row = 2 * cols; - video_screen_size = video_num_lines * video_size_row; -} - /* - * This is the console switching bottom half handler. - * - * Doing console switching in a bottom half handler allows - * us to do the switches asynchronously (needed when we want - * to switch due to a keyboard interrupt), while still giving - * us the option to easily disable it to avoid races when we - * need to write to the console. - */ -static void console_bh(void) -{ - if (want_console >= 0) { - if (want_console != fg_console) { - change_console(want_console); - /* we only changed when the console had already - been allocated - a new console is not created - in an interrupt routine */ - } - want_console = -1; - } - if (do_poke_blanked_console) { /* do not unblank for a LED change */ - do_poke_blanked_console = 0; - poke_blanked_console(); - } -} - -/* - * unsigned long con_init(unsigned long); - * * This routine initializes console interrupts, and does nothing * else. If you want the screen to clear, call tty_write with * the appropriate escape-sequence. - * - * Reads the information preserved by setup.s to determine the current display - * type and sets everything accordingly. - * - * FIXME: return early if we don't _have_ a video card installed. - * */ -__initfunc(unsigned long con_init(unsigned long kmem_start)) -{ - const char *display_desc = "????"; - int currcons = 0; - int orig_x = ORIG_X; - int orig_y = ORIG_Y; - -#ifdef CONFIG_SGI - if (serial_console) { - fg_console = 0; - rs_cons_hook(0, 0, serial_console); - rs_cons_hook(0, 1, serial_console); +struct tty_driver console_driver; +static int console_refcount; - return kmem_start; - } -#endif +__initfunc(unsigned long con_init(unsigned long kmem_start)) +{ + const char *display_desc = NULL; + unsigned int currcons = 0; -#ifdef __sparc__ - if (serial_console) { + if (conswitchp) + display_desc = conswitchp->con_startup(); + if (!display_desc) { fg_console = 0; - -#if CONFIG_SUN_SERIAL - rs_cons_hook(0, 0, serial_console); - rs_cons_hook(0, 1, serial_console); -#endif - return kmem_start; } -#endif memset(&console_driver, 0, sizeof(struct tty_driver)); console_driver.magic = TTY_DRIVER_MAGIC; @@ -2244,11 +2264,6 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); -#if CONFIG_AP1000 - return(kmem_start); -#endif - con_setsize(ORIG_VIDEO_LINES, ORIG_VIDEO_COLS); - timer_table[BLANK_TIMER].fn = blank_screen; timer_table[BLANK_TIMER].expires = 0; if (blankinterval) { @@ -2256,16 +2271,7 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) timer_active |= 1<<BLANK_TIMER; } - kmem_start = con_type_init(kmem_start, &display_desc); - - hardscroll_enabled = (hardscroll_disabled_by_init ? 0 : - (video_type == VIDEO_TYPE_EGAC - || video_type == VIDEO_TYPE_VGAC - || video_type == VIDEO_TYPE_EGAM - || video_type == VIDEO_TYPE_PICA_S3 - || video_type == VIDEO_TYPE_SNI_RM )); - has_wrapped = 0 ; - + /* Unfortunately, kmalloc is not running yet */ /* Due to kmalloc roundup allocating statically is more efficient - so provide MIN_NR_CONSOLES for people with very little memory */ for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) { @@ -2275,11 +2281,12 @@ __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); - vc_scrbuf[currcons] = (unsigned short *) kmem_start; + visual_init(currcons); + screenbuf = (unsigned short *) kmem_start; kmem_start += video_screen_size; kmalloced = 0; - screenbuf_size = video_screen_size; - vc_init(currcons, video_num_lines, video_num_columns, currcons); + vc_init(currcons, video_num_lines, video_num_columns, + currcons || !sw->con_save_screen); for (j=k=0; j<16; j++) { vc_cons[currcons].d->vc_palette[k++] = default_red[j] ; vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ; @@ -2288,77 +2295,100 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) } currcons = fg_console = 0; - - video_mem_start = video_mem_base; - video_mem_end = video_mem_term; - origin = video_mem_start; - scr_end = video_mem_start + video_num_lines * video_size_row; - gotoxy(currcons,orig_x,orig_y); + master_display_fg = vc_cons[currcons].d; set_origin(currcons); + save_screen(); + 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); + printable = 1; + printk("\n"); - /* Figure out the size of the screen and screen font so we - can figure out the appropriate screen size should we load - a different font */ +#ifdef CONFIG_VT_CONSOLE + register_console(&vt_console_driver); +#endif - printable = 1; - if ( video_type == VIDEO_TYPE_VGAC || video_type == VIDEO_TYPE_EGAC - || video_type == VIDEO_TYPE_EGAM || video_type == VIDEO_TYPE_TGAC - || video_type == VIDEO_TYPE_SGI || video_type == VIDEO_TYPE_SUN ) - { - default_font_height = video_font_height = ORIG_VIDEO_POINTS; - /* This may be suboptimal but is a safe bet - go with it */ - video_scan_lines = video_font_height * video_num_lines; + init_bh(CONSOLE_BH, console_bh); + + return kmem_start; +} -#ifdef CONFIG_SERIAL_ECHO - serial_echo_init(SERIAL_ECHO_PORT); -#endif /* CONFIG_SERIAL_ECHO */ +/* + * 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. + */ - printk("Console: %ld point font, %ld scans\n", - video_font_height, video_scan_lines); - } -#if defined(CONFIG_ACER_PICA_61) || defined(CONFIG_SNI_RM200_PCI) - else if (video_type == VIDEO_TYPE_PICA_S3 || - video_type == VIDEO_TYPE_SNI_RM) - { - /* - * Fixme: we should detect this. But still 16 is a reasonable - * assumption. - */ - default_font_height = video_font_height = 16; - /* This may be suboptimal but is a safe bet - go with it */ - video_scan_lines = video_font_height * video_num_lines; - -#ifdef CONFIG_SERIAL_ECHO - serial_echo_init(SERIAL_ECHO_PORT); -#endif /* CONFIG_SERIAL_ECHO */ - - printk("Console: %ld point font, %ld scans\n", - video_font_height, video_scan_lines); - } -#endif +#ifndef VT_BUF_VRAM_ONLY - printk("Console: %s %s %ldx%ld, %d virtual console%s (max %d)\n", - can_do_color ? "colour" : "mono", - display_desc, video_num_columns, video_num_lines, - MIN_NR_CONSOLES, (MIN_NR_CONSOLES == 1) ? "" : "s", - MAX_NR_CONSOLES); +void take_over_console(struct consw *csw, int first, int last, int deflt) +{ + int i; + const char *desc; - con_type_init_finish(); + if (deflt) + conswitchp = csw; + desc = csw->con_startup(); + if (!desc) return; + + for (i = first; i <= last; i++) { + 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); + 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(); +} - /* - * can't register TGA yet, because PCI bus probe has *not* taken - * place before con_init() gets called. Trigger the real TGA hw - * initialization and register_console() event from - * within the bus probing code... :-( - */ -#ifdef CONFIG_VT_CONSOLE - if (video_type != VIDEO_TYPE_TGAC && con_is_present()) - register_console(&vt_console_driver); #endif - init_bh(CONSOLE_BH, console_bh); - return kmem_start; +/* + * Screen blanking + */ + +void set_vesa_blanking(unsigned long arg) +{ + char *argp = (char *)arg + 1; + unsigned int mode; + get_user(mode, argp); + 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) +{ + struct vc_data *c = vc_cons[fg_console].d; + /* + * Power down if currently suspended (1 or 2), + * suspend if currently blanked (0), + * else do nothing (i.e. already powered down (3)). + * Called only if powerdown features are allowed. + */ + switch (vesa_blank_mode) { + case VESA_NO_BLANKING: + c->vc_sw->con_blank(c, VESA_VSYNC_SUSPEND+1); + break; + case VESA_VSYNC_SUSPEND: + case VESA_HSYNC_SUSPEND: + c->vc_sw->con_blank(c, VESA_POWERDOWN+1); + break; + } } void vesa_powerdown_screen(void) @@ -2371,11 +2401,29 @@ void vesa_powerdown_screen(void) void do_blank_screen(int nopowersave) { - int currcons; + int currcons = fg_console; + int i; if (console_blanked) return; + /* entering graphics mode? */ + if (nopowersave) { + hide_cursor(currcons); + save_screen(); + sw->con_blank(vc_cons[currcons].d, -1); + console_blanked = fg_console + 1; + set_origin(currcons); + return; + } + + /* don't blank graphics */ + if (vt_cons[fg_console]->vc_mode != KD_TEXT) { + console_blanked = fg_console + 1; + return; + } + + hide_cursor(fg_console); if(vesa_off_interval && !nopowersave) { timer_table[BLANK_TIMER].fn = vesa_powerdown_screen; timer_table[BLANK_TIMER].expires = jiffies + vesa_off_interval; @@ -2385,18 +2433,12 @@ void do_blank_screen(int nopowersave) timer_table[BLANK_TIMER].fn = unblank_screen; } - /* try not to lose information by blanking, and not to waste memory */ - currcons = fg_console; - has_scrolled = 0; - blank__origin = __origin; - blank_origin = origin; - set_origin(fg_console); - get_scrmem(fg_console); - unblank_origin = origin; - memsetw((void *)blank_origin, BLANK, - 2*video_num_lines*video_num_columns); - hide_cursor(); + save_screen(); + /* 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) { @@ -2411,9 +2453,6 @@ void do_blank_screen(int nopowersave) void do_unblank_screen(void) { int currcons; - int resetorg; - long offset; - if (!console_blanked) return; if (!vc_cons_allocated(fg_console)) { @@ -2428,34 +2467,16 @@ void do_unblank_screen(void) } currcons = fg_console; - offset = 0; - resetorg = 0; - if (console_blanked == fg_console + 1 && origin == unblank_origin - && !has_scrolled) { - /* try to restore the exact situation before blanking */ - resetorg = 1; - offset = (blank_origin - video_mem_base) - - (unblank_origin - video_mem_start); - } - console_blanked = 0; - set_scrmem(fg_console, offset); - set_origin(fg_console); - set_cursor(fg_console); - if (resetorg) - __set_origin(blank__origin); - - vesa_unblank(); #ifdef CONFIG_APM - if (apm_display_unblank()) - return; + apm_display_unblank(); #endif + if (sw->con_blank(vc_cons[currcons].d, 0)) + /* Low-level driver cannot restore -> do it ourselves */ + update_screen(fg_console); + set_cursor(fg_console); } -/* - * If a blank_screen is due to a timer, then a power save is allowed. - * If it is related to console_switching, then avoid vesa_blank(). - */ static void blank_screen(void) { do_blank_screen(0); @@ -2466,62 +2487,60 @@ static void unblank_screen(void) do_unblank_screen(); } -void update_screen(int new_console) +void poke_blanked_console(void) { - static int lock = 0; - - if (new_console == fg_console || lock) - return; - if (!vc_cons_allocated(new_console)) { - /* strange ... */ - printk("update_screen: tty %d not allocated ??\n", new_console+1); + timer_active &= ~(1<<BLANK_TIMER); + if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) return; + if (console_blanked) { + timer_table[BLANK_TIMER].fn = unblank_screen; + timer_table[BLANK_TIMER].expires = 0; + timer_active |= 1<<BLANK_TIMER; + } else if (blankinterval) { + timer_table[BLANK_TIMER].expires = jiffies + blankinterval; + timer_active |= 1<<BLANK_TIMER; } - lock = 1; - - clear_selection(); - - if (!console_blanked) - get_scrmem(fg_console); - else - console_blanked = -1; /* no longer of the form console+1 */ - fg_console = new_console; /* this is the only (nonzero) assignment to fg_console */ - /* consequently, fg_console will always be allocated */ - set_scrmem(fg_console, 0); - set_origin(fg_console); - set_cursor(fg_console); - set_leds(); - compute_shiftstate(); - lock = 0; } /* - * Allocate the console screen memory. + * Palettes */ -static int con_open(struct tty_struct *tty, struct file * filp) -{ - unsigned int idx; - int i; - - idx = MINOR(tty->device) - tty->driver.minor_start; - i = vc_allocate(idx); - if (i) - return i; +void set_palette(void) +{ + 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); +} - vt_cons[idx]->vc_num = idx; - tty->driver_data = vt_cons[idx]; +int set_get_cmap(unsigned char *arg, int set) +{ + int i, j, k; - if (!tty->winsize.ws_row && !tty->winsize.ws_col) { - tty->winsize.ws_row = video_num_lines; - tty->winsize.ws_col = video_num_columns; + for (i = 0; i < 16; i++) + if (set) { + get_user(default_red[i], arg++); + get_user(default_grn[i], arg++); + get_user(default_blu[i], arg++); + } else { + put_user(default_red[i], arg++); + put_user(default_grn[i], arg++); + put_user(default_blu[i], arg++); } - return 0; + if (set) { + for (i = 0; i < MAX_NR_CONSOLES; 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(); + } + return 0; } - /* - * Load palette into the EGA/VGA DAC registers. arg points to a colour + * Load palette into the DAC registers. arg points to a colour * map, 3 bytes per colour, 16 colours, range from 0 to 255. */ @@ -2547,25 +2566,164 @@ void reset_palette (int currcons) } /* - * Load font into the EGA/VGA character generator. arg points to a 8192 - * byte map, 32 bytes per character. Only first H of them are used for - * 8xH fonts (0 < H <= 32). + * Font switching + * + * Currently we only support fonts up to 32 pixels wide, at a maximum height + * of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints, + * depending on width) reserved for each character which is kinda wasty, but + * this is done in order to maintain compatibility with the EGA/VGA fonts. It + * is upto the actual low-level console-driver convert data into its favorite + * format (maybe we should add a `fontoffset' field to the `display' + * structure so we wont have to convert the fontdata all the time. + * /Jes + */ + +#define max_font_size 65536 + +int con_font_op(int currcons, struct console_font_op *op) +{ + int rc = -EINVAL; + int size = max_font_size, set; + u8 *temp = NULL; + struct console_font_op old_op; + + if (vt_cons[currcons]->vc_mode != KD_TEXT) + goto quit; + memcpy(&old_op, op, sizeof(old_op)); + if (op->op == KD_FONT_OP_SET) { + if (!op->data) + return -EINVAL; + if (op->charcount > 512) + goto quit; + if (!op->height) { /* Need to guess font height [compat] */ + int h, i; + u8 *charmap = op->data, tmp; + + /* If from KDFONTOP ioctl, don't allow things which can be done in userland, + so that we can get rid of this soon */ + if (!(op->flags & KD_FONT_FLAG_OLD)) + goto quit; + rc = -EFAULT; + for (h = 32; h > 0; h--) + for (i = 0; i < op->charcount; i++) { + if (get_user(tmp, &charmap[32*i+h-1])) + goto quit; + if (tmp) + goto nonzero; + } + rc = -EINVAL; + goto quit; + nonzero: + rc = -EINVAL; + op->height = h; + } + if (op->width > 32 || op->height > 32) + goto quit; + size = (op->width+7)/8 * 32 * op->charcount; + if (size > max_font_size) + return -ENOSPC; + set = 1; + } else if (op->op == KD_FONT_OP_GET) + set = 0; + else + return sw->con_font_op(vc_cons[currcons].d, op); + if (op->data) { + temp = kmalloc(size, GFP_KERNEL); + if (!temp) + return -ENOMEM; + if (set && copy_from_user(temp, op->data, size)) { + rc = -EFAULT; + goto quit; + } + op->data = temp; + } + rc = sw->con_font_op(vc_cons[currcons].d, op); + op->data = old_op.data; + if (!rc && !set) { + int c = (op->width+7)/8 * 32 * op->charcount; + + if (op->data && op->charcount > old_op.charcount) + rc = -ENOSPC; + if (!(op->flags & KD_FONT_FLAG_OLD)) { + if (op->width > old_op.width || + op->height > old_op.height) + rc = -ENOSPC; + } else { + if (op->width != 8) + rc = -EIO; + else if ((old_op.height && op->height > old_op.height) || + op->height > 32) + rc = -ENOSPC; + } + if (!rc && op->data && copy_to_user(op->data, temp, c)) + rc = -EFAULT; + } +quit: if (temp) + kfree_s(temp, size); + return rc; +} + +/* + * Interface exported to selection and vcs. */ -int con_set_font (char *arg, int ch512) +/* used by selection */ +u16 screen_glyph(int currcons, int offset) { - int i; + u16 w = scr_readw(screenpos(currcons, offset, 1)); + u16 c = w & 0xff; - i = set_get_font (arg,1,ch512); - if ( !i ) { - hashtable_contents_valid = 0; - video_mode_512ch = ch512; - console_charmask = ch512 ? 0x1ff : 0x0ff; - } - return i; + if (w & hi_font_mask) + c |= 0x100; + return c; +} + +/* used by vcs - note the word offset */ +unsigned short *screen_pos(int currcons, int w_offset, int viewed) +{ + return screenpos(currcons, 2 * w_offset, viewed); +} + +void getconsxy(int currcons, char *p) +{ + p[0] = x; + p[1] = y; } -int con_get_font (char *arg) +void putconsxy(int currcons, char *p) { - return set_get_font (arg,0,video_mode_512ch); + gotoxy(currcons, p[0], p[1]); + set_cursor(currcons); } + +u16 vcs_scr_readw(int currcons, u16 *org) +{ + if ((unsigned long)org == pos && softcursor_original != -1) + return softcursor_original; + return scr_readw(org); +} + +void vcs_scr_writew(int currcons, u16 val, u16 *org) +{ + scr_writew(val, org); + if ((unsigned long)org == pos) { + softcursor_original = -1; + add_softcursor(currcons); + } +} + + +/* + * Visible symbols for modules + */ + +EXPORT_SYMBOL(color_table); +EXPORT_SYMBOL(default_red); +EXPORT_SYMBOL(default_grn); +EXPORT_SYMBOL(default_blu); +EXPORT_SYMBOL(video_font_height); +EXPORT_SYMBOL(video_scan_lines); + +#ifndef VT_BUF_VRAM_ONLY +EXPORT_SYMBOL(take_over_console); +#endif diff --git a/drivers/char/console_macros.h b/drivers/char/console_macros.h new file mode 100644 index 000000000..f10984ed0 --- /dev/null +++ b/drivers/char/console_macros.h @@ -0,0 +1,71 @@ +#define cons_num (vc_cons[currcons].d->vc_num) +#define sw (vc_cons[currcons].d->vc_sw) +#define screenbuf (vc_cons[currcons].d->vc_screenbuf) +#define screenbuf_size (vc_cons[currcons].d->vc_screenbuf_size) +#define origin (vc_cons[currcons].d->vc_origin) +#define scr_top (vc_cons[currcons].d->vc_scr_top) +#define visible_origin (vc_cons[currcons].d->vc_visible_origin) +#define scr_end (vc_cons[currcons].d->vc_scr_end) +#define pos (vc_cons[currcons].d->vc_pos) +#define top (vc_cons[currcons].d->vc_top) +#define bottom (vc_cons[currcons].d->vc_bottom) +#define x (vc_cons[currcons].d->vc_x) +#define y (vc_cons[currcons].d->vc_y) +#define vc_state (vc_cons[currcons].d->vc_state) +#define npar (vc_cons[currcons].d->vc_npar) +#define par (vc_cons[currcons].d->vc_par) +#define ques (vc_cons[currcons].d->vc_ques) +#define attr (vc_cons[currcons].d->vc_attr) +#define saved_x (vc_cons[currcons].d->vc_saved_x) +#define saved_y (vc_cons[currcons].d->vc_saved_y) +#define translate (vc_cons[currcons].d->vc_translate) +#define G0_charset (vc_cons[currcons].d->vc_G0_charset) +#define G1_charset (vc_cons[currcons].d->vc_G1_charset) +#define saved_G0 (vc_cons[currcons].d->vc_saved_G0) +#define saved_G1 (vc_cons[currcons].d->vc_saved_G1) +#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) +#define decscnm (vc_cons[currcons].d->vc_decscnm) +#define decom (vc_cons[currcons].d->vc_decom) +#define decawm (vc_cons[currcons].d->vc_decawm) +#define deccm (vc_cons[currcons].d->vc_deccm) +#define decim (vc_cons[currcons].d->vc_decim) +#define deccolm (vc_cons[currcons].d->vc_deccolm) +#define need_wrap (vc_cons[currcons].d->vc_need_wrap) +#define kmalloced (vc_cons[currcons].d->vc_kmalloced) +#define report_mouse (vc_cons[currcons].d->vc_report_mouse) +#define color (vc_cons[currcons].d->vc_color) +#define s_color (vc_cons[currcons].d->vc_s_color) +#define def_color (vc_cons[currcons].d->vc_def_color) +#define foreground (color & 0x0f) +#define background (color & 0xf0) +#define charset (vc_cons[currcons].d->vc_charset) +#define s_charset (vc_cons[currcons].d->vc_s_charset) +#define intensity (vc_cons[currcons].d->vc_intensity) +#define underline (vc_cons[currcons].d->vc_underline) +#define blink (vc_cons[currcons].d->vc_blink) +#define reverse (vc_cons[currcons].d->vc_reverse) +#define s_intensity (vc_cons[currcons].d->vc_s_intensity) +#define s_underline (vc_cons[currcons].d->vc_s_underline) +#define s_blink (vc_cons[currcons].d->vc_s_blink) +#define s_reverse (vc_cons[currcons].d->vc_s_reverse) +#define ulcolor (vc_cons[currcons].d->vc_ulcolor) +#define halfcolor (vc_cons[currcons].d->vc_halfcolor) +#define tab_stop (vc_cons[currcons].d->vc_tab_stop) +#define palette (vc_cons[currcons].d->vc_palette) +#define bell_pitch (vc_cons[currcons].d->vc_bell_pitch) +#define bell_duration (vc_cons[currcons].d->vc_bell_duration) +#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 hi_font_mask (vc_cons[currcons].d->vc_hi_font_mask) + +#define vcmode (vt_cons[currcons]->vc_mode) + +#define structsize (sizeof(struct vc_data) + sizeof(struct vt_struct)) diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index 5a9300bf8..e63386d7c 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c @@ -5,6 +5,8 @@ * to font positions. * * aeb, 950210 + * + * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998 */ #include <linux/kd.h> @@ -14,6 +16,8 @@ #include <linux/init.h> #include <asm/uaccess.h> #include <linux/consolemap.h> +#include <linux/console_struct.h> +#include <linux/vt_kern.h> static unsigned short translations[][256] = { /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ @@ -163,29 +167,36 @@ static unsigned short translations[][256] = { #define MAX_GLYPH 512 /* Max possible glyph value */ -static unsigned char * inv_translate = NULL; -static unsigned char inv_norm_transl[MAX_GLYPH]; -static unsigned char * inverse_translations[4] = { NULL, NULL, NULL, NULL }; +static int inv_translate; + +struct uni_pagedir { + u16 **uni_pgdir[32]; + unsigned long refcount; + unsigned long sum; + unsigned char *inverse_translations[4]; + int readonly; +}; -static void set_inverse_transl(int i) +static struct uni_pagedir *dflt; + +static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int i) { int j, glyph; - unsigned short *p = translations[i]; - unsigned char *q = inverse_translations[i]; + unsigned short *t = translations[i]; + unsigned char *q; + + if (!p) return; + q = p->inverse_translations[i]; if (!q) { - /* slightly messy to avoid calling kmalloc too early */ - q = inverse_translations[i] = ((i == LAT1_MAP) - ? inv_norm_transl - : (unsigned char *) kmalloc(MAX_GLYPH, GFP_KERNEL)); - if (!q) - return; + q = p->inverse_translations[i] = (unsigned char *) + kmalloc(MAX_GLYPH, GFP_KERNEL); + if (!q) return; } - for (j=0; j<MAX_GLYPH; j++) - q[j] = 0; + memset(q, 0, MAX_GLYPH); - for (j=0; j<E_TABSZ; j++) { - glyph = conv_uni_to_pc(p[j]); + for (j = 0; j < E_TABSZ; j++) { + glyph = conv_uni_to_pc(conp, t[j]); if (glyph >= 0 && glyph < MAX_GLYPH && q[glyph] < 32) { /* prefer '-' above SHY etc. */ q[glyph] = j; @@ -195,9 +206,7 @@ static void set_inverse_transl(int i) unsigned short *set_translate(int m) { - if (!inverse_translations[m]) - set_inverse_transl(m); - inv_translate = inverse_translations[m]; + inv_translate = m; return translations[m]; } @@ -208,13 +217,33 @@ unsigned short *set_translate(int m) * was active, or using Unicode. * Still, it is now possible to a certain extent to cut and paste non-ASCII. */ -unsigned char inverse_translate(int glyph) { - if ( glyph < 0 || glyph >= MAX_GLYPH ) +unsigned char inverse_translate(struct vc_data *conp, int glyph) +{ + struct uni_pagedir *p; + + if (glyph < 0 || glyph >= MAX_GLYPH) return 0; + else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc) || + !p->inverse_translations[inv_translate]) + return glyph; else - return ((inv_translate && inv_translate[glyph]) - ? inv_translate[glyph] - : (unsigned char)(glyph & 0xff)); + return p->inverse_translations[inv_translate][glyph]; +} + +static void update_user_maps(void) +{ + int i; + struct uni_pagedir *p, *q = NULL; + + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (!vc_cons_allocated(i)) + continue; + p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; + if (p && p != q) { + set_inverse_transl(vc_cons[i].d, p, USER_MAP); + q = p; + } + } } /* @@ -240,7 +269,7 @@ int con_set_trans_old(unsigned char * arg) p[i] = UNI_DIRECT_BASE | uc; } - set_inverse_transl(USER_MAP); + update_user_maps(); return 0; } @@ -255,7 +284,7 @@ int con_get_trans_old(unsigned char * arg) for (i=0; i<E_TABSZ ; i++) { - ch = conv_uni_to_pc(p[i]); + ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); __put_user((ch & ~0xff) ? 0 : ch, arg+i); } return 0; @@ -277,7 +306,7 @@ int con_set_trans_new(ushort * arg) p[i] = us; } - set_inverse_transl(USER_MAP); + update_user_maps(); return 0; } @@ -310,90 +339,188 @@ int con_get_trans_new(ushort * arg) extern u8 dfont_unicount[]; /* Defined in console_defmap.c */ extern u16 dfont_unitable[]; -int hashtable_contents_valid = 0; /* Use ASCII-only mode for bootup */ +static void con_release_unimap(struct uni_pagedir *p) +{ + u16 **p1; + int i, j; + + if (p == dflt) dflt = NULL; + for (i = 0; i < 32; i++) { + if ((p1 = p->uni_pgdir[i]) != NULL) { + for (j = 0; j < 32; j++) + if (p1[j]) + kfree(p1[j]); + kfree(p1); + } + p->uni_pgdir[i] = NULL; + } + for (i = 0; i < 4; i++) + if (p->inverse_translations[i]) { + kfree(p->inverse_translations[i]); + p->inverse_translations[i] = NULL; + } +} -static u16 **uni_pagedir[32] = +void con_free_unimap(int con) { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; + struct uni_pagedir *p; + struct vc_data *conp = vc_cons[con].d; + + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + if (!p) return; + *conp->vc_uni_pagedir_loc = 0; + if (--p->refcount) return; + con_release_unimap(p); + kfree(p); +} + +static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) +{ + int i, j, k; + struct uni_pagedir *q; + + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (!vc_cons_allocated(i)) + continue; + q = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; + if (!q || q == p || q->sum != p->sum) + continue; + for (j = 0; j < 32; j++) { + u16 **p1, **q1; + p1 = p->uni_pgdir[j]; q1 = q->uni_pgdir[j]; + if (!p1 && !q1) + continue; + if (!p1 || !q1) + break; + for (k = 0; k < 32; k++) { + if (!p1[k] && !q1[k]) + continue; + if (!p1[k] || !q1[k]) + break; + if (memcmp(p1[k], q1[k], 64*sizeof(u16))) + break; + } + if (k < 32) + break; + } + if (j == 32) { + q->refcount++; + *conp->vc_uni_pagedir_loc = (unsigned long)q; + con_release_unimap(p); + kfree(p); + return 1; + } + } + return 0; +} static int -con_insert_unipair(u_short unicode, u_short fontpos) +con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) { - int i, n; - u16 **p1, *p2; - - if ( !(p1 = uni_pagedir[n = unicode >> 11]) ) - { - p1 = uni_pagedir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL); - if ( !p1 ) - return -ENOMEM; - - for ( i = 0 ; i < 32 ; i++ ) - p1[i] = NULL; - } - - if ( !(p2 = p1[n = (unicode >> 6) & 0x1f]) ) - { - p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL); - if ( !p2 ) - return -ENOMEM; - - for ( i = 0 ; i < 64 ; i++ ) - p2[i] = 0xffff; /* No glyph for this character (yet) */ - } - - p2[unicode & 0x3f] = fontpos; - - return 0; + int i, n; + u16 **p1, *p2; + + if (!(p1 = p->uni_pgdir[n = unicode >> 11])) { + p1 = p->uni_pgdir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL); + if (!p1) return -ENOMEM; + for (i = 0; i < 32; i++) + p1[i] = NULL; + } + + if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) { + p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL); + if (!p2) return -ENOMEM; + memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ + } + + p2[unicode & 0x3f] = fontpos; + + p->sum += (fontpos << 20) + unicode; + + return 0; } - + /* ui is a leftover from using a hashtable, but might be used again */ -void -con_clear_unimap(struct unimapinit *ui) +int con_clear_unimap(int con, struct unimapinit *ui) { - int i, j; - u16 **p1; + struct uni_pagedir *p, *q; + struct vc_data *conp = vc_cons[con].d; - for ( i = 0 ; i < 32 ; i++ ) - { - if ( (p1 = uni_pagedir[i]) != NULL ) - { - for ( j = 0 ; j < 32 ; j++ ) - { - if ( p1[j] ) - kfree(p1[j]); - } - kfree(p1); + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + if (p && p->readonly) return -EIO; + if (!p || --p->refcount) { + q = (struct uni_pagedir *)kmalloc(sizeof(*p), GFP_KERNEL); + if (!q) { + if (p) p->refcount++; + return -ENOMEM; + } + memset(q, 0, sizeof(*q)); + q->refcount=1; + *conp->vc_uni_pagedir_loc = (unsigned long)q; + } else { + if (p == dflt) dflt = NULL; + p->refcount++; + p->sum = 0; + con_release_unimap(p); } - uni_pagedir[i] = NULL; - } - - hashtable_contents_valid = 1; + return 0; } int -con_set_unimap(ushort ct, struct unipair *list) +con_set_unimap(int con, ushort ct, struct unipair *list) { - int err = 0, err1, i; - - while( ct-- ) - { - unsigned short unicode, fontpos; - __get_user(unicode, &list->unicode); - __get_user(fontpos, &list->fontpos); - if ( (err1 = con_insert_unipair(unicode,fontpos)) != 0 ) - err = err1; - list++; - } - - for ( i = 0 ; i <= 3 ; i++ ) - set_inverse_transl(i); /* Update all inverse translations */ + int err = 0, err1, i; + struct uni_pagedir *p, *q; + struct vc_data *conp = vc_cons[con].d; + + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + if (p->readonly) return -EIO; + + if (!ct) return 0; + + if (p->refcount > 1) { + int j, k; + u16 **p1, *p2, l; + + err1 = con_clear_unimap(con, NULL); + if (err1) return err1; + + q = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + for (i = 0, l = 0; i < 32; i++) + if ((p1 = p->uni_pgdir[i])) + for (j = 0; j < 32; j++) + if ((p2 = p1[j])) + for (k = 0; k < 64; k++, l++) + if (p2[k] != 0xffff) { + err1 = con_insert_unipair(q, l, p2[k]); + if (err1) { + p->refcount++; + *conp->vc_uni_pagedir_loc = (unsigned long)p; + con_release_unimap(q); + kfree(q); + return err1; + } + } + p = q; + } else if (p == dflt) + dflt = NULL; + + while (ct--) { + unsigned short unicode, fontpos; + __get_user(unicode, &list->unicode); + __get_user(fontpos, &list->fontpos); + if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0) + err = err1; + list++; + } + + if (con_unify_unimap(conp, p)) + return err; + + for (i = 0; i <= 3; i++) + set_inverse_transl(conp, p, i); /* Update all inverse translations */ - return err; + return err; } /* Loads the unimap for the hardware font, as defined in uni_hash.tbl. @@ -401,83 +528,123 @@ con_set_unimap(ushort ct, struct unipair *list) with. This routine is executed at sys_setup time, and when the PIO_FONTRESET ioctl is called. */ -void -con_set_default_unimap(void) +int +con_set_default_unimap(int con) { - int i, j; - u16 *p; - - /* The default font is always 256 characters */ - - con_clear_unimap(NULL); + int i, j, err = 0, err1; + u16 *q; + struct uni_pagedir *p; + struct vc_data *conp = vc_cons[con].d; + + if (dflt) { + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + if (p == dflt) + return 0; + dflt->refcount++; + *conp->vc_uni_pagedir_loc = (unsigned long)dflt; + if (p && --p->refcount) { + con_release_unimap(p); + kfree(p); + } + return 0; + } + + /* The default font is always 256 characters */ - p = dfont_unitable; - for ( i = 0 ; i < 256 ; i++ ) - for ( j = dfont_unicount[i] ; j ; j-- ) - con_insert_unipair(*(p++), i); + err = con_clear_unimap(con,NULL); + if (err) return err; + + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + q = dfont_unitable; + + for (i = 0; i < 256; i++) + for (j = dfont_unicount[i]; j; j--) { + err1 = con_insert_unipair(p, *(q++), i); + if (err1) + err = err1; + } + + if (con_unify_unimap(conp, p)) { + dflt = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + return err; + } - for ( i = 0 ; i <= 3 ; i++ ) - set_inverse_transl(i); /* Update all inverse translations */ + for (i = 0; i <= 3; i++) + set_inverse_transl(conp, p, i); /* Update all inverse translations */ + dflt = p; + return err; } int -con_get_unimap(ushort ct, ushort *uct, struct unipair *list){ +con_get_unimap(int con, ushort ct, ushort *uct, struct unipair *list) +{ int i, j, k, ect; u16 **p1, *p2; + struct uni_pagedir *p; + struct vc_data *conp = vc_cons[con].d; ect = 0; - if (hashtable_contents_valid) - { - for ( i = 0 ; i < 32 ; i++ ) - if ( (p1 = uni_pagedir[i]) != NULL ) - for ( j = 0 ; j < 32 ; j++ ) - if ( (p2 = *(p1++)) != NULL ) - for ( k = 0 ; k < 64 ; k++ ) - { - if ( *p2 < MAX_GLYPH && ect++ < ct ) - { - __put_user((u_short)((i<<11)+(j<<6)+k), - &list->unicode); - __put_user((u_short) *p2, &list->fontpos); - list++; - } - p2++; - } - } + if (*conp->vc_uni_pagedir_loc) { + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + for (i = 0; i < 32; i++) + if ((p1 = p->uni_pgdir[i])) + for (j = 0; j < 32; j++) + if ((p2 = *(p1++))) + for (k = 0; k < 64; k++) { + if (*p2 < MAX_GLYPH && ect++ < ct) { + __put_user((u_short)((i<<11)+(j<<6)+k), + &list->unicode); + __put_user((u_short) *p2, + &list->fontpos); + list++; + } + p2++; + } + } __put_user(ect, uct); return ((ect <= ct) ? 0 : -ENOMEM); } +void con_protect_unimap(int con, int rdonly) +{ + struct uni_pagedir *p = (struct uni_pagedir *) + *vc_cons[con].d->vc_uni_pagedir_loc; + + if (p) p->readonly = rdonly; +} + int -conv_uni_to_pc(long ucs) +conv_uni_to_pc(struct vc_data *conp, long ucs) { - int h; - u16 **p1, *p2; - - /* Only 16-bit codes supported at this time */ - if (ucs > 0xffff) - ucs = 0xfffd; /* U+FFFD: REPLACEMENT CHARACTER */ - else if (ucs < 0x20 || ucs >= 0xfffe) - return -1; /* Not a printable character */ - else if (ucs == 0xfeff || (ucs >= 0x200a && ucs <= 0x200f)) - return -2; /* Zero-width space */ - /* - * UNI_DIRECT_BASE indicates the start of the region in the User Zone - * which always has a 1:1 mapping to the currently loaded font. The - * UNI_DIRECT_MASK indicates the bit span of the region. - */ - else if ( (ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE ) - return ucs & UNI_DIRECT_MASK; + int h; + u16 **p1, *p2; + struct uni_pagedir *p; - if (!hashtable_contents_valid) - return -3; + /* Only 16-bit codes supported at this time */ + if (ucs > 0xffff) + ucs = 0xfffd; /* U+FFFD: REPLACEMENT CHARACTER */ + else if (ucs < 0x20 || ucs >= 0xfffe) + return -1; /* Not a printable character */ + else if (ucs == 0xfeff || (ucs >= 0x200a && ucs <= 0x200f)) + return -2; /* Zero-width space */ + /* + * UNI_DIRECT_BASE indicates the start of the region in the User Zone + * which always has a 1:1 mapping to the currently loaded font. The + * UNI_DIRECT_MASK indicates the bit span of the region. + */ + else if ((ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE) + return ucs & UNI_DIRECT_MASK; - if ( (p1 = uni_pagedir[ucs >> 11]) && - (p2 = p1[(ucs >> 6) & 0x1f]) && - (h = p2[ucs & 0x3f]) < MAX_GLYPH ) - return h; + if (!*conp->vc_uni_pagedir_loc) + return -3; + + p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + if ((p1 = p->uni_pgdir[ucs >> 11]) && + (p2 = p1[(ucs >> 6) & 0x1f]) && + (h = p2[ucs & 0x3f]) < MAX_GLYPH) + return h; - return -4; /* not found */ + return -4; /* not found */ } /* @@ -488,5 +655,9 @@ conv_uni_to_pc(long ucs) __initfunc(void console_map_init(void)) { - con_set_default_unimap(); + int i; + + for (i = 0; i < MAX_NR_CONSOLES; i++) + if (vc_cons_allocated(i) && !*vc_cons[i].d->vc_uni_pagedir_loc) + con_set_default_unimap(i); } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 9f51607fe..976219825 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,9 +1,7 @@ #define BLOCKMOVE -#define NEW_INTR_FLOW #define Z_WAKE -#define NEW_PCI static char rcsid[] = -"$Revision: 2.2.1.1 $$Date: 1998/03/19 16:43:12 $"; +"$Revision: 2.2.1.5 $$Date: 1998/08/10 18:10:28 $"; /* * linux/drivers/char/cyclades.c @@ -33,6 +31,22 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.2.1.5 1998/08/10 18:10:28 ivan + * Fixed Cyclom-4Yo hardware detection bug. + * + * Revision 2.2.1.4 1998/08/04 11:02:50 ivan + * /proc/cyclades implementation with great collaboration of + * Marc Lewis <marc@blarg.net>; + * cyy_interrupt was changed to avoid occurence of kernel oopses + * during PPP operation. + * + * Revision 2.2.1.3 1998/06/01 12:09:10 ivan + * General code review in order to comply with 2.1 kernel standards; + * data loss prevention for slow devices revisited (cy_wait_until_sent + * was created); + * removed conditional compilation for new/old PCI structure support + * (now the driver only supports the new PCI structure). + * * Revision 2.2.1.1 1998/03/19 16:43:12 ivan * added conditional compilation for new/old PCI structure support; * removed kernel series (2.0.x / 2.1.x) conditional compilation. @@ -40,7 +54,7 @@ static char rcsid[] = * Revision 2.1.1.3 1998/03/16 18:01:12 ivan * cleaned up the data loss fix; * fixed XON/XOFF handling once more (Cyclades-Z); - * general revision in the driver routines; + * general review of the driver routines; * introduction of a mechanism to prevent data loss with slow * printers, by forcing a delay before closing the port. * @@ -138,7 +152,7 @@ static char rcsid[] = * Price <stevep@fa.tdktca.com> for help on this) * * Revision 1.36.4.21 1996/09/10 17:00:10 bentson - * shift from cpu-bound to memcopy in cyz_polling operation + * shift from CPU-bound to memcopy in cyz_polling operation * * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson * Added support to set and report higher speeds. @@ -482,14 +496,15 @@ static char rcsid[] = #define ZE_V1 2 #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 -#undef CYCLOM_ENABLE_MONITORING +#undef CY_DEBUG_OPEN +#undef CY_DEBUG_THROTTLE +#undef CY_DEBUG_OTHER +#undef CY_DEBUG_IO +#undef CY_DEBUG_COUNT +#undef CY_DEBUG_DTR +#undef CY_DEBUG_WAIT_UNTIL_SENT +#undef CY_16Y_HACK +#undef CY_ENABLE_MONITORING #undef CY_PCI_DEBUG #if 0 @@ -519,40 +534,42 @@ static char rcsid[] = (cy_readl(&buf_ctrl->tx_bufsize) - 1)) #endif +/* + * Include section + */ +#include <linux/config.h> #include <linux/module.h> - #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/timer.h> +#include <linux/interrupt.h> #include <linux/tty.h> #include <linux/serial.h> -#include <linux/interrupt.h> +#include <linux/major.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/cyclades.h> -#include <linux/delay.h> -#include <linux/major.h> #include <linux/mm.h> +#include <linux/init.h> +#include <linux/delay.h> #include <asm/system.h> #include <asm/io.h> -#include <asm/segment.h> +#include <asm/irq.h> +#include <asm/uaccess.h> #include <asm/bitops.h> -#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> -#ifndef NEW_PCI -#include <linux/bios32.h> -#endif #include <linux/pci.h> - #include <linux/version.h> -#include <asm/uaccess.h> -#include <linux/init.h> +#ifdef CONFIG_PROC_FS +#include <linux/stat.h> +#include <linux/proc_fs.h> +#endif #define cy_put_user put_user @@ -569,7 +586,7 @@ static unsigned long cy_get_user(unsigned long *addr) #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif -#define IS_CYC_Z(card) ((card).num_chips == 1) +#define IS_CYC_Z(card) ((card).num_chips == -1) #define Z_FPGA_CHECK(card) \ ((cy_readl(&((struct RUNTIME_9060 *) \ @@ -585,19 +602,18 @@ static unsigned long cy_get_user(unsigned long *addr) #define STD_COM_FLAGS (0) -#define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 +#define JIFFIES_DIFF(n, j) ((n) - (j)) static DECLARE_TASK_QUEUE(tq_cyclades); static struct tty_driver cy_serial_driver, cy_callout_driver; +static int serial_refcount; static volatile int cy_irq_triggered; static volatile int cy_triggered; static int cy_wild_int_mask; static volatile ucchar *intr_base_addr; - /* This is the address lookup table. The driver will probe for Cyclom-Y/ISA boards at all addresses in here. If you want the driver to probe addresses at a different address, add it to @@ -632,8 +648,6 @@ static struct cyclades_port cy_port[NR_PORTS]; static int cy_next_channel = 0; /* next minor available */ -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]; @@ -641,8 +655,7 @@ static struct termios *serial_termios_locked[NR_PORTS]; /* This is the per-irq data structure, it maps an irq to the corresponding card */ -static struct cyclades_card *IRQ_cards[16]; - +static struct cyclades_card *IRQ_cards[NR_IRQS]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to @@ -654,7 +667,7 @@ static struct cyclades_card *IRQ_cards[16]; * memory if large numbers of serial ports are open. This buffer is * allocated when the first cy_open occurs. */ -static unsigned char *tmp_buf = 0; +static unsigned char *tmp_buf; static struct semaphore tmp_buf_sem = MUTEX; /* @@ -750,6 +763,10 @@ static void cyz_poll(unsigned long); static void show_status(int); #endif +#ifdef CONFIG_PROC_FS +static int cyclades_get_proc_info(char *, char **, off_t , int , int *, void *); +#endif + /* The Cyclades-Z polling cycle is defined by this variable */ static long cyz_polling_cycle = CZ_DEF_POLL; @@ -799,41 +816,6 @@ serial_paranoia_check(struct cyclades_port *info, return 0; } /* serial_paranoia_check */ - -/* The following diagnostic routines allow the driver to spew - information on the screen, even (especially!) during interrupts. - */ -static void -SP(char *data){ - unsigned long flags; - save_flags(flags); cli(); - console_print(data); - restore_flags(flags); -}/* SP */ - -static void -CP(char data){ - unsigned long flags; - char scrn[2]; - save_flags(flags); cli(); - scrn[0] = data; - scrn[1] = '\0'; - console_print(scrn); - restore_flags(flags); -}/* CP */ - -static void CP4(int data) - { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ -static void CP8(int data) - { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ -#if 0 -static void CP16(int data) - { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ -static void CP32(long data) - { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ -#endif - - /* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver @@ -1125,11 +1107,6 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs) if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) { save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index)); save_car = cy_readb(intr_base_addr+(CyCAR<<index)); - if ((save_xir & 0x3) != 0){ - SP("channel "); - CP8(save_xir); - SP(" requesting unexpected interrupt\n"); - } cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3)); cy_writeb((u_long)intr_base_addr+(CySRER<<index), cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty); @@ -1244,11 +1221,13 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); TTY_FRAME; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); + info->idle_stats.frame_errs++; }else if(data & CyPARITY){ *tty->flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); + info->idle_stats.parity_errs++; }else if(data & CyOVERRUN){ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; @@ -1265,6 +1244,7 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); } + info->idle_stats.overruns++; /* These two conditions may imply */ /* a normal read should be done. */ /* }else if(data & CyTIMEOUT){ */ @@ -1281,18 +1261,21 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); /* there was a software buffer overrun and nothing could be done about it!!! */ + info->idle_stats.overruns++; } } else { /* normal character reception */ /* load # chars available from the chip */ char_count = cy_readb(base_addr+(CyRDCR<<index)); -#ifdef CYCLOM_ENABLE_MONITORING +#ifdef CY_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 + info->idle_stats.recv_bytes += char_count; + info->idle_stats.recv_idle = jiffies; while(char_count--){ if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; @@ -1301,7 +1284,7 @@ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip); data = cy_readb(base_addr+(CyRDSR<<index)); *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data; -#ifdef CYCLOM_16Y_HACK +#ifdef CY_16Y_HACK udelay(10L); #endif } @@ -1387,45 +1370,25 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); info->x_break = 0; } -#ifdef NEW_INTR_FLOW - if (!info->xmit_cnt){ - cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); - cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); - goto txdone; - } - if (info->xmit_buf == 0){ - cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); - goto txdone; - } - if (info->tty->stopped || info->tty->hw_stopped){ - cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); - goto txdone; - } -#endif while (char_count-- > 0){ -#ifdef NEW_INTR_FLOW - if (!info->xmit_cnt){ - goto txdone; - } -#else - if (!info->xmit_cnt){ + if (!info->xmit_cnt){ cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); - goto txdone; - } - if (info->xmit_buf == 0){ + cy_readb(base_addr+(CySRER<<index)) & + ~CyTxMpty); + goto txdone; + } + if (info->xmit_buf == 0){ cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + cy_readb(base_addr+(CySRER<<index)) & + ~CyTxMpty); goto txdone; - } - if (info->tty->stopped || info->tty->hw_stopped){ + } + if (info->tty->stopped || info->tty->hw_stopped){ cy_writeb((u_long)base_addr+(CySRER<<index), - cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + cy_readb(base_addr+(CySRER<<index)) & + ~CyTxMpty); + goto txdone; } -#endif /* Because the Embedded Transmit Commands have been enabled, we must check to see if the escape character, NULL, is being sent. If it @@ -1725,14 +1688,12 @@ cyz_poll(unsigned long arg) if ((fw_ver > 241 ? ((u_long)param) : cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) { - /* SP("Open Wakeup\n"); */ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) &&(info->flags & ASYNC_CALLOUT_NOHUP))){ - /* SP("Hangup\n"); */ cy_sched_event(info, Cy_EVENT_HANGUP); } @@ -1744,7 +1705,6 @@ cyz_poll(unsigned long arg) if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){ /* cy_start isn't used because... HW flow is handled by the board */ - /* SP("Write Wakeup\n"); */ cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } @@ -1752,7 +1712,6 @@ cyz_poll(unsigned long arg) if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){ /* cy_stop isn't used because HW flow is handled by the board */ - /* SP("Write stop\n"); */ } } } @@ -1796,17 +1755,18 @@ cyz_poll(unsigned long arg) info->last_active = jiffies; info->jiffies[1] = jiffies; -#ifdef CYCLOM_ENABLE_MONITORING +#ifdef CY_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 + info->idle_stats.recv_bytes += char_count; + info->idle_stats.recv_idle = jiffies; if( tty == 0){ /* flush received characters */ rx_get = (rx_get + char_count) & (rx_bufsize - 1); - /* SP("-"); */ info->rflush_count++; }else{ #ifdef BLOCKMOVE @@ -1986,7 +1946,7 @@ startup(struct cyclades_port * info) base_addr = (unsigned char*) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc startup card %d, chip %d, channel %d, base_addr %lx\n", card, chip, channel, (long)base_addr);/**/ #endif @@ -2004,7 +1964,7 @@ startup(struct cyclades_port * info) cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS); cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:startup raising DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -2019,6 +1979,10 @@ startup(struct cyclades_port * info) clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; restore_flags(flags); } else { struct FIRM_ID *firm_id; @@ -2040,7 +2004,7 @@ startup(struct cyclades_port * info) board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc startup Z card %d, channel %d, base_addr %lx\n", card, channel, (long)base_addr);/**/ #endif @@ -2068,7 +2032,7 @@ startup(struct cyclades_port * info) if (retval != 0){ printk("cyc:startup(2) retval was %x\n", retval); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:startup raising Z DTR\n"); #endif @@ -2080,9 +2044,13 @@ startup(struct cyclades_port * info) } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; } -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk(" cyc startup done\n"); #endif return 0; @@ -2142,7 +2110,7 @@ shutdown(struct cyclades_port * info) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc shutdown Y card %d, chip %d, channel %d, base_addr %lx\n", card, chip, channel, (long)base_addr); #endif @@ -2159,7 +2127,7 @@ shutdown(struct cyclades_port * info) if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc shutdown dropping DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -2183,7 +2151,7 @@ shutdown(struct cyclades_port * info) int retval; base_addr = (unsigned char*) (cy_card[card].base_addr); -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc shutdown Z card %d, channel %d, base_addr %lx\n", card, channel, (long)base_addr); #endif @@ -2217,7 +2185,7 @@ shutdown(struct cyclades_port * info) if (retval != 0){ printk("cyc:shutdown retval (2) was %x\n", retval); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:shutdown dropping Z DTR\n"); #endif } @@ -2230,7 +2198,7 @@ shutdown(struct cyclades_port * info) restore_flags(flags); } -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk(" cyc shutdown done\n"); #endif return; @@ -2262,11 +2230,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, if (info->flags & ASYNC_CLOSING) { interruptible_sleep_on(&info->close_wait); } - if (info->flags & ASYNC_HUP_NOTIFY){ - return -EAGAIN; - }else{ - return -ERESTARTSYS; - } + return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); } /* @@ -2313,7 +2277,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ retval = 0; add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc block_til_ready before block: ttyC%d, count = %d\n", info->line, info->count);/**/ #endif @@ -2321,7 +2285,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, if (!tty_hung_up_p(filp)) info->count--; restore_flags(flags); -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT printk("cyc block_til_ready: (%d): decrementing count to %d\n", current->pid, info->count); #endif @@ -2338,11 +2302,12 @@ block_til_ready(struct tty_struct *tty, struct file * filp, while (1) { save_flags(flags); cli(); - if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)){ cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS); cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:block_til_ready raising DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -2353,11 +2318,8 @@ block_til_ready(struct tty_struct *tty, struct file * filp, 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; - } + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); break; } save_flags(flags); cli(); @@ -2374,7 +2336,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, retval = -ERESTARTSYS; break; } -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", info->line, info->count);/**/ #endif @@ -2408,18 +2370,15 @@ block_til_ready(struct tty_struct *tty, struct file * filp, if (retval != 0){ printk("cyc:block_til_ready retval was %x\n", retval); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:block_til_ready raising Z DTR\n"); #endif 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; - } + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) @@ -2432,7 +2391,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, retval = -ERESTARTSYS; break; } -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", info->line, info->count);/**/ #endif @@ -2443,13 +2402,13 @@ block_til_ready(struct tty_struct *tty, struct file * filp, remove_wait_queue(&info->open_wait, &wait); if (!tty_hung_up_p(filp)){ info->count++; -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT printk("cyc:block_til_ready (%d): incrementing count to %d\n", current->pid, info->count); #endif } info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc:block_til_ready after blocking: ttyC%d, count = %d\n", info->line, info->count);/**/ #endif @@ -2464,11 +2423,12 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * This routine is called whenever a serial port is opened. It * performs the serial-specific initialization for the tty structure. */ -int +static int cy_open(struct tty_struct *tty, struct file * filp) { struct cyclades_port *info; int retval, line; + unsigned long page; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (NR_PORTS <= line)){ @@ -2491,46 +2451,50 @@ cy_open(struct tty_struct *tty, struct file * filp) (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *) ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))) { - printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); + printk ("cyc:Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); } else { - printk("Cyclades-Z firmware not yet loaded\n"); + printk("cyc:Cyclades-Z firmware not yet loaded\n"); } return -ENODEV; } } -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk("cyc:cy_open ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_open")){ return -ENODEV; } -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc:cy_open ttyC%d, count = %d\n", info->line, info->count);/**/ #endif info->count++; -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT printk("cyc:cy_open (%d): incrementing count to %d\n", current->pid, info->count); #endif tty->driver_data = info; info->tty = tty; - /* Some drivers have (incorrect/incomplete) code to test - against a race condition. Should add good code here!!! */ if (!tmp_buf) { - tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); - if (!tmp_buf){ - return -ENOMEM; - } + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; } - 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; + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); } + /* * Start up serial port */ @@ -2543,17 +2507,24 @@ cy_open(struct tty_struct *tty, struct file * filp) retval = block_til_ready(tty, filp, info); if (retval) { -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc:cy_open returning after block_til_ready with %d\n", retval); #endif return retval; } + 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; + } + info->session = current->session; info->pgrp = current->pgrp; -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk(" cyc:cy_open done\n");/**/ #endif @@ -2562,6 +2533,76 @@ cy_open(struct tty_struct *tty, struct file * filp) /* + * cy_wait_until_sent() --- wait until the transmitter is empty + */ +static void cy_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int card,chip,channel,index; + unsigned long orig_jiffies, char_time; + + if (serial_paranoia_check(info, tty->device, "cy_wait_until_sent")) + return; + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout < 0) + timeout = 0; + if (timeout) + char_time = MIN(char_time, timeout); +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("In cy_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + card = info->card; + channel = (info->line) - (cy_card[card].first_line); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char *) + (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + while (cy_readb(base_addr+(CySRER<<index)) & CyTxMpty) { +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("Not clean (jiff=%lu)...", jiffies); +#endif + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + char_time; + schedule(); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + current->state = TASK_RUNNING; + } else { + // Nothing to do! + } + /* Run one more char cycle */ + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + (char_time * 5); + schedule(); + current->state = TASK_RUNNING; +#ifdef CY_DEBUG_WAIT_UNTIL_SENT + printk("Clean (jiff=%lu)...done\n", jiffies); +#endif +} + +/* * This routine is called when a particular tty device is closed. */ static void @@ -2570,7 +2611,7 @@ cy_close(struct tty_struct * tty, struct file * filp) struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; unsigned long flags; -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk("cyc:cy_close ttyC%d\n", info->line); #endif @@ -2578,7 +2619,7 @@ cy_close(struct tty_struct * tty, struct file * filp) || serial_paranoia_check(info, tty->device, "cy_close")){ return; } -#ifdef SERIAL_DEBUG_OPEN +#ifdef CY_DEBUG_OPEN printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); #endif @@ -2586,6 +2627,7 @@ cy_close(struct tty_struct * tty, struct file * filp) /* If the TTY is being hung up, nothing to do */ if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -2602,18 +2644,18 @@ cy_close(struct tty_struct * tty, struct file * filp) "info->count is %d\n", info->count); info->count = 1; } -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT printk("cyc:cy_close at (%d): decrementing count to %d\n", current->pid, info->count - 1); #endif if (--info->count < 0) { -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT printk("cyc:cyc_close setting count to 0\n"); #endif info->count = 0; } if (info->count) { - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -2632,28 +2674,25 @@ cy_close(struct tty_struct * tty, struct file * filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait2 != 0) { /* The port's being forced to wait, - independent on the port settings */ - tty_wait_until_sent(tty, info->closing_wait2*HZ); - } else { - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait*HZ); + if (info->closing_wait != CY_CLOSING_WAIT_NONE) { + tty_wait_until_sent(tty, info->closing_wait); } - /* Waiting for on-board buffers to be empty before closing the port */ if (!IS_CYC_Z(cy_card[info->card])) { -#ifdef NEW_INTR_FLOW unsigned char *base_addr = (unsigned char *) cy_card[info->card].base_addr; int index = cy_card[info->card].bus_index; - - if (cy_readb(base_addr+(CySRER<<index)) & CyTxMpty) { - /* Interrupts are enabled, so go to sleep */ - interruptible_sleep_on(&info->shutdown_wait); + /* Stop accepting input */ + cy_writeb((u_long)base_addr+(CySRER<<index), + cy_readb(base_addr+(CySRER<<index)) & ~CyRxData); + if (info->flags & ASYNC_INITIALIZED) { + /* Waiting for on-board buffers to be empty before closing + the port */ + cy_wait_until_sent(tty, info->timeout); } -#endif } else { #ifdef Z_WAKE + /* Waiting for on-board buffers to be empty before closing the port */ unsigned char *base_addr = (unsigned char *) cy_card[info->card].base_addr; struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); @@ -2693,7 +2732,7 @@ cy_close(struct tty_struct * tty, struct file * filp) ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk(" cyc:cy_close done\n"); #endif @@ -2722,9 +2761,9 @@ cy_write(struct tty_struct * tty, int from_user, { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - int c, total = 0; + int c, ret = 0; -#ifdef SERIAL_DEBUG_IO +#ifdef CY_DEBUG_IO printk("cyc:cy_write ttyC%d\n", info->line); /* */ #endif @@ -2736,42 +2775,61 @@ cy_write(struct tty_struct * tty, int from_user, return 0; } - if (from_user) - down(&tmp_buf_sem); save_flags(flags); - while (1) { - cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) - break; - if (from_user) { - 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); - } 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 0 - SP("CW"); - CP16(c); - SP(" "); -#endif - } - if (from_user) + if (from_user) { + down(&tmp_buf_sem); + while (1) { + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) { + ret = -EFAULT; + } + break; + } + cli(); + 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); + info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } up(&tmp_buf_sem); + } else { + while (1) { + 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; + } + 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; + ret += c; + } + } + + info->idle_stats.xmit_bytes += ret; + info->idle_stats.xmit_idle = jiffies; + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { start_xmit(info); } - restore_flags(flags); - return total; + return ret; } /* cy_write */ @@ -2788,7 +2846,7 @@ 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 +#ifdef CY_DEBUG_IO printk("cyc:cy_put_char ttyC%d\n", info->line); #endif @@ -2807,10 +2865,9 @@ cy_put_char(struct tty_struct *tty, unsigned char ch) info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; + info->idle_stats.xmit_bytes++; + info->idle_stats.xmit_idle = jiffies; restore_flags(flags); -#if 0 - SP("+"); -#endif } /* cy_put_char */ @@ -2826,7 +2883,7 @@ cy_flush_chars(struct tty_struct *tty) unsigned char *base_addr; int card,chip,channel,index; -#ifdef SERIAL_DEBUG_IO +#ifdef CY_DEBUG_IO printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */ #endif @@ -2871,7 +2928,7 @@ cy_write_room(struct tty_struct *tty) struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; int ret; -#ifdef SERIAL_DEBUG_IO +#ifdef CY_DEBUG_IO printk("cyc:cy_write_room ttyC%d\n", info->line); /* */ #endif @@ -2897,7 +2954,7 @@ cy_chars_in_buffer(struct tty_struct *tty) channel = (info->line) - (cy_card[card].first_line); if (!IS_CYC_Z(cy_card[card])) { -#ifdef SERIAL_DEBUG_IO +#ifdef CY_DEBUG_IO printk("cyc:cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */ #endif @@ -2923,7 +2980,7 @@ cy_chars_in_buffer(struct tty_struct *tty) char_count = tx_put - tx_get; else char_count = tx_put - tx_get + tx_bufsize; -#ifdef SERIAL_DEBUG_IO +#ifdef CY_DEBUG_IO printk("cyc:cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt + char_count); /* */ #endif @@ -2951,6 +3008,7 @@ set_line_char(struct cyclades_port * info) int card,chip,channel,index; unsigned cflag, iflag; unsigned short chip_number; + int baud; int i; @@ -2972,47 +3030,21 @@ set_line_char(struct cyclades_port * info) index = cy_card[card].bus_index; /* baud rate */ - i = cflag & CBAUD; - - if (i & CBAUDEX) { - if (i == B57600) - i = 16; -#ifdef B76800 - else if(i == B76800) - i = 17; -#endif - else if(i == B115200) - i = 18; - else if(i == B230400 && (info->chip_rev >= CD1400_REV_J)) { - /* It is a CD1400 rev. J or later */ - i = 20; + baud = tty_get_baud_rate(info->tty); + if (baud > CD1400_MAX_SPEED) { + baud = CD1400_MAX_SPEED; + } + /* find the baud index */ + for (i = 0; i < 20; i++) { + if (baud == baud_table[i]) { + break; } - else - info->tty->termios->c_cflag &= ~CBAUDEX; } + if (i == 20) { + i = 19; /* CD1400_MAX_SPEED */ + } + - 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; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ - switch(info->baud) { - case 57600: - i += 1; break; -#ifdef B76800 - case 76800: - i += 2; break; -#endif - case 115200: - i += 3; break; - case 230400: - i += 5; break; - default: - break; - } - } - } if(info->chip_rev >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ info->tbpr = baud_bpr_60[i]; /* Tx BPR */ @@ -3026,7 +3058,7 @@ set_line_char(struct cyclades_port * info) info->rco = baud_co_25[i]; /* Rx CO */ } if (baud_table[i] == 134) { - info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + info->timeout = (info->xmit_fifo_size*HZ*15/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; @@ -3168,7 +3200,7 @@ set_line_char(struct cyclades_port * info) } else { cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_line_char dropping DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -3180,7 +3212,7 @@ set_line_char(struct cyclades_port * info) } else { cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_line_char raising DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -3214,49 +3246,17 @@ set_line_char(struct cyclades_port * info) buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; /* baud rate */ - switch(i = cflag & CBAUD){ - /* - case B0: cy_writel(&ch_ctrl->comm_baud , 0); break; - */ - case B50: cy_writel(&ch_ctrl->comm_baud , 50); break; - case B75: cy_writel(&ch_ctrl->comm_baud , 75); break; - case B110: cy_writel(&ch_ctrl->comm_baud , 110); break; - case B134: cy_writel(&ch_ctrl->comm_baud , 134); break; - case B150: cy_writel(&ch_ctrl->comm_baud , 150); break; - case B200: cy_writel(&ch_ctrl->comm_baud , 200); break; - case B300: cy_writel(&ch_ctrl->comm_baud , 300); break; - case B600: cy_writel(&ch_ctrl->comm_baud , 600); break; - case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break; - case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break; - case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break; - case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break; - case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break; - case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break; - case B38400: - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){ - cy_writel(&ch_ctrl->comm_baud , 57600); - }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){ - cy_writel(&ch_ctrl->comm_baud , 115200); - }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ - cy_writel(&ch_ctrl->comm_baud , info->baud); - }else{ - cy_writel(&ch_ctrl->comm_baud , 38400); - } - break; - case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break; -#ifdef B76800 - case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break; -#endif - case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break; - case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break; - case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break; + baud = tty_get_baud_rate(info->tty); + if (baud > CD1400_MAX_SPEED) { + baud = CD1400_MAX_SPEED; } + cy_writel(&ch_ctrl->comm_baud , baud); - if ((i = cy_readl(&ch_ctrl->comm_baud)) == 134) { + if (baud == 134) { info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; /* get it right for 134.5 baud */ - } else if (i) { - info->timeout = (info->xmit_fifo_size*HZ*15/i) + 2; + } else if (baud) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud) + 2; /* this needs to be propagated into the card info */ } else { info->timeout = 0; @@ -3318,16 +3318,16 @@ set_line_char(struct cyclades_port * info) cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX); } - if(i == 0){ /* baud rate is zero, turn off line */ + if(baud == 0){ /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_line_char dropping Z DTR\n"); #endif }else{ cy_writel(&ch_ctrl->rs_control, cy_readl(&ch_ctrl->rs_control) | C_RS_DTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_line_char raising Z DTR\n"); #endif } @@ -3343,6 +3343,21 @@ set_line_char(struct cyclades_port * info) } } + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + } /* set_line_char */ @@ -3377,12 +3392,11 @@ set_serial_info(struct cyclades_port * info, struct serial_struct new_serial; struct cyclades_port old_info; - if (!new_info) - return -EFAULT; - copy_from_user(&new_serial,new_info,sizeof(new_serial)); + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; old_info = *info; - if (!suser()) { + if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.close_delay != info->close_delay) || (new_serial.baud_base != info->baud) || ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != @@ -3405,7 +3419,6 @@ set_serial_info(struct cyclades_port * info, (new_serial.flags & ASYNC_FLAGS)); info->close_delay = new_serial.close_delay * HZ/100; info->closing_wait = new_serial.closing_wait * HZ/100; - check_and_exit: if (info->flags & ASYNC_INITIALIZED){ @@ -3462,7 +3475,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) } else { base_addr = (unsigned char*) (cy_card[card].base_addr); - if (cy_card[card].num_chips != 1){ + if (cy_card[card].num_chips != -1){ return -EINVAL; } @@ -3486,8 +3499,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) } } - cy_put_user(result,(unsigned long *) value); - return 0; + return cy_put_user(result,(unsigned long *) value); } /* get_modem_info */ @@ -3535,7 +3547,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, } else { cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info raising DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -3564,7 +3576,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, } else { cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info dropping DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -3601,7 +3613,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, } else { cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info raising DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -3617,7 +3629,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR); } -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info dropping DTR\n"); printk(" status: 0x%x, 0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)), @@ -3649,7 +3661,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, if (arg & TIOCM_DTR){ cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info raising Z DTR\n"); #endif } @@ -3662,7 +3674,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, if (arg & TIOCM_DTR){ cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info clearing Z DTR\n"); #endif } @@ -3678,13 +3690,13 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, if (arg & TIOCM_DTR){ cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info raising Z DTR\n"); #endif }else{ cy_writel(&ch_ctrl[channel].rs_control, cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); -#ifdef SERIAL_DEBUG_DTR +#ifdef CY_DEBUG_DTR printk("cyc:set_modem_info clearing Z DTR\n"); #endif } @@ -3705,7 +3717,6 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, return 0; } /* set_modem_info */ - static void send_break( struct cyclades_port * info, int duration) { @@ -3737,7 +3748,6 @@ int retval; } } /* send_break */ - static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { @@ -3796,11 +3806,11 @@ get_threshold(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = cy_readb(base_addr+(CyCOR3<<index)) & CyREC_FIFO; - cy_put_user(tmp,value); + return cy_put_user(tmp,value); } else { // Nothing to do! + return 0; } - return 0; }/* get_threshold */ @@ -3815,8 +3825,7 @@ set_default_threshold(struct cyclades_port * info, unsigned long value) static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - cy_put_user(info->default_threshold,value); - return 0; + return cy_put_user(info->default_threshold,value); }/* get_default_threshold */ @@ -3862,11 +3871,11 @@ get_timeout(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = cy_readb(base_addr+(CyRTPR<<index)); - cy_put_user(tmp,value); + return cy_put_user(tmp,value); } else { // Nothing to do! + return 0; } - return 0; }/* get_timeout */ @@ -3881,8 +3890,7 @@ set_default_timeout(struct cyclades_port * info, unsigned long value) static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - cy_put_user(info->default_timeout,value); - return 0; + return cy_put_user(info->default_timeout,value); }/* get_default_timeout */ /* @@ -3894,68 +3902,40 @@ 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 + if (serial_paranoia_check(info, tty->device, "cy_ioctl")) + return -ENODEV; + +#ifdef CY_DEBUG_OTHER printk("cyc:cy_ioctl ttyC%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: @@ -3976,109 +3956,63 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = info->rtsdtr_inv; break; case CYGETCARDINFO: - error = verify_area(VERIFY_WRITE, (void *) arg - ,sizeof(struct cyclades_card)); - if (error){ - ret_val = error; - break; - } - copy_to_user((void *)arg, (void *)&cy_card[info->card], - sizeof (struct cyclades_card)); + if (copy_to_user((void *)arg, (void *)&cy_card[info->card], + sizeof (struct cyclades_card))) { + ret_val = -EFAULT; + } ret_val = 0; break; case CYGETCD1400VER: ret_val = info->chip_rev; break; - case CYZPOLLCYCLE: - cyz_polling_cycle = (HZ * arg) / 1000; + case CYZSETPOLLCYCLE: + cyz_polling_cycle = (arg * HZ) / 1000; ret_val = 0; break; + case CYZGETPOLLCYCLE: + ret_val = (cyz_polling_cycle * 1000) / HZ; + break; case CYSETWAIT: - info->closing_wait2 = (unsigned short)arg; + info->closing_wait = (unsigned short)arg * HZ/100; ret_val = 0; break; case CYGETWAIT: - ret_val = info->closing_wait2; + ret_val = info->closing_wait / (HZ/100); 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); + 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 TIOCMGET: + ret_val = get_modem_info(info, (unsigned int *) arg); 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); + ret_val = get_serial_info(info, (struct serial_struct *) arg); break; case TIOCSSERIAL: - error = verify_area(VERIFY_READ, (void *) arg - ,sizeof(struct serial_struct)); - if (error){ - ret_val = error; - break; - } - ret_val = set_serial_info(info, - (struct serial_struct *) arg); + ret_val = set_serial_info(info, (struct serial_struct *) arg); break; default: ret_val = -ENOIOCTLCMD; } -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk(" cyc:cy_ioctl done\n"); #endif @@ -4097,7 +4031,7 @@ 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 +#ifdef CY_DEBUG_OTHER printk("cyc:cy_set_termios ttyC%d\n", info->line); #endif @@ -4141,7 +4075,7 @@ cy_throttle(struct tty_struct * tty) unsigned char *base_addr; int card,chip,channel,index; -#ifdef SERIAL_DEBUG_THROTTLE +#ifdef CY_DEBUG_THROTTLE char buf[64]; printk("cyc:throttle %s: %d....ttyC%d\n", @@ -4197,7 +4131,7 @@ cy_unthrottle(struct tty_struct * tty) unsigned char *base_addr; int card,chip,channel,index; -#ifdef SERIAL_DEBUG_THROTTLE +#ifdef CY_DEBUG_THROTTLE char buf[64]; printk("cyc:unthrottle %s: %d....ttyC%d\n", @@ -4255,7 +4189,7 @@ cy_stop(struct tty_struct *tty) int chip,channel,index; unsigned long flags; -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk("cyc:cy_stop ttyC%d\n", info->line); /* */ #endif @@ -4295,7 +4229,7 @@ cy_start(struct tty_struct *tty) int chip,channel,index; unsigned long flags; -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk("cyc:cy_start ttyC%d\n", info->line); /* */ #endif @@ -4333,7 +4267,7 @@ cy_flush_buffer(struct tty_struct *tty) int card, channel; unsigned long flags; -#ifdef SERIAL_DEBUG_IO +#ifdef CY_DEBUG_IO printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ #endif @@ -4377,7 +4311,7 @@ cy_hangup(struct tty_struct *tty) { struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; -#ifdef SERIAL_DEBUG_OTHER +#ifdef CY_DEBUG_OTHER printk("cyc:cy_hangup ttyC%d\n", info->line); /* */ #endif @@ -4388,7 +4322,7 @@ cy_hangup(struct tty_struct *tty) shutdown(info); info->event = 0; info->count = 0; -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT printk("cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif info->tty = 0; @@ -4422,7 +4356,7 @@ cyy_init_card(volatile ucchar *true_base_addr,int index)) for(chip_number=0; chip_number<CyMAX_CHIPS_PER_CARD; chip_number++){ base_addr = true_base_addr + (cy_chip_offset[chip_number]<<index); - udelay(1000L); + mdelay(1); if(cy_readb(base_addr+(CyCCR<<index)) != 0x00){ /************* printk(" chip #%d at %#6lx is never idle (CCR != 0)\n", @@ -4448,7 +4382,7 @@ cyy_init_card(volatile ucchar *true_base_addr,int index)) } cy_writeb((u_long)base_addr+(CyCCR<<index), CyCHIP_RESET); - udelay(1000L); + mdelay(1); if(cy_readb(base_addr+(CyGFRCR<<index)) == 0x00){ /* @@ -4593,88 +4527,45 @@ cy_detect_pci(void)) { #ifdef CONFIG_PCI -#ifdef NEW_PCI struct pci_dev *pdev = NULL; unsigned char cyy_rev_id; -#else - unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; -#endif unsigned long pci_intr_ctrl; - unsigned char cy_pci_irq; + unsigned char cy_pci_irq = 0; uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; unsigned short i,j,cy_pci_nchan; unsigned short device_id,dev_index = 0; -#ifndef NEW_PCI - unsigned short board_index = 0; -#endif uclong mailbox; uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; -#ifdef NEW_PCI if(pci_present() == 0) { /* PCI bus not present */ -#else - if(pcibios_present() == 0) { /* PCI bus not present */ -#endif return(0); } for (i = 0; i < NR_CARDS; i++) { /* look for a Cyclades card by vendor and device id */ while((device_id = cy_pci_dev_id[dev_index]) != 0) { -#ifdef NEW_PCI if((pdev = pci_find_device(PCI_VENDOR_ID_CYCLADES, device_id, pdev)) == NULL) { dev_index++; /* try next device id */ } else { break; /* found a board */ } -#else - if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, - device_id,board_index, - &cyy_bus, &cyy_dev_fn) != 0) { - dev_index++; /* try next device id */ - board_index = 0; - } else { - board_index++; - break; /* found a board */ - } -#endif } if (device_id == 0) break; /* read PCI configuration area */ -#ifdef NEW_PCI cy_pci_irq = pdev->irq; cy_pci_addr0 = pdev->base_address[0]; cy_pci_addr1 = pdev->base_address[1]; cy_pci_addr2 = pdev->base_address[2]; pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id); -#else - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_INTERRUPT_LINE, &cy_pci_irq); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_0, - (unsigned int *) &cy_pci_addr0); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_1, - (unsigned int *) &cy_pci_addr1); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_2, - (unsigned int *) &cy_pci_addr2); - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_REVISION_ID, &cyy_rev_id); -#endif if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", -#ifdef NEW_PCI pdev->bus->number, pdev->devfn); -#else - cyy_bus, cyy_dev_fn); -#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", @@ -4686,11 +4577,7 @@ cy_detect_pci(void)) #if defined(__alpha__) if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", -#ifdef NEW_PCI pdev->bus->number, pdev->devfn); -#else - cyy_bus, cyy_dev_fn); -#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", @@ -4775,11 +4662,7 @@ cy_detect_pci(void)) }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ /* print message */ printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", -#ifdef NEW_PCI pdev->bus->number, pdev->devfn); -#else - cyy_bus, cyy_dev_fn); -#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", @@ -4789,11 +4672,7 @@ cy_detect_pci(void)) }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ #ifdef CY_PCI_DEBUG printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", -#ifdef NEW_PCI pdev->bus->number, pdev->devfn); -#else - cyy_bus, cyy_dev_fn); -#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", @@ -4908,7 +4787,7 @@ cy_detect_pci(void)) cy_card[j].irq = (int) cy_pci_irq; cy_card[j].bus_index = 1; cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = 1; + cy_card[j].num_chips = -1; IRQ_cards[cy_pci_irq] = &cy_card[j]; /* print message */ @@ -4991,7 +4870,7 @@ cy_detect_pci(void)) cy_card[j].irq = (int) cy_pci_irq; cy_card[j].bus_index = 1; cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = 1; + cy_card[j].num_chips = -1; IRQ_cards[cy_pci_irq] = &cy_card[j]; /* print message */ @@ -5041,6 +4920,65 @@ show_version(void) __DATE__, __TIME__); } /* show_version */ +#ifdef CONFIG_PROC_FS +static int +cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, + int *eof, void *data) +{ + struct cyclades_port *info; + int i; + int len=0; + off_t begin=0; + off_t pos=0; + int size; + __u32 cur_jifs = jiffies; + + size = sprintf(buf, "Dev TimeOpen BytesOut IdleOut BytesIn IdleIn Overruns Ldisc\n"); + + pos += size; + len += size; + + /* Output one line for each known port */ + for (i = 0; i < NR_PORTS && cy_port[i].line >= 0; i++) { + info = &cy_port[i]; + + if (info->count) + size = sprintf(buf+len, + "%3d %8lu %10lu %8lu %10lu %8lu %9lu %6d\n", + info->line, + JIFFIES_DIFF(info->idle_stats.in_use, cur_jifs) / HZ, + info->idle_stats.xmit_bytes, + JIFFIES_DIFF(info->idle_stats.xmit_idle, cur_jifs) / HZ, + info->idle_stats.recv_bytes, + JIFFIES_DIFF(info->idle_stats.recv_idle, cur_jifs) / HZ, + info->idle_stats.overruns, + (long) info->tty->ldisc.num); + else + size = sprintf(buf+len, + "%3d %8lu %10lu %8lu %10lu %8lu %9lu %6ld\n", + info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto done; + } + *eof = 1; +done: + *start = buf + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + if (len < 0) + len = 0; + return len; +} +#endif + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -5070,6 +5008,7 @@ cy_init(void)) unsigned long mailbox; unsigned short chip_number; int nports; + struct proc_dir_entry *ent; show_version(); @@ -5077,6 +5016,7 @@ cy_init(void)) memset(&cy_serial_driver, 0, sizeof(struct tty_driver)); cy_serial_driver.magic = TTY_DRIVER_MAGIC; + cy_serial_driver.name = "cyclades"; cy_serial_driver.name = "ttyC"; cy_serial_driver.major = CYCLADES_MAJOR; cy_serial_driver.minor_start = 0; @@ -5106,6 +5046,7 @@ cy_init(void)) cy_serial_driver.stop = cy_stop; cy_serial_driver.start = cy_start; cy_serial_driver.hangup = cy_hangup; + cy_serial_driver.wait_until_sent = cy_wait_until_sent; /* * The callout device is just like normal device except for @@ -5115,6 +5056,9 @@ cy_init(void)) cy_callout_driver.name = "cub"; cy_callout_driver.major = CYCLADESAUX_MAJOR; cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + cy_callout_driver.read_proc = 0; + cy_callout_driver.proc_entry = 0; + if (tty_register_driver(&cy_serial_driver)) panic("Couldn't register Cyclades serial driver\n"); @@ -5123,7 +5067,7 @@ cy_init(void)) init_bh(CYCLADES_BH, do_cyclades_bh); - for (i = 0; i < 16; i++) { + for (i = 0; i < NR_IRQS; i++) { IRQ_cards[i] = 0; } @@ -5167,7 +5111,7 @@ cy_init(void)) /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { cinfo = &cy_card[board]; - if (cinfo->num_chips == 1){ /* Cyclades-Z */ + if (cinfo->num_chips == -1){ /* Cyclades-Z */ number_z_boards++; mailbox = cy_readl(&((struct RUNTIME_9060 *) cy_card[board].ctl_addr)->mail_box_0); @@ -5199,11 +5143,10 @@ cy_init(void)) info->rco = 0; info->close_delay = 5*HZ/10; info->closing_wait = CLOSING_WAIT_DELAY; - info->closing_wait2 = 0; info->x_char = 0; info->event = 0; info->count = 0; -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT // printk("cyc:cy_init(1) setting Z count to 0\n"); #endif info->blocked_open = 0; @@ -5250,7 +5193,6 @@ cy_init(void)) info->cor5 = 0; info->close_delay = 5*HZ/10; info->closing_wait = CLOSING_WAIT_DELAY; - info->closing_wait2 = 0; chip_number = (port - cinfo->first_line) / 4; if ((info->chip_rev = cy_readb(cinfo->base_addr + (cy_chip_offset[chip_number]<<index) + @@ -5273,7 +5215,7 @@ cy_init(void)) info->x_char = 0; info->event = 0; info->count = 0; -#ifdef SERIAL_DEBUG_COUNT +#ifdef CY_DEBUG_COUNT // printk("cyc:cy_init(2) setting Y count to 0\n"); #endif info->blocked_open = 0; @@ -5307,6 +5249,16 @@ cy_init(void)) #endif } +#ifdef CONFIG_PROC_FS + ent = create_proc_entry("cyclades", S_IFREG | S_IRUGO, 0); + ent->read_proc = cyclades_get_proc_info; +#endif +#if 0 +#ifdef CONFIG_PROC_FS + proc_register(&proc_root, &cyclades_proc_entry); +#endif +#endif + return 0; } /* cy_init */ @@ -5349,6 +5301,10 @@ cleanup_module(void) free_irq(cy_card[i].irq,NULL); } } +#ifdef CONFIG_PROC_FS + remove_proc_entry("cyclades", 0); +#endif + } /* cleanup_module */ #else /* called by linux/init/main.c to parse command line options */ diff --git a/drivers/char/dn_keyb.c b/drivers/char/dn_keyb.c index 3dd385df1..a694d9346 100644 --- a/drivers/char/dn_keyb.c +++ b/drivers/char/dn_keyb.c @@ -573,13 +573,13 @@ __initfunc(int dn_keyb_init(void)) { /* printk("dn_keyb_init\n"); */ - memcpy(plain_map, dnplain_map, sizeof(plain_map)); - memcpy(shift_map, dnshift_map, sizeof(shift_map)); - memcpy(altgr_map, dnaltgr_map, sizeof(altgr_map)); - memcpy(ctrl_map, dnctrl_map, sizeof(ctrl_map)); - memcpy(shift_ctrl_map, dnshift_ctrl_map, sizeof(shift_ctrl_map)); - memcpy(alt_map, dnalt_map, sizeof(alt_map)); - memcpy(ctrl_alt_map, dnctrl_alt_map, sizeof(ctrl_alt_map)); + memcpy(key_maps[0], dnplain_map, sizeof(plain_map)); + memcpy(key_maps[1], dnshift_map, sizeof(plain_map)); + memcpy(key_maps[2], dnaltgr_map, sizeof(plain_map)); + memcpy(key_maps[4], dnctrl_map, sizeof(plain_map)); + memcpy(key_maps[5], dnshift_ctrl_map, sizeof(plain_map)); + memcpy(key_maps[8], dnalt_map, sizeof(plain_map)); + memcpy(key_maps[12], dnctrl_alt_map, sizeof(plain_map)); mouse_dx=0; mouse_dy=0; diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index cac55b5ba..7818e8da3 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -401,9 +401,7 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file, if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4; if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8; - if(put_user(status, &hf->status) < 0) - return -EFAULT; - break; + return put_user(status, &hf->status); } case DSP56K_HOST_CMD: if (arg > 31 || arg < 0) @@ -427,27 +425,19 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file, * Do I need this function at all??? */ #if 0 -static int dsp56k_select(struct inode *inode, struct file *file, int sel_type, - select_table *wait) +static unsigned int dsp56k_poll(struct file *file, poll_table *wait) { - int dev = MINOR(inode->i_rdev) & 0x0f; + int dev = MINOR(file->f_dentry->d_inode->i_rdev) & 0x0f; switch(dev) { case DSP56K_DEV_56001: - - switch(sel_type) { - case SEL_IN: /* read */ - return 1; - case SEL_OUT: /* write */ - return 1; - default: - return 1; - } + /* poll_wait(file, ???, wait); */ + return POLLIN | POLLRDNORM | POLLOUT; default: printk("DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; + return 0; } } #endif @@ -530,22 +520,17 @@ static struct file_operations dsp56k_fops = { /****** Init and module functions ******/ -static int init_error = 0; - -__initfunc(void dsp56k_init(void)) +__initfunc(int dsp56k_init(void)) { - if(!ATARIHW_PRESENT(DSP56K)) { - init_error = 1; + if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) { printk("DSP56k driver: Hardware not present\n"); - return; + return -ENODEV; } -#ifndef MODULE if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) { printk("DSP56k driver: Unable to register driver\n"); - return; + return -ENODEV; } -#endif /* !MODULE */ dsp56k.in_use = 0; @@ -555,20 +540,7 @@ __initfunc(void dsp56k_init(void)) #ifdef MODULE int init_module(void) { - int r; - - init_error = 0; - dsp56k_init(); - if(init_error) - return -EPERM; - - r = register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops); - if(r) { - printk("DSP56k driver: Unable to register driver\n"); - return r; - } - - return 0; + return dsp56k_init(); } void cleanup_module(void) diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 6fee9da6d..9ddf7c33d 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -3956,7 +3956,7 @@ void epca_setup(char *str, int *ints) translate (To an unsigned long obviously), the second argument can be the address of any character variable or a NULL. If a variable is given, the end pointer of the string will be stored - in that variable; if a NULL is given the the end pointer will + in that variable; if a NULL is given the end pointer will not be returned. The last argument is the base to use. If a 0 is indicated, the routine will attempt to determine the proper base by looking at the values prefix (A '0' for octal, diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 553bf273b..522569077 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -37,7 +37,6 @@ * int espserial_init(void); */ -#include <linux/config.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/signal.h> @@ -65,7 +64,7 @@ #include <linux/malloc.h> #include <asm/uaccess.h> -#include "esp.h" +#include <linux/hayesesp.h> #define NR_PORTS 64 /* maximum number of ports */ #define NR_PRIMARY 8 /* maximum number of primary ports */ @@ -74,12 +73,13 @@ static int irq[NR_PRIMARY] = {0,0,0,0,0,0,0,0}; /* IRQ for each base port */ static unsigned int divisor[NR_PRIMARY] = {0,0,0,0,0,0,0,0}; /* custom divisor for each port */ -static unsigned int dma = CONFIG_ESPSERIAL_DMA_CHANNEL; /* DMA channel */ -static unsigned int rx_trigger = CONFIG_ESPSERIAL_RX_TRIGGER; -static unsigned int tx_trigger = CONFIG_ESPSERIAL_TX_TRIGGER; -static unsigned int flow_off = CONFIG_ESPSERIAL_FLOW_OFF; -static unsigned int flow_on = CONFIG_ESPSERIAL_FLOW_ON; -static unsigned int rx_timeout = CONFIG_ESPSERIAL_RX_TMOUT; +static unsigned int dma = ESP_DMA_CHANNEL; /* DMA channel */ +static unsigned int rx_trigger = ESP_RX_TRIGGER; +static unsigned int tx_trigger = ESP_TX_TRIGGER; +static unsigned int flow_off = ESP_FLOW_OFF; +static unsigned int flow_on = ESP_FLOW_ON; +static unsigned int rx_timeout = ESP_RX_TMOUT; +static unsigned int pio_threshold = ESP_PIO_THRESHOLD; MODULE_PARM(irq, "1-8i"); MODULE_PARM(divisor, "1-8i"); @@ -89,6 +89,8 @@ MODULE_PARM(tx_trigger, "i"); MODULE_PARM(flow_off, "i"); MODULE_PARM(flow_on, "i"); MODULE_PARM(rx_timeout, "i"); +MODULE_PARM(pio_threshold, "i"); + /* END */ static char *dma_buffer; @@ -100,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.0"; +static char *serial_version = "2.1"; static DECLARE_TASK_QUEUE(tq_esp); @@ -696,7 +698,7 @@ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) if (num_bytes) { if (dma_bytes || (info->stat_flags & ESP_STAT_USE_PIO) || - (num_bytes <= ESP_PIO_THRESHOLD)) + (num_bytes <= info->config.pio_threshold)) receive_chars_pio(info, num_bytes); else receive_chars_dma(info, num_bytes); @@ -723,7 +725,7 @@ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) if (num_bytes) { if (dma_bytes || (info->stat_flags & ESP_STAT_USE_PIO) || - (num_bytes <= ESP_PIO_THRESHOLD)) + (num_bytes <= info->config.pio_threshold)) transmit_chars_pio(info, num_bytes); else transmit_chars_dma(info, num_bytes); @@ -853,10 +855,10 @@ static _INLINE_ void esp_basic_init(struct esp_struct * info) /* set FIFO trigger levels */ serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER); - serial_out(info, UART_ESI_CMD2, rx_trigger >> 8); - serial_out(info, UART_ESI_CMD2, rx_trigger); - serial_out(info, UART_ESI_CMD2, tx_trigger >> 8); - serial_out(info, UART_ESI_CMD2, tx_trigger); + serial_out(info, UART_ESI_CMD2, info->config.rx_trigger >> 8); + serial_out(info, UART_ESI_CMD2, info->config.rx_trigger); + serial_out(info, UART_ESI_CMD2, info->config.tx_trigger >> 8); + serial_out(info, UART_ESI_CMD2, info->config.tx_trigger); /* Set clock scaling and wait states */ serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR); @@ -910,7 +912,7 @@ static int startup(struct esp_struct * info) /* set receive character timeout */ serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); - serial_out(info, UART_ESI_CMD2, rx_timeout); + serial_out(info, UART_ESI_CMD2, info->config.rx_timeout); /* clear all flags except the "never DMA" flag */ info->stat_flags &= ESP_STAT_NEVER_DMA; @@ -1229,10 +1231,10 @@ static void change_speed(struct esp_struct *info) /* Set high/low water */ serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL); - serial_out(info, UART_ESI_CMD2, flow_off >> 8); - serial_out(info, UART_ESI_CMD2, flow_off); - serial_out(info, UART_ESI_CMD2, flow_on >> 8); - serial_out(info, UART_ESI_CMD2, flow_on); + serial_out(info, UART_ESI_CMD2, info->config.flow_off >> 8); + serial_out(info, UART_ESI_CMD2, info->config.flow_off); + serial_out(info, UART_ESI_CMD2, info->config.flow_on >> 8); + serial_out(info, UART_ESI_CMD2, info->config.flow_on); restore_flags(flags); } @@ -1283,10 +1285,10 @@ static void rs_flush_chars(struct tty_struct *tty) static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { - int c, ret = 0; + int c, t, ret = 0; struct esp_struct *info = (struct esp_struct *)tty->driver_data; unsigned long flags; - + if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; @@ -1295,40 +1297,56 @@ static int rs_write(struct tty_struct * tty, int from_user, if (from_user) down(&tmp_buf_sem); - save_flags(flags); + while (1) { - cli(); - c = MIN(count, MIN(ESP_XMIT_SIZE - info->xmit_cnt - 1, - ESP_XMIT_SIZE - info->xmit_head)); + /* Thanks to R. Wolff for suggesting how to do this with */ + /* interrupts enabled */ + + c = count; + t = ESP_XMIT_SIZE - info->xmit_cnt - 1; + + if (t < c) + c = t; + + t = ESP_XMIT_SIZE - info->xmit_head; + + if (t < c) + c = t; + if (c <= 0) break; if (from_user) { c -= copy_from_user(tmp_buf, buf, c); + if (!c) { if (!ret) ret = -EFAULT; break; } - c = MIN(c, MIN(ESP_XMIT_SIZE - info->xmit_cnt - 1, - ESP_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1); info->xmit_cnt += c; - restore_flags(flags); buf += c; count -= c; ret += c; } + if (from_user) up(&tmp_buf_sem); + + save_flags(flags); cli(); + if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } + restore_flags(flags); return ret; } @@ -1418,7 +1436,7 @@ static void rs_unthrottle(struct tty_struct * tty) serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); - serial_out(info, UART_ESI_CMD2, rx_timeout); + serial_out(info, UART_ESI_CMD2, info->config.rx_timeout); sti(); } @@ -1447,24 +1465,40 @@ static int get_serial_info(struct esp_struct * info, tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; - tmp.reserved_char[0] = 'E'; - tmp.reserved_char[1] = rx_timeout; - tmp.reserved[0] = rx_trigger; - tmp.reserved[1] = tx_trigger; - tmp.reserved[2] = flow_off; - tmp.reserved[3] = flow_on; if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; } +static int get_esp_config(struct esp_struct * info, + struct hayes_esp_config * retinfo) +{ + struct hayes_esp_config tmp; + + if (!retinfo) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + tmp.rx_timeout = info->config.rx_timeout; + tmp.rx_trigger = info->config.rx_trigger; + tmp.tx_trigger = info->config.tx_trigger; + tmp.flow_off = info->config.flow_off; + tmp.flow_on = info->config.flow_on; + tmp.pio_threshold = info->config.pio_threshold; + tmp.dma_channel = (info->stat_flags & ESP_STAT_NEVER_DMA ? 0 : dma); + + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + + return 0; +} + static int set_serial_info(struct esp_struct * info, struct serial_struct * new_info) { struct serial_struct new_serial; struct esp_struct old_info; unsigned int change_irq; - unsigned int change_flow; int retval = 0; struct esp_struct *current_async; @@ -1480,21 +1514,10 @@ static int set_serial_info(struct esp_struct * info, (new_serial.irq < 2) || (new_serial.irq == 6) || (new_serial.irq == 8) || - (new_serial.irq == 13) || - (new_serial.reserved[3] >= new_serial.reserved[2]) || - (new_serial.reserved[0] < 1) || - (new_serial.reserved[1] < 1) || - (new_serial.reserved[2] < 1) || - (new_serial.reserved[3] < 1) || - (new_serial.reserved[0] > 1023) || - (new_serial.reserved[1] > 1023) || - (new_serial.reserved[2] > 1023) || - (new_serial.reserved[3] > 1023)) + (new_serial.irq == 13)) return -EINVAL; change_irq = new_serial.irq != info->irq; - change_flow = ((new_serial.reserved[2] != flow_off) || - (new_serial.reserved[3] != flow_on)); if (change_irq && (info->line % 8)) return -EINVAL; @@ -1539,40 +1562,6 @@ static int set_serial_info(struct esp_struct * info, info->custom_divisor = new_serial.custom_divisor; info->close_delay = new_serial.close_delay * HZ/100; info->closing_wait = new_serial.closing_wait * HZ/100; - flow_off = new_serial.reserved[2]; - flow_on = new_serial.reserved[3]; - - if ((new_serial.reserved[0] != rx_trigger) || - (new_serial.reserved[1] != tx_trigger)) { - unsigned long flags; - - rx_trigger = new_serial.reserved[0]; - tx_trigger = new_serial.reserved[1]; - save_flags(flags); cli(); - serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER); - serial_out(info, UART_ESI_CMD2, rx_trigger >> 8); - serial_out(info, UART_ESI_CMD2, rx_trigger); - serial_out(info, UART_ESI_CMD2, tx_trigger >> 8); - serial_out(info, UART_ESI_CMD2, tx_trigger); - restore_flags(flags); - } - - if (((unsigned char)(new_serial.reserved_char[1]) != - rx_timeout)) { - unsigned long flags; - - rx_timeout = (unsigned char) - (new_serial.reserved_char[1]); - save_flags(flags); cli(); - - if (info->IER & UART_IER_RDI) { - serial_out(info, UART_ESI_CMD1, - ESI_SET_RX_TIMEOUT); - serial_out(info, UART_ESI_CMD2, rx_timeout); - } - - restore_flags(flags); - } if (change_irq) { /* @@ -1602,8 +1591,7 @@ static int set_serial_info(struct esp_struct * info, if (info->flags & ASYNC_INITIALIZED) { if (((old_info.flags & ASYNC_SPD_MASK) != (info->flags & ASYNC_SPD_MASK)) || - (old_info.custom_divisor != info->custom_divisor) || - change_flow) { + (old_info.custom_divisor != info->custom_divisor)) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) @@ -1620,6 +1608,141 @@ static int set_serial_info(struct esp_struct * info, return retval; } +static int set_esp_config(struct esp_struct * info, + struct hayes_esp_config * new_info) +{ + struct hayes_esp_config new_config; + unsigned int change_dma; + int retval = 0; + struct esp_struct *current_async; + + /* Perhaps a non-sysadmin user should be able to do some of these */ + /* operations. I haven't decided yet. */ + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&new_config, new_info, sizeof(new_config))) + return -EFAULT; + + if ((new_config.flow_on >= new_config.flow_off) || + (new_config.rx_trigger < 1) || + (new_config.tx_trigger < 1) || + (new_config.flow_off < 1) || + (new_config.flow_on < 1) || + (new_config.rx_trigger > 1023) || + (new_config.tx_trigger > 1023) || + (new_config.flow_off > 1023) || + (new_config.flow_on > 1023) || + (new_config.pio_threshold < 0) || + (new_config.pio_threshold > 1024)) + return -EINVAL; + + if ((new_config.dma_channel != 1) && (new_config.dma_channel != 3)) + new_config.dma_channel = 0; + + if (info->stat_flags & ESP_STAT_NEVER_DMA) + change_dma = new_config.dma_channel; + else + change_dma = (new_config.dma_channel != dma); + + if (change_dma) { + if (new_config.dma_channel) { + /* PIO mode to DMA mode transition OR */ + /* change current DMA channel */ + + current_async = ports; + + while (current_async) { + if (current_async == info) { + if (current_async->count > 1) + return -EBUSY; + } else if (current_async->count) + return -EBUSY; + + current_async = + current_async->next_port; + } + + shutdown(info); + dma = new_config.dma_channel; + info->stat_flags &= ~ESP_STAT_NEVER_DMA; + + /* all ports must use the same DMA channel */ + + current_async = ports; + + while (current_async) { + esp_basic_init(current_async); + current_async = current_async->next_port; + } + } else { + /* DMA mode to PIO mode only */ + + if (info->count > 1) + return -EBUSY; + + shutdown(info); + info->stat_flags |= ESP_STAT_NEVER_DMA; + esp_basic_init(info); + } + } + + info->config.pio_threshold = new_config.pio_threshold; + + if ((new_config.flow_off != info->config.flow_off) || + (new_config.flow_on != info->config.flow_on)) { + unsigned long flags; + + info->config.flow_off = new_config.flow_off; + info->config.flow_on = new_config.flow_on; + save_flags(flags); cli(); + serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL); + serial_out(info, UART_ESI_CMD2, new_config.flow_off >> 8); + serial_out(info, UART_ESI_CMD2, new_config.flow_off); + serial_out(info, UART_ESI_CMD2, new_config.flow_on >> 8); + serial_out(info, UART_ESI_CMD2, new_config.flow_on); + restore_flags(flags); + } + + if ((new_config.rx_trigger != info->config.rx_trigger) || + (new_config.tx_trigger != info->config.tx_trigger)) { + unsigned long flags; + + info->config.rx_trigger = new_config.rx_trigger; + info->config.tx_trigger = new_config.tx_trigger; + save_flags(flags); cli(); + serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER); + serial_out(info, UART_ESI_CMD2, + new_config.rx_trigger >> 8); + serial_out(info, UART_ESI_CMD2, new_config.rx_trigger); + serial_out(info, UART_ESI_CMD2, + new_config.tx_trigger >> 8); + serial_out(info, UART_ESI_CMD2, new_config.tx_trigger); + restore_flags(flags); + } + + if (new_config.rx_timeout != info->config.rx_timeout) { + unsigned long flags; + + info->config.rx_timeout = new_config.rx_timeout; + save_flags(flags); cli(); + + if (info->IER & UART_IER_RDI) { + serial_out(info, UART_ESI_CMD1, + ESI_SET_RX_TIMEOUT); + serial_out(info, UART_ESI_CMD2, + new_config.rx_timeout); + } + + restore_flags(flags); + } + + if (!(info->flags & ASYNC_INITIALIZED)) + retval = startup(info); + + return retval; +} /* * get_lsr_info - get line status register info @@ -1741,7 +1864,8 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT) && + (cmd != TIOCGHAYESESP) && (cmd != TIOCSHAYESESP)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } @@ -1826,6 +1950,12 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, return error; return 0; + case TIOCGHAYESESP: + return (get_esp_config(info, + (struct hayes_esp_config *)arg)); + case TIOCSHAYESESP: + return (set_esp_config(info, + (struct hayes_esp_config *)arg)); default: return -ENOIOCTLCMD; @@ -2381,13 +2511,15 @@ __initfunc(int espserial_init(void)) init_bh(ESP_BH, do_serial_bh); - for (i = 0; i < NR_PRIMARY; i++) - if (irq[i] != 0) + for (i = 0; i < NR_PRIMARY; i++) { + if (irq[i] != 0) { if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) || (irq[i] == 8) || (irq[i] == 13)) irq[i] = 0; else if (irq[i] == 2) irq[i] = 9; + } + } if ((dma != 1) && (dma != 3)) dma = 0; @@ -2511,6 +2643,12 @@ __initfunc(int espserial_init(void)) info->tqueue_hangup.data = info; 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; info->next_port = ports; ports = info; printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ", diff --git a/drivers/char/esp.h b/drivers/char/esp.h deleted file mode 100644 index 8a207e12e..000000000 --- a/drivers/char/esp.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef ESP_H -#define ESP_H - -#define ESP_IN_MAJOR 57 /* major dev # for dial in */ -#define ESP_OUT_MAJOR 58 /* major dev # for dial out */ -#define ESPC_SCALE 3 -#define UART_ESI_BASE 0x00 -#define UART_ESI_SID 0x01 -#define UART_ESI_RX 0x02 -#define UART_ESI_TX 0x02 -#define UART_ESI_CMD1 0x04 -#define UART_ESI_CMD2 0x05 -#define UART_ESI_STAT1 0x04 -#define UART_ESI_STAT2 0x05 -#define UART_ESI_RWS 0x07 - -#define UART_IER_DMA_TMOUT 0x80 -#define UART_IER_DMA_TC 0x08 - -#define ESI_SET_IRQ 0x04 -#define ESI_SET_DMA_TMOUT 0x05 -#define ESI_SET_SRV_MASK 0x06 -#define ESI_SET_ERR_MASK 0x07 -#define ESI_SET_FLOW_CNTL 0x08 -#define ESI_SET_FLOW_CHARS 0x09 -#define ESI_SET_FLOW_LVL 0x0a -#define ESI_SET_TRIGGER 0x0b -#define ESI_SET_RX_TIMEOUT 0x0c -#define ESI_SET_FLOW_TMOUT 0x0d -#define ESI_WRITE_UART 0x0e -#define ESI_READ_UART 0x0f -#define ESI_SET_MODE 0x10 -#define ESI_GET_ERR_STAT 0x12 -#define ESI_GET_UART_STAT 0x13 -#define ESI_GET_RX_AVAIL 0x14 -#define ESI_GET_TX_AVAIL 0x15 -#define ESI_START_DMA_RX 0x16 -#define ESI_START_DMA_TX 0x17 -#define ESI_ISSUE_BREAK 0x1a -#define ESI_FLUSH_RX 0x1b -#define ESI_FLUSH_TX 0x1c -#define ESI_SET_BAUD 0x1d -#define ESI_SET_ENH_IRQ 0x1f -#define ESI_SET_REINTR 0x20 -#define ESI_SET_PRESCALAR 0x23 -#define ESI_NO_COMMAND 0xff - -#define ESP_STAT_RX_TIMEOUT 0x01 -#define ESP_STAT_DMA_RX 0x02 -#define ESP_STAT_DMA_TX 0x04 -#define ESP_STAT_NEVER_DMA 0x08 -#define ESP_STAT_USE_PIO 0x10 - -/* Always use PIO for this number (or less) of bytes */ -#define ESP_PIO_THRESHOLD 32 - -#define ESP_EVENT_WRITE_WAKEUP 0 -#define ESP_MAGIC 0x53ee -#define ESP_XMIT_SIZE 4096 - -struct esp_struct { - int magic; - int port; - int irq; - int flags; /* defined in tty.h */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int timeout; - int stat_flags; - int custom_divisor; - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - int IER; /* Interrupt Enable Register */ - int MCR; /* Modem control register */ - unsigned long event; - unsigned long last_active; - int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - long session; /* Session of opening process */ - long pgrp; /* pgrp of opening process */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - struct tq_struct tqueue; - struct tq_struct tqueue_hangup; - struct termios normal_termios; - struct termios callout_termios; - struct wait_queue *open_wait; - struct wait_queue *close_wait; - struct wait_queue *delta_msr_wait; - struct wait_queue *break_wait; - struct async_icount icount; /* kernel counters for the 4 input interrupts */ - struct esp_struct *next_port; /* For the linked list */ -}; - -struct esp_pio_buffer -{ - unsigned char data[1024]; - struct esp_pio_buffer *next; -}; - -#endif /* ESP_H */ - diff --git a/drivers/char/fbmem.c b/drivers/char/fbmem.c index fd2b28ec9..8f8612b35 100644 --- a/drivers/char/fbmem.c +++ b/drivers/char/fbmem.c @@ -8,6 +8,9 @@ * for more details. */ +#include <linux/config.h> +#include <linux/module.h> + #include <linux/types.h> #include <linux/errno.h> #include <linux/sched.h> @@ -16,30 +19,153 @@ #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> -#define FB_MAJOR 29 -#define FB_MODES_SHIFT 5 /* 32 modes per framebuffer */ -#define FB_NUM_MINORS 256 /* 256 Minors */ -#define FB_MAX (FB_NUM_MINORS / (1 << FB_MODES_SHIFT)) + /* + * 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); + +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_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_IDX(node) (MINOR(node) >> FB_MODES_SHIFT) #define GET_FB_VAR_IDX(node) (MINOR(node) & ((1 << FB_MODES_SHIFT)-1)) -struct fb_ops *registered_fb[FB_MAX]; -struct fb_var_screeninfo *registered_fb_var[FB_MAX]; -int registered_fb_var_num[FB_MAX]; -int fb_curr_open[FB_MAX]; -int fb_open_count[FB_MAX]; +struct fb_info *registered_fb[FB_MAX]; +int num_registered_fb = 0; + +char con2fb_map[MAX_NR_CONSOLES]; static inline int PROC_CONSOLE(void) { @@ -56,167 +182,250 @@ static inline int PROC_CONSOLE(void) return MINOR(current->tty->device) - 1; } -static long -fb_read(struct inode *inode, struct file *file, char *buf, unsigned long count) +#ifdef CONFIG_PROC_FS +static int fbmem_read_proc(char *buf, char **start, off_t offset, + int len, int *eof, void *private) +{ + struct fb_info **fi; + + len = 0; + for (fi = registered_fb; fi < ®istered_fb[FB_MAX] && len < 4000; fi++) + if (*fi) + len += sprintf(buf + len, "%d %s\n", + GET_FB_IDX((*fi)->node), + (*fi)->modename); + *start = buf + offset; + return len > offset ? len - offset : 0; +} +#endif + +static ssize_t +fb_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - unsigned long p = file->f_pos; - struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]; + 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; - int copy_size; + ssize_t copy_size; - if (! fb) + if (! fb || ! info->disp) return -ENODEV; - fb->fb_get_fix(&fix,PROC_CONSOLE()); - base_addr=(char *) fix.smem_start; + 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); - copy_to_user(buf, base_addr+p, copy_size); - file->f_pos += copy_size; + if (copy_to_user(buf, base_addr+p, copy_size)) + return -EFAULT; + *ppos += copy_size; return copy_size; } -static long -fb_write(struct inode *inode, struct file *file, const char *buf, - unsigned long count) +static ssize_t +fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - unsigned long p = file->f_pos; - struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]; + 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; - int copy_size; + ssize_t copy_size; - if (! fb) + if (! fb || ! info->disp) return -ENODEV; - fb->fb_get_fix(&fix, PROC_CONSOLE()); - base_addr=(char *) fix.smem_start; + + 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); - copy_from_user(base_addr+p, buf, copy_size); + 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) { - struct fb_ops *fb = registered_fb[GET_FB_IDX(inode->i_rdev)]; + 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; - - int i,fbidx,vidx; + struct fb_con2fbmap con2fb; + int i; if (! fb) return -ENODEV; switch (cmd) { case FBIOGET_VSCREENINFO: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct fb_var_screeninfo)); - if (i) return i; - fbidx=GET_FB_IDX(inode->i_rdev); - vidx=GET_FB_VAR_IDX(inode->i_rdev); - if (! vidx) /* ask device driver for current */ - i=fb->fb_get_var(&var, PROC_CONSOLE()); - else - var=registered_fb_var[fbidx][vidx-1]; - copy_to_user((void *) arg, &var, sizeof(var)); - return i; + 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: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct fb_var_screeninfo)); - if (i) return i; - copy_from_user(&var, (void *) arg, sizeof(var)); - i=fb->fb_set_var(&var, PROC_CONSOLE()); - copy_to_user((void *) arg, &var, sizeof(var)); - fbidx=GET_FB_IDX(inode->i_rdev); - vidx=GET_FB_VAR_IDX(inode->i_rdev); - if (! i && vidx) - registered_fb_var[fbidx][vidx-1]=var; - return i; + 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: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct fb_fix_screeninfo)); - if (i) return i; - i=fb->fb_get_fix(&fix, PROC_CONSOLE()); - copy_to_user((void *) arg, &fix, sizeof(fix)); - return i; + 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: - i = verify_area(VERIFY_READ, (void *) arg, - sizeof(struct fb_cmap)); - if (i) return i; - copy_from_user(&cmap, (void *) arg, sizeof(cmap)); - i = verify_area(VERIFY_READ, (void *) cmap.red, - cmap.len * sizeof(unsigned short)); - if (i) return i; - i = verify_area(VERIFY_READ, (void *) cmap.green, - cmap.len * sizeof(unsigned short)); - if (i) return i; - i = verify_area(VERIFY_READ, (void *) cmap.blue, - cmap.len * sizeof(unsigned short)); - if (i) return i; - if (cmap.transp) { - i = verify_area(VERIFY_READ, (void *) cmap.transp, - cmap.len * sizeof(unsigned short)); - if (i) return i; - } - return (fb->fb_set_cmap(&cmap, 0, PROC_CONSOLE())); + if (copy_from_user(&cmap, (void *) arg, sizeof(cmap))) + return -EFAULT; + return (fb->fb_set_cmap(&cmap, 0, PROC_CONSOLE(), info)); case FBIOGETCMAP: - i = verify_area(VERIFY_READ, (void *) arg, - sizeof(struct fb_cmap)); - if (i) return i; - copy_from_user(&cmap, (void *) arg, sizeof(cmap)); - i = verify_area(VERIFY_WRITE, (void *) cmap.red, - cmap.len * sizeof(unsigned short)); - if (i) return i; - i = verify_area(VERIFY_WRITE, (void *) cmap.green, - cmap.len * sizeof(unsigned short)); - if (i) return i; - i = verify_area(VERIFY_WRITE, (void *) cmap.blue, - cmap.len * sizeof(unsigned short)); - if (i) return i; - if (cmap.transp) { - i = verify_area(VERIFY_WRITE, (void *) cmap.transp, - cmap.len * sizeof(unsigned short)); - if (i) return i; - } - return (fb->fb_get_cmap(&cmap, 0, PROC_CONSOLE())); + if (copy_from_user(&cmap, (void *) arg, sizeof(cmap))) + return -EFAULT; + return (fb->fb_get_cmap(&cmap, 0, PROC_CONSOLE(), info)); case FBIOPAN_DISPLAY: - i = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct fb_var_screeninfo)); - if (i) return i; - copy_from_user(&var, (void *) arg, sizeof(var)); - i=fb->fb_pan_display(&var, PROC_CONSOLE()); - copy_to_user((void *) arg, &var, sizeof(var)); - fbidx=GET_FB_IDX(inode->i_rdev); - vidx=GET_FB_VAR_IDX(inode->i_rdev); - if (! i && vidx) - registered_fb_var[fbidx][vidx-1]=var; + 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())); + return fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE(), + info); } } -static int fb_mmap(struct file *file, struct vm_area_struct * vma) +static int +fb_mmap(struct file *file, struct vm_area_struct * vma) { - struct fb_ops *fb = registered_fb[GET_FB_IDX(file->f_dentry->d_inode->i_rdev)]; + 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) + if (!fb) return -ENODEV; - fb->fb_get_fix(&fix, PROC_CONSOLE()); - if ((vma->vm_end - vma->vm_start + vma->vm_offset) > fix.smem_len) + 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 += __pa(fix.smem_start); + 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 write-through cache mode */ + /* 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; +#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; @@ -228,34 +437,25 @@ static int fb_mmap(struct file *file, struct vm_area_struct * vma) static int fb_open(struct inode *inode, struct file *file) { - int fbidx=GET_FB_IDX(inode->i_rdev); - int vidx=GET_FB_VAR_IDX(inode->i_rdev); - struct fb_ops *fb = registered_fb[fbidx]; - int err; - - if (! vidx) /* fb?current always succeeds */ - return 0; - if (vidx > registered_fb_var_num[fbidx]) - return -EINVAL; - if (fb_curr_open[fbidx] && fb_curr_open[fbidx] != vidx) - return -EBUSY; - if (file->f_mode & 2) /* only set parameters if opened writeable */ - if ((err=fb->fb_set_var(registered_fb_var[fbidx] + vidx-1, PROC_CONSOLE()))) - return err; - fb_curr_open[fbidx] = vidx; - fb_open_count[fbidx]++; - return 0; + 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); - int vidx=GET_FB_VAR_IDX(inode->i_rdev); - if (! vidx) - return 0; - if (! (--fb_open_count[fbidx])) - fb_curr_open[fbidx]=0; + int fbidx = GET_FB_IDX(inode->i_rdev); + struct fb_info *info = registered_fb[fbidx]; + + info->fbops->fb_release(info,1); return 0; } @@ -273,37 +473,181 @@ static struct file_operations fb_fops = { }; int -register_framebuffer(char *id, int *node, struct fb_ops *fbops, int fbvar_num, - struct fb_var_screeninfo *fbvar) +register_framebuffer(struct fb_info *fb_info) { - int i; + 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]) + if (!registered_fb[i]) break; - if (i == FB_MAX) - return -ENXIO; - registered_fb[i]=fbops; - registered_fb_var[i]=fbvar; - registered_fb_var_num[i]=fbvar_num; - *node=GET_INODE(i); + 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(int node) +unregister_framebuffer(const struct fb_info *fb_info) { - int i=GET_FB_IDX(node); - if (! registered_fb[i]) + 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; - registered_fb_var[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 bc00c4e86..5441daee9 100644 --- a/drivers/char/ftape/lowlevel/fdc-io.c +++ b/drivers/char/ftape/lowlevel/fdc-io.c @@ -214,7 +214,7 @@ int fdc_command(const __u8 * cmd_data, int cmd_len) * sending the step pulses to the drive. Therefore the * non-interrupt level driver has no chance to tell * whether the isr() just has issued a seek. Therefore - * we HAVE TO have a look at the the ft_hide_interrupt + * we HAVE TO have a look at the ft_hide_interrupt * flag: it signals the non-interrupt level part of * the driver that it has to wait for the fdc until it * has completet seeking. diff --git a/drivers/char/ftape/lowlevel/ftape_syms.c b/drivers/char/ftape/lowlevel/ftape_syms.c index dce372118..332de841c 100644 --- a/drivers/char/ftape/lowlevel/ftape_syms.c +++ b/drivers/char/ftape/lowlevel/ftape_syms.c @@ -20,7 +20,7 @@ * $Revision: 1.4 $ * $Date: 1997/10/17 00:03:51 $ * - * This file contains the the symbols that the ftape low level + * This file contains the symbols that the ftape low level * part of the QIC-40/80/3010/3020 floppy-tape driver "ftape" * exports to it's high level clients */ diff --git a/drivers/char/ftape/zftape/zftape_syms.c b/drivers/char/ftape/zftape/zftape_syms.c index 09e77bf10..139b19cf1 100644 --- a/drivers/char/ftape/zftape/zftape_syms.c +++ b/drivers/char/ftape/zftape/zftape_syms.c @@ -20,7 +20,7 @@ * $Revision: 1.3 $ * $Date: 1997/10/05 19:19:14 $ * - * This file contains the the symbols that the zftape frontend to + * This file contains the symbols that the zftape frontend to * the ftape floppy tape driver exports */ diff --git a/drivers/char/h8.c b/drivers/char/h8.c index 73941a059..287698298 100644 --- a/drivers/char/h8.c +++ b/drivers/char/h8.c @@ -1,6 +1,4 @@ /* - */ -/* * Hitachi H8/337 Microcontroller driver * * The H8 is used to deal with the power and thermal environment @@ -27,6 +25,7 @@ #include <linux/miscdevice.h> #include <linux/lists.h> #include <linux/ioport.h> +#include <linux/poll.h> #define __KERNEL_SYSCALLS__ #include <asm/unistd.h> @@ -55,12 +54,6 @@ int h8_init(void); int h8_display_blank(void); int h8_display_unblank(void); -static int h8_open(struct inode *, struct file *); -static void h8_release(struct inode *, struct file *); -static long h8_read(struct inode *, struct file *, char *, u_long); -static int h8_select(struct inode *, struct file *, int, select_table *); -static int h8_ioctl(struct inode *, struct file *, u_int, u_long); - static void h8_intr(int irq, void *dev_id, struct pt_regs *regs); #ifdef CONFIG_PROC_FS @@ -113,14 +106,14 @@ static char driver_version[] = "X0.0";/* no spaces */ static struct file_operations h8_fops = { NULL, /* lseek */ - h8_read, + NULL, NULL, /* write */ NULL, /* readdir */ - h8_select, - h8_ioctl, + NULL, + NULL, NULL, /* mmap */ - h8_open, - h8_release, + NULL, + NULL, NULL, /* fsync */ NULL /* fasync */ }; @@ -328,7 +321,7 @@ int init_module(void) request_region(h8_base, 8, "h8"); #ifdef CONFIG_PROC_FS - proc_register_dynamic(&proc_root, &h8_proc_entry); + proc_register(&proc_root, &h8_proc_entry); #endif QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *); @@ -362,7 +355,7 @@ int h8_init(void) printk("H8 at 0x%x IRQ %d\n", h8_base, h8_irq); #ifdef CONFIG_PROC_FS - proc_register_dynamic(&proc_root, &h8_proc_entry); + proc_register(&proc_root, &h8_proc_entry); #endif misc_register(&h8_device); @@ -439,38 +432,6 @@ int h8_get_info(char *buf, char **start, off_t fpos, int length, int dummy) } #endif -static long h8_read(struct inode *inode, struct file *fp, char *buf, - u_long count) -{ - printk("h8_read: IMPDEL\n"); - return 0; -} - -static int h8_select(struct inode *inode, struct file *fp, int sel_type, - select_table * wait) -{ - printk("h8_select: IMPDEL\n"); - return 0; -} - -static int h8_ioctl(struct inode * inode, struct file *filp, - u_int cmd, u_long arg) -{ - printk("h8_ioctl: IMPDEL\n"); - return 0; -} - -static void h8_release(struct inode * inode, struct file * filp) -{ - printk("h8_release: IMPDEL\n"); -} - -static int h8_open(struct inode * inode, struct file * filp) -{ - printk("h8_open: IMPDEL\n"); - return 0; -} - /* Called from console driver -- must make sure h8_enabled. */ int h8_display_blank(void) { @@ -775,7 +736,7 @@ h8_cmd_done(h8_cmd_q_t *qp) case H8_RD_SN: case H8_RD_ENET_ADDR: - printk("H8: Read ethernet addr - command done - address: %x - %x - %x - %x - %x - %x \n", + printk("H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n", qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2], qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]); QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); @@ -908,7 +869,7 @@ h8_cmd_done(h8_cmd_q_t *qp) } /* - * Retrieve the current cpu temperature and case temperature. Provides + * Retrieve the current CPU temperature and case temperature. Provides * the feedback for the thermal control algorithm. Synchcronized via * sleep() for priority so that no other actions in the process will take * place before the data becomes available. @@ -954,7 +915,7 @@ h8_get_max_temp(void) /* * Assigns an upper limit to the value of the H8 thermal interrupt. * As an example setting a value of 115 F here will cause the - * interrupt to trigger when the cpu temperature reaches 115 F. + * interrupt to trigger when the CPU temperature reaches 115 F. */ static void h8_set_upper_therm_thold(int thold) @@ -1072,7 +1033,7 @@ h8_monitor_thread(void * unused) /* * If an external DC supply is removed or added make - * appropriate cpu speed adjustments. + * appropriate CPU speed adjustments. */ if (h8_event_mask & H8_MANAGE_BATTERY) { h8_run_level_3_manage(H8_RUN); @@ -1106,7 +1067,7 @@ h8_manage_therm(void) h8_set_cpu_speed(h8_udamp); h8_clear_event_mask(H8_MANAGE_UTHERM); h8_set_event_mask(H8_MANAGE_LTHERM); - /* Check again in 30 seconds for cpu temperature */ + /* Check again in 30 seconds for CPU temperature */ h8_start_monitor_timer(H8_TIMEOUT_INTERVAL); } else if (h8_event_mask & H8_MANAGE_LTHERM) { /* See how cool the system has become as a result @@ -1116,7 +1077,7 @@ h8_manage_therm(void) if (curr_temp[0] < (h8_uthermal_threshold - h8_uthermal_window)) { /* System cooling has progressed to a point - that the cpu may be speeded up. */ + that the CPU may be sped up. */ h8_set_upper_therm_thold(h8_uthermal_threshold); h8_set_cpu_speed(h8_ldamp); /* adjustable */ if(h8_debug & 0x10) @@ -1144,7 +1105,7 @@ h8_set_cpu_speed(int speed_divisor) /* * global_rpb_counter is consumed by alpha_delay() in determining just * how much time to delay. It is necessary that the number of microseconds - * in DELAY(n) be kept consistent over a variety of cpu clock speeds. + * in DELAY(n) be kept consistent over a variety of CPU clock speeds. * To that end global_rpb_counter is here adjusted. */ @@ -1183,7 +1144,7 @@ h8_set_cpu_speed(int speed_divisor) } /* - * Gets value stored in rpb representing cpu clock speed and adjusts this + * Gets value stored in rpb representing CPU clock speed and adjusts this * value based on the current clock speed divisor. */ u_long diff --git a/drivers/char/h8.h b/drivers/char/h8.h index 533c4ece1..b59aadea8 100644 --- a/drivers/char/h8.h +++ b/drivers/char/h8.h @@ -183,7 +183,7 @@ #define H8_SYSTEM_DELAY_TEST 0x100000 #define H8_POWER_SWITCH_TEST 0x200000 -/* cpu speeds and clock divisor values */ +/* CPU speeds and clock divisor values */ #define MHZ_14 5 #define MHZ_28 4 #define MHZ_57 3 diff --git a/drivers/char/hfmodem/Makefile b/drivers/char/hfmodem/Makefile index 42629693b..2219cd52e 100644 --- a/drivers/char/hfmodem/Makefile +++ b/drivers/char/hfmodem/Makefile @@ -25,7 +25,7 @@ all: all_targets .PHONY: all gentbl: gentbl.c - $(HOSTCC) -Wall $< -o $@ -lm + $(HOSTCC) $(HOSTCFLAGS) $< -o $@ -lm TBLHDR := tables.h diff --git a/drivers/char/hfmodem/refclock.c b/drivers/char/hfmodem/refclock.c index 6617673ca..2ca829fbf 100644 --- a/drivers/char/hfmodem/refclock.c +++ b/drivers/char/hfmodem/refclock.c @@ -30,6 +30,7 @@ #include <linux/sched.h> #include <linux/time.h> #include <linux/hfmodem.h> +#include <asm/processor.h> /* --------------------------------------------------------------------- */ @@ -65,49 +66,13 @@ static int rdtsc_ok = 1; /* --------------------------------------------------------------------- */ #ifdef __i386__ - __initfunc(static void i386_capability(void)) { - unsigned long flags; - unsigned long fl1; - union { - struct { - unsigned int ebx, edx, ecx; - } r; - unsigned char s[13]; - } id; - unsigned int eax; - unsigned int x86_capability; - - save_flags(flags); - flags |= 0x200000; - restore_flags(flags); - save_flags(flags); - fl1 = flags; - flags &= ~0x200000; - restore_flags(flags); - save_flags(flags); - if (!(fl1 & 0x200000) || (flags & 0x200000)) { - printk(KERN_WARNING "%s: cpu does not support CPUID\n", hfmodem_drvname); - return; - } - __asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) : - "0" (0)); - id.s[12] = 0; - if (eax < 1) { - printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability " - "list\n", hfmodem_drvname, id.s); - return; - } - printk(KERN_INFO "%s: cpu: vendor string %s ", hfmodem_drvname, id.s); - __asm__ ("cpuid" : "=a" (eax), "=d" (x86_capability) : "0" (1) : "ebx", "ecx"); - printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15, eax & 15, - x86_capability); - if (x86_capability & 0x10) + if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) rdtsc_ok = 1; else printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname); -} +} #endif /* __i386__ */ /* --------------------------------------------------------------------- */ diff --git a/drivers/char/i2c.c b/drivers/char/i2c.c index 5507d94ba..9741f4be4 100644 --- a/drivers/char/i2c.c +++ b/drivers/char/i2c.c @@ -5,6 +5,7 @@ * */ +#include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -14,8 +15,7 @@ #include <linux/locks.h> #include <linux/sched.h> #include <linux/malloc.h> - -#include "i2c.h" +#include <linux/i2c.h> #define REGPRINT(x) if (verbose) (x) #define I2C_DEBUG(x) if (i2c_debug) (x) @@ -34,11 +34,18 @@ static struct i2c_bus *busses[I2C_BUS_MAX]; static struct i2c_driver *drivers[I2C_DRIVER_MAX]; static int bus_count = 0, driver_count = 0; +extern int i2c_tuner_init(void); +extern int msp3400c_init(void); + int i2c_init(void) { printk(KERN_INFO "i2c: initialized%s\n", scan ? " (i2c bus scan enabled)" : ""); /* anything to do here ? */ +#ifdef CONFIG_VIDEO_BT848 + i2c_tuner_init(); + msp3400c_init(); +#endif return 0; } diff --git a/drivers/char/i2c.h b/drivers/char/i2c.h deleted file mode 100644 index 66aef224c..000000000 --- a/drivers/char/i2c.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef I2C_H -#define I2C_H - -/* - * linux i2c interface. Works a little bit like the scsi subsystem. - * There are: - * - * i2c the basic control module (like scsi_mod) - * bus driver a driver with a i2c bus (hostadapter driver) - * chip driver a driver for a chip connected - * to a i2c bus (cdrom/hd driver) - * - * A device will be attached to one bus and one chip driver. Every chip - * driver gets a unique ID. - * - * A chip driver can provide a ioctl-like callback for the - * communication with other parts of the kernel (not every i2c chip is - * useful without other devices, a TV card tuner for example). - * - * "i2c internal" parts of the structs: only the i2c module is allowed to - * write to them, for others they are read-only. - * - */ - -#define I2C_BUS_MAX 4 /* max # of bus drivers */ -#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ -#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ - -struct i2c_bus; -struct i2c_driver; -struct i2c_device; - -#define I2C_DRIVERID_MSP3400 1 -#define I2C_DRIVERID_TUNER 2 -#define I2C_DRIVERID_VIDEOTEXT 3 - -#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ - -/* - * struct for a driver for a i2c chip (tuner, soundprocessor, - * videotext, ... ). - * - * a driver will register within the i2c module. The i2c module will - * callback the driver (i2c_attach) for every device it finds on a i2c - * bus at the specified address. If the driver decides to "accept" - * the, device, it must return a struct i2c_device, and NULL - * otherwise. - * - * i2c_detach = i2c_attach ** -1 - * - * i2c_command will be used to pass commands to the driver in a - * ioctl-line manner. - * - */ - -struct i2c_driver -{ - char name[32]; /* some useful label */ - int id; /* device type ID */ - unsigned char addr_l, addr_h; /* address range of the chip */ - - int (*attach)(struct i2c_device *device); - int (*detach)(struct i2c_device *device); - int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); - - /* i2c internal */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; -}; - - -/* - * this holds the informations about a i2c bus available in the system. - * - * a chip with a i2c bus interface (like bt848) registers the bus within - * the i2c module. This struct provides functions to access the i2c bus. - * - * One must hold the spinlock to access the i2c bus (XXX: is the irqsave - * required? Maybe better use a semaphore?). - * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing - * to bang their i2c bus from an interrupt. - * - * attach/detach_inform is a callback to inform the bus driver about - * attached chip drivers. - * - */ - -/* needed: unsigned long flags */ - -#define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); -#define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); - -struct i2c_bus -{ - char name[32]; /* some useful label */ - int id; - void *data; /* free for use by the bus driver */ - - spinlock_t bus_lock; - - /* attach/detach inform callbacks */ - void (*attach_inform)(struct i2c_bus *bus, int id); - void (*detach_inform)(struct i2c_bus *bus, int id); - - /* Software I2C */ - void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); - int (*i2c_getdataline)(struct i2c_bus *bus); - - /* Hardware I2C */ - int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); - int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); - - /* internal data for i2c module */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; -}; - - -/* - * This holds per-device data for a i2c device - */ - -struct i2c_device -{ - char name[32]; /* some useful label */ - void *data; /* free for use by the chip driver */ - unsigned char addr; /* chip addr */ - - /* i2c internal */ - struct i2c_bus *bus; - struct i2c_driver *driver; -}; - - -/* ------------------------------------------------------------------- */ -/* i2c module functions */ - -/* register/unregister a i2c bus */ -int i2c_register_bus(struct i2c_bus *bus); -int i2c_unregister_bus(struct i2c_bus *bus); - -/* register/unregister a chip driver */ -int i2c_register_driver(struct i2c_driver *driver); -int i2c_unregister_driver(struct i2c_driver *driver); - -/* send a command to a chip using the ioctl-like callback interface */ -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg); - -/* i2c bus access functions */ -void i2c_start(struct i2c_bus *bus); -void i2c_stop(struct i2c_bus *bus); -void i2c_one(struct i2c_bus *bus); -void i2c_zero(struct i2c_bus *bus); -int i2c_ack(struct i2c_bus *bus); - -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); -unsigned char i2c_readbyte(struct i2c_bus *bus,int last); - -/* i2c (maybe) hardware functions */ -int i2c_read(struct i2c_bus *bus, unsigned char addr); -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); - -#endif /* I2C_H */ diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 404f9c88c..b41dd0690 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.5"; +static char *stli_drvversion = "5.4.6"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -1425,12 +1425,12 @@ static int stli_write(struct tty_struct *tty, int from_user, const unsigned char (tail - head - 1); count = MIN(len, count); EBRDDISABLE(brdp); + restore_flags(flags); down(&stli_tmpwritesem); copy_from_user(stli_tmpwritebuf, chbuf, count); up(&stli_tmpwritesem); chbuf = &stli_tmpwritebuf[0]; - restore_flags(flags); } /* @@ -3361,7 +3361,6 @@ static void stli_ecpmcreset(stlibrd_t *brdp) static void stli_onbinit(stlibrd_t *brdp) { unsigned long memconf; - int i; #if DEBUG printk("stli_onbinit(brdp=%d)\n", (int) brdp); @@ -3370,13 +3369,12 @@ static void stli_onbinit(stlibrd_t *brdp) outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR)); udelay(10); outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR)); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); memconf = (brdp->memaddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT; outb(memconf, (brdp->iobase + ONB_ATMEMAR)); outb(0x1, brdp->iobase); - udelay(1000); + mdelay(1); } /*****************************************************************************/ @@ -3425,7 +3423,6 @@ static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) static void stli_onbreset(stlibrd_t *brdp) { - int i; #if DEBUG printk("stli_onbreset(brdp=%x)\n", (int) brdp); @@ -3434,8 +3431,7 @@ static void stli_onbreset(stlibrd_t *brdp) outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR)); udelay(10); outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR)); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); } /*****************************************************************************/ @@ -3447,7 +3443,6 @@ static void stli_onbreset(stlibrd_t *brdp) static void stli_onbeinit(stlibrd_t *brdp) { unsigned long memconf; - int i; #if DEBUG printk("stli_onbeinit(brdp=%d)\n", (int) brdp); @@ -3457,15 +3452,14 @@ static void stli_onbeinit(stlibrd_t *brdp) outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR)); udelay(10); outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR)); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); memconf = (brdp->memaddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL; outb(memconf, (brdp->iobase + ONB_EIMEMARL)); memconf = (brdp->memaddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH; outb(memconf, (brdp->iobase + ONB_EIMEMARH)); outb(0x1, brdp->iobase); - udelay(1000); + mdelay(1); } /*****************************************************************************/ @@ -3521,7 +3515,6 @@ static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line) static void stli_onbereset(stlibrd_t *brdp) { - int i; #if DEBUG printk("stli_onbereset(brdp=%x)\n", (int) brdp); @@ -3530,8 +3523,7 @@ static void stli_onbereset(stlibrd_t *brdp) outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR)); udelay(10); outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR)); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); } /*****************************************************************************/ @@ -3542,7 +3534,6 @@ static void stli_onbereset(stlibrd_t *brdp) static void stli_bbyinit(stlibrd_t *brdp) { - int i; #if DEBUG printk("stli_bbyinit(brdp=%d)\n", (int) brdp); @@ -3551,10 +3542,9 @@ static void stli_bbyinit(stlibrd_t *brdp) outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR)); udelay(10); outb(0, (brdp->iobase + BBY_ATCONFR)); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); outb(0x1, brdp->iobase); - udelay(1000); + mdelay(1); } /*****************************************************************************/ @@ -3587,7 +3577,6 @@ static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line) static void stli_bbyreset(stlibrd_t *brdp) { - int i; #if DEBUG printk("stli_bbyreset(brdp=%x)\n", (int) brdp); @@ -3596,8 +3585,7 @@ static void stli_bbyreset(stlibrd_t *brdp) outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR)); udelay(10); outb(0, (brdp->iobase + BBY_ATCONFR)); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); } /*****************************************************************************/ @@ -3608,15 +3596,13 @@ static void stli_bbyreset(stlibrd_t *brdp) static void stli_stalinit(stlibrd_t *brdp) { - int i; #if DEBUG printk("stli_stalinit(brdp=%d)\n", (int) brdp); #endif outb(0x1, brdp->iobase); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); } /*****************************************************************************/ @@ -3646,7 +3632,6 @@ static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) static void stli_stalreset(stlibrd_t *brdp) { volatile unsigned long *vecp; - int i; #if DEBUG printk("stli_stalreset(brdp=%x)\n", (int) brdp); @@ -3655,8 +3640,7 @@ static void stli_stalreset(stlibrd_t *brdp) vecp = (volatile unsigned long *) (brdp->membase + 0x30); *vecp = 0xffff0000; outb(0, brdp->iobase); - for (i = 0; (i < 1000); i++) - udelay(1000); + mdelay(1000); } /*****************************************************************************/ @@ -4167,10 +4151,9 @@ static inline int stli_eisamemprobe(stlibrd_t *brdp) outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR)); udelay(10); outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR)); - for (i = 0; (i < 100); i++) - udelay(1000); + mdelay(100); outb(0x1, brdp->iobase); - udelay(1000); + mdelay(1); stli_onbeenable(brdp); } else { return(-ENODEV); diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c index 4994dabb4..25a7c2b47 100644 --- a/drivers/char/joystick.c +++ b/drivers/char/joystick.c @@ -1,13 +1,12 @@ /* - * $Id: joystick.c,v 1.3 1997/12/16 05:35:12 ralf Exp $ - * - * Copyright (C) 1997 Vojtech Pavlik + * 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. + * 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> @@ -15,30 +14,25 @@ #include <linux/ioport.h> #include <linux/errno.h> #include <linux/mm.h> -#include <linux/ptrace.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> -#include <asm/ptrace.h> -#include <asm/uaccess.h> -#include <asm/param.h> #define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */ #define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */ -#define JS_BUTTON_PERIOD HZ/50 /* button valid time (20 ms) */ -#define JS_AXIS_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */ -#define JS_AXIS_MAX_PERIOD HZ/25*2 /* axis max valid time (80 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_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 8 /* initial precision for all axes */ +#define JS_RETRIES 4 /* number of retries */ +#define JS_DEF_PREC 16 /* initial precision for all axes */ #define JS_NUM 2 /* number of joysticks */ @@ -49,7 +43,7 @@ #define PIT_DATA 0x40 /* timer 0 data port */ #define JS_PORT 0x201 /* joystick port */ -#define JS_TRIGGER 0xff /* triggers one-shots */ +#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 */ @@ -95,7 +89,7 @@ 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_axis_time = 0; /* last read axis time */ +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 */ @@ -106,7 +100,7 @@ static unsigned int js_buttons = 0; MODULE_AUTHOR("Vojtech Pavlik <vojtech@atrey.karlin.mff.cuni.cz>"); MODULE_SUPPORTED_DEVICE("js"); -MODULE_PARM(js, "0-1b"); +MODULE_PARM(js, "1-2b"); static char js[] = {0, 0}; @@ -118,7 +112,7 @@ static char js[] = {0, 0}; static inline int get_pit(void) { int t, flags; - + save_flags(flags); cli(); outb(PIT_READ_TIMER, PIT_MODE); @@ -132,12 +126,14 @@ static inline int get_pit(void) * count_bits() counts set bits in a byte. */ -static int count_bits(unsigned char c) +static int count_bits(unsigned int c) { - int i, t = 0; - for (i = 0; i < 8; i++) - if (c & (1 << i)) t++; - return t; + int i = 0; + while (c) { + i += c & 1; + c >>= 1; + } + return i; } /* @@ -149,36 +145,34 @@ 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] ? corr->coef[0] : value - corr->coef[1] + corr->coef[0]) : value; - if (t == corr->coef[0]) return 32768; + 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 < corr->coef[0] ? ((corr->coef[2] * t) >> 14) + corr->coef[3] : - ((corr->coef[4] * t) >> 14) + corr->coef[5]; + t = t < 0 ? ((corr->coef[2] * t) >> 14) : ((corr->coef[3] * t) >> 14); break; - default: + default: return 0; } - if (t < 0) return 0; - if (t > 65535) return 65535; + if (t < -32767) return -32767; + if (t > 32767) return 32767; - return t; + return t; } /* - * js_compare() compares two close axis values and decides + * 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); + return (x < y + prec) && (y < x + prec); } /* - * js_probe() probes for joysticks + * js_probe() probes for joysticks */ inline int js_probe(void) @@ -196,10 +190,10 @@ inline int js_probe(void) } else switch (t & JS_AXES) { case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */ - case 0x03: jsd[0].exist = 0x00; jsd[1].exist = 0xcc; break; /* joystick 1 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 */ + default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */ } js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES; @@ -208,7 +202,7 @@ inline int js_probe(void) 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. @@ -226,16 +220,61 @@ static void js_do_timer(unsigned long data) js_mark_time = jiffies; mark_bh(JS_BH); } - } + } else - if ((jiffies > js_axis_time + JS_AXIS_MAX_PERIOD) && !js_mark_time) { + 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_BUTTON_PERIOD; + 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. */ @@ -246,7 +285,7 @@ static void js_do_bh(void) int i, j, k; unsigned int t; - if (jiffies > js_axis_time + JS_AXIS_MIN_PERIOD) { + if (jiffies > js_bh_time + JS_BH_MIN_PERIOD) { unsigned int old_axis[4]; unsigned int t_low, t_high; @@ -274,8 +313,8 @@ static void js_do_bh(void) 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; + t = (t1l = inb(PIT_DATA)) | + (t1h = inb(PIT_DATA)) << 8; restore_flags(flags); do { @@ -286,7 +325,7 @@ static void js_do_bh(void) joy_state = (joy_state << 8) | jss; i++; } - + cli(); outb(PIT_READ_TIMER, PIT_MODE); t1l = inb(PIT_DATA); @@ -299,15 +338,15 @@ static void js_do_bh(void) * Process the gathered axis data in joy_state. */ - joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */ + 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 = js_correct(DELTA_TX(t, t_low, t_high), &js_axis[j].corr); + 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; + js_axis[j].value = jsm; again = 1; } } @@ -316,7 +355,7 @@ static void js_do_bh(void) t_high = t_high >> 8; } - } while (retries++ < JS_RETRIES && again); + } while (retries++ < JS_RETRIES && again); /* * Check if joystick lost. @@ -348,18 +387,13 @@ static void js_do_bh(void) k = 0; for (j = 0; j < 4; j++) if ((1 << j) & jsd[i].exist) { - if (!js_compare(js_axis[j].value, old_axis[j], js_axis[j].corr.prec)) { - jsd[i].buff[jsd[i].ahead].time = js_mark_time; - jsd[i].buff[jsd[i].ahead].type = JS_EVENT_AXIS; - jsd[i].buff[jsd[i].ahead].number = k; - jsd[i].buff[jsd[i].ahead].value = js_axis[j].value; - jsd[i].ahead++; - if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0; - } + 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_axis_time = jiffies; + js_bh_time = jiffies; } js_mark_time = 0; @@ -373,14 +407,8 @@ static void js_do_bh(void) k = 0; for (j = 4; j < 8; j++) if ((1 << j) & jsd[i].exist) { - if ((1 << j) & (js_buttons ^ js_fifo[t].event)) { - jsd[i].buff[jsd[i].ahead].time = js_fifo[t].time; - jsd[i].buff[jsd[i].ahead].type = JS_EVENT_BUTTON; - jsd[i].buff[jsd[i].ahead].number = k; - jsd[i].buff[jsd[i].ahead].value = (js_fifo[t].event >> j) & 1; - jsd[i].ahead++; - if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0; - } + 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++; } } @@ -388,27 +416,10 @@ static void js_do_bh(void) } /* - * Sync ahead with bhead and cut too long tails. + * Synchronize the buffer. */ - - 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_sync_buff(); } @@ -435,7 +446,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) unsigned long blocks = count / sizeof(struct js_event); unsigned long i = 0, j; int t, u = curl->tail; - int retval = 0; + int retval = 0; /* * Check user data. @@ -459,38 +470,46 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) * Handle (non)blocking i/o. */ - if (count != sizeof(struct JS_DATA_TYPE)) { - if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) { - add_wait_queue(&jsd[minor].wait, &wait); - current->state = TASK_INTERRUPTIBLE; - while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_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; - } + 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); } + current->state = TASK_RUNNING; + remove_wait_queue(&jsd[minor].wait, &wait); + } + + if (retval) return retval; - 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++) @@ -498,17 +517,21 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) if (curl->startup & (1 << j)) { tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; tmpevent.number = t; - tmpevent.value = js_axis[j].value; - if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) + 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++; + t++; } +/* + * Initial axis state. + */ + t = 0; for (j = 4; j < 8 && (i < blocks) && !retval; j++) if (jsd[minor].exist & (1 << j)) { @@ -516,17 +539,20 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) 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))) + 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++; + 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))) @@ -536,7 +562,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) curl->tail = t; i++; } - + } else @@ -562,6 +588,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) 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); } @@ -580,7 +607,7 @@ static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) } if (!curl) jsd[minor].tail = t; } - + return retval ? retval : i*sizeof(struct js_event); } @@ -595,19 +622,16 @@ static unsigned int js_poll(struct file *file, poll_table *wait) curl = file->private_data; poll_wait(file, &jsd[minor].wait, wait); - if (GOF(curl->tail) != jsd[minor].ahead) + if (GOF(curl->tail) != jsd[minor].ahead) return POLLIN | POLLRDNORM; - return 0; + return 0; } /* * js_ioctl handles misc ioctl calls. */ -static int js_ioctl(struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) +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; @@ -637,13 +661,13 @@ static int js_ioctl(struct inode *inode, sizeof(struct js_corr))) return -EFAULT; j++; } - js_axis_time = 0; + 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, + if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr, sizeof(struct js_corr))) return -EFAULT; j++; } @@ -651,7 +675,7 @@ static int js_ioctl(struct inode *inode, default: return -EINVAL; } - + return 0; } @@ -672,15 +696,15 @@ static int js_open(struct inode *inode, struct file *file) return -ENODEV; if (!jsd[minor].exist) { js_probe(); - if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n", - minor, count_bits(jsd[minor].exist & JS_AXES), JS_PORT); + 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_BUTTON_PERIOD; + if (!jsd[0].list && !jsd[1].list) { + js_timer.expires = jiffies + JS_TIMER_PERIOD; add_timer(&js_timer); } @@ -760,7 +784,7 @@ __initfunc(void js_setup(char *str, int *ints)) { js[0] = ((ints[0] > 0) ? ints[1] : 0 ); - js[1] = ((ints[0] > 1) ? ints[2] : 0 ); + js[1] = ((ints[0] > 1) ? ints[2] : 0 ); } #endif @@ -770,7 +794,7 @@ __initfunc(void js_setup(char *str, int *ints)) */ #ifdef MODULE -int init_module(void) +int init_module(void) #else __initfunc(int js_init(void)) #endif @@ -793,8 +817,8 @@ __initfunc(int js_init(void)) } for (i = 0; i < JS_NUM; i++) { - if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n", - i, count_bits(jsd[i].exist & JS_AXES), JS_PORT); + 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; @@ -803,7 +827,7 @@ __initfunc(int js_init(void)) } for (i = 0; i < 4; i++) { - js_axis[i].corr.type = JS_CORR_NONE; + js_axis[i].corr.type = JS_CORR_NONE; js_axis[i].corr.prec = JS_DEF_PREC; } @@ -812,7 +836,7 @@ __initfunc(int js_init(void)) enable_bh(JS_BH); init_timer(&js_timer); js_timer.function = js_do_timer; - + return 0; } diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index d2aa0509e..8804df949 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -12,6 +12,7 @@ * Added decr/incr_console, dynamic keymaps, Unicode support, * dynamic function/string keys, led setting, Sept 1994 * `Sticky' modifier keys, 951006. + * * 11-11-96: SAK should now work in the raw mode (Martin Mares) * * Modified to provide 'generic' keyboard support by Hamish Macdonald @@ -19,6 +20,7 @@ * parts by Geert Uytterhoeven, May 1997 * * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) + * 30-07-98: Dead keys redone, aeb@cwi.nl. */ #include <linux/config.h> @@ -58,9 +60,6 @@ #endif extern void ctrl_alt_del(void); -extern void reset_vc(unsigned int new_console); -extern void scrollback(int); -extern void scrollfront(int); struct wait_queue * keypress_wait = NULL; struct console; @@ -104,12 +103,13 @@ typedef void (k_handfn)(unsigned char value, char up_flag); static k_handfn do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, - do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore; + do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_dead2, + do_ignore; static k_hand key_handler[16] = { do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, - do_meta, do_ascii, do_lock, do_lowercase, do_slock, - do_ignore, do_ignore, do_ignore + do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_dead2, + do_ignore, do_ignore }; /* Key types processed even in raw modes */ @@ -138,12 +138,13 @@ const int max_vals[] = { 255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1, NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1, 255, - NR_LOCK - 1 + NR_LOCK - 1, 255 }; const int NR_TYPES = SIZE(max_vals); -static void put_queue(int); +/* N.B. drivers/macintosh/mac_keyb.c needs to call put_queue */ +void put_queue(int); static unsigned char handle_diacr(unsigned char); /* kbd_pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ @@ -199,7 +200,7 @@ void handle_scancode(unsigned char scancode) mark_bh(CONSOLE_BH); add_keyboard_randomness(scancode); - tty = ttytab[fg_console]; + tty = ttytab? ttytab[fg_console]: NULL; kbd = kbd_table + fg_console; if ((raw_mode = (kbd->kbdmode == VC_RAW))) { put_queue(scancode); @@ -315,7 +316,7 @@ void handle_scancode(unsigned char scancode) #ifdef CONFIG_FORWARD_KEYBOARD extern int forward_chars; -static void put_queue(int ch) +void put_queue(int ch) { if (forward_chars == fg_console+1){ kbd_forward_char (ch); @@ -323,17 +324,17 @@ static void put_queue(int ch) wake_up(&keypress_wait); if (tty) { tty_insert_flip_char(tty, ch, 0); - tty_schedule_flip(tty); + con_schedule_flip(tty); } } } #else -static void put_queue(int ch) +void put_queue(int ch) { wake_up(&keypress_wait); if (tty) { tty_insert_flip_char(tty, ch, 0); - tty_schedule_flip(tty); + con_schedule_flip(tty); } } #endif @@ -348,7 +349,7 @@ static void puts_queue(char *cp) tty_insert_flip_char(tty, *cp, 0); cp++; } - tty_schedule_flip(tty); + con_schedule_flip(tty); } static void applkey(int key, char mode) @@ -362,6 +363,10 @@ static void applkey(int key, char mode) static void enter(void) { + if (diacr) { + put_queue(diacr); + diacr = 0; + } put_queue(13); if (vc_kbd_mode(kbd,VC_CRLF)) put_queue(10); @@ -460,7 +465,7 @@ static void send_intr(void) if (!tty) return; tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); + con_schedule_flip(tty); } static void scroll_forw(void) @@ -558,41 +563,49 @@ static void do_self(unsigned char value, char up_flag) static unsigned char ret_diacr[NR_DEAD] = {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; -/* If a dead key pressed twice, output a character corresponding to it, */ -/* otherwise just remember the dead key. */ - +/* Obsolete - for backwards compatibility only */ static void do_dead(unsigned char value, char up_flag) { + value = ret_diacr[value]; + do_dead2(value,up_flag); +} + +/* + * Handle dead key. Note that we now may have several + * dead keys modifying the same character. Very useful + * for Vietnamese. + */ +static void do_dead2(unsigned char value, char up_flag) +{ if (up_flag) return; - value = ret_diacr[value]; - if (diacr == value) { /* pressed twice */ - diacr = 0; - put_queue(value); - return; - } - diacr = value; + diacr = (diacr ? handle_diacr(value) : value); } -/* If space is pressed, return the character corresponding the pending */ -/* dead key, otherwise try to combine the two. */ - +/* + * We have a combining character DIACR here, followed by the character CH. + * If the combination occurs in the table, return the corresponding value. + * Otherwise, if CH is a space or equals DIACR, return DIACR. + * Otherwise, conclude that DIACR was not combining after all, + * queue it and return CH. + */ unsigned char handle_diacr(unsigned char ch) { int d = diacr; int i; diacr = 0; - if (ch == ' ') - return d; for (i = 0; i < accent_table_size; i++) { if (accent_table[i].diacr == d && accent_table[i].base == ch) return accent_table[i].result; } + if (ch == ' ' || ch == d) + return d; + put_queue(d); return ch; } diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 7df3eb655..1036f1046 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -16,6 +16,8 @@ * Parport sharing hacking by Andrea Arcangeli <arcangeli@mbox.queen.it> * 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 */ /* This driver should, in theory, work with any parallel port that has an @@ -62,6 +64,23 @@ * can force it using the parameters described above. */ +/* + * The new interrupt handling code take care of the buggy handshake + * of some HP and Epson printer: + * ___ + * ACK _______________ ___________ + * |__| + * ____ + * BUSY _________ _______ + * |____________| + * + * I discovered this using the printer scanner that you can find at: + * + * ftp://e-mind.com/pub/linux/pscan/ + * + * 11 May 98, Andrea Arcangeli + */ + #include <linux/module.h> #include <linux/init.h> @@ -93,7 +112,7 @@ struct lp_struct lp_table[LP_NO] = #ifdef LP_STATS 0, 0, {0}, #endif - NULL, 0} + NULL, 0, 0, 0} }; /* Test if printer is ready (and optionally has no error conditions) */ @@ -135,8 +154,12 @@ static int lp_preempt(void *handle) static __inline__ void lp_yield (int minor) { - if (!parport_yield_blocking (lp_table[minor].dev) && need_resched) - schedule (); + if (!parport_yield_blocking (lp_table[minor].dev)) + { + if (current->need_resched) + schedule (); + } else + lp_table[minor].irq_missed = 1; } static __inline__ void lp_schedule(int minor) @@ -145,6 +168,7 @@ static __inline__ void lp_schedule(int minor) 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 (); lp_parport_claim(minor); } else @@ -165,7 +189,6 @@ static int lp_reset(int minor) static inline int lp_char(char lpchar, int minor) { - unsigned char status; unsigned int wait = 0; unsigned long count = 0; #ifdef LP_STATS @@ -175,12 +198,10 @@ static inline int lp_char(char lpchar, int minor) for (;;) { lp_yield(minor); - status = r_str (minor); - if (LP_READY(minor, status)) + if (LP_READY(minor, r_str(minor))) break; - if (!LP_POLLED(minor) || ++count == LP_CHAR(minor) || - signal_pending(current)) - return 0; + if (++count == LP_CHAR(minor) || signal_pending(current)) + return 0; } w_dtr(minor, lpchar); @@ -205,7 +226,15 @@ static inline int lp_char(char lpchar, int minor) udelay(1); #endif /* take strobe low */ - w_ctr(minor, LP_PSELECP | LP_PINITP); + if (LP_POLLED(minor)) + /* take strobe low */ + w_ctr(minor, LP_PSELECP | LP_PINITP); + else + { + lp_table[minor].irq_detected = 0; + lp_table[minor].irq_missed = 0; + w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN); + } #ifdef LP_STATS /* update waittime statistics */ @@ -231,6 +260,9 @@ static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (waitqueue_active (&lp_dev->wait_q)) wake_up_interruptible(&lp_dev->wait_q); + + lp_dev->irq_detected = 1; + lp_dev->irq_missed = 0; } static void lp_error(int minor) @@ -241,10 +273,12 @@ static void lp_error(int minor) lp_parport_release(minor); schedule(); lp_parport_claim(minor); + lp_table[minor].irq_missed = 1; } } -static int lp_check_status(int minor) { +static int lp_check_status(int minor) +{ unsigned int last = lp_table[minor].last_error; unsigned char status = r_str(minor); if ((status & LP_POUTPA)) { @@ -282,7 +316,6 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) unsigned long total_bytes_written = 0; unsigned long bytes_written; struct lp_struct *lp = &lp_table[minor]; - unsigned char status; if (minor >= LP_NO) return -ENXIO; @@ -290,13 +323,20 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) return -ENXIO; lp_table[minor].last_error = 0; + lp_table[minor].irq_detected = 0; + lp_table[minor].irq_missed = 1; + + w_ctr(minor, LP_PSELECP | LP_PINITP); do { bytes_written = 0; copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); if (copy_from_user(lp->lp_buffer, buf, copy_size)) + { + w_ctr(minor, LP_PSELECP | LP_PINITP); return -EFAULT; + } while (copy_size) { if (lp_char(lp->lp_buffer[bytes_written], minor)) { @@ -314,7 +354,9 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) LP_STAT(minor).sleeps++; #endif - if (signal_pending(current)) { + if (signal_pending(current)) + { + w_ctr(minor, LP_PSELECP | LP_PINITP); if (total_bytes_written + bytes_written) return total_bytes_written + bytes_written; else @@ -325,9 +367,15 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) lp->runchars = 0; #endif - if (LP_POLLED(minor)) { - if (lp_check_status(minor)) - return rc ? rc : -EIO; + if (lp_check_status(minor)) + { + w_ctr(minor, LP_PSELECP | LP_PINITP); + return rc ? rc : -EIO; + } + + if (LP_POLLED(minor) || + lp_table[minor].irq_missed) + { lp_polling: #if defined(LP_DEBUG) && defined(LP_STATS) printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n", minor, lp->runchars, LP_TIME(minor)); @@ -342,28 +390,19 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) /* * We can' t sleep on the interrupt * since another pardevice need the port. + * We must check this in a cli() protected + * envinroment to avoid parport sharing + * starvation. */ sti(); goto lp_polling; } - w_ctr(minor, LP_PSELECP | LP_PINITP | LP_PINTEN); - status = r_str(minor); - if (!(status & LP_PACK) || (status & LP_PBUSY)) + if (!lp_table[minor].irq_detected) { - /* - * The interrupt is happened in the - * meantime so don' t wait for it. - */ - w_ctr(minor, LP_PSELECP | LP_PINITP); - sti(); - continue; + current->timeout = jiffies + LP_TIMEOUT_INTERRUPT; + interruptible_sleep_on(&lp->wait_q); } - current->timeout = jiffies + LP_TIMEOUT_INTERRUPT; - interruptible_sleep_on(&lp->wait_q); - w_ctr(minor, LP_PSELECP | LP_PINITP); sti(); - if (lp_check_status(minor)) - return rc ? rc : -EIO; } } } @@ -374,6 +413,7 @@ static int lp_write_buf(unsigned int minor, const char *buf, int count) } while (count > 0); + w_ctr(minor, LP_PSELECP | LP_PINITP); return total_bytes_written; } @@ -459,7 +499,7 @@ static ssize_t lp_read(struct file * file, char * buf, status = (r_str(minor) & 0x40); udelay(50); counter++; - if (need_resched) + if (current->need_resched) schedule (); } while ((status == 0x40) && (counter < 20)); if (counter == 20) { @@ -479,7 +519,7 @@ static ssize_t lp_read(struct file * file, char * buf, status=(r_str(minor) & 0x40); udelay(20); counter++; - if (need_resched) + if (current->need_resched) schedule (); } while ( (status == 0) && (counter < 20) ); if (counter == 20) { /* Timeout */ @@ -524,7 +564,7 @@ static int lp_open(struct inode * inode, struct file * file) return -ENXIO; if ((LP_F(minor) & LP_EXIST) == 0) return -ENXIO; - if (test_and_set_bit(LP_BUSY_BIT_POS, &LP_F(minor)) & LP_BUSY) + if (test_and_set_bit(LP_BUSY_BIT_POS, &LP_F(minor))) return -EBUSY; MOD_INC_USE_COUNT; diff --git a/drivers/char/lp_intern.c b/drivers/char/lp_intern.c index 7bb4ec634..45f2a289b 100644 --- a/drivers/char/lp_intern.c +++ b/drivers/char/lp_intern.c @@ -32,9 +32,16 @@ #include <asm/atarihw.h> #include <asm/atariints.h> #endif +#ifdef CONFIG_MVME16x +#include <asm/mvme16xhw.h> +#endif +#ifdef CONFIG_BVME6000 +#include<asm/bvme6000hw.h> +#endif #include <linux/lp_intern.h> static int minor = -1; +MODULE_PARM(minor,"i"); static void lp_int_out(int, int); static int lp_int_busy(int); @@ -70,6 +77,25 @@ lp_int_out (int c, int dev) break; } #endif +#ifdef CONFIG_MVME16x + case MACH_MVME16x: + { + int wait = 0; + while (wait != lp_table[dev]->wait) wait++; + mvmelp.data = c; + break; + } +#endif +#ifdef CONFIG_BVME6000 + case MACH_BVME6000: + { + int wait = 0; + while (wait != lp_table[dev]->wait) wait++; + bvmepit.padr = c; + bvmepit.pacr |= 0x02; + break; + } +#endif } } @@ -86,6 +112,14 @@ lp_int_busy (int dev) case MACH_ATARI: return mfp.par_dt_reg & 1; #endif +#ifdef CONFIG_MVME16x + case MACH_MVME16x: + return mvmelp.isr & 1; +#endif +#ifdef CONFIG_BVME6000 + case MACH_BVME6000: + return 0 /* !(bvmepit.psr & 0x40) */ ; +#endif default: return 0; } @@ -102,6 +136,15 @@ lp_int_pout (int dev) #endif #ifdef CONFIG_ATARI case MACH_ATARI: + return 0; +#endif +#ifdef CONFIG_MVME16x + case MACH_MVME16x: + return mvmelp.isr & 2; +#endif +#ifdef CONFIG_BVME6000 + case MACH_BVME6000: + return 0; #endif default: return 0; @@ -121,6 +164,14 @@ lp_int_online (int dev) case MACH_ATARI: return !(mfp.par_dt_reg & 1); #endif +#ifdef CONFIG_MVME16x + case MACH_MVME16x: + return mvmelp.isr & 4; +#endif +#ifdef CONFIG_BVME6000 + case MACH_BVME6000: + return 1; +#endif default: return 0; } @@ -128,6 +179,14 @@ lp_int_online (int dev) static void lp_int_interrupt(int irq, void *data, struct pt_regs *fp) { +#ifdef CONFIG_MVME16x + if (MACH_IS_MVME16x) + mvmelp.ack_icr |= 0x08; +#endif +#ifdef CONFIG_BVME6000 + if (MACH_IS_BVME6000) + bvmepit.pacr &= ~0x02; +#endif lp_interrupt(minor); } @@ -192,6 +251,62 @@ __initfunc(int lp_internal_init(void)) tab.type = LP_ATARI; } #endif +#ifdef CONFIG_MAC + if (MACH_IS_MAC) + return -ENODEV; +#endif +#ifdef CONFIG_MVME16x + if (MACH_IS_MVME16x) + { + unsigned long flags; + + if (!(mvme16x_config & MVME16x_CONFIG_GOT_LP)) + return -ENODEV; + + save_flags(flags); + cli(); + mvmelp.ack_icr = 0x08; + mvmelp.flt_icr = 0x08; + mvmelp.sel_icr = 0x08; + mvmelp.pe_icr = 0x08; + mvmelp.bsy_icr = 0x08; + mvmelp.cr = 0x10; + mvmelp.ack_icr = 0xd9; /* Int on trailing edge of ACK */ + restore_flags(flags); + + if (lp_irq) + tab.irq = request_irq(MVME167_IRQ_PRN, lp_int_interrupt, + 0, "builtin printer port", lp_int_interrupt); + tab.base = (void *)&mvmelp; /* dummy, not used */ + tab.type = LP_MVME167; + } +#endif +#ifdef CONFIG_BVME6000 + if (MACH_IS_BVME6000) + { + unsigned long flags; + + save_flags(flags); + cli(); + bvmepit.pgcr = 0x0f; + bvmepit.psrr = 0x18; + bvmepit.paddr = 0xff; + bvmepit.pcdr = (bvmepit.pcdr & 0xfc) | 0x02; + bvmepit.pcddr |= 0x03; + bvmepit.pacr = 0x78; + bvmepit.pbcr = 0x00; + bvmepit.pivr = BVME_IRQ_PRN; + bvmepit.pgcr = 0x1f; + restore_flags(flags); + + if (lp_irq) + tab.irq = request_irq(BVME_IRQ_PRN, lp_int_interrupt, + 0, "builtin printer port", lp_int_interrupt); + tab.base = (void *)&bvmepit; /* dummy, not used */ + tab.type = LP_BVME6000; + } +#endif + if ((minor = register_parallel(&tab, minor)) < 0) { printk("builtin lp init: cant get a minor\n"); @@ -204,6 +319,14 @@ __initfunc(int lp_internal_init(void)) if (MACH_IS_ATARI) free_irq(IRQ_MFP_BUSY, lp_int_interrupt); #endif +#ifdef CONFIG_MVME16x + if (MACH_IS_MVME16x) + free_irq(MVME167_IRQ_PRN, lp_int_interrupt); +#endif +#ifdef CONFIG_BVME6000 + if (MACH_IS_BVME6000) + free_irq(BVME_IRQ_PRN, lp_int_interrupt); +#endif } return -ENODEV; } @@ -228,6 +351,14 @@ if (lp_irq) { if (MACH_IS_ATARI) free_irq(IRQ_MFP_BUSY, lp_int_interrupt); #endif +#ifdef CONFIG_MVME16x + if (MACH_IS_MVME16x) + free_irq(MVME167_IRQ_PRN, lp_int_interrupt); +#endif +#ifdef CONFIG_BVME6000 + if (MACH_IS_BVME6000) + free_irq(BVME_IRQ_PRN, lp_int_interrupt); +#endif } unregister_parallel(minor); } diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c index 4a000e9aa..5079b2e62 100644 --- a/drivers/char/lp_m68k.c +++ b/drivers/char/lp_m68k.c @@ -102,7 +102,7 @@ static int lp_char_polled(char lpchar, int dev) do { count ++; - if(need_resched) + if (current->need_resched) schedule(); } while (lp_table[dev]->lp_is_busy(dev) && count < lp_table[dev]->chars); @@ -173,13 +173,14 @@ void lp_interrupt(int dev) } #if WHICH_DRIVER == FORCE_INTERRUPT -static long lp_write(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t lp_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) #else -static long lp_write_interrupt(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t lp_write_interrupt(struct file *file, const char *buf, + size_t count, loff_t *ppos) #endif { + struct inode *inode = file->f_dentry->d_inode; unsigned long total_bytes_written = 0; unsigned int flags; int rc; @@ -190,7 +191,9 @@ static long lp_write_interrupt(struct inode *inode, struct file *file, lp_table[dev]->bytes_written = 0; /* init buffer read-pointer */ lp_error = 0; lp_table[dev]->copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); - copy_from_user(lp_table[dev]->lp_buffer, buf, lp_table[dev]->copy_size); + if (copy_from_user(lp_table[dev]->lp_buffer, buf, + lp_table[dev]->copy_size)) + return -EFAULT; while (lp_table[dev]->copy_size) { save_flags(flags); cli(); /* no interrupts now */ @@ -223,10 +226,9 @@ static long lp_write_interrupt(struct inode *inode, struct file *file, rc = total_bytes_written + lp_table[dev]->bytes_written; if (signal_pending(current)) { - if (rc) - return rc; - else - return -EINTR; + if (rc == 0) + rc = -EINTR; + return rc; } if (lp_error) { @@ -267,13 +269,14 @@ void (*lp_interrupt)() = NULL; #if WHICH_DRIVER != FORCE_INTERRUPT #if WHICH_DRIVER == FORCE_POLLING -static long lp_write(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t lp_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) #else -static long lp_write_polled(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t lp_write_polled(struct file *file, const char *buf, + size_t count, loff_t *ppos) #endif { + struct inode *inode = file->f_dentry->d_inode; char *temp = buf; int dev = MINOR(inode->i_rdev); @@ -287,52 +290,53 @@ static long lp_write_polled(struct inode *inode, struct file *file, temp = buf; while (count > 0) { - if (lp_char_polled(get_user(temp), dev)) { + int c; + if (get_user(c, temp)) + return -EFAULT; + if (lp_char_polled(c, dev)) { /* only update counting vars if character was printed */ count--; temp++; #ifdef LP_DEBUG lp_total_chars++; #endif } else { /* if printer timed out */ + unsigned long timeout = LP_TIMEOUT_POLLED; + int error = 0; if (lp_table[dev]->lp_has_pout(dev)) { printk(KERN_NOTICE "lp%d: out of paper\n",dev); if (lp_table[dev]->flags & LP_ABORT) - return temp - buf ? temp-buf : -ENOSPC; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + LP_TIMEOUT_POLLED; - schedule(); + error = -ENOSPC; } else if (!lp_table[dev]->lp_is_online(dev)) { printk(KERN_NOTICE "lp%d: off-line\n",dev); if (lp_table[dev]->flags & LP_ABORT) - return temp - buf ? temp-buf : -EIO; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + LP_TIMEOUT_POLLED; - schedule(); + error = -EIO; } else /* not offline or out of paper. on fire? */ if (lp_table[dev]->lp_is_busy(dev)) { printk(KERN_NOTICE "lp%d: on fire\n",dev); if (lp_table[dev]->flags & LP_ABORT) - return temp - buf ? temp-buf : -EFAULT; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + LP_TIMEOUT_POLLED; - schedule(); + error = -EIO; } + else + timeout = lp_table[dev]->time; /* check for signals before going to sleep */ - if (signal_pending(current)) { + if (error == 0 && signal_pending(current)) + error = -EINTR; + if (error) { if (temp != buf) return temp-buf; else - return -EINTR; + return error; } + #ifdef LP_DEBUG printk("lp sleeping at %d characters for %d jiffies\n", - lp_total_chars, lp_table[dev]->time); + lp_total_chars, timeout); lp_total_chars = 0; #endif current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + lp_table[dev]->time; + current->timeout = jiffies + timeout; schedule(); } } @@ -343,13 +347,13 @@ static long lp_write_polled(struct inode *inode, struct file *file, unsigned int lp_irq = 0; #if WHICH_DRIVER == PREFER_INTERRUPT -static long lp_write(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t lp_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) { if (lp_irq) - return lp_write_interrupt(inode, file, buf, count); + return lp_write_interrupt(file, buf, count, ppos); else - return lp_write_polled(inode, file, buf, count); + return lp_write_polled(file, buf, count, ppos); } #endif @@ -363,8 +367,12 @@ static int lp_open(struct inode *inode, struct file *file) int dev = MINOR(inode->i_rdev); int ret; + MOD_INC_USE_COUNT; + + ret = -ENODEV; if (dev >= MAX_LP) - return -ENODEV; + goto out_err; + #ifdef CONFIG_KMOD if (!lp_table[dev]) { char modname[30]; @@ -374,22 +382,25 @@ static int lp_open(struct inode *inode, struct file *file) } #endif if (!lp_table[dev]) - return -ENODEV; + goto out_err; if (!(lp_table[dev]->flags & LP_EXIST)) - return -ENODEV; + goto out_err; + ret = -EBUSY; if (lp_table[dev]->flags & LP_BUSY) - return -EBUSY; + goto out_err; lp_table[dev]->flags |= LP_BUSY; ret = lp_table[dev]->lp_open(dev); if (ret != 0) { lp_table[dev]->flags &= ~LP_BUSY; - } - else { - MOD_INC_USE_COUNT; + goto out_err; } return ret; + +out_err: + MOD_DEC_USE_COUNT; + return ret; } static int lp_release(struct inode *inode, struct file *file) @@ -407,15 +418,16 @@ static int lp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = MINOR(inode->i_rdev); - int retval = 0; + int retval = -ENODEV; #ifdef LP_DEBUG printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg); #endif if (minor >= max_lp) - return -ENODEV; + goto out; if (!(lp_table[minor]->flags & LP_EXIST)) - return -ENODEV; + goto out; + retval = 0; switch (cmd) { case LPTIME: lp_table[minor]->time = arg; @@ -437,11 +449,11 @@ static int lp_ioctl(struct inode *inode, struct file *file, retval = lp_irq; break; default: + retval = -EINVAL; if (lp_table[minor]->lp_ioctl) retval = lp_table[minor]->lp_ioctl(minor, cmd, arg); - else - retval = -EINVAL; } +out: return retval; } diff --git a/drivers/char/mac_SCC.c b/drivers/char/mac_SCC.c new file mode 100644 index 000000000..0295ab4b4 --- /dev/null +++ b/drivers/char/mac_SCC.c @@ -0,0 +1,1543 @@ +/* + * mac_SCC.c: m68k version of + * + * macserial.c: Serial port driver for Power Macintoshes. + * Extended for the 68K mac by Alan Cox. + * Rewritten to m68k serial design by Michael Schmitz + * + * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. + * + * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ + +/* + * Design note for the m68k rewrite: + * The structure of the m68k serial code requires separation of the low-level + * functions that talk directly to the hardware from the Linux serial driver + * code interfacing to the tty layer. The reason for this separation is simply + * the fact that the m68k serial hardware is, unlike the i386, based on a + * variety of chips, and the rs_* serial routines need to be shared. + * + * I've tried to make consistent use of the async_struct info populated in the + * midlevel code, and introduced an async_private struct to hold the Macintosh + * SCC internals (this was added to the async_struct for the PowerMac driver). + * Exception: the console and kgdb hooks still use the zs_soft[] data, and this + * is still filled in by the probe_sccs() routine, which provides some data + * for mac_SCC_init as well. Interrupts are registered in mac_SCC_init, so + * the console/kgdb stuff probably won't work before proper serial init, and + * I have to rely on keeping info and zs_soft consistent at least for the + * console/kgdb port. + * + * Update (16-11-97): The SCC interrupt handling was suffering from the problem + * that the autovector SCC interrupt was registered only once, hence only one + * async_struct was passed to the interrupt function and only interrupts from + * the corresponding channel could be handled (yes, major design flaw). + * The autovector interrupt is now registered by the main interrupt initfunc, + * and uses a handler that will call the registered SCC specific interrupts in + * turn. The SCC init has to register these as machspec interrupts now, as is + * done for the VIA interrupts elsewhere. + */ + +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/config.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> +#include <asm/setup.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/macints.h> +#ifndef CONFIG_MAC +#include <asm/prom.h> +#endif +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/bitops.h> + +#include "mac_SCC.h" + +/* + * It would be nice to dynamically allocate everything that + * depends on NUM_SERIAL, so we could support any number of + * Z8530s, but for now... + */ +#define NUM_SERIAL 2 /* Max number of ZS chips supported */ +#define NUM_CHANNELS (NUM_SERIAL * 2) /* 2 channels per chip */ + +#ifdef CONFIG_MAC +/* + * All the Macintosh 68K boxes that have an MMU also have hardware + * recovery delays. + */ +#define RECOVERY_DELAY +#else +/* On PowerMacs, the hardware takes care of the SCC recovery time, + but we need the eieio to make sure that the accesses occur + in the order we want. */ +#define RECOVERY_DELAY eieio() +#endif + +struct mac_zschannel *zs_kgdbchan; +struct mac_zschannel zs_channels[NUM_CHANNELS]; + +struct m68k_async_struct zs_soft[NUM_CHANNELS]; +struct m68k_async_private zs_soft_private[NUM_CHANNELS]; +int zs_channels_found; +struct m68k_async_struct *zs_chain; /* list of all channels */ + +struct tty_struct zs_ttys[NUM_CHANNELS]; +/** struct tty_struct *zs_constty; **/ + +/* Console hooks... */ +static int zs_cons_chanout = 0; +static int zs_cons_chanin = 0; +struct m68k_async_struct *zs_consinfo = 0; +struct mac_zschannel *zs_conschan; + +static unsigned char kgdb_regs[16] = { + 0, 0, 0, /* write 0, 1, 2 */ + (Rx8 | RxENABLE), /* write 3 */ + (X16CLK | SB1 | PAR_EVEN), /* write 4 */ + (Tx8 | TxENAB), /* write 5 */ + 0, 0, 0, /* write 6, 7, 8 */ + (NV), /* write 9 */ + (NRZ), /* write 10 */ + (TCBR | RCBR), /* write 11 */ + 1, 0, /* 38400 baud divisor, write 12 + 13 */ + (BRENABL), /* write 14 */ + (DCDIE) /* write 15 */ +}; + +#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ + +/* Debugging... DEBUG_INTR is bad to use when one of the zs + * lines is your console ;( + */ +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW + +#define RS_STROBE_TIME 10 +#define RS_ISR_PASS_LIMIT 256 + +#define _INLINE_ inline + +static void probe_sccs(void); + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +/***************************** Prototypes *****************************/ + +static void SCC_init_port( struct m68k_async_struct *info, int type, int channel ); +#if 0 +#ifdef MODULE +static void SCC_deinit_port( struct m68k_async_struct *info, int channel ); +#endif +#endif + +/* FIXME !!! Currently, only autovector interrupt used! */ +#if 0 +static void SCC_rx_int (int irq, void *data, struct pt_regs *fp); +static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp); +static void SCC_tx_int (int irq, void *data, struct pt_regs *fp); +static void SCC_stat_int (int irq, void *data, struct pt_regs *fp); +static void SCC_ri_int (int irq, void *data, struct pt_regs *fp); +#endif + +static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct + *tty, struct file *file ); +static void SCC_init( struct m68k_async_struct *info ); +static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr ); +static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag ); +static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base, + int divisor ); +static void SCC_change_speed( struct m68k_async_struct *info ); +#if 0 +static int SCC_clocksrc( unsigned baud_base, unsigned channel ); +#endif +static void SCC_throttle( struct m68k_async_struct *info, int status ); +static void SCC_set_break( struct m68k_async_struct *info, int break_flag ); +static void SCC_get_serial_info( struct m68k_async_struct *info, struct + serial_struct *retinfo ); +static unsigned int SCC_get_modem_info( struct m68k_async_struct *info ); +static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int + new_rts ); +static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct + m68k_async_struct *info, unsigned int cmd, unsigned long arg ); +static void SCC_stop_receive (struct m68k_async_struct *info); +static int SCC_trans_empty (struct m68k_async_struct *info); + +/************************* End of Prototypes **************************/ + + +static SERIALSWITCH SCC_switch = { + SCC_init, SCC_deinit, SCC_enab_tx_int, + SCC_check_custom_divisor, SCC_change_speed, + SCC_throttle, SCC_set_break, + SCC_get_serial_info, SCC_get_modem_info, + SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty, + SCC_check_open +}; + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the memcpy_fromfs 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[4096]; /* This is cheating */ +static struct semaphore tmp_buf_sem = MUTEX; + +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0 }; + +/* + * Reading and writing Z8530 registers. + */ +static inline unsigned char read_zsreg(struct mac_zschannel *channel, + unsigned char reg) +{ + unsigned char retval; + + if (reg != 0) { + *channel->control = reg; + RECOVERY_DELAY; + } + retval = *channel->control; + RECOVERY_DELAY; + return retval; +} + +static inline void write_zsreg(struct mac_zschannel *channel, + unsigned char reg, unsigned char value) +{ + if (reg != 0) { + *channel->control = reg; + RECOVERY_DELAY; + } + *channel->control = value; + RECOVERY_DELAY; + return; +} + +static inline unsigned char read_zsdata(struct mac_zschannel *channel) +{ + unsigned char retval; + + retval = *channel->data; + RECOVERY_DELAY; + return retval; +} + +static inline void write_zsdata(struct mac_zschannel *channel, + unsigned char value) +{ + *channel->data = value; + RECOVERY_DELAY; + return; +} + +static inline void load_zsregs(struct mac_zschannel *channel, + unsigned char *regs) +{ + ZS_CLEARERR(channel); + ZS_CLEARFIFO(channel); + /* Load 'em up */ + write_zsreg(channel, R4, regs[R4]); + write_zsreg(channel, R10, regs[R10]); + write_zsreg(channel, R3, regs[R3] & ~RxENABLE); + write_zsreg(channel, R5, regs[R5] & ~TxENAB); + write_zsreg(channel, R1, regs[R1]); + write_zsreg(channel, R9, regs[R9]); + write_zsreg(channel, R11, regs[R11]); + write_zsreg(channel, R12, regs[R12]); + write_zsreg(channel, R13, regs[R13]); + write_zsreg(channel, R14, regs[R14]); + write_zsreg(channel, R15, regs[R15]); + write_zsreg(channel, R3, regs[R3]); + write_zsreg(channel, R5, regs[R5]); + return; +} + +/* Sets or clears DTR/RTS on the requested line */ +static inline void zs_rtsdtr(struct m68k_async_struct *ss, int set) +{ + if (set) + ss->private->curregs[5] |= (RTS | DTR); + else + ss->private->curregs[5] &= ~(RTS | DTR); + write_zsreg(ss->private->zs_channel, 5, ss->private->curregs[5]); + return; +} + +static inline void kgdb_chaninit(struct m68k_async_struct *ss, int intson, int bps) +{ + int brg; + + if (intson) { + kgdb_regs[R1] = INT_ALL_Rx; + kgdb_regs[R9] |= MIE; + } else { + kgdb_regs[R1] = 0; + kgdb_regs[R9] &= ~MIE; + } + brg = BPS_TO_BRG(bps, ZS_CLOCK/16); + kgdb_regs[R12] = brg; + kgdb_regs[R13] = brg >> 8; + load_zsregs(ss->private->zs_channel, kgdb_regs); +} + +/* Utility routines for the Zilog */ +static inline int get_zsbaud(struct m68k_async_struct *ss) +{ + struct mac_zschannel *channel = ss->private->zs_channel; + int brg; + + /* The baud rate is split up between two 8-bit registers in + * what is termed 'BRG time constant' format in my docs for + * the chip, it is a function of the clk rate the chip is + * receiving which happens to be constant. + */ + brg = (read_zsreg(channel, 13) << 8); + brg |= read_zsreg(channel, 12); + return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->private->clk_divisor))); +} + +/* On receive, this clears errors and the receiver interrupts */ +static inline void SCC_recv_clear(struct mac_zschannel *zsc) +{ + write_zsreg(zsc, 0, ERR_RES); + write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */ +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +extern void breakpoint(void); /* For the KGDB frame character */ + +static /*_INLINE_*/ void receive_chars(struct m68k_async_struct *info, + struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch, stat, flag; + + while ((read_zsreg(info->private->zs_channel, 0) & Rx_CH_AV) != 0) { + + stat = read_zsreg(info->private->zs_channel, R1); + ch = read_zsdata(info->private->zs_channel); + +#ifdef SCC_DEBUG + printk("mac_SCC: receive_chars stat=%X char=%X \n", stat, ch); +#endif + +#if 0 /* KGDB not yet supported */ + /* Look for kgdb 'stop' character, consult the gdb documentation + * for remote target debugging and arch/sparc/kernel/sparc-stub.c + * to see how all this works. + */ + if ((info->kgdb_channel) && (ch =='\003')) { + breakpoint(); + continue; + } +#endif + + if (!tty) + continue; + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + tty_flip_buffer_push(tty); + + if (stat & Rx_OVR) { + flag = TTY_OVERRUN; + /* reset the error indication */ + write_zsreg(info->private->zs_channel, 0, ERR_RES); + } else if (stat & FRM_ERR) { + /* this error is not sticky */ + flag = TTY_FRAME; + } else if (stat & PAR_ERR) { + flag = TTY_PARITY; + /* reset the error indication */ + write_zsreg(info->private->zs_channel, 0, ERR_RES); + } else + flag = 0; + + if (tty->flip.buf_num + && tty->flip.count >= TTY_FLIPBUF_SIZE) { +#ifdef SCC_DEBUG_OVERRUN + printk("mac_SCC: flip buffer overrun!\n"); +#endif + return; + } + + if (!tty->flip.buf_num + && tty->flip.count >= 2*TTY_FLIPBUF_SIZE) { + printk("mac_SCC: double flip buffer overrun!\n"); + return; + } + + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = flag; + *tty->flip.char_buf_ptr++ = ch; + info->icount.rx++; + tty_flip_buffer_push(tty); + } +#if 0 +clear_and_exit: + SCC_recv_clear(info->private->zs_channel); +#endif +} + +/* that's SCC_enable_tx_int, basically */ + +static void transmit_chars(struct m68k_async_struct *info) +{ + if ((read_zsreg(info->private->zs_channel, 0) & Tx_BUF_EMP) == 0) + return; + info->private->tx_active = 0; + + if (info->x_char) { + /* Send next char */ + write_zsdata(info->private->zs_channel, info->x_char); + info->x_char = 0; + info->private->tx_active = 1; + return; + } + + if ((info->xmit_cnt <= 0) || info->tty->stopped + || info->private->tx_stopped) { + write_zsreg(info->private->zs_channel, 0, RES_Tx_P); + return; + } + + /* Send char */ + write_zsdata(info->private->zs_channel, info->xmit_buf[info->xmit_tail++]); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->icount.tx++; + info->xmit_cnt--; + info->private->tx_active = 1; + + if (info->xmit_cnt < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); +} + +static /*_INLINE_*/ void status_handle(struct m68k_async_struct *info) +{ + unsigned char status; + + /* Get status from Read Register 0 */ + status = read_zsreg(info->private->zs_channel, 0); + + /* Check for DCD transitions */ + if (((status ^ info->private->read_reg_zero) & DCD) != 0 + && info->tty && C_CLOCAL(info->tty)) { + if (status & DCD) { + wake_up_interruptible(&info->open_wait); + } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) { + if (info->tty) + tty_hangup(info->tty); + } + } + + /* Check for CTS transitions */ + if (info->tty && C_CRTSCTS(info->tty)) { + /* + * For some reason, on the Power Macintosh, + * it seems that the CTS bit is 1 when CTS is + * *negated* and 0 when it is asserted. + * The DCD bit doesn't seem to be inverted + * like this. + */ + if ((status & CTS) == 0) { + if (info->private->tx_stopped) { + info->private->tx_stopped = 0; + if (!info->private->tx_active) + transmit_chars(info); + } + } else { + info->private->tx_stopped = 1; + } + } + + /* Clear status condition... */ + write_zsreg(info->private->zs_channel, 0, RES_EXT_INT); + info->private->read_reg_zero = status; +} + +/* + * This is the serial driver's generic interrupt routine + */ +void mac_SCC_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *) dev_id; + unsigned char zs_intreg; + int shift; + + /* NOTE: The read register 3, which holds the irq status, + * does so for both channels on each chip. Although + * the status value itself must be read from the A + * channel and is only valid when read from channel A. + * Yes... broken hardware... + */ +#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT) + +#ifdef SCC_DEBUG + printk("mac_SCC: interrupt; port: %lx channel: %lx \n", + info->port, info->private->zs_channel); +#endif + + if (info->private->zs_chan_a == info->private->zs_channel) + shift = 3; /* Channel A */ + else + shift = 0; /* Channel B */ + + for (;;) { + zs_intreg = read_zsreg(info->private->zs_chan_a, 3); +#ifdef SCC_DEBUG + printk("mac_SCC: status %x shift %d shifted %x \n", + zs_intreg, shift, zs_intreg >> shift); +#endif + zs_intreg = zs_intreg >> shift; + if ((zs_intreg & CHAN_IRQMASK) == 0) + break; + + if (zs_intreg & CHBRxIP) + receive_chars(info, regs); + if (zs_intreg & CHBTxIP) + transmit_chars(info); + if (zs_intreg & CHBEXT) + status_handle(info); + } +} + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * ------------------------------------------------------------ + */ + +static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag ) +{ + unsigned long flags; + + if (enab_flag) { +#if 0 + save_flags(flags); cli(); + if (info->private->curregs[5] & TxENAB) { + info->private->curregs[5] &= ~TxENAB; + info->private->pendregs[5] &= ~TxENAB; + write_zsreg(info->private->zs_channel, 5, + info->private->curregs[5]); + } + restore_flags(flags); +#endif + /* FIXME: should call transmit_chars here ??? */ + transmit_chars(info); + } else { + save_flags(flags); cli(); +#if 0 + if ( info->xmit_cnt && info->xmit_buf && + !(info->private->curregs[5] & TxENAB)) { + info->private->curregs[5] |= TxENAB; + info->private->pendregs[5] = info->private->curregs[5]; + write_zsreg(info->private->zs_channel, 5, + info->private->curregs[5]); + } +#else + if ( info->xmit_cnt && info->xmit_buf && + !info->private->tx_active) { + transmit_chars(info); + } +#endif + restore_flags(flags); + } + +} + +#if 0 +/* + * leftover from original driver ... + */ +static int SCC_startup(struct m68k_async_struct * info) +{ + unsigned long flags; + + save_flags(flags); cli(); + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d (irq %d)...", info->line, info->irq); +#endif + + /* + * Clear the receive FIFO. + */ + ZS_CLEARFIFO(info->private->zs_channel); + info->xmit_fifo_size = 1; + + /* + * Clear the interrupt registers. + */ + write_zsreg(info->private->zs_channel, 0, ERR_RES); + write_zsreg(info->private->zs_channel, 0, RES_H_IUS); + + /* + * Turn on RTS and DTR. + */ + zs_rtsdtr(info, 1); + + /* + * Finally, enable sequencing and interrupts + */ + info->private->curregs[1] = (info->private->curregs[1] & ~0x18) + | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + info->private->pendregs[1] = info->private->curregs[1]; + info->private->curregs[3] |= (RxENABLE | Rx8); + info->private->pendregs[3] = info->private->curregs[3]; + info->private->curregs[5] |= (TxENAB | Tx8); + info->private->pendregs[5] = info->private->curregs[5]; + info->private->curregs[9] |= (NV | MIE); + info->private->pendregs[9] = info->private->curregs[9]; + write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]); + write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]); + write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]); + + /* + * Set the speed of the serial port + */ + SCC_change_speed(info); + + /* Save the current value of RR0 */ + info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0); + + restore_flags(flags); + return 0; +} +#endif + +/* FIXME: are these required ?? */ +static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty, + struct file *file ) +{ + /* check on the basis of info->whatever ?? */ + if (info->private->kgdb_channel || info->private->is_cons) + return -EBUSY; + return( 0 ); +} + +static void SCC_init( struct m68k_async_struct *info ) +{ + /* FIXME: init currently done in probe_sccs() */ + + /* BUT: startup part needs to be done here! */ + +#ifdef SCC_DEBUG + printk("mac_SCC: init, info %lx, info->port %lx \n", info, info->port); +#endif + /* + * Clear the receive FIFO. + */ + ZS_CLEARFIFO(info->private->zs_channel); + info->xmit_fifo_size = 1; + + /* + * Clear the interrupt registers. + */ + write_zsreg(info->private->zs_channel, 0, ERR_RES); + write_zsreg(info->private->zs_channel, 0, RES_H_IUS); + + /* + * Turn on RTS and DTR. + */ + zs_rtsdtr(info, 1); + + /* + * Finally, enable sequencing and interrupts + */ + info->private->curregs[1] = (info->private->curregs[1] & ~0x18) + | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + info->private->pendregs[1] = info->private->curregs[1]; + info->private->curregs[3] |= (RxENABLE | Rx8); + info->private->pendregs[3] = info->private->curregs[3]; + info->private->curregs[5] |= (TxENAB | Tx8); + info->private->pendregs[5] = info->private->curregs[5]; + info->private->curregs[9] |= (NV | MIE); + info->private->pendregs[9] = info->private->curregs[9]; + write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]); + write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]); + write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]); + + /* + * Set the speed of the serial port - done in startup() !! + */ +#if 0 + SCC_change_speed(info); +#endif + + /* Save the current value of RR0 */ + info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0); + +} + +static void SCC_init_port( struct m68k_async_struct *info, int type, int channel ) +{ + static int got_autovector = 0; + +#ifdef SCC_DEBUG + printk("mac_SCC: init_port, info %x \n", info); +#endif + info->sw = &SCC_switch; + info->private = &zs_soft_private[channel]; + info->private->zs_channel = &zs_channels[channel]; + info->irq = IRQ4; + info->private->clk_divisor = 16; + info->private->zs_baud = get_zsbaud(info); + info->port = (int) info->private->zs_channel->control; + + /* + * MSch: Extended interrupt scheme: + * The generic m68k interrupt code can't use multiple handlers for + * the same interrupt source (no chained interrupts). + * We have to plug in a 'master' interrupt handler instead, calling + * mac_SCC_interrupt with the proper arguments ... + */ + + if (!got_autovector) { + if(sys_request_irq(IRQ4, mac_SCC_handler, 0, "SCC master", info)) + panic("macserial: can't get irq %d", IRQ4); +#ifdef SCC_DEBUG + printk("mac_SCC: got SCC master interrupt %d, channel %d info %p\n", + IRQ4, channel, info); +#endif + got_autovector = 1; + } + + if (info->private->zs_chan_a == info->private->zs_channel) { + /* Channel A */ + if (request_irq(IRQ_SCCA, mac_SCC_interrupt, 0, "SCC A", info)) + panic("mac_SCC: can't get irq %d", IRQ_SCCA); +#ifdef SCC_DEBUG + printk("mac_SCC: got SCC A interrupt %d, channel %d info %p\n", + IRQ_SCCA, channel, info); +#endif + } else { + /* Channel B */ + if (request_irq(IRQ_SCCB, mac_SCC_interrupt, 0, "SCC B", info)) + panic("mac_SCC: can't get irq %d", IRQ_SCCB); +#ifdef SCC_DEBUG + printk("mac_SCC: got SCC B interrupt %d, channel %d info %p\n", + IRQ_SCCB, channel, info); +#endif + } + + /* If console serial line, then enable interrupts. */ + if (info->private->is_cons) { + printk("mac_SCC: console line %lx; enabling interrupt!\n", info); + write_zsreg(info->private->zs_channel, R1, + (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB)); + write_zsreg(info->private->zs_channel, R9, (NV | MIE)); + write_zsreg(info->private->zs_channel, R10, (NRZ)); + write_zsreg(info->private->zs_channel, R3, (Rx8 | RxENABLE)); + write_zsreg(info->private->zs_channel, R5, (Tx8 | TxENAB)); + } + /* If this is the kgdb line, enable interrupts because we + * now want to receive the 'control-c' character from the + * client attached to us asynchronously. + */ + if (info->private->kgdb_channel) { + printk("mac_SCC: kgdb line %lx; enabling interrupt!\n", info); + kgdb_chaninit(info, 1, info->private->zs_baud); + } + /* Report settings (in m68kserial.c) */ +#ifndef CONFIG_MAC + printk("ttyS%d at 0x%08x (irq = %d)", info->line, + info->port, info->irq); + printk(" is a Z8530 SCC\n"); +#endif + +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void SCC_deinit(struct m68k_async_struct * info, int leave_dtr) +{ + unsigned long flags; + + save_flags(flags); cli(); /* Disable interrupts */ + + info->private->pendregs[1] = info->private->curregs[1] = 0; + write_zsreg(info->private->zs_channel, 1, 0); /* no interrupts */ + + info->private->curregs[3] &= ~RxENABLE; + info->private->pendregs[3] = info->private->curregs[3]; + write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]); + + info->private->curregs[5] &= ~TxENAB; + + if (!leave_dtr) + info->private->curregs[5] &= ~(DTR | RTS); + else + info->private->curregs[5] &= ~(RTS); + + info->private->pendregs[5] = info->private->curregs[5]; + write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]); + + restore_flags(flags); +} + +/* FIXME !!! */ +static int SCC_check_custom_divisor( struct m68k_async_struct *info, + int baud_base, int divisor ) +{ + return 0; +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void SCC_change_speed(struct m68k_async_struct *info) +{ + unsigned short port; + unsigned cflag; + int i; + int brg; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!(port = info->port)) + return; + i = cflag & CBAUD; + + if (i == 0 && !(info->flags & ASYNC_SPD_MASK)) { + /* speed == 0 -> drop DTR */ + save_flags(flags); + cli(); + info->private->curregs[5] &= ~(DTR | RTS); + write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]); + restore_flags(flags); + return; + } + + + if (i & CBAUDEX) { + /* XXX CBAUDEX is not obeyed. + * It is impossible at a 32bits PPC. XXX?? + * But we have to report this to user ... someday. + */ + i = B9600; + } + + save_flags(flags); cli(); + info->private->zs_baud = baud_table[i]; + info->private->clk_divisor = 16; + + info->private->curregs[4] = X16CLK; + info->private->curregs[11] = TCBR | RCBR; + brg = BPS_TO_BRG(info->private->zs_baud, + ZS_CLOCK/info->private->clk_divisor); + info->private->curregs[12] = (brg & 255); + info->private->curregs[13] = ((brg >> 8) & 255); + info->private->curregs[14] = BRENABL; + + /* byte size and parity */ + info->private->curregs[3] &= ~RxNBITS_MASK; + info->private->curregs[5] &= ~TxNBITS_MASK; + switch (cflag & CSIZE) { + case CS5: + info->private->curregs[3] |= Rx5; + info->private->curregs[5] |= Tx5; + break; + case CS6: + info->private->curregs[3] |= Rx6; + info->private->curregs[5] |= Tx6; + break; + case CS7: + info->private->curregs[3] |= Rx7; + info->private->curregs[5] |= Tx7; + break; + case CS8: + default: /* defaults to 8 bits */ + info->private->curregs[3] |= Rx8; + info->private->curregs[5] |= Tx8; + break; + } + info->private->pendregs[3] = info->private->curregs[3]; + info->private->pendregs[5] = info->private->curregs[5]; + + info->private->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); + if (cflag & CSTOPB) { + info->private->curregs[4] |= SB2; + } else { + info->private->curregs[4] |= SB1; + } + if (cflag & PARENB) { + info->private->curregs[4] |= PAR_ENA; + } + if (!(cflag & PARODD)) { + info->private->curregs[4] |= PAR_EVEN; + } + info->private->pendregs[4] = info->private->curregs[4]; + + info->private->curregs[15] &= ~(DCDIE | CTSIE); + if (!(cflag & CLOCAL)) { + info->private->curregs[15] |= DCDIE; + } + if (cflag & CRTSCTS) { + info->private->curregs[15] |= CTSIE; + if ((read_zsreg(info->private->zs_channel, 0) & CTS) != 0) + info->private->tx_stopped = 1; + } else + info->private->tx_stopped = 0; + info->private->pendregs[15] = info->private->curregs[15]; + + /* Load up the new values */ + load_zsregs(info->private->zs_channel, info->private->curregs); + + restore_flags(flags); +} + +/* This is for console output over ttya/ttyb */ +static void SCC_put_char(char ch) +{ + struct mac_zschannel *chan = zs_conschan; + int loops = 0; + unsigned long flags; + + if(!chan) + return; + + save_flags(flags); cli(); + while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0 && loops < 10000) { + loops++; + udelay(5); + } + write_zsdata(chan, ch); + restore_flags(flags); +} + +/* These are for receiving and sending characters under the kgdb + * source level kernel debugger. + */ +void putDebugChar(char kgdb_char) +{ + struct mac_zschannel *chan = zs_kgdbchan; + + while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0) + udelay(5); + write_zsdata(chan, kgdb_char); +} + +char getDebugChar(void) +{ + struct mac_zschannel *chan = zs_kgdbchan; + + while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0) + udelay(5); + return read_zsdata(chan); +} + +/* + * Fair output driver allows a process to speak. + */ +static void SCC_fair_output(void) +{ + int left; /* Output no more than that */ + unsigned long flags; + struct m68k_async_struct *info = zs_consinfo; + char c; + + if (info == 0) return; + if (info->xmit_buf == 0) return; + + save_flags(flags); cli(); + left = info->xmit_cnt; + while (left != 0) { + c = info->xmit_buf[info->xmit_tail]; + info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + restore_flags(flags); + + SCC_put_char(c); + + save_flags(flags); cli(); + left = MIN(info->xmit_cnt, left-1); + } + + restore_flags(flags); + return; +} + +/* + * zs_console_print is registered for printk. + */ +static void zs_console_print(const char *p) +{ + char c; + + while ((c = *(p++)) != 0) { + if (c == '\n') + SCC_put_char('\r'); + SCC_put_char(c); + } + + /* Comment this if you want to have a strict interrupt-driven output */ + SCC_fair_output(); +} + +/* FIXME: check with SCC_enab_tx_int!! */ +#if 0 +static void rs_flush_chars(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || info->private->tx_stopped || + !info->xmit_buf) + return; + + /* Enable transmitter */ + save_flags(flags); cli(); + transmit_chars(info); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, total = 0; + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit_buf) + return 0; + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + down(&tmp_buf_sem); + memcpy_fromfs(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 && !info->tx_stopped + && !info->tx_active) + transmit_chars(info); + restore_flags(flags); + return total; +} +#endif + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void SCC_throttle(struct m68k_async_struct *info, int status) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + if (status) { + /* + * Here we want to turn off the RTS line. On Macintoshes, + * we only get the DTR line, which goes to both DTR and + * RTS on the modem. RTS doesn't go out to the serial + * port socket. So you should make sure your modem is + * set to ignore DTR if you're using CRTSCTS. + */ + info->private->curregs[5] &= ~(DTR | RTS); + info->private->pendregs[5] &= ~(DTR | RTS); + write_zsreg(info->private->zs_channel, 5, + info->private->curregs[5]); + } else { + /* Assert RTS and DTR lines */ + info->private->curregs[5] |= DTR | RTS; + info->private->pendregs[5] |= DTR | RTS; + write_zsreg(info->private->zs_channel, 5, + info->private->curregs[5]); + } + + restore_flags(flags); + +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static void SCC_get_serial_info(struct m68k_async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + retinfo->baud_base = info->baud_base; + retinfo->custom_divisor = info->custom_divisor; +} + +/* FIXME: set_serial_info needs check_custom_divisor !!! */ + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int SCC_get_lsr_info(struct m68k_async_struct * info, unsigned int *value) +{ + unsigned char status; + + cli(); + status = read_zsreg(info->private->zs_channel, 0); + sti(); + return status; +} + +static unsigned int SCC_get_modem_info(struct m68k_async_struct *info) +{ + unsigned char control, status; + unsigned int result; + + cli(); + control = info->private->curregs[5]; + status = read_zsreg(info->private->zs_channel, 0); + sti(); + result = ((control & RTS) ? TIOCM_RTS: 0) + | ((control & DTR) ? TIOCM_DTR: 0) + | ((status & DCD) ? TIOCM_CAR: 0) + | ((status & CTS) ? 0: TIOCM_CTS); + return result; +} + +/* FIXME: zs_setdtr was used in rs_open ... */ + +static int SCC_set_modem_info(struct m68k_async_struct *info, + int new_dtr, int new_rts) +{ + int error; + unsigned int arg, bits; + + bits = (new_rts ? RTS: 0) + (new_dtr ? DTR: 0); + info->private->curregs[5] = (info->private->curregs[5] & ~(DTR | RTS)) | bits; + info->private->pendregs[5] = info->private->curregs[5]; + write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]); + sti(); + return 0; +} + +/* + * This routine sends a break character out the serial port. + */ +static void SCC_set_break(struct m68k_async_struct * info, int break_flag) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + if (break_flag) { + info->private->curregs[5] |= SND_BRK; + write_zsreg(info->private->zs_channel, 5, + info->private->curregs[5]); + } else { + info->private->curregs[5] &= ~SND_BRK; + write_zsreg(info->private->zs_channel, 5, + info->private->curregs[5]); + } + + restore_flags(flags); +} + +/* FIXME: these have to be enabled in rs_ioctl !! */ + +static int SCC_ioctl(struct tty_struct *tty, struct file * file, + struct m68k_async_struct * info, unsigned int cmd, + unsigned long arg) +{ + int error; + int retval; + + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + else + return SCC_get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct m68k_async_struct)); + if (error) + return error; + copy_to_user((struct m68k_async_struct *) arg, + info, sizeof(struct m68k_async_struct)); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void SCC_stop_receive (struct m68k_async_struct *info) +{ + /* disable Rx */ + info->private->curregs[3] &= ~RxENABLE; + info->private->pendregs[3] = info->private->curregs[3]; + write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]); + /* disable Rx interrupts */ + info->private->curregs[1] &= ~(0x18); /* disable any rx ints */ + info->private->pendregs[1] = info->private->curregs[1]; + write_zsreg(info->private->zs_channel, 1, info->private->curregs[1]); + ZS_CLEARFIFO(info->private->zs_channel); +} + +static int SCC_trans_empty (struct m68k_async_struct *info) +{ + return (read_zsreg(info->private->zs_channel, 1) & ALL_SNT) != 0; +} + +/* Finally, routines used to initialize the serial driver. */ + +#ifdef CONFIG_MAC + +/* + * Mac: use boot_info data; assume 2 channels + */ + +static void probe_sccs(void) +{ + int n; + +#define ZS_CONTROL 0x50F04000 +#define ZS_DATA (ZS_CONTROL+4) +#define ZS_IRQ 5 +#define ZS_MOVE -2 +#define ZS_DATA_MOVE 4 +#define ZS_CH_A_FIRST 2 + + /* last-ditch fixup for NetBSD booter case */ + if (mac_bi_data.sccbase == 0) + mac_bi_data.sccbase = ZS_CONTROL; + + /* testing: fix up broken 24 bit addresses (ClassicII) */ + if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase) + mac_bi_data.sccbase |= 0x50000000; + + for(n=0;n<2;n++) + { +#if 0 + zs_channels[n].control = (volatile unsigned char *) + ZS_CONTROL+ZS_MOVE*n; + zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n; +#else + zs_channels[n].control = (volatile unsigned char *) + (mac_bi_data.sccbase+ZS_CH_A_FIRST)+ZS_MOVE*n; + zs_channels[n].data = (volatile unsigned char *) + (mac_bi_data.sccbase+ZS_CH_A_FIRST+ZS_DATA_MOVE)+ZS_MOVE*n; +#endif + zs_soft[n].private = &zs_soft_private[n]; + zs_soft[n].private->zs_channel = &zs_channels[n]; + zs_soft[n].irq = IRQ4; +#if 0 + if (request_irq(ch->intrs[0], rs_interrupt, 0, + "SCC", &zs_soft[n])) + panic("macserial: can't get irq %d", + ch->intrs[0]); +#endif + if (n & 1) + zs_soft[n].private->zs_chan_a = &zs_channels[n-1]; + else + zs_soft[n].private->zs_chan_a = &zs_channels[n]; + } + + zs_channels_found=2; +} + +#else + +/* + * PowerMAC - query the PROM + */ + +static void show_serial_version(void) +{ + printk("PowerMac Z8530 serial driver version 1.00\n"); +} + +/* Ask the PROM how many Z8530s we have and initialize their zs_channels */ +static void +probe_sccs() +{ + struct device_node *dev, *ch; + struct m68k_async_struct **pp; + int n; + + n = 0; + pp = &zs_chain; + for (dev = find_devices("escc"); dev != 0; dev = dev->next) { + if (n >= NUM_CHANNELS) { + printk("Sorry, can't use %s: no more channels\n", + dev->full_name); + continue; + } + for (ch = dev->child; ch != 0; ch = ch->sibling) { + if (ch->n_addrs < 1 || ch ->n_intrs < 1) { + printk("Can't use %s: %d addrs %d intrs\n", + ch->full_name, ch->n_addrs, ch->n_intrs); + continue; + } + zs_channels[n].control = (volatile unsigned char *) + ch->addrs[0].address; + zs_channels[n].data = zs_channels[n].control + + ch->addrs[0].size / 2; + zs_soft[n].private = &zs_soft_private[n]; + zs_soft[n].private->zs_channel = &zs_channels[n]; + zs_soft[n].irq = ch->intrs[0]; + if (request_irq(ch->intrs[0], mac_SCC_interrupt, 0, + "SCC", &zs_soft[n])) + panic("macserial: can't get irq %d", + ch->intrs[0]); + /* XXX this assumes the prom puts chan A before B */ + if (n & 1) + zs_soft[n].private->zs_chan_a = &zs_channels[n-1]; + else + zs_soft[n].private->zs_chan_a = &zs_channels[n]; + + *pp = &zs_soft[n]; + pp = &zs_soft[n].private->zs_next; + ++n; + } + } + *pp = 0; + zs_channels_found = n; +} + +#endif + +extern void register_console(void (*proc)(const char *)); + +static inline void +rs_cons_check(struct m68k_async_struct *ss, int channel) +{ + int i, o, io; + static consout_registered = 0; + static msg_printed = 0; + + i = o = io = 0; + + /* Is this one of the serial console lines? */ + if ((zs_cons_chanout != channel) && + (zs_cons_chanin != channel)) + return; + zs_conschan = ss->private->zs_channel; + zs_consinfo = ss; + + /* Register the console output putchar, if necessary */ + if (zs_cons_chanout == channel) { + o = 1; + /* double whee.. */ + if (!consout_registered) { + register_console(zs_console_print); + consout_registered = 1; + } + } + + if (zs_cons_chanin == channel) { + i = 1; + } + if (o && i) + io = 1; + if (ss->private->zs_baud != 9600) + panic("Console baud rate weirdness"); + + /* Set flag variable for this port so that it cannot be + * opened for other uses by accident. + */ + ss->private->is_cons = 1; + + if (io) { + if(!msg_printed) { + printk("zs%d: console I/O\n", ((channel>>1)&1)); + msg_printed = 1; + } + } else { + printk("zs%d: console %s\n", ((channel>>1)&1), + (i==1 ? "input" : (o==1 ? "output" : "WEIRD"))); + } + + /* FIXME : register interrupt here??? */ +} + +volatile int test_done; + +/* rs_init inits the driver */ +int mac_SCC_init(void) +{ + int channel, line, nr = 0, i; + unsigned long flags; + struct serial_struct req; + struct m68k_async_struct *info; + + 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 + ) + return( -ENODEV ); + + if (zs_chain == 0) + probe_sccs(); + + save_flags(flags); + cli(); + + /* + * FIXME: init of rs_table entry and register_serial now done, + * but possible clash of zs_soft[channel] and rs_table[channel]!! + * zs_soft initialized in probe_sccs(), some settings copied to + * info = &rs_table[channel], which is used by the mid-level code. + * The info->private part is shared among both! + */ + + for (channel = 0; channel < zs_channels_found; ++channel) { + req.line = channel; + req.type = SER_SCC_MAC; + req.port = zs_soft[channel].private->zs_channel->control; + + if ((line = register_serial( &req )) >= 0) { + SCC_init_port( &rs_table[line], req.type, line ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line ); + } + + restore_flags(flags); + + return( nr > 0 ? 0 : -ENODEV ); +} + +/* Hooks for running a serial console. con_init() calls this if the + * console is being run over one of the serial ports. + * 'channel' is decoded as 0=modem 1=printer, 'chip' is ignored. + */ +void +rs_cons_hook(int chip, int out, int channel) +{ + if (zs_chain == 0) + probe_sccs(); + zs_soft[channel].private->clk_divisor = 16; + zs_soft[channel].private->zs_baud = get_zsbaud(&zs_soft[channel]); + rs_cons_check(&zs_soft[channel], channel); + if (out) + zs_cons_chanout = channel; + else + zs_cons_chanin = channel; + + /* FIXME : register interrupt here??? */ +} + +/* This is called at boot time to prime the kgdb serial debugging + * serial line. The 'tty_num' argument is 0 for /dev/ttyS0 and 1 + * for /dev/ttyS1 which is determined in setup_arch() from the + * boot command line flags. + */ +void +rs_kgdb_hook(int tty_num) +{ + if (zs_chain == 0) + probe_sccs(); + zs_kgdbchan = zs_soft[tty_num].private->zs_channel; + zs_soft[tty_num].private->clk_divisor = 16; + zs_soft[tty_num].private->zs_baud = get_zsbaud(&zs_soft[tty_num]); + zs_soft[tty_num].private->kgdb_channel = 1; /* This runs kgdb */ + zs_soft[tty_num ^ 1].private->kgdb_channel = 0; /* This does not */ + /* Turn on transmitter/receiver at 8-bits/char */ + kgdb_chaninit(&zs_soft[tty_num], 0, 9600); + ZS_CLEARERR(zs_kgdbchan); + ZS_CLEARFIFO(zs_kgdbchan); + + /* FIXME : register interrupt here??? */ +} + diff --git a/drivers/char/mac_SCC.h b/drivers/char/mac_SCC.h new file mode 100644 index 000000000..5e903e1db --- /dev/null +++ b/drivers/char/mac_SCC.h @@ -0,0 +1,321 @@ +/* + * macserial.h: Definitions for the Macintosh Z8530 serial driver. + * + * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras. + * + * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + */ +#ifndef _MAC_SCC_H +#define _MAC_SCC_H + +/* + * For the close wait times, 0 means wait forever for serial port to + * flush its output. 65535 means don't wait at all. + */ +#define ZILOG_CLOSING_WAIT_INF 0 +#define ZILOG_CLOSING_WAIT_NONE 65535 + +/* + * Definitions for ZILOG_struct (and serial_struct) flags field + */ +#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes + on the callout port */ +#define ZILOG_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ +#define ZILOG_SAK 0x0004 /* Secure Attention Key (Orange book) */ +#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ + +#define ZILOG_SPD_MASK 0x0030 +#define ZILOG_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ + +#define ZILOG_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ +#define ZILOG_SPD_CUST 0x0030 /* Use user-specified divisor */ + +#define ZILOG_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ +#define ZILOG_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ +#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ +#define ZILOG_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ +#define ZILOG_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ + +#define ZILOG_FLAGS 0x0FFF /* Possible legal ZILOG flags */ +#define ZILOG_USR_MASK 0x0430 /* Legal flags that non-privileged + * users can set or reset */ + +/* Internal flags used only by kernel/chr_drv/serial.c */ +#define ZILOG_INITIALIZED 0x80000000 /* Serial port was initialized */ +#define ZILOG_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ +#define ZILOG_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ +#define ZILOG_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ +#define ZILOG_CLOSING 0x08000000 /* Serial port is closing */ +#define ZILOG_CTS_FLOW 0x04000000 /* Do CTS flow control */ +#define ZILOG_CHECK_CD 0x02000000 /* i.e., CLOCAL */ + +/* Software state per channel */ + +#ifdef __KERNEL__ +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * For definitions of the flags field, see tty.h + */ + +/* MSch: gone to <asm/serial.h> */ + +#define SERIAL_MAGIC 0x5301 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#define SERIAL_XMIT_SIZE 4096 + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define RS_EVENT_WRITE_WAKEUP 0 + +#endif /* __KERNEL__ */ + +/* Conversion routines to/from brg time constants from/to bits + * per second. + */ +#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) +#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) + +/* The Zilog register set */ + +#define FLAG 0x7e + +/* Write Register 0 */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 */ + +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ +#define INT_ERR_Rx 0x18 /* Int on error only */ + +#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ +#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ +#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ + +/* Write Register #2 (Interrupt Vector) */ + +/* Write Register 3 */ + +#define RxENABLE 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxNBITS_MASK 0xc0 + +/* Write Register 4 */ + +#define PAR_ENA 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ +#define SB_MASK 0xc + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xC0 /* x64 clock mode */ +#define XCLK_MASK 0xC0 + +/* Write Register 5 */ + +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENAB 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define TxNBITS_MASK 0x60 +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 8 (transmit buffer) */ + +/* Write Register 9 (Master interrupt control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (misc control bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (lower byte of baud rate generator time constant) */ + +/* Write Register 13 (upper byte of baud rate generator time constant) */ + +/* Write Register 14 (Misc control bits) */ +#define BRENABL 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (external/status interrupt control) */ +#define ZCIE 2 /* Zero count IE */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC_HUNT 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define FRM_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (channel b only) - Interrupt vector */ + +/* Read Register 3 (interrupt pending register) ch a only */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 8 (receive data register) */ + +/* Read Register 10 (misc status bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (lower byte of baud rate generator constant) */ + +/* Read Register 13 (upper byte of baud rate generator constant) */ + +/* Read Register 15 (value of WR 15) */ + +/* Misc macros */ +#define ZS_CLEARERR(channel) (write_zsreg(channel, 0, ERR_RES)) +#define ZS_CLEARFIFO(channel) do { volatile unsigned char garbage; \ + garbage = read_zsdata(channel); \ + garbage = read_zsdata(channel); \ + garbage = read_zsdata(channel); \ + } while(0) + +#endif /* !(_MAC_SCC_H) */ diff --git a/drivers/char/macmouse.c b/drivers/char/macmouse.c index 0cf1fab20..2d7145203 100644 --- a/drivers/char/macmouse.c +++ b/drivers/char/macmouse.c @@ -227,7 +227,7 @@ static ssize_t read_mouse(struct file *file, char *buffer, size_t count, static unsigned int mouse_poll(struct file *file, poll_table *wait) { - poll_wait(&mouse.wait, wait); + poll_wait(file, &mouse.wait, wait); if (mouse.ready) return POLLIN | POLLRDNORM; return 0; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 9a0fe22de..2e1c46973 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -27,19 +27,22 @@ #include <asm/pgtable.h> #ifdef CONFIG_SOUND +void soundcore_init(void); +#ifdef CONFIG_SOUND_OSS void soundcard_init(void); #endif +#ifdef CONFIG_DMASOUND +void dmasound_init(void); +#endif +#endif #ifdef CONFIG_ISDN int isdn_init(void); #endif -#ifdef CONFIG_PCWATCHDOG -int pcwatchdog_init(void); -#endif #ifdef CONFIG_VIDEO_DEV extern int videodev_init(void); #endif -#if defined(CONFIG_FB) -extern void fbmem_init( void ); +#ifdef CONFIG_FB +extern void fbmem_init(void); #endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, @@ -60,7 +63,7 @@ static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, written+=sz; } #endif - if (copy_from_user(p, buf, count) < 0) + if (copy_from_user(p, buf, count)) return -EFAULT; written += count; *ppos += written; @@ -101,7 +104,7 @@ static ssize_t read_mem(struct file * file, char * buf, } } #endif - if (copy_to_user(buf, __va(p), count) < 0) + if (copy_to_user(buf, __va(p), count)) return -EFAULT; read += count; *ppos += read; @@ -260,45 +263,55 @@ static ssize_t write_null(struct file * file, const char * buf, */ static inline size_t read_zero_pagealigned(char * buf, size_t size) { + struct mm_struct *mm; struct vm_area_struct * vma; unsigned long addr=(unsigned long)buf; + mm = current->mm; + /* Oops, this was forgotten before. -ben */ + down(&mm->mmap_sem); + /* For private mappings, just map in zero pages. */ - for (vma = find_vma(current->mm, addr); vma; vma = vma->vm_next) { + for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { unsigned long count; if (vma->vm_start > addr || (vma->vm_flags & VM_WRITE) == 0) - return size; + goto out_up; if (vma->vm_flags & VM_SHARED) break; count = vma->vm_end - addr; if (count > size) count = size; - flush_cache_range(current->mm, addr, addr + count); - zap_page_range(current->mm, addr, count); + flush_cache_range(mm, addr, addr + count); + zap_page_range(mm, addr, count); zeromap_page_range(addr, count, PAGE_COPY); - flush_tlb_range(current->mm, addr, addr + count); + flush_tlb_range(mm, addr, addr + count); size -= count; buf += count; addr += count; if (size == 0) - return 0; + goto out_up; } + + up(&mm->mmap_sem); - /* The shared case is hard. Lets do the conventional zeroing. */ + /* The shared case is hard. Let's do the conventional zeroing. */ do { unsigned long unwritten = clear_user(buf, PAGE_SIZE); if (unwritten) return size + unwritten - PAGE_SIZE; - if (need_resched) + if (current->need_resched) schedule(); buf += PAGE_SIZE; size -= PAGE_SIZE; } while (size); return size; +out_up: + up(&mm->mmap_sem); + return size; } static ssize_t read_zero(struct file * file, char * buf, @@ -533,18 +546,24 @@ __initfunc(int chr_dev_init(void)) defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \ defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \ defined (CONFIG_AMIGAMOUSE) || defined (CONFIG_ATARIMOUSE) || \ - defined (CONFIG_PCWATCHDOG) || \ + 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(); #endif #ifdef CONFIG_SOUND + soundcore_init(); +#ifdef CONFIG_SOUND_OSS soundcard_init(); +#endif +#ifdef CONFIG_DMASOUND + dmasound_init(); +#endif #endif #ifdef CONFIG_JOYSTICK /* - * Some joysticks only appear when the soundcard they are + * Some joysticks only appear when the sound card they are * connected to is configured. Keep the sound/joystick ordering. */ js_init(); @@ -558,6 +577,9 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_FTAPE ftape_init(); #endif +#ifdef CONFIG_VIDEO_BT848 + i2c_init(); +#endif #ifdef CONFIG_VIDEO_DEV videodev_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index e2b5bfa41..8b4b16b99 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -65,13 +65,16 @@ static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list } 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 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); extern void streamable_init(void); extern void watchdog_init(void); @@ -79,13 +82,13 @@ extern void wdt_init(void); extern void acq_init(void); extern void pcwatchdog_init(void); extern int rtc_init(void); +extern int ds1286_init(void); extern int dsp56k_init(void); extern int nvram_init(void); extern int radio_init(void); extern void hfmodem_init(void); -#ifdef CONFIG_PC110_PAD extern int pc110pad_init(void); -#endif +extern int pmu_device_init(void); #ifdef CONFIG_PROC_FS static int misc_read_proc(char *buf, char **start, off_t offset, @@ -206,6 +209,9 @@ __initfunc(int misc_init(void)) 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 @@ -224,9 +230,15 @@ __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 + adb_mouse_init(); +#endif #ifdef CONFIG_PC110_PAD pc110pad_init(); #endif @@ -254,7 +266,7 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_H8 h8_init(); #endif -#ifdef CONFIG_RTC +#if defined(CONFIG_RTC) || defined(CONFIG_SUN_MOSTEK_RTC) rtc_init(); #endif #ifdef CONFIG_SGI_DS1286 @@ -275,6 +287,9 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_HFMODEM hfmodem_init(); #endif +#ifdef CONFIG_PMAC_PBOOK + pmu_device_init(); +#endif #ifdef CONFIG_SGI_GRAPHICS gfx_register (); streamable_init (); diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index 0d5f02638..18cce49dd 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -5,17 +5,17 @@ * * what works and what doesn't: * - * AM-Mono + * AM mono * probably doesn't (untested) * - * FM-Mono - * should work. The stereo modes are backward compatible to FM-mono, - * therefore FM-Mono should be allways available. + * FM-mono + * should work. FM stereo modes are backward-compatible to mono. + * Therefore FM mono should always be available. * - * FM-Stereo (B/G, used in germany) + * FM stereo (B/G, used in Germany) * should work, with autodetect * - * FM-Stereo (satellite) + * FM stereo (satellite) * should work, no autodetect (i.e. default is mono, but you can * switch to stereo -- untested) * @@ -37,34 +37,49 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/malloc.h> -/* #include <asm/smp_lock.h> */ /* kernel_thread */ #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> -#include "i2c.h" +#include <linux/i2c.h> #include <linux/videodev.h> #include "msp3400.h" -int debug = 0; /* insmod parameter */ -struct msp3400c -{ +/* sound mixer stuff */ +#include <linux/config.h> + +#if LINUX_VERSION_CODE > 0x020140 /* need modular sound driver */ +# if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) +# define REGISTER_MIXER 1 +# endif +#endif + + +static int debug = 0; /* insmod parameter */ + +struct msp3400c { struct i2c_bus *bus; int nicam; int mode; int norm; - int volume; int stereo; + int mixer; + int left, right; /* volume */ + int bass, treble; + /* thread */ - struct task_struct *thread; - struct semaphore *wait; - struct semaphore *notify; - int active,restart,rmmod; + struct task_struct *thread; + struct semaphore *wait; + struct semaphore *notify; + int active,restart,rmmod; + + int watch_stereo; + struct timer_list wake_stereo; }; #define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ @@ -88,82 +103,73 @@ static int msp3400c_reset(struct i2c_bus *bus) { int ret = 0; - udelay(2000); + mdelay(2); i2c_start(bus); i2c_sendbyte(bus, I2C_MSP3400C,2000); i2c_sendbyte(bus, 0x00,0); i2c_sendbyte(bus, 0x80,0); i2c_sendbyte(bus, 0x00,0); i2c_stop(bus); - udelay(2000); + mdelay(2); i2c_start(bus); if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, 0x00,0) || - 0 != i2c_sendbyte(bus, 0x00,0) || - 0 != i2c_sendbyte(bus, 0x00,0)) - { + 0 != i2c_sendbyte(bus, 0x00,0) || + 0 != i2c_sendbyte(bus, 0x00,0) || + 0 != i2c_sendbyte(bus, 0x00,0)) { ret = -1; printk(KERN_ERR "msp3400: chip reset failed, penguin on i2c bus?\n"); } i2c_stop(bus); - udelay(2000); + mdelay(2); return ret; } -static int msp3400c_read(struct i2c_bus *bus, int dev, int addr) +static int +msp3400c_read(struct i2c_bus *bus, int dev, int addr) { int ret=0; short val = 0; i2c_start(bus); if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, dev+1, 0) || - 0 != i2c_sendbyte(bus, addr >> 8, 0) || - 0 != i2c_sendbyte(bus, addr & 0xff, 0)) - { + 0 != i2c_sendbyte(bus, dev+1, 0) || + 0 != i2c_sendbyte(bus, addr >> 8, 0) || + 0 != i2c_sendbyte(bus, addr & 0xff, 0)) { ret = -1; - } - else - { + } else { i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) - { + if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) { ret = -1; - } - else - { + } else { val |= (int)i2c_readbyte(bus,0) << 8; val |= (int)i2c_readbyte(bus,1); } } i2c_stop(bus); - if (-1 == ret) - { + if (-1 == ret) { printk(KERN_WARNING "msp3400: I/O error, trying reset (read %s 0x%x)\n", - (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr); + (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr); msp3400c_reset(bus); } return val; } -static int msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val) +static int +msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val) { int ret = 0; i2c_start(bus); if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, dev, 0) || - 0 != i2c_sendbyte(bus, addr >> 8, 0) || - 0 != i2c_sendbyte(bus, addr & 0xff, 0) || - 0 != i2c_sendbyte(bus, val >> 8, 0) || - 0 != i2c_sendbyte(bus, val & 0xff, 0)) - { + 0 != i2c_sendbyte(bus, dev, 0) || + 0 != i2c_sendbyte(bus, addr >> 8, 0) || + 0 != i2c_sendbyte(bus, addr & 0xff, 0) || + 0 != i2c_sendbyte(bus, val >> 8, 0) || + 0 != i2c_sendbyte(bus, val & 0xff, 0)) ret = -1; - } i2c_stop(bus); - if (-1 == ret) - { - printk(KERN_ERR "msp3400: I/O error, trying reset (write %s 0x%x)\n", - (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr); + if (-1 == ret) { + printk(KERN_WARNING "msp3400: I/O error, trying reset (write %s 0x%x)\n", + (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr); msp3400c_reset(bus); } return ret; @@ -182,8 +188,7 @@ static int msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val) #define MSP_MODE_FM_NICAM1 5 #define MSP_MODE_FM_NICAM2 6 -static struct MSP_INIT_DATA_DEM -{ +static struct MSP_INIT_DATA_DEM { int fir1[6]; int fir2[6]; int cdo1; @@ -193,43 +198,48 @@ static struct MSP_INIT_DATA_DEM int dfp_src; int dfp_matrix; } msp_init_data[] = { - /* AM (for carrier detect / msp3400) */ - { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0500, 0x0020, 0x3000}, - - /* AM (for carrier detect / msp3410) */ - { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0100, 0x0020, 0x3000}, - - /* FM Radio */ - { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, - MSP_CARRIER(10.7), MSP_CARRIER(10.7), 0x00d0, 0x0480, 0x0020, 0x3002 }, - - /* Terrestial FM-mono */ - { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0480, 0x0030, 0x3000}, - - /* Sat FM-mono */ - { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), 0x00c6, 0x0480, 0x0000, 0x3000}, - - /* NICAM B/G, D/K */ - { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0040, 0x0120, 0x3000}, - - /* NICAM I */ - { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0040, 0x0120, 0x3000}, + /* AM (for carrier detect / msp3400) */ + { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0500, 0x0020, 0x3000}, + + /* AM (for carrier detect / msp3410) */ + { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0100, 0x0020, 0x3000}, + + /* FM Radio */ + { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, + MSP_CARRIER(10.7), MSP_CARRIER(10.7), + 0x00d0, 0x0480, 0x0020, 0x3002 }, + + /* Terrestial FM-mono */ + { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0480, 0x0030, 0x3000}, + + /* Sat FM-mono */ + { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0480, 0x0000, 0x3000}, + + /* NICAM B/G, D/K */ + { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0040, 0x0120, 0x3000}, + + /* NICAM I */ + { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0040, 0x0120, 0x3000}, }; -struct CARRIER_DETECT -{ +struct CARRIER_DETECT { int cdo; char *name; }; -static struct CARRIER_DETECT carrier_detect_main[] = -{ +static struct CARRIER_DETECT carrier_detect_main[] = { /* main carrier */ { MSP_CARRIER(4.5), "4.5 NTSC" }, { MSP_CARRIER(5.5), "5.5 PAL B/G" }, @@ -262,15 +272,39 @@ static void msp3400c_setcarrier(struct i2c_bus *bus, int cdo1, int cdo2) msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); } -static void msp3400c_setvolume(struct i2c_bus *bus, int vol) +static void msp3400c_setvolume(struct i2c_bus *bus, int left, int right) { - int val = (vol * 0x73 / 65535) << 8; + int vol,val,balance; + + vol = (left > right) ? left : right; + val = (vol * 0x73 / 65535) << 8; + balance = 0; + if (vol > 0) + balance = ((right-left) * 127) / vol; - dprintk("msp3400: setvolume: 0x%02x\n",val>>8); + dprintk("msp3400: setvolume: %d:%d 0x%02x 0x%02x\n", + left,right,val>>8,balance); msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ /* scart - on/off only */ msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); + msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0001, balance << 8); +} + +static void msp3400c_setbass(struct i2c_bus *bus, int bass) +{ + int val = ((bass-32768) * 0x60 / 65535) << 8; + + dprintk("msp3400: setbass: %d 0x%02x\n",bass, val>>8); + msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ +} + +static void msp3400c_settreble(struct i2c_bus *bus, int treble) +{ + int val = ((treble-32768) * 0x60 / 65535) << 8; + + dprintk("msp3400: settreble: %d 0x%02x\n",treble, val>>8); + msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ } static void msp3400c_setmode(struct msp3400c *msp, int type) @@ -282,38 +316,37 @@ static void msp3400c_setmode(struct msp3400c *msp, int type) msp->stereo = VIDEO_SOUND_MONO; msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ - msp_init_data[type].ad_cv); + msp_init_data[type].ad_cv); for (i = 5; i >= 0; i--) /* fir 1 */ msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0001, - msp_init_data[type].fir1[i]); + msp_init_data[type].fir1[i]); msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0040); msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0000); for (i = 5; i >= 0; i--) msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, - msp_init_data[type].fir2[i]); + msp_init_data[type].fir2[i]); msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ - msp_init_data[type].mode_reg); + msp_init_data[type].mode_reg); msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1, - msp_init_data[type].cdo2); + msp_init_data[type].cdo2); msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, - msp_init_data[type].dfp_src); + msp_init_data[type].dfp_src); msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, - msp_init_data[type].dfp_src); + msp_init_data[type].dfp_src); msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, - msp_init_data[type].dfp_src); + msp_init_data[type].dfp_src); msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, - msp_init_data[type].dfp_matrix); + msp_init_data[type].dfp_matrix); - if (msp->nicam) - { + if (msp->nicam) { /* msp3410 needs some more initialization */ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000); } } @@ -322,86 +355,81 @@ static void msp3400c_setstereo(struct msp3400c *msp, int mode) int nicam=0; /* channel source: FM/AM or nicam */ /* switch demodulator */ - switch (msp->mode) - { - case MSP_MODE_FM_TERRA: - dprintk("msp3400: B/G setstereo: %d\n",mode); - msp->stereo = mode; - msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5)); - switch (mode) - { - case VIDEO_SOUND_STEREO: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001); - break; - case VIDEO_SOUND_MONO: - case VIDEO_SOUND_LANG1: - case VIDEO_SOUND_LANG2: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000); - break; - } + switch (msp->mode) { + case MSP_MODE_FM_TERRA: + dprintk("msp3400: B/G setstereo: %d\n",mode); + msp->stereo = mode; + msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5)); + switch (mode) { + case VIDEO_SOUND_STEREO: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001); break; - case MSP_MODE_FM_SAT: - dprintk("msp3400: sat setstereo: %d\n",mode); - msp->stereo = mode; - switch (mode) - { - case VIDEO_SOUND_MONO: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); - break; - case VIDEO_SOUND_STEREO: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); - break; - case VIDEO_SOUND_LANG1: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - case VIDEO_SOUND_LANG2: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - } + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_LANG1: + case VIDEO_SOUND_LANG2: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000); break; - case MSP_MODE_FM_NICAM1: - dprintk("msp3400: NICAM1 setstereo: %d\n",mode); - msp->stereo = mode; - msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5)); - nicam=0x0100; + } + break; + case MSP_MODE_FM_SAT: + dprintk("msp3400: sat setstereo: %d\n",mode); + msp->stereo = mode; + switch (mode) { + case VIDEO_SOUND_MONO: + msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); break; - default: - /* can't do stereo - abort here */ - return; - } - - /* switch audio */ - switch (mode) - { case VIDEO_SOUND_STEREO: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0020|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0020|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0020|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005, 0x4000); + msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); break; - case VIDEO_SOUND_MONO: case VIDEO_SOUND_LANG1: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0000|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0000|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0000|nicam); + msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); break; case VIDEO_SOUND_LANG2: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0010|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0010|nicam); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0010|nicam); + msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); break; + } + break; + case MSP_MODE_FM_NICAM1: + dprintk("msp3400: NICAM1 setstereo: %d\n",mode); + msp->stereo = mode; + msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5)); + nicam=0x0100; + break; + default: + /* can't do stereo - abort here */ + return; + } + + /* switch audio */ + switch (mode) { + case VIDEO_SOUND_STEREO: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0020|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0020|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0020|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005,0x4000); + break; + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_LANG1: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0000|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0000|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0000|nicam); + break; + case VIDEO_SOUND_LANG2: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0010|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0010|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0010|nicam); + break; } } /* ----------------------------------------------------------------------- */ struct REGISTER_DUMP { - int addr; + int addr; char *name; }; -struct REGISTER_DUMP d1[] = -{ +struct REGISTER_DUMP d1[] = { { 0x007e, "autodetect" }, { 0x0023, "C_AD_BITS " }, { 0x0038, "ADD_BITS " }, @@ -414,15 +442,23 @@ struct REGISTER_DUMP d1[] = * in the ioctl while doing the sound carrier & stereo detect */ -int msp3400c_thread(void *data) +static void msp3400c_stereo_wake(unsigned long data) +{ + struct msp3400c *msp = (struct msp3400c*)data; /* XXX alpha ??? */ + + if (!msp->active) + up(msp->wait); +} + +static int msp3400c_thread(void *data) { unsigned long flags; struct msp3400c *msp = data; struct semaphore sem = MUTEX_LOCKED; struct CARRIER_DETECT *cd; - int count, max1,max2,val1,val2, val,this, check_stereo; - int i; + int count, max1,max2,val1,val2, val,this; + int newstereo; /* lock_kernel(); */ @@ -442,57 +478,87 @@ int msp3400c_thread(void *data) if(msp->notify != NULL) up(msp->notify); - for (;;) - { + for (;;) { if (msp->rmmod) goto done; dprintk("msp3400: thread: sleep\n"); down_interruptible(&sem); dprintk("msp3400: thread: wakeup\n"); - if (msp->rmmod) + if (msp->rmmod || signal_pending(current)) goto done; -#if 0 - if (VIDEO_MODE_RADIO == msp->norm) - { - msp->active = 1; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ/10; - schedule(); - if (signal_pending(current)) - goto done; + + if (VIDEO_MODE_RADIO == msp->norm) + continue; /* nothing to do */ + + msp->active = 1; + + if (msp->watch_stereo) { + /* do that stereo/multilang handling */ LOCK_I2C_BUS(msp->bus); - val1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); - val2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c); + newstereo = msp->stereo; + switch (msp->mode) { + case MSP_MODE_FM_TERRA: + val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18); + dprintk("msp3400: stereo detect register: %d\n",val); + + if (val > 4096) { + newstereo = VIDEO_SOUND_STEREO; + } else if (val < -4096) { + newstereo = VIDEO_SOUND_LANG1; + } else { + newstereo = VIDEO_SOUND_MONO; + } + break; + case MSP_MODE_FM_NICAM1: + val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23); + switch ((val & 0x1e) >> 1) { + case 0: + case 8: + newstereo = VIDEO_SOUND_STEREO; + break; + default: + newstereo = VIDEO_SOUND_MONO; + break; + } + break; + } + if (msp->stereo != newstereo) { + dprintk("msp3400: watch: stereo %d ==> %d\n", + msp->stereo,newstereo); + msp3400c_setstereo(msp,newstereo); + } UNLOCK_I2C_BUS(msp->bus); - printk("msp3400: DC %d/%d\n",val1,val2); + if (msp->watch_stereo) { + del_timer(&msp->wake_stereo); + msp->wake_stereo.expires = jiffies + 5*HZ; + add_timer(&msp->wake_stereo); + } + msp->active = 0; continue; } -#endif - - if (VIDEO_MODE_RADIO == msp->norm) - continue; /* nothing to do */ - msp->active = 1; -restart: + restart: LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus, 0); + msp3400c_setvolume(msp->bus, 0, 0); msp3400c_setmode(msp, MSP_MODE_AM_DETECT); - val1 = val2 = max1 = max2 = check_stereo = 0; + val1 = val2 = 0; + max1 = max2 = -1; + del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; /* carrier detect pass #1 -- main carrier */ cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); - for (this = 0; this < count; this++) - { + for (this = 0; this < count; this++) { msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); UNLOCK_I2C_BUS(msp->bus); + current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + HZ/25; schedule(); if (signal_pending(current)) goto done; - if (msp->restart) - { + if (msp->restart) { msp->restart = 0; goto restart; } @@ -505,22 +571,20 @@ restart: } /* carrier detect pass #2 -- second (stereo) carrier */ - switch (max1) - { - case 1: /* 5.5 */ - cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); - break; - case 3: /* 6.5 */ - cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); - break; - case 0: /* 4.5 */ - case 2: /* 6.0 */ - default: - cd = NULL; count = 0; - break; + switch (max1) { + case 1: /* 5.5 */ + cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); + break; + case 3: /* 6.5 */ + cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); + break; + case 0: /* 4.5 */ + case 2: /* 6.0 */ + default: + cd = NULL; count = 0; + break; } - for (this = 0; this < count; this++) - { + for (this = 0; this < count; this++) { msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); UNLOCK_I2C_BUS(msp->bus); @@ -529,8 +593,7 @@ restart: schedule(); if (signal_pending(current)) goto done; - if (msp->restart) - { + if (msp->restart) { msp->restart = 0; goto restart; } @@ -542,106 +605,46 @@ restart: dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); } - /* programm the msp3400 according to the results */ - switch (max1) - { - case 0: /* 4.5 */ - case 1: /* 5.5 */ - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); - msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo, - carrier_detect_main[max1].cdo); - if (max2 == 0) - { - /* B/G FM-stereo */ - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); - check_stereo = 1; - } - if (max2 == 1 && msp->nicam) - { - /* B/G NICAM */ - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); - /* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */ - msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85), - MSP_CARRIER(5.5)); - check_stereo = 1; - } - break; - case 2: /* 6.0 */ - case 3: /* 6.5 */ - default: - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); - msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo, - carrier_detect_main[max1].cdo); - msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); - break; - } - - /* unmute */ - msp3400c_setvolume(msp->bus, msp->volume); - - if (check_stereo) - { - /* stereo available -- check current mode */ - UNLOCK_I2C_BUS(msp->bus); - - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ; - schedule(); - if (signal_pending(current)) - goto done; - if (msp->restart) - { - msp->restart = 0; - goto restart; + /* program the msp3400 according to the results */ + switch (max1) { + case 0: /* 4.5 */ + case 1: /* 5.5 */ + msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo, + carrier_detect_main[max1].cdo); + if (max2 == 0) { + /* B/G FM-stereo */ + msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp->watch_stereo = 1; } - - LOCK_I2C_BUS(msp->bus); - switch (msp->mode) - { - case MSP_MODE_FM_TERRA: - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18); - dprintk("msp3400: stereo detect register: %d\n",val); - - if (val > 4096) - { - msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); - } - else if (val < -4096) - { - msp3400c_setstereo(msp, VIDEO_SOUND_LANG1); - } - else - { - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); - } - break; - case MSP_MODE_FM_NICAM1: - val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23); - switch ((val & 0x1e) >> 1) - { - case 0: - case 8: - msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); - break; - default: - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); - break; - } - - /* dump registers (for debugging) */ - if (debug) - { - for (i=0; i<sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) - { - val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM, d1[i].addr); - printk(KERN_DEBUG "msp3400: %s = 0x%x\n", - d1[i].name,val); - } - } - break; + if (max2 == 1 && msp->nicam) { + /* B/G NICAM */ + msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + /* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */ + msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85), + MSP_CARRIER(5.5)); + msp->watch_stereo = 1; } + break; + case 2: /* 6.0 */ + case 3: /* 6.5 */ + default: + msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo, + carrier_detect_main[max1].cdo); + msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); + break; } + + /* unmute */ + msp3400c_setvolume(msp->bus, msp->left, msp->right); UNLOCK_I2C_BUS(msp->bus); + + if (msp->watch_stereo) { + del_timer(&msp->wake_stereo); + msp->wake_stereo.expires = jiffies + HZ; + add_timer(&msp->wake_stereo); + } msp->active = 0; } @@ -656,12 +659,15 @@ done: return 0; } -int msp3410d_thread(void *data) + +#if 0 /* not finished yet */ + +static int msp3410d_thread(void *data) { unsigned long flags; struct msp3400c *msp = data; struct semaphore sem = MUTEX_LOCKED; - int i, val; + int i, val; /* lock_kernel(); */ @@ -681,8 +687,7 @@ int msp3410d_thread(void *data) if(msp->notify != NULL) up(msp->notify); - for (;;) - { + for (;;) { if (msp->rmmod) goto done; dprintk("msp3410: thread: sleep\n"); @@ -696,7 +701,7 @@ int msp3410d_thread(void *data) msp->active = 1; -restart: + restart: LOCK_I2C_BUS(msp->bus); /* mute */ msp3400c_setvolume(msp->bus, 0); @@ -714,16 +719,14 @@ restart: schedule(); if (signal_pending(current)) goto done; - if (msp->restart) - { + if (msp->restart) { msp->restart = 0; goto restart; } LOCK_I2C_BUS(msp->bus); /* debug register dump */ - for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) - { + for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) { val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM,d1[i].addr); printk(KERN_DEBUG "msp3400: %s = 0x%x\n",d1[i].name,val); } @@ -744,6 +747,151 @@ done: up(msp->notify); return 0; } +#endif + +/* ----------------------------------------------------------------------- */ +/* mixer stuff -- with the modular sound driver in 2.1.x we can easily */ +/* register the msp3400 as mixer device */ + +#ifdef REGISTER_MIXER + +#include <linux/soundcard.h> +#include <../drivers/sound/sound_config.h> +#include <../drivers/sound/dev_table.h> + +static int mix_to_v4l(int i) +{ + int r; + + r = ((i & 0xff) * 65536 + 50) / 100; + if (r > 65535) r = 65535; + if (r < 0) r = 0; + return r; +} + +static int v4l_to_mix(int i) +{ + int r; + + r = (i * 100 + 32768) / 65536; + if (r > 100) r = 100; + if (r < 0) r = 0; + return r | (r << 8); +} + +static int v4l_to_mix2(int l, int r) +{ + r = (r * 100 + 32768) / 65536; + if (r > 100) r = 100; + if (r < 0) r = 0; + l = (l * 100 + 32768) / 65536; + if (l > 100) l = 100; + if (l < 0) l = 0; + return (r << 8) | l; +} + +static int msp3400c_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + struct msp3400c *msp = mixer_devs[dev]->devc; + unsigned long flags; + int ret,val = 0; + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if (get_user(val, (int *)arg)) + return -EFAULT; + + switch (cmd) { + case MIXER_READ(SOUND_MIXER_RECMASK): + case MIXER_READ(SOUND_MIXER_CAPS): + case MIXER_READ(SOUND_MIXER_RECSRC): + case MIXER_WRITE(SOUND_MIXER_RECSRC): + ret = 0; + break; + + case MIXER_READ(SOUND_MIXER_STEREODEVS): + ret = SOUND_MASK_VOLUME; + break; + case MIXER_READ(SOUND_MIXER_DEVMASK): + ret = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE; + break; + + case MIXER_WRITE(SOUND_MIXER_VOLUME): + msp->left = mix_to_v4l(val); + msp->right = mix_to_v4l(val >> 8); + LOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(msp->bus,msp->left,msp->right); + UNLOCK_I2C_BUS(msp->bus); + /* fall */ + case MIXER_READ(SOUND_MIXER_VOLUME): + ret = v4l_to_mix2(msp->left, msp->right); + break; + + case MIXER_WRITE(SOUND_MIXER_BASS): + msp->bass = mix_to_v4l(val); + LOCK_I2C_BUS(msp->bus); + msp3400c_setbass(msp->bus,msp->bass); + UNLOCK_I2C_BUS(msp->bus); + /* fall */ + case MIXER_READ(SOUND_MIXER_BASS): + ret = v4l_to_mix(msp->bass); + break; + + case MIXER_WRITE(SOUND_MIXER_TREBLE): + msp->treble = mix_to_v4l(val); + LOCK_I2C_BUS(msp->bus); + msp3400c_settreble(msp->bus,msp->treble); + UNLOCK_I2C_BUS(msp->bus); + /* fall */ + case MIXER_READ(SOUND_MIXER_TREBLE): + ret = v4l_to_mix(msp->treble); + break; + + default: + return -EINVAL; + } + if (put_user(ret, (int *)arg)) + return -EFAULT; + return 0; +} + +struct mixer_operations msp3400c_mixer = { + "video4linux", + "TV card sound (msp3400)", + msp3400c_mixer_ioctl +}; + +static int msp3400c_mixer_init(struct msp3400c *msp) +{ + int m; + + msp->mixer = m = sound_alloc_mixerdev(); + if (m == -1) + return -1; + + mixer_devs[m] = (struct mixer_operations *) + kmalloc(sizeof(struct mixer_operations), GFP_KERNEL); + if (mixer_devs[m] == NULL) { + printk(KERN_ERR "msp3400c: can't allocate memory\n"); + sound_unload_mixerdev(m); + return -1; + } + memcpy(mixer_devs[m],&msp3400c_mixer,sizeof(struct mixer_operations)); + mixer_devs[m]->devc = msp; + return 0; +} + +static int msp3400c_mixer_close(struct msp3400c *msp) +{ + int m = msp->mixer; + + if (m != -1 ) { + sound_unload_mixerdev(m); + kfree(mixer_devs[m]); + } + return 0; +} + +#endif /* ----------------------------------------------------------------------- */ @@ -752,35 +900,30 @@ static int msp3400c_attach(struct i2c_device *device) unsigned long flags; struct semaphore sem = MUTEX_LOCKED; struct msp3400c *msp; - int rev1,rev2; - - /* - * MSP3400's are for now only assumed to live on busses - * connected to a BT848. Adjust as and when you get new - * funky cards using these components. - */ - - if(device->bus->id != I2C_BUSID_BT848) - return -EINVAL; + int rev1,rev2; device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL); if (NULL == msp) return -ENOMEM; memset(msp,0,sizeof(struct msp3400c)); msp->bus = device->bus; - msp->volume = 65535; + msp->left = 65535; + msp->right = 65535; + msp->bass = 32768; + msp->treble = 32768; LOCK_I2C_BUS(msp->bus); - if (-1 == msp3400c_reset(msp->bus)) - { + if (-1 == msp3400c_reset(msp->bus)) { UNLOCK_I2C_BUS(msp->bus); kfree(msp); - return -1; + return -EIO; } msp3400c_setmode(msp, MSP_MODE_FM_TERRA); - msp3400c_setvolume(msp->bus, msp->volume); - + msp3400c_setvolume(msp->bus, msp->left, msp->right); + msp3400c_setbass(msp->bus, msp->bass); + msp3400c_settreble(msp->bus, msp->treble); + rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e); rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f); @@ -793,17 +936,28 @@ static int msp3400c_attach(struct i2c_device *device) sprintf(device->name,"MSP34%02d%c-%c%d", (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); msp->nicam = (((rev2>>8)&0xff) == 10) ? 1 : 0; - printk(KERN_INFO "msp3400: init: chip=%s%s\n", - device->name, msp->nicam ? ", can decode nicam" : ""); - MOD_INC_USE_COUNT; + /* timer for stereo checking */ + msp->wake_stereo.function = msp3400c_stereo_wake; + msp->wake_stereo.data = (unsigned long)msp; + /* startup control thread */ + MOD_INC_USE_COUNT; msp->notify = &sem; kernel_thread(msp3400c_thread, (void *)msp, 0); down(&sem); msp->notify = NULL; if (!msp->active) up(msp->wait); + + printk(KERN_INFO "msp3400: init: chip=%s",device->name); + if (msp->nicam) + printk(", has NICAM support"); +#ifdef REGISTER_MIXER + if (0 == msp3400c_mixer_init(msp)) + printk(", registered as sound mixer"); +#endif + printk("\n"); return 0; } @@ -813,14 +967,22 @@ static int msp3400c_detach(struct i2c_device *device) struct semaphore sem = MUTEX_LOCKED; struct msp3400c *msp = (struct msp3400c*)device->data; +#ifdef REGISTER_MIXER + msp3400c_mixer_close(msp); +#endif + /* shutdown control thread */ - msp->notify = &sem; - msp->rmmod = 1; - if (!msp->active) - up(msp->wait); - down(&sem); - msp->notify = NULL; - + del_timer(&msp->wake_stereo); + if (msp->thread) + { + msp->notify = &sem; + msp->rmmod = 1; + if (!msp->active) + up(msp->wait); + down(&sem); + msp->notify = NULL; + } + LOCK_I2C_BUS(msp->bus); msp3400c_reset(msp->bus); UNLOCK_I2C_BUS(msp->bus); @@ -834,67 +996,90 @@ static int msp3400c_command(struct i2c_device *device, unsigned int cmd, void *arg) { unsigned long flags; - struct msp3400c *msp = (struct msp3400c*)device->data; - int *iarg = (int*)arg; - - switch (cmd) - { - case MSP_SET_RADIO: - msp->norm = VIDEO_MODE_RADIO; - LOCK_I2C_BUS(msp->bus); - msp3400c_setmode(msp,MSP_MODE_FM_RADIO); - msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); - UNLOCK_I2C_BUS(msp->bus); - break; - case MSP_SET_TVNORM: - msp->norm = *iarg; - break; - case MSP_NEWCHANNEL: - if (!msp->active) - up(msp->wait); - else - msp->restart = 1; - break; + struct msp3400c *msp = (struct msp3400c*)device->data; + int *iarg = (int*)arg; - case MSP_GET_VOLUME: - *iarg = msp->volume; - break; - case MSP_SET_VOLUME: - msp->volume = *iarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus,msp->volume); - UNLOCK_I2C_BUS(msp->bus); - break; + switch (cmd) { + case MSP_SET_RADIO: + msp->norm = VIDEO_MODE_RADIO; + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + LOCK_I2C_BUS(msp->bus); + msp3400c_setmode(msp,MSP_MODE_FM_RADIO); + msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); + UNLOCK_I2C_BUS(msp->bus); + break; + case MSP_SET_TVNORM: + msp->norm = *iarg; + break; + case MSP_NEWCHANNEL: + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + if (!msp->active) + up(msp->wait); + else + msp->restart = 1; + break; + + case MSP_GET_VOLUME: + *iarg = (msp->left > msp->right) ? msp->left : msp->right; + break; + case MSP_SET_VOLUME: + msp->left = msp->right = *iarg; + LOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(msp->bus,msp->left, msp->right); + UNLOCK_I2C_BUS(msp->bus); + break; - case MSP_GET_STEREO: - *iarg = msp->stereo; - break; - case MSP_SET_STEREO: - if (*iarg) - { - LOCK_I2C_BUS(msp->bus); - msp3400c_setstereo(msp,*iarg); - UNLOCK_I2C_BUS(msp->bus); - } - break; + case MSP_GET_BASS: + *iarg = msp->bass; + break; + case MSP_SET_BASS: + msp->bass = *iarg; + LOCK_I2C_BUS(msp->bus); + msp3400c_setbass(msp->bus,msp->bass); + UNLOCK_I2C_BUS(msp->bus); + break; - case MSP_GET_DC: + case MSP_GET_TREBLE: + *iarg = msp->treble; + break; + case MSP_SET_TREBLE: + msp->treble = *iarg; + LOCK_I2C_BUS(msp->bus); + msp3400c_settreble(msp->bus,msp->treble); + UNLOCK_I2C_BUS(msp->bus); + break; + + case MSP_GET_STEREO: + *iarg = msp->stereo; + break; + case MSP_SET_STEREO: + if (*iarg) { + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); LOCK_I2C_BUS(msp->bus); - *iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) + - (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c); + msp3400c_setstereo(msp,*iarg); UNLOCK_I2C_BUS(msp->bus); - break; + } + break; + + case MSP_GET_DC: + LOCK_I2C_BUS(msp->bus); + *iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) + + (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c); + UNLOCK_I2C_BUS(msp->bus); + break; - default: - return -EINVAL; + default: + return -EINVAL; } return 0; } /* ----------------------------------------------------------------------- */ -struct i2c_driver i2c_driver_msp = -{ +struct i2c_driver i2c_driver_msp = { "msp3400", /* name */ I2C_DRIVERID_MSP3400, /* ID */ I2C_MSP3400C, I2C_MSP3400C, /* addr range */ @@ -904,10 +1089,12 @@ struct i2c_driver i2c_driver_msp = msp3400c_command }; +EXPORT_NO_SYMBOLS; + #ifdef MODULE int init_module(void) #else -int msp3400c_init(void) + int msp3400c_init(void) #endif { i2c_register_driver(&i2c_driver_msp); @@ -921,3 +1108,10 @@ void cleanup_module(void) } #endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/char/msp3400.h b/drivers/char/msp3400.h index 1b4eedab0..e4029c03e 100644 --- a/drivers/char/msp3400.h +++ b/drivers/char/msp3400.h @@ -15,4 +15,9 @@ #define MSP_GET_DC _IOW('m',8,int) +#define MSP_GET_BASS _IOR('m', 9,int) +#define MSP_SET_BASS _IOW('m',10,int) +#define MSP_GET_TREBLE _IOR('m',11,int) +#define MSP_SET_TREBLE _IOW('m',12,int) + #endif /* MSP3400_H */ diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index a434cf405..1cf7e9d01 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -190,7 +190,7 @@ static int opost(unsigned char c, struct tty_struct *tty) * opost_block --- to speed up block console writes, among other * things. */ -static int opost_block(struct tty_struct * tty, +static ssize_t opost_block(struct tty_struct * tty, const unsigned char * inbuf, unsigned int nr) { char buf[80]; @@ -863,9 +863,9 @@ static inline void copy_from_read_buf(struct tty_struct *tty, static ssize_t read_chan(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) { + unsigned char *b = buf; struct wait_queue wait = { current, NULL }; int c; - unsigned char *b = buf; int minimum, time; ssize_t retval = 0; ssize_t size; @@ -896,25 +896,23 @@ do_it_again: } } - if (tty->icanon) { - minimum = time = 0; - current->timeout = (unsigned long) -1; - } else { + minimum = time = 0; + current->timeout = (unsigned long) -1; + if (!tty->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { - current->timeout = (unsigned long) -1; if (time) tty->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || (tty->minimum_to_wake > minimum)) tty->minimum_to_wake = minimum; } else { + current->timeout = 0; if (time) { current->timeout = time + jiffies; time = 0; - } else - current->timeout = 0; + } tty->minimum_to_wake = minimum = 1; } } @@ -922,10 +920,10 @@ do_it_again: add_wait_queue(&tty->read_wait, &wait); disable_bh(TQUEUE_BH); - while (1) { + while (nr) { /* First test for status change. */ if (tty->packet && tty->link->ctrl_status) { - if (b != buf || !nr) + if (b != buf) break; put_user(tty->link->ctrl_status, b++); nr--; @@ -966,7 +964,7 @@ do_it_again: current->state = TASK_RUNNING; /* Deal with packet mode. */ - if (tty->packet && b == buf && nr) { + if (tty->packet && b == buf) { put_user(TIOCPKT_DATA, b++); nr--; } @@ -1013,7 +1011,7 @@ do_it_again: if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) check_unthrottle(tty); - if (b - buf >= minimum || !nr) + if (b - buf >= minimum) break; if (time) current->timeout = time + jiffies; @@ -1027,25 +1025,27 @@ do_it_again: current->state = TASK_RUNNING; current->timeout = 0; size = b - buf; - if (size && nr) - clear_bit(TTY_PUSH, &tty->flags); - if (!size && test_and_clear_bit(TTY_PUSH, &tty->flags)) - goto do_it_again; - if (!size && !retval) - clear_bit(TTY_PUSH, &tty->flags); - return (size ? size : retval); + if (size) { + retval = size; + if (nr) + clear_bit(TTY_PUSH, &tty->flags); + } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) + goto do_it_again; + + return retval; } static ssize_t write_chan(struct tty_struct * tty, struct file * file, const unsigned char * buf, size_t nr) { + const unsigned char *b = buf; struct wait_queue wait = { current, NULL }; int c; - const unsigned char *b = buf; - ssize_t retval = 0, num; + ssize_t retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && + if (L_TOSTOP(tty) && + file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) { retval = tty_check_change(tty); if (retval) @@ -1065,7 +1065,11 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file, } if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { while (nr > 0) { - num = opost_block(tty, b, nr); + ssize_t num = opost_block(tty, b, nr); + if (num < 0) { + retval = num; + goto break_out; + } b += num; nr -= num; if (nr == 0) @@ -1081,7 +1085,7 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file, c = tty->driver.write(tty, 1, b, nr); if (c < 0) { retval = c; - break; + goto break_out; } b += c; nr -= c; @@ -1094,6 +1098,7 @@ static ssize_t write_chan(struct tty_struct * tty, struct file * file, } schedule(); } +break_out: current->state = TASK_RUNNING; remove_wait_queue(&tty->write_wait, &wait); return (b - buf) ? b - buf : retval; diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index fba8c7d51..a3353f4af 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -156,7 +156,7 @@ static __inline__ void nvram_set_checksum_int( void ) * * They're only built if CONFIG_ATARI is defined, because Atari drivers use * them. For other configurations (PC), the rest of the kernel can't rely on - * them being present (this driver couldn't be configured at all, or as a + * them being present (this driver may not be configured at all, or as a * module), so they access config information themselves. */ @@ -227,12 +227,15 @@ static long long nvram_llseek(struct file *file,loff_t offset, int origin ) return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL ); } -static ssize_t nvram_read( struct file * file, - char * buf, size_t count, loff_t *ppos ) +static ssize_t nvram_read(struct file * file, + char * buf, size_t count, loff_t *ppos ) { unsigned long flags; unsigned i = *ppos; char *tmp = buf; + + if (i != *ppos) + return -EINVAL; save_flags(flags); cli(); @@ -250,12 +253,16 @@ static ssize_t nvram_read( struct file * file, return( tmp - buf ); } -static ssize_t nvram_write( struct file * file, const char * buf, size_t count, loff_t *ppos ) +static ssize_t nvram_write(struct file * file, + const char * buf, size_t count, loff_t *ppos ) { unsigned long flags; unsigned i = *ppos; const char *tmp = buf; char c; + + if (i != *ppos) + return -EINVAL; save_flags(flags); cli(); @@ -411,7 +418,7 @@ __initfunc(int nvram_init(void)) if (!CHECK_DRIVER_INIT()) return( -ENXIO ); - printk( "Non-volatile memory driver v%s\n", NVRAM_VERSION ); + printk(KERN_INFO "Non-volatile memory driver v%s\n", NVRAM_VERSION ); misc_register( &nvram_dev ); #ifdef CONFIG_PROC_FS if ((proc_nvram = create_proc_entry( "nvram", 0, 0 ))) @@ -618,7 +625,7 @@ static char *colors[] = { #define fieldsize(a) (sizeof(a)/sizeof(*a)) static int atari_proc_infos( unsigned char *nvram, char *buffer, int *len, - off_t *begin, off_t offset, int size ) + off_t *begin, off_t offset, int size ) { int checksum = nvram_check_checksum(); int i; @@ -645,7 +652,7 @@ static int atari_proc_infos( unsigned char *nvram, char *buffer, int *len, /* the following entries are defined only for the Falcon */ if ((atari_mch_cookie >> 16) != ATARI_MCH_FALCON) - return; + return 1; PRINT_PROC( "OS language : " ); if (nvram[6] < fieldsize(languages)) diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index c466ae4a4..67d652aab 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -37,7 +37,7 @@ */ /* - * Some x86 BIOSes do not correctly initializes the keyboard, so the + * Some x86 BIOSes do not correctly initialize the keyboard, so the * "kbd-reset" command line options can be given to force a reset. * [Ranger] */ @@ -155,6 +155,19 @@ static char *initialize_kbd2(void) | KBD_MODE_DISABLE_MOUSE | KBD_MODE_KCC); + /* ibm powerpc portables need this to use scan-code set 1 -- Cort */ + kbd_write_command(KBD_CCMD_READ_MODE); + if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { + /* + * If the controller does not support conversion, + * Set the keyboard to scan-code set 1. + */ + kbd_write_output(0xF0); + kbd_wait_for_input(); + kbd_write_output(0x01); + kbd_wait_for_input(); + } + kbd_write_output(KBD_CMD_ENABLE); if (kbd_wait_for_input() != KBD_REPLY_ACK) return "Enable keyboard: no ACK"; @@ -187,7 +200,7 @@ void initialize_kbd(void) -unsigned char kbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */ +unsigned char pckbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */ /* used only by send_data - set by keyboard_interrupt */ static volatile unsigned char reply_expected = 0; @@ -208,7 +221,7 @@ static inline void kb_wait(void) do { if (! (kbd_read_status() & KBD_STAT_IBF)) return; - udelay(1000); + mdelay(1); timeout--; } while (timeout); #ifdef KBD_REPORT_TIMEOUTS @@ -522,7 +535,7 @@ static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) unsigned char scancode; /* mouse data? */ - if (status & kbd_read_mask & KBD_STAT_MOUSE_OBF) { + if (status & pckbd_read_mask & KBD_STAT_MOUSE_OBF) { #if defined(CONFIG_SGI) && defined(CONFIG_PSMOUSE) scancode = kbd_read_input(); aux_interrupt(status, scancode); @@ -566,7 +579,7 @@ static int send_data(unsigned char data) return 1; if (resend) break; - udelay(1000); + mdelay(1); if (!--timeout) { #ifdef KBD_REPORT_TIMEOUTS printk(KERN_WARNING "Keyboard timeout\n"); diff --git a/drivers/char/pc_keyb.h b/drivers/char/pc_keyb.h index 1bd7ab8b2..4357e099f 100644 --- a/drivers/char/pc_keyb.h +++ b/drivers/char/pc_keyb.h @@ -23,7 +23,7 @@ * Internal variables of the driver */ -extern unsigned char kbd_read_mask; +extern unsigned char pckbd_read_mask; extern unsigned char aux_device_present; /* diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c index 12ad2e53c..4bd0f0f29 100644 --- a/drivers/char/pcwd.c +++ b/drivers/char/pcwd.c @@ -126,7 +126,7 @@ __initfunc(static int pcwd_checkcard(void)) /* Sleep 1/2 second (or 500000 microseconds :) */ - udelay(500000L); + mdelay(500); done = 0; /* If there's a heart beat in both instances, then this means we @@ -455,7 +455,7 @@ __initfunc(static int send_command(int cmd)) int i; outb_p(cmd, current_readport + 2); - udelay(1000L); + mdelay(1); i = inb(current_readport); i = inb(current_readport); diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index eecdaee5a..9fe8bcc23 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -619,7 +619,7 @@ static void pcxe_close(struct tty_struct * tty, struct file * filp) ** worth noting that while I'm not sure what this hunk of code is supposed ** to do, it is not present in the serial.c driver. Hmmm. If you know, ** please send me a note. brian@ilinx.com -** Dont know either what this is supposed to do clameter@waterf.org. +** Don't know either what this is supposed to do clameter@waterf.org. */ if(tty->ldisc.num != ldiscs[N_TTY].num) { if(tty->ldisc.close) diff --git a/drivers/char/pms.c b/drivers/char/pms.c index c17cb20ae..81c8fbb79 100644 --- a/drivers/char/pms.c +++ b/drivers/char/pms.c @@ -630,7 +630,7 @@ static int pms_capture(struct pms_device *dev, char *buf, int rgb555, int count) while (cnt <= 0) { /* - * Dont copy too far + * Don't copy too far */ int dt=dw; if(dt+len>count) @@ -893,6 +893,7 @@ struct video_device pms_template= pms_close, pms_read, pms_write, + NULL, /* FIXME - we can use POLL on this board with the irq */ pms_ioctl, NULL, pms_init_done, diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index 29f86263b..17c6c5383 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -636,7 +636,7 @@ __initfunc(int psaux_init(void)) printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n"); aux_present = 1; #ifdef CONFIG_VT - kbd_read_mask = AUX_STAT_OBF; + pckbd_read_mask = AUX_STAT_OBF; #endif } else { return -EIO; diff --git a/drivers/char/pty.c b/drivers/char/pty.c index fb136c3e8..a7ea98497 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -7,6 +7,7 @@ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 */ +#include <linux/config.h> #include <linux/module.h> /* For EXPORT_SYMBOL */ #include <linux/errno.h> @@ -35,9 +36,9 @@ struct pty_struct { #define PTY_MAGIC 0x5001 static struct tty_driver pty_driver, pty_slave_driver; -static struct tty_driver old_pty_driver, old_pty_slave_driver; static int pty_refcount; +/* Note: one set of tables for BSD and one for Unix98 */ static struct tty_struct *pty_table[NR_PTYS]; static struct termios *pty_termios[NR_PTYS]; static struct termios *pty_termios_locked[NR_PTYS]; @@ -46,6 +47,20 @@ static struct termios *ttyp_termios[NR_PTYS]; static struct termios *ttyp_termios_locked[NR_PTYS]; static struct pty_struct pty_state[NR_PTYS]; +#ifdef CONFIG_UNIX98_PTYS +/* These are global because they are accessed in tty_io.c */ +struct tty_driver ptm_driver[UNIX98_NR_MAJORS]; +struct tty_driver pts_driver[UNIX98_NR_MAJORS]; + +static struct tty_struct *ptm_table[UNIX98_NR_MAJORS][NR_PTYS]; +static struct termios *ptm_termios[UNIX98_NR_MAJORS][NR_PTYS]; +static struct termios *ptm_termios_locked[UNIX98_NR_MAJORS][NR_PTYS]; +static struct tty_struct *pts_table[UNIX98_NR_MAJORS][NR_PTYS]; +static struct termios *pts_termios[UNIX98_NR_MAJORS][NR_PTYS]; +static struct termios *pts_termios_locked[UNIX98_NR_MAJORS][NR_PTYS]; +static struct pty_struct ptm_state[UNIX98_NR_MAJORS][NR_PTYS]; +#endif + #define MIN(a,b) ((a) < (b) ? (a) : (b)) static void pty_close(struct tty_struct * tty, struct file * filp) @@ -71,7 +86,15 @@ static void pty_close(struct tty_struct * tty, struct file * filp) if (tty->driver.subtype == PTY_TYPE_MASTER) { tty_hangup(tty->link); set_bit(TTY_OTHER_CLOSED, &tty->flags); - devpts_pty_kill(MINOR(tty->device) - tty->driver.minor_start); +#ifdef CONFIG_UNIX98_PTYS + { + unsigned int major = MAJOR(tty->device) - UNIX98_PTY_MASTER_MAJOR; + if ( major < UNIX98_NR_MAJORS ) { + devpts_pty_kill( MINOR(tty->device) + - tty->driver.minor_start + tty->driver.name_base ); + } + } +#endif } } @@ -200,16 +223,20 @@ static int pty_chars_in_buffer(struct tty_struct *tty) return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); } -/* - * Return the minor device number of a given pty. This lets us - * open a master pty with the multi-headed ptmx device, then - * find out which one we got after it is open, with an ioctl. +/* + * Return the device number of a Unix98 PTY (only!). This lets us open a + * master pty with the multi-headed ptmx device, then find out which + * one we got after it is open, with an ioctl. */ -static int pty_get_device_minor(struct tty_struct *tty, unsigned int *value) +#ifdef CONFIG_UNIX98_PTYS +static int pty_get_device_number(struct tty_struct *tty, unsigned int *value) { - unsigned int result = MINOR(tty->device); + unsigned int result = MINOR(tty->device) + - tty->driver.minor_start + tty->driver.name_base; return put_user(result, value); } +#endif + /* Set the lock flag on a pty */ static int pty_set_lock(struct tty_struct *tty, int * arg) { @@ -223,22 +250,37 @@ static int pty_set_lock(struct tty_struct *tty, int * arg) return 0; } -static int pty_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) +static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { if (!tty) { printk("pty_ioctl called with NULL tty!\n"); return -EIO; } switch(cmd) { - case TIOCGPTN: /* Get PT Number */ - return pty_get_device_minor(tty, (unsigned int *)arg); case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int *) arg); } return -ENOIOCTLCMD; } +#ifdef CONFIG_UNIX98_PTYS +static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + if (!tty) { + printk("pty_unix98_ioctl called with NULL tty!\n"); + return -EIO; + } + switch(cmd) { + case TIOCGPTN: /* Get PT Number */ + return pty_get_device_number(tty, (unsigned int *)arg); + } + + return pty_bsd_ioctl(tty,file,cmd,arg); +} +#endif + static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; @@ -267,7 +309,7 @@ static int pty_open(struct tty_struct *tty, struct file * filp) line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_PTYS)) goto out; - pty = pty_state + line; + pty = (struct pty_struct *)(tty->driver.driver_state) + line; tty->driver_data = pty; retval = -EIO; @@ -294,6 +336,10 @@ static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios) __initfunc(int pty_init(void)) { + int i; + + /* Traditional BSD devices */ + memset(&pty_state, 0, sizeof(pty_state)); memset(&pty_driver, 0, sizeof(struct tty_driver)); pty_driver.magic = TTY_DRIVER_MAGIC; @@ -314,6 +360,7 @@ __initfunc(int pty_init(void)) pty_driver.table = pty_table; pty_driver.termios = pty_termios; pty_driver.termios_locked = pty_termios_locked; + pty_driver.driver_state = pty_state; pty_driver.other = &pty_slave_driver; pty_driver.open = pty_open; @@ -337,37 +384,60 @@ __initfunc(int pty_init(void)) pty_slave_driver.table = ttyp_table; pty_slave_driver.termios = ttyp_termios; pty_slave_driver.termios_locked = ttyp_termios_locked; + pty_slave_driver.driver_state = pty_state; pty_slave_driver.other = &pty_driver; - old_pty_driver = pty_driver; - old_pty_driver.driver_name = "compat_pty_master"; - old_pty_driver.proc_entry = 0; - old_pty_driver.major = TTY_MAJOR; - old_pty_driver.minor_start = 128; - old_pty_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS; - old_pty_driver.other = &old_pty_slave_driver; - - old_pty_slave_driver = pty_slave_driver; - old_pty_slave_driver.driver_name = "compat_pty_slave"; - old_pty_slave_driver.proc_entry = 0; - old_pty_slave_driver.major = TTY_MAJOR; - old_pty_slave_driver.minor_start = 192; - old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS; - old_pty_slave_driver.other = &old_pty_driver; - - /* only the master pty gets this ioctl (which is why we - * assign it here, instead of up with the rest of the - * pty_driver initialization. <cananian@alumni.princeton.edu> - */ - pty_driver.ioctl = pty_ioctl; - if (tty_register_driver(&pty_driver)) panic("Couldn't register pty driver"); if (tty_register_driver(&pty_slave_driver)) panic("Couldn't register pty slave driver"); - if (tty_register_driver(&old_pty_driver)) - panic("Couldn't register compat pty driver"); - if (tty_register_driver(&old_pty_slave_driver)) - panic("Couldn't register compat pty slave driver"); + + /* + * only the master pty gets this ioctl (which is why we + * assign it here, instead of up with the rest of the + * pty_driver initialization. <cananian@alumni.princeton.edu> + */ + pty_driver.ioctl = pty_bsd_ioctl; + + /* Unix98 devices */ +#ifdef CONFIG_UNIX98_PTYS + printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS); + for ( i = 0 ; i < UNIX98_NR_MAJORS ; i++ ) { + ptm_driver[i] = pty_driver; + ptm_driver[i].name = "ptm"; + ptm_driver[i].proc_entry = 0; + ptm_driver[i].major = UNIX98_PTY_MASTER_MAJOR+i; + ptm_driver[i].minor_start = 0; + ptm_driver[i].name_base = i*NR_PTYS; + ptm_driver[i].num = NR_PTYS; + ptm_driver[i].other = &pts_driver[i]; + ptm_driver[i].table = ptm_table[i]; + ptm_driver[i].termios = ptm_termios[i]; + ptm_driver[i].termios_locked = ptm_termios_locked[i]; + ptm_driver[i].driver_state = ptm_state[i]; + + pts_driver[i] = pty_slave_driver; + pts_driver[i].name = "pts"; + pts_driver[i].proc_entry = 0; + pts_driver[i].major = UNIX98_PTY_SLAVE_MAJOR+i; + pts_driver[i].minor_start = 0; + pts_driver[i].name_base = i*NR_PTYS; + pts_driver[i].num = ptm_driver[i].num; + pts_driver[i].other = &ptm_driver[i]; + pts_driver[i].table = pts_table[i]; + pts_driver[i].termios = pts_termios[i]; + pts_driver[i].termios_locked = pts_termios_locked[i]; + pts_driver[i].driver_state = ptm_state[i]; + + ptm_driver[i].ioctl = pty_unix98_ioctl; + + if (tty_register_driver(&ptm_driver[i])) + panic("Couldn't register Unix98 ptm driver major %d", + ptm_driver[i].major); + if (tty_register_driver(&pts_driver[i])) + panic("Couldn't register Unix98 pts driver major %d", + pts_driver[i].major); + } +#endif return 0; } diff --git a/drivers/char/radio-aimslab.c b/drivers/char/radio-aimslab.c new file mode 100644 index 000000000..014def0c4 --- /dev/null +++ b/drivers/char/radio-aimslab.c @@ -0,0 +1,373 @@ +/* radiotrack (radioreveal) driver for Linux radio support + * (c) 1997 M. Kirkwood + * Coverted to new API by Alan Cox <Alan.Cox@linux.org> + * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> + * + * TODO: Allow for more than one of these foolish entities :-) + * + * Notes on the hardware (reverse engineered from other peoples' + * reverse engineering of AIMS' code :-) + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * + * The signal strength query is unsurprisingly inaccurate. And it seems + * to indicate that (on my card, at least) the frequency setting isn't + * too great. (I have to tune up .025MHz from what the freq should be + * to get a report that the thing is tuned.) + * + * Volume control is (ugh) analogue: + * out(port, start_increasing_volume); + * wait(a_wee_while); + * out(port, stop_changing_the_volume); + * + */ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/delay.h> /* udelay */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT */ + +#ifndef CONFIG_RADIO_RTRACK_PORT +#define CONFIG_RADIO_RTRACK_PORT -1 +#endif + +static int io = CONFIG_RADIO_RTRACK_PORT; +static int users = 0; + +struct rt_device +{ + int port; + int curvol; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +static void sleep_delay(long n) +{ + /* Sleep nicely for 'n' uS */ + int d=n/(1000000/HZ); + if(!d) + udelay(n); + else + { + /* Yield CPU time */ + unsigned long x=jiffies; + while((jiffies-x)<=d) + schedule(); + } +} + +static void rt_decvol(void) +{ + outb(0x58, io); /* volume down + sigstr + on */ + sleep_delay(100000); + outb(0xd8, io); /* volume steady + sigstr + on */ +} + +static void rt_incvol(void) +{ + outb(0x98, io); /* volume up + sigstr + on */ + sleep_delay(100000); + outb(0xd8, io); /* volume steady + sigstr + on */ +} + +static void rt_mute(struct rt_device *dev) +{ + dev->muted = 1; + outb(0xd0, io); /* volume steady, off */ +} + +static int rt_setvol(struct rt_device *dev, int vol) +{ + int i; + + if(vol == dev->curvol) { /* requested volume = current */ + if (dev->muted) { /* user is unmuting the card */ + dev->muted = 0; + outb (0xd8, io); /* enable card */ + } + + return 0; + } + + if(vol == 0) { /* volume = 0 means mute the card */ + outb(0x48, io); /* volume down but still "on" */ + sleep_delay(2000000); /* make sure it's totally down */ + outb(0xd0, io); /* volume steady, off */ + return 0; + } + + dev->muted = 0; + if(vol > dev->curvol) + for(i = dev->curvol; i < vol; i++) + rt_incvol(); + else + for(i = dev->curvol; i > vol; i--) + rt_decvol(); + + dev->curvol = vol; + + return 0; +} + +/* the 128+64 on these outb's is to keep the volume stable while tuning + * without them, the volume _will_ creep up with each frequency change + * and bit 4 (+16) is to keep the signal strength meter enabled + */ + +void send_0_byte(int port, struct rt_device *dev) +{ + if ((dev->curvol == 0) || (dev->muted)) { + outb_p(128+64+16+ 1, port); /* wr-enable + data low */ + outb_p(128+64+16+2+1, port); /* clock */ + } + else { + outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */ + outb_p(128+64+16+8+2+1, port); /* clock */ + } + sleep_delay(1000); +} + +void send_1_byte(int port, struct rt_device *dev) +{ + if ((dev->curvol == 0) || (dev->muted)) { + outb_p(128+64+16+4 +1, port); /* wr-enable+data high */ + outb_p(128+64+16+4+2+1, port); /* clock */ + } + else { + outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */ + outb_p(128+64+16+8+4+2+1, port); /* clock */ + } + + sleep_delay(1000); +} + +static int rt_setfreq(struct rt_device *dev, unsigned long freq) +{ + int i; + + /* adapted from radio-aztech.c */ + + /* We want to compute x * 100 / 16 without overflow + * So we compute x*6 + (x/100)*25 to give x*6.25 + */ + + freq = freq * 6 + freq/4; /* massage the data a little */ + freq += 1070; /* IF = 10.7 MHz */ + freq /= 5; /* ref = 25 kHz */ + + send_0_byte (io, dev); /* 0: LSB of frequency */ + + for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ + if (freq & (1 << i)) + send_1_byte (io, dev); + else + send_0_byte (io, dev); + + send_0_byte (io, dev); /* 14: test bit - always 0 */ + send_0_byte (io, dev); /* 15: test bit - always 0 */ + + send_0_byte (io, dev); /* 16: band data 0 - always 0 */ + send_0_byte (io, dev); /* 17: band data 1 - always 0 */ + send_0_byte (io, dev); /* 18: band data 2 - always 0 */ + send_0_byte (io, dev); /* 19: time base - always 0 */ + + send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz) */ + send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz) */ + send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz) */ + send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */ + + if ((dev->curvol == 0) || (dev->muted)) + outb (0xd0, io); /* volume steady + sigstr */ + else + outb (0xd8, io); /* volume steady + sigstr + on */ + + return 0; +} + +int rt_getsigstr(struct rt_device *dev) +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int rt_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct rt_device *rt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow=(88*16); + v.rangehigh=(108*16); + v.flags=0; + v.mode=VIDEO_MODE_AUTO; + v.signal=0xFFFF*rt_getsigstr(rt); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq))) + return -EFAULT; + rt_setfreq(rt, rt->curfreq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + v.volume=rt->curvol * 6554; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + + if(v.flags&VIDEO_AUDIO_MUTE) + rt_mute(rt); + else + rt_setvol(rt,v.volume/6554); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int rt_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void rt_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct rt_device rtrack_unit; + +static struct video_device rtrack_radio= +{ + "RadioTrack radio", + VID_TYPE_TUNER, + VID_HARDWARE_RTRACK, + rt_open, + rt_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* No poll */ + rt_ioctl, + NULL, + NULL +}; + +__initfunc(int rtrack_init(struct video_init *v)) +{ + if (check_region(io, 2)) + { + printk(KERN_ERR "rtrack: port 0x%x already in use\n", io); + return -EBUSY; + } + + rtrack_radio.priv=&rtrack_unit; + + if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io, 2, "rtrack"); + printk(KERN_INFO "AIMSlab Radiotrack/radioreveal card driver.\n"); + + /* mute card - prevents noisy bootups */ + + /* this ensures that the volume is all the way down */ + outb(0x48, io); /* volume down but still "on" */ + sleep_delay(2000000); /* make sure it's totally down */ + outb(0xc0, io); /* steady volume, mute card */ + rtrack_unit.curvol = 0; + + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("M.Kirkwood"); +MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + return rtrack_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&rtrack_radio); + release_region(io,2); +} + +#endif diff --git a/drivers/char/radio-aztech.c b/drivers/char/radio-aztech.c new file mode 100644 index 000000000..aaeaa7265 --- /dev/null +++ b/drivers/char/radio-aztech.c @@ -0,0 +1,324 @@ +/* aztech.c - Aztech radio card driver for Linux 2.1 by Russell Kroll + * + * Heavily modified to support the new 2.1 radio card interfaces by + * Russell Kroll (rkroll@exploits.org) + * + * Based on code by + * + * Quay Ly + * Donald Song + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) + * William McGrath (wmcgrath@twilight.vtc.vsc.edu) + * + * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ + * along with more information on the card itself. + * + * Notable changes from the original source: + * - includes stripped down to the essentials + * - for loops used as delays replaced with udelay() + * - #defines removed, changed to static values + * - tuning structure changed - no more character arrays, other changes +*/ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/delay.h> /* udelay */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_AZTECH_PORT */ + +/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ + +#ifndef CONFIG_RADIO_AZTECH_PORT +#define CONFIG_RADIO_AZTECH_PORT -1 +#endif + +static int io = CONFIG_RADIO_AZTECH_PORT; +static int radio_wait_time = 1000; +static int users = 0; + +struct az_device +{ + int curvol; + unsigned long curfreq; + int stereo; +}; + +static int volconvert(int level) +{ + level>>=14; /* Map 16bits down to 2 bit */ + level&=3; + + /* convert to card-friendly values */ + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return 4; + case 3: + return 5; + } + return 0; /* Quieten gcc */ +} + +static void send_0_byte (struct az_device *dev) +{ + udelay(radio_wait_time); + outb_p(2+volconvert(dev->curvol), io); + outb_p(64+2+volconvert(dev->curvol), io); +} + +static void send_1_byte (struct az_device *dev) +{ + udelay (radio_wait_time); + outb_p(128+2+volconvert(dev->curvol), io); + outb_p(128+64+2+volconvert(dev->curvol), io); +} + +static int az_setvol(struct az_device *dev, int vol) +{ + outb (volconvert(vol), io); + return 0; +} + +/* thanks to Michael Dwyer for giving me a dose of clues in + * the signal strength department.. + * + * This card has a stereo bit - bit 0 set = mono, not set = stereo + * It also has a "signal" bit - bit 1 set = bad signal, not set = good + * + */ + +static int az_getsigstr(struct az_device *dev) +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int az_getstereo(struct az_device *dev) +{ + if (inb(io) & 1) /* bit set = mono */ + return 0; + return 1; /* stereo */ +} + +static int az_setfreq(struct az_device *dev, unsigned long frequency) +{ + int i; + + /* 6.25 * */ + frequency = frequency*6 + frequency/4; /* massage data a bit */ + + frequency += 1070; /* tuning needs 24 data bits */ + frequency /= 5; + + send_0_byte (dev); /* 0: LSB of frequency */ + + for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ + if (frequency & (1 << i)) + send_1_byte (dev); + else + send_0_byte (dev); + + send_0_byte (dev); /* 14: test bit - always 0 */ + send_0_byte (dev); /* 15: test bit - always 0 */ + send_0_byte (dev); /* 16: band data 0 - always 0 */ + if (dev->stereo) /* 17: stereo (1 to enable) */ + send_1_byte (dev); + else + send_0_byte (dev); + + send_1_byte (dev); /* 18: band data 1 - unknown */ + send_0_byte (dev); /* 19: time base - always 0 */ + send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */ + send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */ + send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */ + send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */ + + /* latch frequency */ + + udelay (radio_wait_time); + outb_p(128+64+volconvert(dev->curvol), io); + + return 0; +} + +static int az_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct az_device *az=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow=(879*16)/10; + v.rangehigh=(1078*16)/10; + v.flags=0; + v.mode=VIDEO_MODE_AUTO; + v.signal=0xFFFF*az_getsigstr(az); + if(az_getstereo(az)) + v.flags|=VIDEO_TUNER_STEREO_ON; + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if(copy_to_user(arg, &az->curfreq, sizeof(az->curfreq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if(copy_from_user(&az->curfreq, arg,sizeof(az->curfreq))) + return -EFAULT; + az_setfreq(az, az->curfreq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + if(az->stereo) + v.mode=VIDEO_SOUND_STEREO; + else + v.mode=VIDEO_SOUND_MONO; + v.volume=az->curvol; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + az->curvol=v.volume; + + az->stereo=(v.mode&VIDEO_SOUND_STEREO)?1:0; + if(v.flags&VIDEO_AUDIO_MUTE) + az_setvol(az,0); + else + az_setvol(az,az->curvol); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int az_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void az_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct az_device aztech_unit; + +static struct video_device aztech_radio= +{ + "Aztech radio", + VID_TYPE_TUNER, + VID_HARDWARE_AZTECH, + az_open, + az_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* No poll */ + az_ioctl, + NULL, + NULL +}; + +__initfunc(int aztech_init(struct video_init *v)) +{ + if (check_region(io, 2)) + { + printk(KERN_ERR "aztech: port 0x%x already in use\n", io); + return -EBUSY; + } + + aztech_radio.priv=&aztech_unit; + + if(video_register_device(&aztech_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io, 2, "aztech"); + printk(KERN_INFO "Aztech radio card driver v0.40/19980422 rkroll@exploits.org\n"); + /* mute card - prevents noisy bootups */ + outb (0, io); + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); +MODULE_DESCRIPTION("A driver for the Aztech radio card."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + return aztech_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&aztech_radio); + release_region(io,2); +} + +#endif diff --git a/drivers/char/radio-rtrack2.c b/drivers/char/radio-rtrack2.c new file mode 100644 index 000000000..7daeb4e01 --- /dev/null +++ b/drivers/char/radio-rtrack2.c @@ -0,0 +1,272 @@ +/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff + * + * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood + * Coverted to new API by Alan Cox <Alan.Cox@linux.org> + * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> + * + * TODO: Allow for more than one of these foolish entities :-) + * + */ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/delay.h> /* udelay */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_RTRACK2_PORT */ + +#ifndef CONFIG_RADIO_RTRACK2_PORT +#define CONFIG_RADIO_RTRACK2_PORT -1 +#endif + +static int io = CONFIG_RADIO_RTRACK2_PORT; +static int users = 0; + +struct rt_device +{ + int port; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +static void rt_mute(struct rt_device *dev) +{ + if(dev->muted) + return; + outb(1, io); + dev->muted = 1; +} + +static void rt_unmute(struct rt_device *dev) +{ + if(dev->muted == 0) + return; + outb(0, io); + dev->muted = 0; +} + +static void zero(void) +{ + outb_p(1, io); + outb_p(3, io); + outb_p(1, io); +} + +static void one(void) +{ + outb_p(5, io); + outb_p(7, io); + outb_p(5, io); +} + +static int rt_setfreq(struct rt_device *dev, unsigned long freq) +{ + int i; + + freq = freq / 200 + 856; + + outb_p(0xc8, io); + outb_p(0xc9, io); + outb_p(0xc9, io); + + for (i = 0; i < 10; i++) + zero (); + + for (i = 14; i >= 0; i--) + if (freq & (1 << i)) + one (); + else + zero (); + + outb_p(0xc8, io); + if (!dev->muted) + outb_p(0, io); + return 0; +} + +int rt_getsigstr(struct rt_device *dev) +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int rt_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct rt_device *rt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow=88*16000; + v.rangehigh=108*16000; + v.flags=VIDEO_TUNER_LOW; + v.mode=VIDEO_MODE_AUTO; + v.signal=0xFFFF*rt_getsigstr(rt); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq))) + return -EFAULT; + rt_setfreq(rt, rt->curfreq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE; + v.volume=1; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + + if(v.flags&VIDEO_AUDIO_MUTE) + rt_mute(rt); + else + rt_unmute(rt); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int rt_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void rt_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct rt_device rtrack2_unit; + +static struct video_device rtrack2_radio= +{ + "RadioTrack II radio", + VID_TYPE_TUNER, + VID_HARDWARE_RTRACK2, + rt_open, + rt_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* Can't poll */ + rt_ioctl, + NULL, + NULL +}; + +__initfunc(int rtrack2_init(struct video_init *v)) +{ + if (check_region(io, 4)) + { + printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io); + return -EBUSY; + } + + rtrack2_radio.priv=&rtrack2_unit; + + if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io, 4, "rtrack2"); + printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n"); + + /* mute card - prevents noisy bootups */ + outb(1, io); + rtrack2_unit.muted = 1; + + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Ben Pfaff"); +MODULE_DESCRIPTION("A driver for the RadioTrack II radio card."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n"); + return -EINVAL; + } + return rtrack2_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&rtrack2_radio); + release_region(io,4); +} + +#endif + +/* + Local variables: + compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c" + End: +*/ diff --git a/drivers/char/radio-sf16fmi.c b/drivers/char/radio-sf16fmi.c new file mode 100644 index 000000000..13659fd85 --- /dev/null +++ b/drivers/char/radio-sf16fmi.c @@ -0,0 +1,285 @@ +/* SF16FMI radio driver for Linux radio support + * heavily based on rtrack driver... + * (c) 1997 M. Kirkwood + * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz + * + * Fitted to new interface by Alan Cox <alan.cox@linux.org> + * + * Notes on the hardware + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * No volume control - only mute/unmute - you have to use line volume + * control on SB-part of SF16FMI + * + */ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/delay.h> /* udelay */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_SF16MI_PORT */ + +#include "rsf16fmi.h" + +struct fmi_device +{ + int port; + int curvol; + unsigned long curfreq; + int flags; +}; + +#ifndef CONFIG_RADIO_SF16FMI_PORT +#define CONFIG_RADIO_SF16FMI_PORT -1 +#endif + +static int io = CONFIG_RADIO_SF16FMI_PORT; +static int users = 0; + +/* local things */ +/* freq in 1/16kHz to internal number */ +#define RSF16_ENCODE(x) ((x/16+10700)/50) + +static void outbits(int bits, int data, int port) +{ + while(bits--) { + if(data & 1) { + outb(5, port); + udelay(6); + outb(7, port); + udelay(6); + } else { + outb(1, port); + udelay(6); + outb(3, port); + udelay(6); + } + data>>=1; + } +} + +static void fmi_mute(int port) +{ + outb(0x00, port); +} + +static void fmi_unmute(int port) +{ + outb(0x08, port); +} + +static int fmi_setfreq(struct fmi_device *dev, unsigned long freq) +{ + int myport = dev->port; + + outbits(16, RSF16_ENCODE(freq), myport); + outbits(8, 0xC0, myport); + /* we should wait here... */ + return 0; +} + +static int fmi_getsigstr(struct fmi_device *dev) +{ + int val; + int res; + int myport = dev->port; + + val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ + outb(val, myport); + outb(val | 0x10, myport); + udelay(140000); + res = (int)inb(myport+1); + outb(val, myport); + return (res & 2) ? 0 : 1; +} + +static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct fmi_device *fmi=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + if (fmi->flags & VIDEO_TUNER_LOW) { + v.rangelow = 87500 * 16; + v.rangehigh = 108000 * 16; + } else { + v.rangelow=(int)(175*8 /* 87.5 *16 */); + v.rangehigh=(int)(108*16); + } + v.flags=fmi->flags; + v.mode=VIDEO_MODE_AUTO; + v.signal=0xFFFF*fmi_getsigstr(fmi); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + fmi->flags = v.flags & VIDEO_TUNER_LOW; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long tmp = fmi->curfreq; + if (!(fmi->flags & VIDEO_TUNER_LOW)) + tmp /= 1000; + if(copy_to_user(arg, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long tmp; + if(copy_from_user(&tmp, arg, sizeof(tmp))) + return -EFAULT; + if (!(fmi->flags & VIDEO_TUNER_LOW)) + tmp *= 1000; + fmi->curfreq = tmp; + fmi_setfreq(fmi, fmi->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE; + v.mode=VIDEO_SOUND_MONO; + v.volume=fmi->curvol; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + fmi->curvol=v.volume; + if(v.flags&VIDEO_AUDIO_MUTE) + fmi_mute(fmi->port); + else if(fmi->curvol) + fmi_unmute(fmi->port); + else + fmi_mute(fmi->port); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int fmi_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void fmi_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct fmi_device fmi_unit; + +static struct video_device fmi_radio= +{ + "SF16FMI radio", + VID_TYPE_TUNER, + VID_HARDWARE_SF16MI, + fmi_open, + fmi_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* Can't poll */ + fmi_ioctl, + NULL, + NULL +}; + +__initfunc(int fmi_init(struct video_init *v)) +{ + if (check_region(io, 2)) + { + printk(KERN_ERR "fmi: port 0x%x already in use\n", io); + return -EBUSY; + } + + fmi_unit.port=io; + fmi_unit.flags = VIDEO_TUNER_LOW; + fmi_radio.priv=&fmi_unit; + + if(video_register_device(&fmi_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io, 2, "fmi"); + printk(KERN_INFO "SF16FMI radio card driver.\n"); + printk(KERN_INFO "(c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz.\n"); + /* mute card - prevents noisy bootups */ + fmi_mute(io); + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); +MODULE_DESCRIPTION("A driver for the SF16MI radio."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + return fmi_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&fmi_radio); + release_region(io,2); +} + +#endif diff --git a/drivers/char/radio-zoltrix.c b/drivers/char/radio-zoltrix.c new file mode 100644 index 000000000..c820c8a36 --- /dev/null +++ b/drivers/char/radio-zoltrix.c @@ -0,0 +1,333 @@ +/* zoltrix radio plus driver for Linux radio support + * (c) 1998 C. van Schaik <carl@leg.uct.ac.za> + * + * BUGS + * Due to the inconsistancy in reading from the signal flags + * it is difficult to get an accurate tuned signal. + * + * There seems to be a problem with the volume setting that I must still + * figure out. + * It seems that the card has is not linear to 0 volume. It cuts off + * at a low frequency, and it is not possible (at least I have not found) + * to get fine volume control over the low volume range. + * + * Some code derived from code by Frans Brinkman + */ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/delay.h> /* udelay */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_ZOLTRIX_PORT */ + +#ifndef CONFIG_RADIO_ZOLTRIX_PORT +#define CONFIG_RADIO_ZOLTRIX_PORT -1 +#endif + +static int io = CONFIG_RADIO_ZOLTRIX_PORT; +static int users = 0; + +struct zol_device { + int port; + int curvol; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +static void sleep_delay(long n) +{ + /* Sleep nicely for 'n' uS */ + int d = n / (1000000 / HZ); + if (!d) + udelay(n); + else { + /* Yield CPU time */ + unsigned long x = jiffies; + while ((jiffies - x) <= d) + schedule(); + } +} + +static void zol_mute(struct zol_device *dev) +{ + dev->muted = 1; + outb(0, io); + outb(0, io); + inb(io + 3); /* Zoltrix needs to be read to confirm */ +} + +static void zol_on(int vol) +{ + int l; + outb(vol, io); + sleep_delay(10000); + l = inb(io + 2); +} + +static int zol_setvol(struct zol_device *dev, int vol) +{ + if (vol == dev->curvol) { /* requested volume = current */ + if (dev->muted) { /* user is unmuting the card */ + dev->muted = 0; + zol_on(vol); + } + return 0; + } + if (vol == 0) { /* volume = 0 means mute the card */ + zol_mute(dev); + return 0; + } + dev->muted = 0; + dev->curvol = vol; + + zol_on(vol); + + return 0; +} + +static int zol_setfreq(struct zol_device *dev, unsigned long freq) +{ + /* tunes the radio to the desired frequency */ + unsigned long long bitmask, f, m; + int i; + + m = (freq * 25 / 4 - 8800) * 2; + f = (unsigned long long) m + 0x4d1c; + + bitmask = 0xc480402c10080000ull; + i = 45; + + zol_mute(dev); + + outb(0x40, io); + outb(0xc0, io); + + bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( /*stereo */ 0 << 31)); + while (i--) { + if ((bitmask & 0x8000000000000000ull) != 0) { + outb(0x80, io); + sleep_delay(50); + outb(0x00, io); + sleep_delay(50); + outb(0x80, io); + sleep_delay(50); + } else { + outb(0xc0, io); + sleep_delay(50); + outb(0x40, io); + sleep_delay(50); + outb(0xc0, io); + sleep_delay(50); + } + bitmask *= 2; + } + /* termination sequence */ + outb(0x80, io); + outb(0xc0, io); + outb(0x40, io); + zol_on(dev->curvol); + return 0; +} + +/* Get signal strength */ + +int zol_getsigstr(struct zol_device *dev) +{ + int a, b; + + outb(0x00, io); /* This stuff I found to do nothing */ + outb(dev->curvol, io); + sleep_delay(20000); + + a = inb(io); + sleep_delay(1000); + b = inb(io); + + if ((a == b) && (a == 0xdf)) /* I found this out by playing */ + /* with a binary scanner on the card io */ + return (1); + else + return (0); + + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int zol_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct zol_device *rt = dev->priv; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability v; + v.type = VID_TYPE_TUNER; + v.channels = 1; + v.audios = 1; + /* No we don't do pictures */ + v.maxwidth = 0; + v.maxheight = 0; + v.minwidth = 0; + v.minheight = 0; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if (copy_from_user(&v, arg, sizeof(v)) != 0) + return -EFAULT; + if (v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow = (int) (88.0 * 16); + v.rangehigh = (int) (108.0 * 16); + v.flags = 0; + v.mode = VIDEO_MODE_AUTO; + v.signal = 0xFFFF * zol_getsigstr(rt); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.tuner != 0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if (copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if (copy_from_user(&rt->curfreq, arg, sizeof(rt->curfreq))) + return -EFAULT; + zol_setfreq(rt, rt->curfreq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v, 0, sizeof(v)); + v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + v.volume = rt->curvol * 4095; + strcpy(v.name, "Radio"); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.audio) + return -EINVAL; + + if (v.flags & VIDEO_AUDIO_MUTE) + zol_mute(rt); + else + zol_setvol(rt, v.volume / 4096); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int zol_open(struct video_device *dev, int flags) +{ + if (users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void zol_close(struct video_device *dev) +{ + users--; + MOD_DEC_USE_COUNT; +} + +static struct zol_device zoltrix_unit; + +static struct video_device zoltrix_radio = +{ + "Zoltrix Radio Plus", + VID_TYPE_TUNER, + VID_HARDWARE_ZOLTRIX, + zol_open, + zol_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, + zol_ioctl, + NULL, + NULL +}; + +__initfunc(int zoltrix_init(struct video_init *v)) +{ + if (check_region(io, 2)) { + printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io); + return -EBUSY; + } + zoltrix_radio.priv = &zoltrix_unit; + + if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO) == -1) + return -EINVAL; + + request_region(io, 2, "zoltrix"); + printk(KERN_INFO "Zoltrix Radio Plus card driver.\n"); + + /* mute card - prevents noisy bootups */ + + /* this ensures that the volume is all the way down */ + + outb(0, io); + outb(0, io); + sleep_delay(20000); + inb(io + 3); + + zoltrix_unit.curvol = 0; + + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("C.van Schaik"); +MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if (io == -1) { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + return zoltrix_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&zoltrix_radio); + release_region(io, 2); +} + +#endif diff --git a/drivers/char/radio.c b/drivers/char/radio.c deleted file mode 100644 index 2b79e9872..000000000 --- a/drivers/char/radio.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Radio Card Device Driver for Linux - * - * (c) 1997 Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk> - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/errno.h> -#include <linux/fs.h> - -#include <linux/miscdevice.h> - -#include <asm/uaccess.h> - -#include <linux/config.h> - -#include <linux/radio.h> -#ifdef CONFIG_RADIO_RTRACK -#include "rtrack.h" -#endif -#ifdef CONFIG_RADIO_WINRADIO -#include "winradio.h" -#endif - -int radio_open(struct inode *inode, struct file *file); -int radio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); - -/* /dev/radio interface */ -static struct file_operations radio_fops = { - NULL, /* seek */ - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* select */ - &radio_ioctl, /* ioctl */ - NULL, /* mmap */ - &radio_open, /* we're not allowed NULL, it seems... */ - NULL /* release */ -}; - -static struct miscdevice radio_miscdevice = { - RADIO_MINOR, /* minor device number */ - "radio", /* title */ - &radio_fops, /* file operations */ - NULL, NULL /* previous and next (not our business) */ -}; - - -static struct radio_device *firstdevice, *lastdevice; -static int numdevices; - -__initfunc(void radio_init(void)) -{ - /* register the handler for the device number... */ - if(misc_register(&radio_miscdevice)) { - printk("radio: couldn't register misc device\n"); - return; - } - - /* do some general initialisation stuff */ - lastdevice = firstdevice = NULL; - numdevices = 0; - -#ifdef CONFIG_RADIO_RTRACK - radiotrack_init(); -#endif -#ifdef CONFIG_RADIO_WINRADIO - printk("oooops. no winradio support yet... :-(\n"); -#endif -/* etc.... */ - - printk("radio: registered %d devices\n", numdevices); -} - - -/* according to drivers/char/misc.c, the "open" call must not be NULL. - * I'm not sure if I approve, but I didn't write it, so... - */ -int radio_open(struct inode *inode, struct file *file) -{ - return 0; -} - - -/* append a device to the linked list... */ -int radio_add_device(struct radio_device *newdev) -{ - if(firstdevice == NULL) { - firstdevice = newdev; - } else { - lastdevice->next = newdev; - } - lastdevice = newdev; numdevices++; - newdev->cap->dev_num=numdevices; - newdev->next = NULL; /* don't need, but... */ - return(numdevices); -} - -struct radio_device *getdev(int index) -{ -struct radio_device *retval; - - if(index > numdevices) - return NULL; /* let's have a bit less of that */ - - retval = firstdevice; - for(;index;index--) - retval = retval->next; - - return retval; -} - - -/* interface routine */ -int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ -struct radio_device *dev; -struct radio_ctl *ctl_arg = (struct radio_ctl*)arg; -int nowrite; -int val, ret; - - if((void*)arg == NULL) - return -EFAULT; /* XXX - check errnos are OK */ - - - switch(cmd) { - case RADIO_NUMDEVS: - return (put_user(numdevices,(int*)arg) ? -EFAULT : 0); - - - case RADIO_GETCAPS: - /* p'raps I should verify for read then write ?? */ - if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_cap))) - return -EFAULT; - if((dev = getdev(((struct radio_cap*)arg)->dev_num)) == NULL) - return -EINVAL; - copy_to_user((void*)arg, dev->cap, sizeof(struct radio_cap)); - return 0; - - - case RADIO_GETBNDCAP: - if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_band))) - return -EFAULT; - - if((dev = getdev(((struct radio_band*)arg)->dev_num)) == NULL) - return -EINVAL; - - val = ((struct radio_band*)arg)->index; - if(val >= dev->cap->num_bwidths) - return -EINVAL; /* XXX errno */ - - copy_to_user((void*)arg, (dev->bands)+(val*sizeof(struct radio_band)), - sizeof(struct radio_band)); - return 0; - } - - -/* now, we know that arg points to a struct radio_ctl */ - /* get the requested device */ - if(verify_area(VERIFY_READ, ctl_arg, sizeof(struct radio_ctl))) - return -EFAULT; - - if((dev = getdev(ctl_arg->dev_num)) == NULL) - return -EINVAL; - - nowrite = verify_area(VERIFY_WRITE, ctl_arg, sizeof(struct radio_ctl)); - - val = ctl_arg->value; - - switch(cmd) { - case RADIO_SETVOL: - if((val < dev->cap->volmin) || (val > dev->cap->volmax)) - return -EINVAL; - if((ret = (*(dev->setvol))(dev, val))) - return ret; - dev->curvol = val; - return 0; - - case RADIO_GETVOL: - if(nowrite) - return -EFAULT; - ctl_arg->value = dev->curvol; - return 0; - - - case RADIO_SETBAND: - if(val >= dev->cap->num_bwidths) - return -EINVAL; - if((ret = (*(dev->setband))(dev, val))) - return ret; - dev->curband = val; - return 0; - - case RADIO_GETBAND: - if(nowrite) - return -EFAULT; - ctl_arg->value = dev->curband; - return 0; - - - case RADIO_SETFREQ: { - struct radio_band *bp; - - bp = (dev->bands) + ((dev->curband) * sizeof(struct radio_band)); - if((val < bp->freqmin) || (val > bp->freqmax)) - return -EINVAL; - if((ret = (*(dev->setfreq))(dev, val))) - return ret; - dev->curfreq = val; - return 0; - } - - case RADIO_GETFREQ: - if(nowrite) - return -EFAULT; - ctl_arg->value = dev->curfreq; - return 0; - - - case RADIO_GETSIGSTR: - if(nowrite) - return -EFAULT; - ctl_arg->value = (*(dev->getsigstr))(dev); - return 0; - - - default: - return -ENOSYS; - } -} diff --git a/drivers/char/random.c b/drivers/char/random.c index 13d5d6d36..b7ed4e10b 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,9 +1,10 @@ /* * random.c -- A strong random number generator * - * Version 1.03, last modified 26-Apr-97 + * Version 1.04, last modified 26-Apr-98 * - * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved. + * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998. All rights + * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -73,12 +74,12 @@ * an *estimate* of how many bits of randomness have been stored into * the random number generator's internal state. * - * When random bytes are desired, they are obtained by taking the MD5 - * hash of the contents of the "entropy pool". The MD5 hash avoids + * When random bytes are desired, they are obtained by taking the SHA + * hash of the contents of the "entropy pool". The SHA hash avoids * exposing the internal state of the entropy pool. It is believed to * be computationally infeasible to derive any useful information - * about the input of MD5 from its output. Even if it is possible to - * analyze MD5 in some clever way, as long as the amount of data + * about the input of SHA from its output. Even if it is possible to + * analyze SHA in some clever way, as long as the amount of data * returned from the generator is less than the inherent entropy in * the pool, the output data is totally unpredictable. For this * reason, the routine decreases its internal estimate of how many @@ -88,7 +89,7 @@ * If this estimate goes to zero, the routine can still generate * random numbers; however, an attacker may (at least in theory) be * able to infer the future output of the generator from prior - * outputs. This requires successful cryptanalysis of MD5, which is + * outputs. This requires successful cryptanalysis of SHA, which is * not believed to be feasible, but there is a remote possibility. * Nonetheless, these numbers should be useful for the vast majority * of purposes. @@ -162,12 +163,14 @@ * sequence: * * echo "Initializing random number generator..." + * random_seed=/var/run/random-seed * # Carry a random seed from start-up to start-up * # Load and then save 512 bytes, which is the size of the entropy pool - * if [ -f /etc/random-seed ]; then - * cat /etc/random-seed >/dev/urandom + * if [ -f $random_seed ]; then + * cat $random_seed >/dev/urandom * fi - * dd if=/dev/urandom of=/etc/random-seed count=1 + * dd if=/dev/urandom of=$random_seed count=1 + * chmod 600 $random_seed * * and the following lines in an appropriate script which is run as * the system is shutdown: @@ -175,10 +178,14 @@ * # Carry a random seed from shut-down to start-up * # Save 512 bytes, which is the size of the entropy pool * echo "Saving random seed..." - * dd if=/dev/urandom of=/etc/random-seed count=1 + * random_seed=/var/run/random-seed + * dd if=/dev/urandom of=$random_seed count=1 + * chmod 600 $random_seed * - * For example, on many Linux systems, the appropriate scripts are - * usually /etc/rc.d/rc.local and /etc/rc.d/rc.0, respectively. + * For example, on most modern systems using the System V init + * scripts, such code fragments would be found in + * /etc/rc.d/init.d/random. On older Linux systems, the correct script + * location might be in /etc/rcb.d/rc.local or /etc/rc.d/rc.0. * * Effectively, these commands cause the contents of the entropy pool * to be saved at shut-down time and reloaded into the entropy pool at @@ -204,18 +211,17 @@ * ================= * * Ideas for constructing this random number generator were derived - * from the Pretty Good Privacy's random number generator, and from - * private discussions with Phil Karn. Colin Plumb provided a faster - * random number generator, which speed up the mixing function of the - * entropy pool, taken from PGP 3.0 (under development). It has since - * been modified by myself to provide better mixing in the case where - * the input values to add_entropy_word() are mostly small numbers. - * Dale Worley has also contributed many useful ideas and suggestions - * to improve this driver. + * from Pretty Good Privacy's random number generator, and from private + * discussions with Phil Karn. Colin Plumb provided a faster random + * number generator, which speed up the mixing function of the entropy + * pool, taken from PGPfone. Dale Worley has also contributed many + * useful ideas and suggestions to improve this driver. * * Any flaws in the design are solely my responsibility, and should * not be attributed to the Phil, Colin, or any of authors of PGP. * + * The code for SHA transform was taken from Peter Gutmann's + * implementation, which has been placed in the public domain. * The code for MD5 transform was taken from Colin Plumb's * implementation, which has been placed in the public domain. The * MD5 cryptographic checksum was devised by Ronald Rivest, and is @@ -237,6 +243,7 @@ #include <linux/poll.h> #include <linux/init.h> +#include <asm/processor.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> @@ -246,26 +253,66 @@ */ #undef RANDOM_BENCHMARK #undef BENCHMARK_NOINT +#define ROTATE_PARANOIA -/* - * The pool is stirred with a primitive polynomial of degree 128 - * over GF(2), namely x^128 + x^99 + x^59 + x^31 + x^9 + x^7 + 1. - * For a pool of size 64, try x^64+x^62+x^38+x^10+x^6+x+1. - */ #define POOLWORDS 128 /* Power of 2 - note that this is 32-bit words */ #define POOLBITS (POOLWORDS*32) -#if POOLWORDS == 128 -#define TAP1 99 /* The polynomial taps */ -#define TAP2 59 -#define TAP3 31 -#define TAP4 9 -#define TAP5 7 -#elif POOLWORDS == 64 -#define TAP1 62 /* The polynomial taps */ -#define TAP2 38 -#define TAP3 10 -#define TAP4 6 -#define TAP5 1 +/* + * The pool is stirred with a primitive polynomial of degree POOLWORDS + * over GF(2). The taps for various sizes are defined below. They are + * chosen to be evenly spaced (minimum RMS distance from evenly spaced; + * the numbers in the comments are a scaled squared error sum) except + * for the last tap, which is 1 to get the twisting happening as fast + * as possible. + */ +#if POOLWORDS == 2048 /* 115 x^2048+x^1638+x^1231+x^819+x^411+x^1+1 */ +#define TAP1 1638 +#define TAP2 1231 +#define TAP3 819 +#define TAP4 411 +#define TAP5 1 +#elif POOLWORDS == 1024 /* 290 x^1024+x^817+x^615+x^412+x^204+x^1+1 */ +/* Alt: 115 x^1024+x^819+x^616+x^410+x^207+x^2+1 */ +#define TAP1 817 +#define TAP2 615 +#define TAP3 412 +#define TAP4 204 +#define TAP5 1 +#elif POOLWORDS == 512 /* 225 x^512+x^411+x^308+x^208+x^104+x+1 */ +/* Alt: 95 x^512+x^409+x^307+x^206+x^102+x^2+1 + * 95 x^512+x^409+x^309+x^205+x^103+x^2+1 */ +#define TAP1 411 +#define TAP2 308 +#define TAP3 208 +#define TAP4 104 +#define TAP5 1 +#elif POOLWORDS == 256 /* 125 x^256+x^205+x^155+x^101+x^52+x+1 */ +#define TAP1 205 +#define TAP2 155 +#define TAP3 101 +#define TAP4 52 +#define TAP5 1 +#elif POOLWORDS == 128 /* 105 x^128+x^103+x^76+x^51+x^25+x+1 */ +/* Alt: 70 x^128+x^103+x^78+x^51+x^27+x^2+1 */ +#define TAP1 103 +#define TAP2 76 +#define TAP3 51 +#define TAP4 25 +#define TAP5 1 +#elif POOLWORDS == 64 /* 15 x^64+x^52+x^39+x^26+x^14+x+1 */ +#define TAP1 52 +#define TAP2 39 +#define TAP3 26 +#define TAP4 14 +#define TAP5 1 +#elif POOLWORDS == 32 /* 15 x^32+x^26+x^20+x^14+x^7+x^1+1 */ +#define TAP1 26 +#define TAP2 20 +#define TAP3 14 +#define TAP4 7 +#define TAP5 1 +#elif POOLWORDS & (POOLWORDS-1) +#error POOLWORDS must be a power of 2 #else #error No primitive polynomial available for chosen POOLWORDS #endif @@ -279,11 +326,38 @@ * Also see M. Matsumoto & Y. Kurita, 1994. Twisted GFSR generators * II. ACM Transactions on Mdeling and Computer Simulation 4:254-266) * - * Thanks to Colin Plumb for suggesting this. (Note that the behavior - * of the 1.0 version of the driver was equivalent to using a second - * element of 0x80000000). + * Thanks to Colin Plumb for suggesting this. + * We have not analyzed the resultant polynomial to prove it primitive; + * in fact it almost certainly isn't. Nonetheless, the irreducible factors + * of a random large-degree polynomial over GF(2) are more than large enough + * that periodicity is not a concern. + * + * The input hash is much less sensitive than the output hash. All that + * we want of it is that it be a good non-cryptographic hash; i.e. it + * not produce collisions when fed "random" data of the sort we expect + * to see. As long as the pool state differs for different inputs, we + * have preserved the input entropy and done a good job. The fact that an + * intelligent attacker can construct inputs that will produce controlled + * alterations to the pool's state is not important because we don't + * consider such inputs to contribute any randomness. + * The only property we need with respect to them is + * that the attacker can't increase his/her knowledge of the pool's state. + * Since all additions are reversible (knowing the final state and the + * input, you can reconstruct the initial state), if an attacker has + * any uncertainty about the initial state, he/she can only shuffle that + * uncertainty about, but never cause any collisions (which would + * decrease the uncertainty). + * + * The chosen system lets the state of the pool be (essentially) the input + * modulo the generator polymnomial. Now, for random primitive polynomials, + * this is a universal class of hash functions, meaning that the chance + * of a collision is limited by the attacker's knowledge of the generator + * polynomail, so if it is chosen at random, an attacker can never force + * a collision. Here, we use a fixed polynomial, but we *can* assume that + * ###--> it is unknown to the processes generating the input entropy. <-### + * Because of this important property, this is a good, collision-resistant + * hash; hash collisions will occur no more often than chance. */ -static __u32 twist_table[2] = { 0, 0xEDB88320 }; /* * The minimum number of bits to release a "wait on input". Should @@ -303,7 +377,9 @@ static __u32 twist_table[2] = { 0, 0xEDB88320 }; struct random_bucket { unsigned add_ptr; unsigned entropy_count; +#ifdef ROTATE_PARANOIA int input_rotate; +#endif __u32 pool[POOLWORDS]; }; @@ -332,8 +408,8 @@ struct random_benchmark timer_benchmark; /* There is one of these per entropy source */ struct timer_rand_state { - unsigned long last_time; - int last_delta,last_delta2; + __u32 last_time; + __s32 last_delta,last_delta2; int dont_count_entropy:1; }; @@ -356,11 +432,10 @@ static ssize_t random_write(struct file * file, const char * buffer, static int random_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); -static inline void fast_add_entropy_word(struct random_bucket *r, - const __u32 input); +static inline void fast_add_entropy_words(struct random_bucket *r, + __u32 x, __u32 y); -static void add_entropy_word(struct random_bucket *r, - const __u32 input); +static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y); #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -391,33 +466,36 @@ extern inline __u32 rotate_left(int i, __u32 word) * More asm magic.... * * For entropy estimation, we need to do an integral base 2 - * logarithm. By default, use an open-coded C version, although we do - * have a version which takes advantage of the Intel's x86's "bsr" - * instruction. + * logarithm. + * + * Note the "12bits" suffix - this is used for numbers between + * 0 and 4095 only. This allows a few shortcuts. */ -#if (!defined (__i386__)) -static inline __u32 int_ln(__u32 word) +#if 0 /* Slow but clear version */ +static inline __u32 int_ln_12bits(__u32 word) { __u32 nbits = 0; - while (1) { - word >>= 1; - if (!word) - break; + while (word >>= 1) nbits++; - } return nbits; } -#else -static inline __u32 int_ln(__u32 word) +#else /* Faster (more clever) version, courtesy Colin Plumb */ +static inline __u32 int_ln_12bits(__u32 word) { - __asm__("bsrl %1,%0\n\t" - "jnz 1f\n\t" - "movl $0,%0\n" - "1:" - :"=r" (word) - :"r" (word)); - return word; + /* Smear msbit right to make an n-bit mask */ + word |= word >> 8; + word |= word >> 4; + word |= word >> 2; + word |= word >> 1; + /* Remove one bit to make this a logarithm */ + word >>= 1; + /* Count the bits set in the word */ + word -= (word >> 1) & 0x555; + word = (word & 0x333) + ((word >> 2) & 0x333); + word += (word >> 4); + word += (word >> 8); + return word & 15; } #endif @@ -429,19 +507,22 @@ static inline __u32 int_ln(__u32 word) */ static void init_std_data(struct random_bucket *r) { - __u32 word, *p; + __u32 words[2], *p; int i; struct timeval tv; do_gettimeofday(&tv); - add_entropy_word(r, tv.tv_sec); - add_entropy_word(r, tv.tv_usec); - - for (p = (__u32 *) &system_utsname, - i = sizeof(system_utsname) / sizeof(__u32); - i ; i--, p++) { - memcpy(&word, p, sizeof(__u32)); - add_entropy_word(r, word); + add_entropy_words(r, tv.tv_sec, tv.tv_usec); + + /* + * This doesnt lock system.utsname. Howeve we are generating + * entropy so a race with a name set here is fine. + */ + p = (__u32 *)&system_utsname; + for (i = sizeof(system_utsname) / sizeof(words); i; i--) { + memcpy(words, p, sizeof(words)); + add_entropy_words(r, words[0], words[1]); + p += sizeof(words)/sizeof(*words); } } @@ -513,54 +594,90 @@ void rand_initialize_blkdev(int major, int mode) * This function adds a byte into the entropy "pool". It does not * update the entropy estimate. The caller must do this if appropriate. * - * The pool is stirred with a primitive polynomial of degree 128 - * over GF(2), namely x^128 + x^99 + x^59 + x^31 + x^9 + x^7 + 1. - * For a pool of size 64, try x^64+x^62+x^38+x^10+x^6+x+1. - * - * We rotate the input word by a changing number of bits, to help - * assure that all bits in the entropy get toggled. Otherwise, if we - * consistently feed the entropy pool small numbers (like jiffies and - * scancodes, for example), the upper bits of the entropy pool don't - * get affected. --- TYT, 10/11/95 + * This function is tuned for speed above most other considerations. + * + * The pool is stirred with a primitive polynomial of the appropriate degree, + * and then twisted. We twist by three bits at a time because it's + * cheap to do so and helps slightly in the expected case where the + * entropy is concentrated in the low-order bits. */ -static inline void fast_add_entropy_word(struct random_bucket *r, - const __u32 input) +#define MASK(x) ((x) & (POOLWORDS-1)) /* Convenient abreviation */ +static inline void fast_add_entropy_words(struct random_bucket *r, + __u32 x, __u32 y) { - unsigned i; - int new_rotate; - __u32 w; + static __u32 const twist_table[8] = { + 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 }; + unsigned i, j; + + i = MASK(r->add_ptr - 2); /* i is always even */ + r->add_ptr = i; + +#ifdef ROTATE_PARANOIA + j = r->input_rotate + 14; + if (i) + j -= 7; + r->input_rotate = j & 31; + + x = rotate_left(r->input_rotate, x); + y = rotate_left(r->input_rotate, y); +#endif /* - * Normally, we add 7 bits of rotation to the pool. At the - * beginning of the pool, add an extra 7 bits rotation, so - * that successive passes spread the input bits across the - * pool evenly. + * XOR in the various taps. Even though logically, we compute + * x and then compute y, we read in y then x order because most + * caches work slightly better with increasing read addresses. + * If a tap is even then we can use the fact that i is even to + * avoid a masking operation. Every polynomial has at least one + * even tap, so j is always used. + * (Is there a nicer way to arrange this code?) */ - new_rotate = r->input_rotate + 14; - if ((i = r->add_ptr--)) - new_rotate -= 7; - r->input_rotate = new_rotate & 31; - - w = rotate_left(r->input_rotate, input); - - /* XOR in the various taps */ - w ^= r->pool[(i+TAP1)&(POOLWORDS-1)]; - w ^= r->pool[(i+TAP2)&(POOLWORDS-1)]; - w ^= r->pool[(i+TAP3)&(POOLWORDS-1)]; - w ^= r->pool[(i+TAP4)&(POOLWORDS-1)]; - w ^= r->pool[(i+TAP5)&(POOLWORDS-1)]; - w ^= r->pool[i&(POOLWORDS-1)]; - /* Use a twisted GFSR for the mixing operation */ - r->pool[i&(POOLWORDS-1)] = (w >> 1) ^ twist_table[w & 1]; +#if TAP1 & 1 + y ^= r->pool[MASK(i+TAP1)]; x ^= r->pool[MASK(i+TAP1+1)]; +#else + j = MASK(i+TAP1); y ^= r->pool[j]; x ^= r->pool[j+1]; +#endif +#if TAP2 & 1 + y ^= r->pool[MASK(i+TAP2)]; x ^= r->pool[MASK(i+TAP2+1)]; +#else + j = MASK(i+TAP2); y ^= r->pool[j]; x ^= r->pool[j+1]; +#endif +#if TAP3 & 1 + y ^= r->pool[MASK(i+TAP3)]; x ^= r->pool[MASK(i+TAP3+1)]; +#else + j = MASK(i+TAP3); y ^= r->pool[j]; x ^= r->pool[j+1]; +#endif +#if TAP4 & 1 + y ^= r->pool[MASK(i+TAP4)]; x ^= r->pool[MASK(i+TAP4+1)]; +#else + j = MASK(i+TAP4); y ^= r->pool[j]; x ^= r->pool[j+1]; +#endif +#if TAP5 == 1 + /* We need to pretend to write pool[i+1] before computing y */ + y ^= r->pool[i]; + x ^= r->pool[i+1]; + x ^= r->pool[MASK(i+TAP5+1)]; + y ^= r->pool[i+1] = x = (x >> 3) ^ twist_table[x & 7]; + r->pool[i] = (y >> 3) ^ twist_table[y & 7]; +#else +# if TAP5 & 1 + y ^= r->pool[MASK(i+TAP5)]; x ^= r->pool[MASK(i+TAP5+1)]; +# else + j = MASK(i+TAP5); y ^= r->pool[j]; x ^= r->pool[j+1]; +# endif + y ^= r->pool[i]; + x ^= r->pool[i+1]; + r->pool[i] = (y >> 3) ^ twist_table[y & 7]; + r->pool[i+1] = (x >> 3) ^ twist_table[x & 7]; +#endif } /* * For places where we don't need the inlined version */ -static void add_entropy_word(struct random_bucket *r, - const __u32 input) +static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y) { - fast_add_entropy_word(r, input); + fast_add_entropy_words(r, x, y); } /* @@ -578,19 +695,18 @@ static void add_entropy_word(struct random_bucket *r, static void add_timer_randomness(struct random_bucket *r, struct timer_rand_state *state, unsigned num) { - int delta, delta2, delta3; __u32 time; + __s32 delta, delta2, delta3; #ifdef RANDOM_BENCHMARK begin_benchmark(&timer_benchmark); #endif #if defined (__i386__) - if (boot_cpu_data.x86_capability & 16) { - unsigned long low, high; + if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) { + __u32 high; __asm__(".byte 0x0f,0x31" - :"=a" (low), "=d" (high)); - time = (__u32) low; - num ^= (__u32) high; + :"=a" (time), "=d" (high)); + num ^= high; } else { time = jiffies; } @@ -598,42 +714,53 @@ static void add_timer_randomness(struct random_bucket *r, time = jiffies; #endif - fast_add_entropy_word(r, (__u32) num); - fast_add_entropy_word(r, time); + fast_add_entropy_words(r, (__u32)num, time); /* - * Calculate number of bits of randomness we probably - * added. We take into account the first and second order - * deltas in order to make our estimate. + * Calculate number of bits of randomness we probably added. + * We take into account the first, second and third-order deltas + * in order to make our estimate. */ - if (!state->dont_count_entropy && - (r->entropy_count < POOLBITS)) { + if ((r->entropy_count < POOLBITS) && !state->dont_count_entropy) { delta = time - state->last_time; state->last_time = time; - if (delta < 0) delta = -delta; delta2 = delta - state->last_delta; state->last_delta = delta; - if (delta2 < 0) delta2 = -delta2; delta3 = delta2 - state->last_delta2; state->last_delta2 = delta2; - if (delta3 < 0) delta3 = -delta3; - delta = MIN(MIN(delta, delta2), delta3) >> 1; - /* Limit entropy estimate to 12 bits */ + if (delta < 0) + delta = -delta; + if (delta2 < 0) + delta2 = -delta2; + if (delta3 < 0) + delta3 = -delta3; + if (delta > delta2) + delta = delta2; + if (delta > delta3) + delta = delta3; + + /* + * delta is now minimum absolute delta. + * Round down by 1 bit on general principles, + * and limit entropy entimate to 12 bits. + */ + delta >>= 1; delta &= (1 << 12) - 1; - r->entropy_count += int_ln(delta); + r->entropy_count += int_ln_12bits(delta); /* Prevent overflow */ if (r->entropy_count > POOLBITS) r->entropy_count = POOLBITS; + + /* Wake up waiting processes, if we have enough entropy. */ + if (r->entropy_count >= WAIT_INPUT_BITS) + wake_up_interruptible(&random_read_wait); } - /* Wake up waiting processes, if we have enough entropy. */ - if (r->entropy_count >= WAIT_INPUT_BITS) - wake_up_interruptible(&random_read_wait); #ifdef RANDOM_BENCHMARK end_benchmark(&timer_benchmark); #endif @@ -672,52 +799,84 @@ void add_blkdev_randomness(int major) 0x200+major); } +/* + * This chunk of code defines a function + * void HASH_TRANSFORM(__u32 digest[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE], + * __u32 const data[16]) + * + * The function hashes the input data to produce a digest in the first + * HASH_BUFFER_SIZE words of the digest[] array, and uses HASH_EXTRA_SIZE + * more words for internal purposes. (This buffer is exported so the + * caller can wipe it once rather than this code doing it each call, + * and tacking it onto the end of the digest[] array is the quick and + * dirty way of doing it.) + * + * It so happens that MD5 and SHA share most of the initial vector + * used to initialize the digest[] array before the first call: + * 1) 0x67452301 + * 2) 0xefcdab89 + * 3) 0x98badcfe + * 4) 0x10325476 + * 5) 0xc3d2e1f0 (SHA only) + * + * For /dev/random purposes, the length of the data being hashed is + * fixed in length (at POOLWORDS words), so appending a bit count in + * the usual way is not cryptographically necessary. + */ #define USE_SHA #ifdef USE_SHA -#define SMALL_VERSION /* Optimize for space over time */ - #define HASH_BUFFER_SIZE 5 +#define HASH_EXTRA_SIZE 80 #define HASH_TRANSFORM SHATransform +/* Various size/speed tradeoffs are available. Choose 0..3. */ +#define SHA_CODE_SIZE 0 + /* - * SHA transform algorithm, taken from code written by Peter Gutman, - * and apparently in the public domain. + * SHA transform algorithm, taken from code written by Peter Gutmann, + * and placed in the public domain. */ /* The SHA f()-functions. */ -#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ -#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ -#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ -#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ +#define f1(x,y,z) ( z ^ (x & (y^z)) ) /* Rounds 0-19: x ? y : z */ +#define f2(x,y,z) (x ^ y ^ z) /* Rounds 20-39: XOR */ +#define f3(x,y,z) ( (x & y) + (z & (x ^ y)) ) /* Rounds 40-59: majority */ +#define f4(x,y,z) (x ^ y ^ z) /* Rounds 60-79: XOR */ /* The SHA Mysterious Constants */ -#define K1 0x5A827999L /* Rounds 0-19 */ -#define K2 0x6ED9EBA1L /* Rounds 20-39 */ -#define K3 0x8F1BBCDCL /* Rounds 40-59 */ -#define K4 0xCA62C1D6L /* Rounds 60-79 */ +#define K1 0x5A827999L /* Rounds 0-19: sqrt(2) * 2^30 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39: sqrt(3) * 2^30 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59: sqrt(5) * 2^30 */ +#define K4 0xCA62C1D6L /* Rounds 60-79: sqrt(10) * 2^30 */ #define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) -#define expand(W,i) ( W[ i & 15 ] = \ - ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ - W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) - #define subRound(a, b, c, d, e, f, k, data) \ ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) -static void SHATransform(__u32 *digest, __u32 *data) - { +static void SHATransform(__u32 digest[85], __u32 const data[16]) +{ __u32 A, B, C, D, E; /* Local vars */ - __u32 eData[ 16 ]; /* Expanded data */ -#ifdef SMALL_VERSION - int i; __u32 TEMP; -#endif + int i; +#define W (digest + HASH_BUFFER_SIZE) /* Expanded data array */ + + /* + * Do the preliminary expansion of 16 to 80 words. Doing it + * out-of-line line this is faster than doing it in-line on + * register-starved machines like the x86, and not really any + * slower on real processors. + */ + memcpy(W, data, 16*sizeof(__u32)); + for (i = 0; i < 64; i++) { + TEMP = W[i] ^ W[i+2] ^ W[i+8] ^ W[i+13]; + W[i+16] = ROTL(1, TEMP); + } /* Set up first buffer and local data buffer */ A = digest[ 0 ]; @@ -725,112 +884,161 @@ static void SHATransform(__u32 *digest, __u32 *data) C = digest[ 2 ]; D = digest[ 3 ]; E = digest[ 4 ]; - memcpy( eData, data, 16*sizeof(__u32)); -#ifdef SMALL_VERSION + /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */ +#if SHA_CODE_SIZE == 0 /* - * Approximately 50% of the speed of the optimized version, but + * Approximately 50% of the speed of the largest version, but * takes up 1/16 the space. Saves about 6k on an i386 kernel. */ - for (i=0; i < 80; i++) { + for (i = 0; i < 80; i++) { + if (i < 40) { if (i < 20) - TEMP = f1(B, C, D) + K1; - else if (i < 40) - TEMP = f2(B, C, D) + K2; - else if (i < 60) - TEMP = f3(B, C, D) + K3; + TEMP = f1(B, C, D) + K1; else - TEMP = f4(B, C, D) + K4; - TEMP += ROTL (5, A) + E + - ((i > 15) ? expand(eData, i) : eData[i]); - E = D; D = C; C = ROTL(30, B); B = A; A = TEMP; + TEMP = f2(B, C, D) + K2; + } else { + if (i < 60) + TEMP = f3(B, C, D) + K3; + else + TEMP = f4(B, C, D) + K4; + } + TEMP += ROTL(5, A) + E + W[i]; + E = D; D = C; C = ROTL(30, B); B = A; A = TEMP; } +#elif SHA_CODE_SIZE == 1 + for (i = 0; i < 20; i++) { + TEMP = f1(B, C, D) + K1 + ROTL(5, A) + E + W[i]; + E = D; D = C; C = ROTL(30, B); B = A; A = TEMP; + } + for (; i < 40; i++) { + TEMP = f2(B, C, D) + K2 + ROTL(5, A) + E + W[i]; + E = D; D = C; C = ROTL(30, B); B = A; A = TEMP; + } + for (; i < 60; i++) { + TEMP = f3(B, C, D) + K3 + ROTL(5, A) + E + W[i]; + E = D; D = C; C = ROTL(30, B); B = A; A = TEMP; + } + for (; i < 80; i++) { + TEMP = f4(B, C, D) + K4 + ROTL(5, A) + E + W[i]; + E = D; D = C; C = ROTL(30, B); B = A; A = TEMP; + } +#elif SHA_CODE_SIZE == 2 + for (i = 0; i < 20; i += 5) { + subRound( A, B, C, D, E, f1, K1, W[ i ] ); + subRound( E, A, B, C, D, f1, K1, W[ i+1 ] ); + subRound( D, E, A, B, C, f1, K1, W[ i+2 ] ); + subRound( C, D, E, A, B, f1, K1, W[ i+3 ] ); + subRound( B, C, D, E, A, f1, K1, W[ i+4 ] ); + } + for (; i < 40; i += 5) { + subRound( A, B, C, D, E, f2, K2, W[ i ] ); + subRound( E, A, B, C, D, f2, K2, W[ i+1 ] ); + subRound( D, E, A, B, C, f2, K2, W[ i+2 ] ); + subRound( C, D, E, A, B, f2, K2, W[ i+3 ] ); + subRound( B, C, D, E, A, f2, K2, W[ i+4 ] ); + } + for (; i < 60; i += 5) { + subRound( A, B, C, D, E, f3, K3, W[ i ] ); + subRound( E, A, B, C, D, f3, K3, W[ i+1 ] ); + subRound( D, E, A, B, C, f3, K3, W[ i+2 ] ); + subRound( C, D, E, A, B, f3, K3, W[ i+3 ] ); + subRound( B, C, D, E, A, f3, K3, W[ i+4 ] ); + } + for (; i < 80; i += 5) { + subRound( A, B, C, D, E, f4, K4, W[ i ] ); + subRound( E, A, B, C, D, f4, K4, W[ i+1 ] ); + subRound( D, E, A, B, C, f4, K4, W[ i+2 ] ); + subRound( C, D, E, A, B, f4, K4, W[ i+3 ] ); + subRound( B, C, D, E, A, f4, K4, W[ i+4 ] ); + } +#elif SHA_CODE_SIZE == 3 /* Really large version */ + subRound( A, B, C, D, E, f1, K1, W[ 0 ] ); + subRound( E, A, B, C, D, f1, K1, W[ 1 ] ); + subRound( D, E, A, B, C, f1, K1, W[ 2 ] ); + subRound( C, D, E, A, B, f1, K1, W[ 3 ] ); + subRound( B, C, D, E, A, f1, K1, W[ 4 ] ); + subRound( A, B, C, D, E, f1, K1, W[ 5 ] ); + subRound( E, A, B, C, D, f1, K1, W[ 6 ] ); + subRound( D, E, A, B, C, f1, K1, W[ 7 ] ); + subRound( C, D, E, A, B, f1, K1, W[ 8 ] ); + subRound( B, C, D, E, A, f1, K1, W[ 9 ] ); + subRound( A, B, C, D, E, f1, K1, W[ 10 ] ); + subRound( E, A, B, C, D, f1, K1, W[ 11 ] ); + subRound( D, E, A, B, C, f1, K1, W[ 12 ] ); + subRound( C, D, E, A, B, f1, K1, W[ 13 ] ); + subRound( B, C, D, E, A, f1, K1, W[ 14 ] ); + subRound( A, B, C, D, E, f1, K1, W[ 15 ] ); + subRound( E, A, B, C, D, f1, K1, W[ 16 ] ); + subRound( D, E, A, B, C, f1, K1, W[ 17 ] ); + subRound( C, D, E, A, B, f1, K1, W[ 18 ] ); + subRound( B, C, D, E, A, f1, K1, W[ 19 ] ); + + subRound( A, B, C, D, E, f2, K2, W[ 20 ] ); + subRound( E, A, B, C, D, f2, K2, W[ 21 ] ); + subRound( D, E, A, B, C, f2, K2, W[ 22 ] ); + subRound( C, D, E, A, B, f2, K2, W[ 23 ] ); + subRound( B, C, D, E, A, f2, K2, W[ 24 ] ); + subRound( A, B, C, D, E, f2, K2, W[ 25 ] ); + subRound( E, A, B, C, D, f2, K2, W[ 26 ] ); + subRound( D, E, A, B, C, f2, K2, W[ 27 ] ); + subRound( C, D, E, A, B, f2, K2, W[ 28 ] ); + subRound( B, C, D, E, A, f2, K2, W[ 29 ] ); + subRound( A, B, C, D, E, f2, K2, W[ 30 ] ); + subRound( E, A, B, C, D, f2, K2, W[ 31 ] ); + subRound( D, E, A, B, C, f2, K2, W[ 32 ] ); + subRound( C, D, E, A, B, f2, K2, W[ 33 ] ); + subRound( B, C, D, E, A, f2, K2, W[ 34 ] ); + subRound( A, B, C, D, E, f2, K2, W[ 35 ] ); + subRound( E, A, B, C, D, f2, K2, W[ 36 ] ); + subRound( D, E, A, B, C, f2, K2, W[ 37 ] ); + subRound( C, D, E, A, B, f2, K2, W[ 38 ] ); + subRound( B, C, D, E, A, f2, K2, W[ 39 ] ); + + subRound( A, B, C, D, E, f3, K3, W[ 40 ] ); + subRound( E, A, B, C, D, f3, K3, W[ 41 ] ); + subRound( D, E, A, B, C, f3, K3, W[ 42 ] ); + subRound( C, D, E, A, B, f3, K3, W[ 43 ] ); + subRound( B, C, D, E, A, f3, K3, W[ 44 ] ); + subRound( A, B, C, D, E, f3, K3, W[ 45 ] ); + subRound( E, A, B, C, D, f3, K3, W[ 46 ] ); + subRound( D, E, A, B, C, f3, K3, W[ 47 ] ); + subRound( C, D, E, A, B, f3, K3, W[ 48 ] ); + subRound( B, C, D, E, A, f3, K3, W[ 49 ] ); + subRound( A, B, C, D, E, f3, K3, W[ 50 ] ); + subRound( E, A, B, C, D, f3, K3, W[ 51 ] ); + subRound( D, E, A, B, C, f3, K3, W[ 52 ] ); + subRound( C, D, E, A, B, f3, K3, W[ 53 ] ); + subRound( B, C, D, E, A, f3, K3, W[ 54 ] ); + subRound( A, B, C, D, E, f3, K3, W[ 55 ] ); + subRound( E, A, B, C, D, f3, K3, W[ 56 ] ); + subRound( D, E, A, B, C, f3, K3, W[ 57 ] ); + subRound( C, D, E, A, B, f3, K3, W[ 58 ] ); + subRound( B, C, D, E, A, f3, K3, W[ 59 ] ); + + subRound( A, B, C, D, E, f4, K4, W[ 60 ] ); + subRound( E, A, B, C, D, f4, K4, W[ 61 ] ); + subRound( D, E, A, B, C, f4, K4, W[ 62 ] ); + subRound( C, D, E, A, B, f4, K4, W[ 63 ] ); + subRound( B, C, D, E, A, f4, K4, W[ 64 ] ); + subRound( A, B, C, D, E, f4, K4, W[ 65 ] ); + subRound( E, A, B, C, D, f4, K4, W[ 66 ] ); + subRound( D, E, A, B, C, f4, K4, W[ 67 ] ); + subRound( C, D, E, A, B, f4, K4, W[ 68 ] ); + subRound( B, C, D, E, A, f4, K4, W[ 69 ] ); + subRound( A, B, C, D, E, f4, K4, W[ 70 ] ); + subRound( E, A, B, C, D, f4, K4, W[ 71 ] ); + subRound( D, E, A, B, C, f4, K4, W[ 72 ] ); + subRound( C, D, E, A, B, f4, K4, W[ 73 ] ); + subRound( B, C, D, E, A, f4, K4, W[ 74 ] ); + subRound( A, B, C, D, E, f4, K4, W[ 75 ] ); + subRound( E, A, B, C, D, f4, K4, W[ 76 ] ); + subRound( D, E, A, B, C, f4, K4, W[ 77 ] ); + subRound( C, D, E, A, B, f4, K4, W[ 78 ] ); + subRound( B, C, D, E, A, f4, K4, W[ 79 ] ); #else - /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */ - subRound( A, B, C, D, E, f1, K1, eData[ 0 ] ); - subRound( E, A, B, C, D, f1, K1, eData[ 1 ] ); - subRound( D, E, A, B, C, f1, K1, eData[ 2 ] ); - subRound( C, D, E, A, B, f1, K1, eData[ 3 ] ); - subRound( B, C, D, E, A, f1, K1, eData[ 4 ] ); - subRound( A, B, C, D, E, f1, K1, eData[ 5 ] ); - subRound( E, A, B, C, D, f1, K1, eData[ 6 ] ); - subRound( D, E, A, B, C, f1, K1, eData[ 7 ] ); - subRound( C, D, E, A, B, f1, K1, eData[ 8 ] ); - subRound( B, C, D, E, A, f1, K1, eData[ 9 ] ); - subRound( A, B, C, D, E, f1, K1, eData[ 10 ] ); - subRound( E, A, B, C, D, f1, K1, eData[ 11 ] ); - subRound( D, E, A, B, C, f1, K1, eData[ 12 ] ); - subRound( C, D, E, A, B, f1, K1, eData[ 13 ] ); - subRound( B, C, D, E, A, f1, K1, eData[ 14 ] ); - subRound( A, B, C, D, E, f1, K1, eData[ 15 ] ); - subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) ); - subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) ); - subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) ); - subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) ); - - subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) ); - subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) ); - subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) ); - subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) ); - subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) ); - subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) ); - subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) ); - subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) ); - subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) ); - subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) ); - subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) ); - subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) ); - subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) ); - subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) ); - subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) ); - subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) ); - subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) ); - subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) ); - subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) ); - subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) ); - - subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) ); - subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) ); - subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) ); - subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) ); - subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) ); - subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) ); - subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) ); - subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) ); - subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) ); - subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) ); - subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) ); - subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) ); - subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) ); - subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) ); - subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) ); - subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) ); - subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) ); - subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) ); - subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) ); - subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) ); - - subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) ); - subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) ); - subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) ); - subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) ); - subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) ); - subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) ); - subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) ); - subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) ); - subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) ); - subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) ); - subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) ); - subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) ); - subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) ); - subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) ); - subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) ); - subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) ); - subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) ); - subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) ); - subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) ); - subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) ); -#endif /* SMALL_VERSION */ +#error Illegal SHA_CODE_SIZE +#endif /* Build message digest */ digest[ 0 ] += A; @@ -838,7 +1046,10 @@ static void SHATransform(__u32 *digest, __u32 *data) digest[ 2 ] += C; digest[ 3 ] += D; digest[ 4 ] += E; - } + + /* W is wiped by the caller */ +#undef W +} #undef ROTL #undef f1 @@ -849,11 +1060,12 @@ static void SHATransform(__u32 *digest, __u32 *data) #undef K2 #undef K3 #undef K4 -#undef expand #undef subRound -#else +#else /* !USE_SHA - Use MD5 */ + #define HASH_BUFFER_SIZE 4 +#define HASH_EXTRA_SIZE 0 #define HASH_TRANSFORM MD5Transform /* @@ -881,8 +1093,7 @@ static void SHATransform(__u32 *digest, __u32 *data) * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -static void MD5Transform(__u32 buf[4], - __u32 const in[16]) +static void MD5Transform(__u32 buf[HASH_BUFFER_SIZE], __u32 const in[16]) { __u32 a, b, c, d; @@ -971,10 +1182,10 @@ static void MD5Transform(__u32 buf[4], #undef F4 #undef MD5STEP -#endif +#endif /* !USE_SHA */ -#if POOLWORDS % 16 +#if POOLWORDS % 16 != 0 #error extract_entropy() assumes that POOLWORDS is a multiple of 16 words. #endif /* @@ -987,8 +1198,8 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, size_t nbytes, int to_user) { ssize_t ret, i; - __u32 tmp[HASH_BUFFER_SIZE]; - char *cp,*dp; + __u32 tmp[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE]; + __u32 x; add_timer_randomness(r, &extract_timer_state, nbytes); @@ -1016,31 +1227,33 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, #endif for (i = 0; i < POOLWORDS; i += 16) HASH_TRANSFORM(tmp, r->pool+i); - /* Modify pool so next hash will produce different results */ - add_entropy_word(r, tmp[0]); - add_entropy_word(r, tmp[1]); - add_entropy_word(r, tmp[2]); - add_entropy_word(r, tmp[3]); -#ifdef USE_SHA - add_entropy_word(r, tmp[4]); -#endif - /* - * Run the hash transform one more time, since we want - * to add at least minimal obscuring of the inputs to - * add_entropy_word(). - */ - HASH_TRANSFORM(tmp, r->pool); /* - * In case the hash function has some recognizable - * output pattern, we fold it in half. + * The following code does two separate things that happen + * to both work two words at a time, so are convenient + * to do together. + * + * First, this feeds the output back into the pool so + * that the next call will return different results. + * Any perturbation of the pool's state would do, even + * changing one bit, but this mixes the pool nicely. + * + * Second, this folds the output in half to hide the data + * fed back into the pool from the user and further mask + * any patterns in the hash output. (The exact folding + * pattern is not important; the one used here is quick.) */ - cp = (char *) tmp; - dp = cp + (HASH_BUFFER_SIZE*sizeof(__u32)) - 1; - for (i=0; i < HASH_BUFFER_SIZE*sizeof(__u32)/2; i++) { - *cp ^= *dp; - cp++; dp--; + for (i = 0; i < HASH_BUFFER_SIZE/2; i++) { + x = tmp[i + (HASH_BUFFER_SIZE+1)/2]; + add_entropy_words(r, tmp[i], x); + tmp[i] ^= x; } +#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); + x ^= (x >> 16); /* Fold it in half */ + ((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x; +#endif /* Copy data to destination buffer */ i = MIN(nbytes, HASH_BUFFER_SIZE*sizeof(__u32)/2); @@ -1055,11 +1268,11 @@ static ssize_t extract_entropy(struct random_bucket *r, char * buf, nbytes -= i; buf += i; add_timer_randomness(r, &extract_timer_state, nbytes); - if (to_user && need_resched) + if (to_user && current->need_resched) schedule(); } - /* Wipe data from memory */ + /* Wipe data just returned from memory */ memset(tmp, 0, sizeof(tmp)); return ret; @@ -1105,8 +1318,7 @@ random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) } n = extract_entropy(&random_state, buf, n, 1); if (n < 0) { - if (count == 0) - retval = n; + retval = n; break; } count += n; @@ -1154,33 +1366,36 @@ static ssize_t random_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) { - ssize_t i, bytes, ret = 0; + int ret = 0; + size_t bytes; + unsigned i; __u32 buf[16]; const char *p = buffer; - ssize_t c = count; + size_t c = count; while (c > 0) { bytes = MIN(c, sizeof(buf)); bytes -= copy_from_user(&buf, p, bytes); if (!bytes) { - if (!ret) - ret = -EFAULT; + ret = -EFAULT; break; } c -= bytes; p += bytes; - ret += bytes; - i = (bytes+sizeof(__u32)-1) / sizeof(__u32); - while (i--) - add_entropy_word(&random_state, buf[i]); + i = (unsigned)((bytes-1) / (2 * sizeof(__u32))); + do { + add_entropy_words(&random_state, buf[2*i], buf[2*i+1]); + } while (i--); } - if (ret > 0) { + if (p == buffer) { + return (ssize_t)ret; + } else { file->f_dentry->d_inode->i_mtime = CURRENT_TIME; mark_inode_dirty(file->f_dentry->d_inode); + return (ssize_t)(p - buffer); } - return ret; } static int @@ -1344,19 +1559,17 @@ struct file_operations urandom_fops = { #define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) -#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) - -/* FF, GG and HH are MD4 transformations for rounds 1, 2 and 3 */ -/* Rotation is separate from addition to prevent recomputation */ -#define FF(a, b, c, d, x, s) \ - {(a) += F ((b), (c), (d)) + (x); \ - (a) = ROTL ((s), (a));} -#define GG(a, b, c, d, x, s) \ - {(a) += G ((b), (c), (d)) + (x) + 013240474631UL; \ - (a) = ROTL ((s), (a));} -#define HH(a, b, c, d, x, s) \ - {(a) += H ((b), (c), (d)) + (x) + 015666365641UL; \ - (a) = ROTL ((s), (a));} +/* + * The generic round function. The application is so specific that + * we don't bother protecting all the arguments with parens, as is generally + * good macro practice, in favor of extra legibility. + * Rotation is separate from addition to prevent recomputation + */ +#define ROUND(f, a, b, c, d, x, s) \ + (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) +#define K1 0 +#define K2 013240474631UL +#define K3 015666365641UL /* * Basic cut-down MD4 transform. Returns only 32 bits of result. @@ -1366,39 +1579,100 @@ static __u32 halfMD4Transform (__u32 const buf[4], __u32 const in[8]) __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; /* Round 1 */ - FF (a, b, c, d, in[ 0], 3); - FF (d, a, b, c, in[ 1], 7); - FF (c, d, a, b, in[ 2], 11); - FF (b, c, d, a, in[ 3], 19); - FF (a, b, c, d, in[ 4], 3); - FF (d, a, b, c, in[ 5], 7); - FF (c, d, a, b, in[ 6], 11); - FF (b, c, d, a, in[ 7], 19); + ROUND(F, a, b, c, d, in[0] + K1, 3); + ROUND(F, d, a, b, c, in[1] + K1, 7); + ROUND(F, c, d, a, b, in[2] + K1, 11); + ROUND(F, b, c, d, a, in[3] + K1, 19); + ROUND(F, a, b, c, d, in[4] + K1, 3); + ROUND(F, d, a, b, c, in[5] + K1, 7); + ROUND(F, c, d, a, b, in[6] + K1, 11); + ROUND(F, b, c, d, a, in[7] + K1, 19); /* Round 2 */ - GG (a, b, c, d, in[ 0], 3); - GG (d, a, b, c, in[ 4], 5); - GG (c, d, a, b, in[ 1], 9); - GG (b, c, d, a, in[ 5], 13); - GG (a, b, c, d, in[ 2], 3); - GG (d, a, b, c, in[ 6], 5); - GG (c, d, a, b, in[ 3], 9); - GG (b, c, d, a, in[ 7], 13); + ROUND(G, a, b, c, d, in[1] + K2, 3); + ROUND(G, d, a, b, c, in[3] + K2, 5); + ROUND(G, c, d, a, b, in[5] + K2, 9); + ROUND(G, b, c, d, a, in[7] + K2, 13); + ROUND(G, a, b, c, d, in[0] + K2, 3); + ROUND(G, d, a, b, c, in[2] + K2, 5); + ROUND(G, c, d, a, b, in[4] + K2, 9); + ROUND(G, b, c, d, a, in[6] + K2, 13); /* Round 3 */ - HH (a, b, c, d, in[ 0], 3); - HH (d, a, b, c, in[ 4], 9); - HH (c, d, a, b, in[ 2], 11); - HH (b, c, d, a, in[ 6], 15); - HH (a, b, c, d, in[ 1], 3); - HH (d, a, b, c, in[ 5], 9); - HH (c, d, a, b, in[ 3], 11); - HH (b, c, d, a, in[ 7], 15); + ROUND(H, a, b, c, d, in[3] + K3, 3); + ROUND(H, d, a, b, c, in[7] + K3, 9); + ROUND(H, c, d, a, b, in[2] + K3, 11); + ROUND(H, b, c, d, a, in[6] + K3, 15); + ROUND(H, a, b, c, d, in[1] + K3, 3); + ROUND(H, d, a, b, c, in[5] + K3, 9); + ROUND(H, c, d, a, b, in[0] + K3, 11); + ROUND(H, b, c, d, a, in[4] + K3, 15); return buf[1] + b; /* "most hashed" word */ /* Alternative: return sum of all words? */ } +#if 0 /* May be needed for IPv6 */ + +static __u32 twothirdsMD4Transform (__u32 const buf[4], __u32 const in[12]) +{ + __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ + ROUND(F, a, b, c, d, in[ 0] + K1, 3); + ROUND(F, d, a, b, c, in[ 1] + K1, 7); + ROUND(F, c, d, a, b, in[ 2] + K1, 11); + ROUND(F, b, c, d, a, in[ 3] + K1, 19); + ROUND(F, a, b, c, d, in[ 4] + K1, 3); + ROUND(F, d, a, b, c, in[ 5] + K1, 7); + ROUND(F, c, d, a, b, in[ 6] + K1, 11); + ROUND(F, b, c, d, a, in[ 7] + K1, 19); + ROUND(F, a, b, c, d, in[ 8] + K1, 3); + ROUND(F, d, a, b, c, in[ 9] + K1, 7); + ROUND(F, c, d, a, b, in[10] + K1, 11); + ROUND(F, b, c, d, a, in[11] + K1, 19); + + /* Round 2 */ + ROUND(G, a, b, c, d, in[ 1] + K2, 3); + ROUND(G, d, a, b, c, in[ 3] + K2, 5); + ROUND(G, c, d, a, b, in[ 5] + K2, 9); + ROUND(G, b, c, d, a, in[ 7] + K2, 13); + ROUND(G, a, b, c, d, in[ 9] + K2, 3); + ROUND(G, d, a, b, c, in[11] + K2, 5); + ROUND(G, c, d, a, b, in[ 0] + K2, 9); + ROUND(G, b, c, d, a, in[ 2] + K2, 13); + ROUND(G, a, b, c, d, in[ 4] + K2, 3); + ROUND(G, d, a, b, c, in[ 6] + K2, 5); + ROUND(G, c, d, a, b, in[ 8] + K2, 9); + ROUND(G, b, c, d, a, in[10] + K2, 13); + + /* Round 3 */ + ROUND(H, a, b, c, d, in[ 3] + K3, 3); + ROUND(H, d, a, b, c, in[ 7] + K3, 9); + ROUND(H, c, d, a, b, in[11] + K3, 11); + ROUND(H, b, c, d, a, in[ 2] + K3, 15); + ROUND(H, a, b, c, d, in[ 6] + K3, 3); + ROUND(H, d, a, b, c, in[10] + K3, 9); + ROUND(H, c, d, a, b, in[ 1] + K3, 11); + ROUND(H, b, c, d, a, in[ 5] + K3, 15); + ROUND(H, a, b, c, d, in[ 9] + K3, 3); + ROUND(H, d, a, b, c, in[ 0] + K3, 9); + ROUND(H, c, d, a, b, in[ 4] + K3, 11); + ROUND(H, b, c, d, a, in[ 8] + K3, 15); + + return buf[1] + b; /* "most hashed" word */ + /* Alternative: return sum of all words? */ +} +#endif + +#undef ROUND +#undef F +#undef G +#undef H +#undef K1 +#undef K2 +#undef K3 + /* This should not be decreased so low that ISNs wrap too fast. */ #define REKEY_INTERVAL 300 #define HASH_BITS 24 @@ -1417,8 +1691,7 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, */ do_gettimeofday(&tv); /* We need the usecs below... */ - if (!rekey_time || - (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { + if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { rekey_time = tv.tv_sec; /* First three words are overwritten below. */ get_random_bytes(&secret+3, sizeof(secret)-12); @@ -1439,13 +1712,13 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, secret[2]=(sport << 16) + dport; seq = (halfMD4Transform(secret+8, secret) & - ((1<<HASH_BITS)-1)) + (count << HASH_BITS); + ((1<<HASH_BITS)-1)) + count; /* * As close as possible to RFC 793, which - * suggests using a 250kHz clock. - * Further reading shows this assumes 2Mb/s networks. - * For 10Mb/s ethernet, a 1MHz clock is appropriate. + * suggests using a 250 kHz clock. + * Further reading shows this assumes 2 Mb/s networks. + * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. * That's funny, Linux has one built in! Use it! * (Networks are faster now - should this be increased?) */ @@ -1463,53 +1736,97 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, * Dan Bernstein and Eric Schenk. * * For linux I implement the 1 minute counter by looking at the jiffies clock. - * The count is passed in as a parameter; - * + * The count is passed in as a parameter, so this code doesn't much care. */ -__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, - __u16 sport, __u16 dport, __u32 sseq, __u32 count) + +#define COOKIEBITS 24 /* Upper bits store count */ +#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1) + +static int syncookie_init = 0; +static __u32 syncookie_secret[2][16-3+HASH_BUFFER_SIZE]; + +__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, __u16 sport, + __u16 dport, __u32 sseq, __u32 count, __u32 data) { - static int is_init = 0; - static __u32 secret[2][16]; - __u32 tmp[16]; - __u32 seq; + __u32 tmp[16 + HASH_BUFFER_SIZE + HASH_EXTRA_SIZE]; + __u32 seq; /* - * Pick two random secret the first time we open a TCP connection. + * Pick two random secrets the first time we need a cookie. */ - if (is_init == 0) { - get_random_bytes(&secret[0], sizeof(secret[0])); - get_random_bytes(&secret[1], sizeof(secret[1])); - is_init = 1; + if (syncookie_init == 0) { + get_random_bytes(syncookie_secret, sizeof(syncookie_secret)); + syncookie_init = 1; } /* * Compute the secure sequence number. * The output should be: - * MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number - * + (count * 2^24) - * + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24). - * Where count increases every minute by 1. + * HASH(sec1,saddr,sport,daddr,dport,sec1) + sseq + (count * 2^24) + * + (HASH(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24). + * Where sseq is their sequence number and count increases every + * minute by 1. + * As an extra hack, we add a small "data" value that encodes the + * MSS into the second hash value. */ - memcpy(tmp, secret[0], sizeof(tmp)); - tmp[8]=saddr; - tmp[9]=daddr; - tmp[10]=(sport << 16) + dport; - HASH_TRANSFORM(tmp, tmp); - seq = tmp[1]; - - memcpy(tmp, secret[1], sizeof(tmp)); - tmp[8]=saddr; - tmp[9]=daddr; - tmp[10]=(sport << 16) + dport; - tmp[11]=count; /* minute counter */ - HASH_TRANSFORM(tmp, tmp); - - seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff); + memcpy(tmp+3, syncookie_secret[0], sizeof(syncookie_secret[0])); + tmp[0]=saddr; + tmp[1]=daddr; + tmp[2]=(sport << 16) + dport; + HASH_TRANSFORM(tmp+16, tmp); + seq = tmp[17] + sseq + (count << COOKIEBITS); + + memcpy(tmp+3, syncookie_secret[1], sizeof(syncookie_secret[1])); + tmp[0]=saddr; + tmp[1]=daddr; + tmp[2]=(sport << 16) + dport; + tmp[3] = count; /* minute counter */ + HASH_TRANSFORM(tmp+16, tmp); + + /* Add in the second hash and the data */ + return seq + ((tmp[17] + data) & COOKIEMASK); +} - /* Zap lower 3 bits to leave room for the MSS representation */ - return (seq & 0xfffff8); +/* + * This retrieves the small "data" value from the syncookie. + * If the syncookie is bad, the data returned will be out of + * range. This must be checked by the caller. + * + * The count value used to generate the cookie must be within + * "maxdiff" if the current (passed-in) "count". The return value + * is (__u32)-1 if this test fails. + */ +__u32 check_tcp_syn_cookie(__u32 cookie, __u32 saddr, __u32 daddr, __u16 sport, + __u16 dport, __u32 sseq, __u32 count, __u32 maxdiff) +{ + __u32 tmp[16 + HASH_BUFFER_SIZE + HASH_EXTRA_SIZE]; + __u32 diff; + + if (syncookie_init == 0) + return (__u32)-1; /* Well, duh! */ + + /* Strip away the layers from the cookie */ + memcpy(tmp+3, syncookie_secret[0], sizeof(syncookie_secret[0])); + tmp[0]=saddr; + tmp[1]=daddr; + tmp[2]=(sport << 16) + dport; + HASH_TRANSFORM(tmp+16, tmp); + cookie -= tmp[17] + sseq; + /* Cookie is now reduced to (count * 2^24) ^ (hash % 2^24) */ + + diff = (count - (cookie >> COOKIEBITS)) & ((__u32)-1 >> COOKIEBITS); + if (diff >= maxdiff) + return (__u32)-1; + + memcpy(tmp+3, syncookie_secret[1], sizeof(syncookie_secret[1])); + tmp[0] = saddr; + tmp[1] = daddr; + tmp[2] = (sport << 16) + dport; + tmp[3] = count - diff; /* minute counter */ + HASH_TRANSFORM(tmp+16, tmp); + + return (cookie - tmp[17]) & COOKIEMASK; /* Leaving the data behind */ } #endif @@ -1532,7 +1849,7 @@ static inline unsigned long long get_clock_cnt(void) { unsigned long low, high; __asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high)); - return (((unsigned long long) high << 31) | low); + return (((unsigned long long) high << 32) | low); } __initfunc(static void diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index cb4e9d498..20e818ce6 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -48,6 +48,9 @@ #if (LINUX_VERSION_CODE > 66304) #define NEW_MODULES +#ifdef LOCAL_ROCKET_H /* We're building standalone */ +#define MODULE +#endif #endif #ifdef NEW_MODULES @@ -81,6 +84,9 @@ #include <linux/ioport.h> #ifdef ENABLE_PCI #include <linux/pci.h> +#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */ +#include <linux/bios32.h> +#endif #endif #if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */ #include <linux/init.h> @@ -89,12 +95,17 @@ #endif #include "rocket_int.h" +#ifdef LOCAL_ROCKET_H +#include "rocket.h" +#include "version.h" +#else #include <linux/rocket.h> - -#define ROCKET_VERSION "1.14a" -#define ROCKET_DATE "19-Jul-97" +#define ROCKET_VERSION "1.14b" +#define ROCKET_DATE "29-Jun-98" +#endif /* LOCAL_ROCKET_H */ #define ROCKET_PARANOIA_CHECK +#define ROCKET_SOFT_FLOW #undef ROCKET_DEBUG_OPEN #undef ROCKET_DEBUG_INTR @@ -177,9 +188,30 @@ static unsigned long time_stat_long = 0; static unsigned long time_counter = 0; #endif +#if ((LINUX_VERSION_CODE > 0x020111) && defined(MODULE)) +MODULE_AUTHOR("Theodore Ts'o"); +MODULE_DESCRIPTION("Comtrol Rocketport driver"); +MODULE_PARM(board1, "i"); +MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); +MODULE_PARM(board2, "i"); +MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); +MODULE_PARM(board3, "i"); +MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); +MODULE_PARM(board4, "i"); +MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); +MODULE_PARM(controller, "i"); +MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); +MODULE_PARM(support_low_speed, "i"); +MODULE_PARM_DESC(support_low_speed, "0 means support 50 baud, 1 means support 460400 baud"); +#endif + /* * Provide backwards compatibility for kernels prior to 2.1.8. */ +#if (LINUX_VERSION_CODE < 0x20000) +typedef dev_t kdev_t; +#endif + #if (LINUX_VERSION_CODE < 131336) int copy_from_user(void *to, const void *from_user, unsigned long len) { @@ -202,6 +234,12 @@ int copy_to_user(void *to_user, const void *from, unsigned long len) memcpy_tofs(to_user, from, len); return 0; } + +static inline int signal_pending(struct task_struct *p) +{ + return (p->signal & (~p->blocked != 0)); +} + #else #include <asm/uaccess.h> #endif @@ -720,6 +758,27 @@ static void configure_r_port(struct r_port *info) info->intmask |= DELTA_CD; restore_flags(flags); } + + /* + * Handle software flow control in the board + */ +#ifdef ROCKET_SOFT_FLOW + if (I_IXON(info->tty)) { + sEnTxSoftFlowCtl(cp); + if (I_IXANY(info->tty)) { + sEnIXANY(cp); + } else { + sDisIXANY(cp); + } + sSetTxXONChar(cp, START_CHAR(info->tty)); + sSetTxXOFFChar(cp, STOP_CHAR(info->tty)); + } else { + sDisTxSoftFlowCtl(cp); + sDisIXANY(cp); + sClrTxXOFF(cp); + } +#endif + /* * Set up ignore/read mask words */ @@ -728,7 +787,7 @@ static void configure_r_port(struct r_port *info) info->read_status_mask |= STMFRAMEH | STMPARITYH; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= STMBREAKH; - + /* * Characters to ignore */ @@ -751,7 +810,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, { struct wait_queue wait = { current, NULL }; int retval; - int do_clocal = 0; + int do_clocal = 0, extra_count = 0; unsigned long flags; /* @@ -820,8 +879,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, info->line, info->count); #endif save_flags(flags); cli(); - if (!tty_hung_up_p(filp)) + if (!tty_hung_up_p(filp)) { + extra_count = 1; info->count--; + } restore_flags(flags); info->blocked_open++; while (1) { @@ -857,7 +918,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); cli(); - if (!tty_hung_up_p(filp)) + if (extra_count) info->count++; restore_flags(flags); info->blocked_open--; @@ -1315,7 +1376,12 @@ static int set_config(struct r_port * info, struct rocket_config * new_info) if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; - if (!capable(CAP_SYS_ADMIN)) { +#ifdef CAP_SYS_ADMIN + if (!capable(CAP_SYS_ADMIN)) +#else + if (!suser()) +#endif + { if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) return -EPERM; @@ -1754,7 +1820,6 @@ static int rp_write(struct tty_struct * tty, int from_user, save_flags(flags); while (1) { - cli(); if (info->tty == 0) { restore_flags(flags); goto end; @@ -1778,10 +1843,10 @@ static int rp_write(struct tty_struct * tty, int from_user, /* In case we got pre-empted */ if (info->tty == 0) goto end_intr; - c = MIN(c, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1, - XMIT_BUF_SIZE - info->xmit_head)); - } + cli(); + c = MIN(c, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1, + XMIT_BUF_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, b, c); info->xmit_head = (info->xmit_head + c) & (XMIT_BUF_SIZE-1); info->xmit_cnt += c; @@ -1868,6 +1933,35 @@ static void rp_flush_buffer(struct tty_struct *tty) } #ifdef ENABLE_PCI +#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */ +/* For compatibility */ +static struct pci_dev *pci_find_slot(char bus, char device_fn) +{ + unsigned short vendor_id, device_id; + int ret, error; + static struct pci_dev ret_struct; + + error = pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, + &vendor_id); + ret = pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, + &device_id); + if (error == 0) + error = ret; + + if (error) { + printk("PCI RocketPort error: %s not initializing due to error" + "reading configuration space\n", + pcibios_strerror(error)); + return(0); + } + + memset(&ret_struct, 0, sizeof(ret_struct)); + ret_struct.device = device_id; + + return &ret_struct; +} +#endif + __initfunc(int register_PCI(int i, char bus, char device_fn)) { int num_aiops, aiop, max_num_aiops, num_chan, chan; @@ -1875,8 +1969,23 @@ __initfunc(int register_PCI(int i, char bus, char device_fn)) char *str; CONTROLLER_t *ctlp; struct pci_dev *dev = pci_find_slot(bus, device_fn); +#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */ + int ret; + unsigned int port; +#endif + if (!dev) + return 0; + +#if (LINUX_VERSION_CODE >= 0x020163) /* 2.1.99 */ rcktpt_io_addr[i] = dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; +#else + ret = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, + &port); + if (ret) + return 0; + rcktpt_io_addr[i] = port & PCI_BASE_ADDRESS_IO_MASK; +#endif switch(dev->device) { case PCI_DEVICE_ID_RP4QUAD: str = "Quadcable"; @@ -1902,6 +2011,18 @@ __initfunc(int register_PCI(int i, char bus, char device_fn)) str = "32"; max_num_aiops = 4; break; + case PCI_DEVICE_ID_RPP4: + str = "Plus Quadcable"; + max_num_aiops = 1; + break; + case PCI_DEVICE_ID_RPP8: + str = "Plus Octacable"; + max_num_aiops = 1; + break; + case PCI_DEVICE_ID_RP8M: + str = "8-port Modem"; + max_num_aiops = 1; + break; default: str = "(unknown/unsupported)"; max_num_aiops = 0; @@ -1936,20 +2057,48 @@ __initfunc(static int init_PCI(int boards_found)) int i, count = 0; for(i=0; i < (NUM_BOARDS - boards_found); i++) { - if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA, - i, &bus, &device_fn)) + if (!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP4QUAD, i, &bus, &device_fn)) + if (register_PCI(count+boards_found, bus, device_fn)) + count++; + if (!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP8J, i, &bus, &device_fn)) + if (register_PCI(count+boards_found, bus, device_fn)) + count++; + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP8OCTA, i, &bus, &device_fn)) + if(register_PCI(count+boards_found, bus, device_fn)) + count++; + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP8INTF, i, &bus, &device_fn)) + if(register_PCI(count+boards_found, bus, device_fn)) + count++; + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP16INTF, i, &bus, &device_fn)) + if(register_PCI(count+boards_found, bus, device_fn)) + count++; + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP32INTF, i, &bus, &device_fn)) + if(register_PCI(count+boards_found, bus, device_fn)) + count++; + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP4QUAD, i, &bus, &device_fn)) + if(register_PCI(count+boards_found, bus, device_fn)) + count++; + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP8J, i, &bus, &device_fn)) if(register_PCI(count+boards_found, bus, device_fn)) count++; - if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF, - i, &bus, &device_fn)) + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RPP4, i, &bus, &device_fn)) if(register_PCI(count+boards_found, bus, device_fn)) count++; - if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF, - i, &bus, &device_fn)) + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RPP8, i, &bus, &device_fn)) if(register_PCI(count+boards_found, bus, device_fn)) count++; - if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF, - i, &bus, &device_fn)) + if(!pcibios_find_device(PCI_VENDOR_ID_RP, + PCI_DEVICE_ID_RP8M, i, &bus, &device_fn)) if(register_PCI(count+boards_found, bus, device_fn)) count++; } @@ -2073,7 +2222,7 @@ __initfunc(int rp_init(void)) isa_boards_found++; } #ifdef ENABLE_PCI - if (pci_present()) { + if (pcibios_present()) { if(isa_boards_found < NUM_BOARDS) pci_boards_found = init_PCI(isa_boards_found); } else { diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 8b2ea2212..604e8733b 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -483,6 +483,18 @@ Call: sDisCTSFlowCtl(ChP) } /*************************************************************************** +Function: sDisIXANY +Purpose: Disable IXANY Software Flow Control +Call: sDisIXANY(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sDisIXANY(ChP) \ +{ \ + (ChP)->R[0x0e] = 0x86; \ + sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x0c]); \ +} + +/*************************************************************************** Function: DisParity Purpose: Disable parity Call: sDisParity(ChP) @@ -573,6 +585,18 @@ Call: sEnCTSFlowCtl(ChP) } /*************************************************************************** +Function: sEnIXANY +Purpose: Enable IXANY Software Flow Control +Call: sEnIXANY(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnIXANY(ChP) \ +{ \ + (ChP)->R[0x0e] = 0x21; \ + sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x0c]); \ +} + +/*************************************************************************** Function: EnParity Purpose: Enable parity Call: sEnParity(ChP) @@ -647,6 +671,18 @@ Call: sEnTransmit(ChP) } /*************************************************************************** +Function: sEnTxSoftFlowCtl +Purpose: Enable Tx Software Flow Control +Call: sEnTxSoftFlowCtl(ChP) + CHANNEL_T *ChP; Ptr to channel structure +*/ +#define sEnTxSoftFlowCtl(ChP) \ +{ \ + (ChP)->R[0x06] = 0xc5; \ + sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x04]); \ +} + +/*************************************************************************** Function: sGetAiopIntStatus Purpose: Get the AIOP interrupt status Call: sGetAiopIntStatus(CtlP,AiopNum) @@ -985,6 +1021,32 @@ Call: sSetStop2(ChP) } /*************************************************************************** +Function: sSetTxXOFFChar +Purpose: Set the Tx XOFF flow control character +Call: sSetTxXOFFChar(ChP,Ch) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Ch; The value to set the Tx XOFF character to +*/ +#define sSetTxXOFFChar(ChP,CH) \ +{ \ + (ChP)->R[0x07] = (CH); \ + sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x04]); \ +} + +/*************************************************************************** +Function: sSetTxXONChar +Purpose: Set the Tx XON flow control character +Call: sSetTxXONChar(ChP,Ch) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Ch; The value to set the Tx XON character to +*/ +#define sSetTxXONChar(ChP,CH) \ +{ \ + (ChP)->R[0x0b] = (CH); \ + sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x08]); \ +} + +/*************************************************************************** Function: sStartRxProcessor Purpose: Start a channel's receive processor Call: sStartRxProcessor(ChP) @@ -1133,17 +1195,25 @@ struct r_port { #undef PCI_DEVICE_ID_RP32INTF #endif -#define PCI_VENDOR_ID_RP 0x11fe -#define PCI_DEVICE_ID_RP32INTF 0x0001 -#define PCI_DEVICE_ID_RP8INTF 0x0002 -#define PCI_DEVICE_ID_RP16INTF 0x0003 -#define PCI_DEVICE_ID_RP8OCTA 0x0005 +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_DEVICE_ID_RP32INTF 0x0001 +#define PCI_DEVICE_ID_RP8INTF 0x0002 +#define PCI_DEVICE_ID_RP16INTF 0x0003 +#define PCI_DEVICE_ID_RP8OCTA 0x0005 -#ifndef RP4QUAD -#define PCI_DEVICE_ID_RP4QUAD 0x0004 +#ifndef PCI_DEVICE_ID_RP4QUAD +#define PCI_DEVICE_ID_RP4QUAD 0x0004 #endif -#ifndef RP8J -#define PCI_DEVICE_ID_RP8J 0x0006 +#ifndef PCI_DEVICE_ID_RP8J +#define PCI_DEVICE_ID_RP8J 0x0006 #endif - +#ifndef PCI_DEVICE_ID_RPP4 +#define PCI_DEVICE_ID_RPP4 0x000A +#endif +#ifndef PCI_DEVICE_ID_RPP8 +#define PCI_DEVICE_ID_RPP8 0x000B +#endif +#ifndef PCI_DEVICE_ID_RP8M +#define PCI_DEVICE_ID_RP8M 0x000C +#endif diff --git a/drivers/char/rsf16fmi.h b/drivers/char/rsf16fmi.h new file mode 100644 index 000000000..b71e35bdd --- /dev/null +++ b/drivers/char/rsf16fmi.h @@ -0,0 +1,13 @@ +/* SF16FMI FMRadio include file. + * (c) 1998 Petr Vandrovec + * + * Not in include/linux/ because there's no need for anyone + * to know about these details, I reckon. + */ + +#ifndef __RSF16FMI_H +#define __RSF16FMI_H + +int radiosf16fmi_init(void); + +#endif /* __RSF16FMI_H */ diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index b0c8148b5..b7c117c31 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -135,11 +135,8 @@ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); wake_up_interruptible(&rtc_wait); - if (rtc_status & RTC_TIMER_ON) { - del_timer(&rtc_irq_timer); - rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; - add_timer(&rtc_irq_timer); - } + if (rtc_status & RTC_TIMER_ON) + mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); } /* @@ -597,9 +594,7 @@ void rtc_dropped_irq(unsigned long data) unsigned long flags; printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq); - del_timer(&rtc_irq_timer); - rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; - add_timer(&rtc_irq_timer); + mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); save_flags(flags); cli(); diff --git a/drivers/char/rtrack.c b/drivers/char/rtrack.c deleted file mode 100644 index efe01046a..000000000 --- a/drivers/char/rtrack.c +++ /dev/null @@ -1,213 +0,0 @@ -/* radiotrack (radioreveal) driver for Linux radio support - * (c) 1997 M. Kirkwood - */ -/* TODO: Allow for more than one of these foolish entities :-) */ - -/* Notes on the hardware (reverse engineered from other peoples' - * reverse engineering of AIMS' code :-) - * - * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); - * - * The signal strength query is unsurprisingly inaccurate. And it seems - * to indicate that (on my card, at least) the frequency setting isn't - * too great. (I have to tune up .025MHz from what the freq should be - * to get a report that the thing is tuned.) - * - * Volume control is (ugh) analogue: - * out(port, start_increasing_volume); - * wait(a_wee_while); - * out(port, stop_changing_the_volume); - * - */ - -#include <linux/config.h> -#include <linux/radio.h> -#include <linux/ioport.h> - -#include <linux/delay.h> -#include <asm/io.h> -#include <asm/uaccess.h> - -#include "rtrack.h" - -/* Let's just be a bit careful here, shall we? */ -#if (CONFIG_RADIO_RTRACK_PORT != 0x20f) && (CONFIG_RADIO_RTRACK_PORT != 0x30f) -#error Invalid port specified for RadioTrack -#endif - -/* local prototypes */ -void outbits(int bits, int data, int port); -void decvol(int port); -void incvol(int port); -void mute(int port); -void unmute(int port); - -/* public structurey-type things */ -int rt_port = CONFIG_RADIO_RTRACK_PORT; - -struct radio_cap rt_cap = { - 0, /* device index (not dealt with here) */ - RADIO_TYPE_RTRACK, /* type */ - 1, /* number of bandwidths */ - 0, 10 /* volmin, volmax */ -}; - -/* we only get one band/protocol with radiotrack */ -struct radio_band rt_band = { - 0, /* device index */ - 0, /* bandwidth "index" */ - RADIO_PROTOCOL_FM, - RADIO_BAND_FM_STD, - RADIO_FM_FRTOINT(88.0), /* freq range */ - RADIO_FM_FRTOINT(108.0), - 0,1 /* sig strength range */ -}; - -/* p'raps these should become struct radio_ops and struct radio_status? */ -struct radio_device rt_dev = { - &rt_cap, - &rt_band, - &rt_setvol, - 0, /* curvol */ - &rt_setband, - 0, /* curband */ - &rt_setfreq, - 0, /* curfreq */ - &rt_getsigstr, - NULL, /* next (to be filled in later) */ - &rt_port /* misc */ -}; - - -void radiotrack_init() -{ -int dev_num, i; - -/* XXX - probe here?? - XXX */ - -/* try to grab the i/o port */ - if(check_region(rt_port,2)) { - printk("rtrack.c: port 0x%x already used\n", rt_port); - return; - } - - request_region(rt_port,2,"rtrack"); - - dev_num = radio_add_device(&rt_dev); -/* initialise the card */ - /* set the volume very low */ - for(i=rt_cap.volmax; i>rt_cap.volmin; i--) - decvol(rt_port); - rt_dev.curvol = rt_cap.volmin; -} - - -int rt_setvol(struct radio_device *dev, int vol) -{ -int port, i; - - if(vol == dev->curvol) - return 0; - - port = *(int*)(dev->misc); - if(vol == 0) - mute(port); - - if(vol > dev->curvol) - for(i = dev->curvol; i < vol; i++) - incvol(port); - else - for(i = dev->curvol; i > vol; i--) - decvol(port); - - if(dev->curvol == 0) - unmute(port); - - return 0; -} - - -int rt_setband(struct radio_device *dev, int band) -{ -/* we know in advance that we only have one band, and - * the wrapper checks the validity of all the inputs - */ - return 0; -} - -int rt_setfreq(struct radio_device *dev, int freq) -{ -int myport = *(int*)(dev->misc); - - outbits(16, RTRACK_ENCODE(freq), myport); - outbits(8, 0xa0, myport); -/* XXX - get rid of this once setvol is implemented properly - XXX */ -/* these insist on turning the thing on. not sure I approve... */ - udelay(1000); - outb(0, myport); - outb(0xc8, myport); - - return 0; -} - -int rt_getsigstr(struct radio_device *dev) -{ -int res; -int myport = *(int*)(dev->misc); - - outb(0xf8, myport); - udelay(200000); - res = (int)inb(myport); - udelay(10000); - outb(0xe8, myport); - if(res == 0xfd) - return 1; - else - return 0; -} - - -/* local things */ -void outbits(int bits, int data, int port) -{ - while(bits--) { - if(data & 1) { - outw(5, port); - outw(5, port); - outw(7, port); - outw(7, port); - } else { - outw(1, port); - outw(1, port); - outw(3, port); - outw(3, port); - } - data>>=1; - } -} - -void decvol(int port) -{ - outb(0x48, port); - udelay(100000); - outb(0xc8, port); -} - -void incvol(int port) -{ - outb(0x88, port); - udelay(100000); - outb(0xc8, port); -} - -void mute(int port) -{ - outb(0, port); - outb(0xc0, port); -} - -void unmute(int port) -{ - outb(0, port); - outb(0xc8, port); -} diff --git a/drivers/char/rtrack.h b/drivers/char/rtrack.h deleted file mode 100644 index b5a9bc124..000000000 --- a/drivers/char/rtrack.h +++ /dev/null @@ -1,25 +0,0 @@ -/* RadioTrack (RadioReveal) include file. - * (c) 1997 M. Kirkwood - * - * Not in include/linux/ because there's no need for anyone - * to know about these details, I reckon. - */ - -#ifndef __RTRACK_H -#define __RTRACK_H - -#include <linux/radio.h> - -void radiotrack_init(void); -int rt_setvol(struct radio_device *dev, int vol); -int rt_setband(struct radio_device *dev, int vol); -int rt_setfreq(struct radio_device *dev, int vol); -int rt_getsigstr(struct radio_device *dev); - -/* frequency encoding stuff... */ -/* we have to careful not to introduce fp stuff here */ -#define RTRACK_ENCODE(x) (((((x)*2)/5)-(40*88))+0xf6c) -#define RTRACK_DECODE(x) (((((x)-0xf6c)+(40*88))*5)/2) -/* we shouldn't actually need the decode macro (or the excessive bracketing :-) */ - -#endif /* __RTRACK_H */ diff --git a/drivers/char/saa5249.c b/drivers/char/saa5249.c new file mode 100644 index 000000000..252fcabbe --- /dev/null +++ b/drivers/char/saa5249.c @@ -0,0 +1,707 @@ +/* + * Cleaned up to use existing videodev interface and allow the idea + * of multiple teletext decoders on the video4linux iface. Changed i2c + * to cover addressing clashes on device busses. It's also rebuilt so + * you can add arbitary multiple teletext devices to Linux video4linux + * now (well 32 anyway). + * + * Alan Cox <Alan.Cox@linux.org> + * + * The original driver was heavily modified to match the i2c interface + * It was truncated to use the WinTV boards, too. + * + * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de> + * + * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $ + * + * Derived From + * + * vtx.c: + * This is a loadable character-device-driver for videotext-interfaces + * (aka teletext). Please check the Makefile/README for a list of supported + * interfaces. + * + * Copyright (c) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de> + * + * + * 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 <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <stdarg.h> +#include <linux/i2c.h> +#include <linux/videotext.h> +#include <linux/videodev.h> + +#define VTX_VER_MAJ 1 +#define VTX_VER_MIN 7 + + + +#define NUM_DAUS 4 +#define NUM_BUFS 8 +#define IF_NAME "SAA5249" + +static const int disp_modes[8][3] = +{ + { 0x46, 0x03, 0x03 }, /* DISPOFF */ + { 0x46, 0xcc, 0xcc }, /* DISPNORM */ + { 0x44, 0x0f, 0x0f }, /* DISPTRANS */ + { 0x46, 0xcc, 0x46 }, /* DISPINS */ + { 0x44, 0x03, 0x03 }, /* DISPOFF, interlaced */ + { 0x44, 0xcc, 0xcc }, /* DISPNORM, interlaced */ + { 0x44, 0x0f, 0x0f }, /* DISPTRANS, interlaced */ + { 0x44, 0xcc, 0x46 } /* DISPINS, interlaced */ +}; + + + +#define PAGE_WAIT 30 /* Time in jiffies between requesting page and */ + /* checking status bits */ +#define PGBUF_EXPIRE 1500 /* Time in jiffies to wait before retransmitting */ + /* page regardless of infobits */ +typedef struct { + u8 pgbuf[VTX_VIRTUALSIZE]; /* Page-buffer */ + u8 laststat[10]; /* Last value of infobits for DAU */ + u8 sregs[7]; /* Page-request registers */ + unsigned long expire; /* Time when page will be expired */ + unsigned clrfound : 1; /* VTXIOCCLRFOUND has been called */ + unsigned stopped : 1; /* VTXIOCSTOPDAU has been called */ +} vdau_t; + +struct saa5249_device +{ + vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */ + /* real DAU, so we have to simulate some more) */ + int vtx_use_count; + int is_searching[NUM_DAUS]; + int disp_mode; + int virtual_mode; + struct i2c_bus *bus; +}; + + +#define CCTWR 34 /* I²C write/read-address of vtx-chip */ +#define CCTRD 35 +#define NOACK_REPEAT 10 /* Retry access this many times on failure */ +#define CLEAR_DELAY 5 /* Time in jiffies required to clear a page */ +#define I2C_TIMEOUT 300 /* open/close/SDA-check timeout in jiffies */ +#define READY_TIMEOUT 3 /* Time in jiffies to wait for ready signal of I²C-bus interface */ +#define INIT_DELAY 500 /* Time in usec to wait at initialization of CEA interface */ +#define START_DELAY 10 /* Time in usec to wait before starting write-cycle (CEA) */ + +#define VTX_DEV_MINOR 0 + +/* General defines and debugging support */ + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define RESCHED \ + do { \ + if (current->need_resched) \ + schedule(); \ + } while (0) + +static struct video_device saa_template; /* Declared near bottom */ + +/* + * We do most of the hard work when we become a device on the i2c. + */ + +static int saa5249_attach(struct i2c_device *device) +{ + int pgbuf; + int err; + struct video_device *vd; + struct saa5249_device *t; + /* Only attach these chips to the BT848 bus for now */ + + if(device->bus->id!=I2C_BUSID_BT848) + return -EINVAL; + + printk(KERN_DEBUG "saa5249_attach: bus %p\n", device->bus); + strcpy(device->name, IF_NAME); + + /* + * Now create a video4linux device + */ + + vd=(struct video_device *)kmalloc(sizeof(struct video_device), GFP_KERNEL); + if(vd==NULL) + return -ENOMEM; + + memcpy(vd, &saa_template, sizeof(*vd)); + + /* + * Attach an saa5249 device + */ + + t=(struct saa5249_device *)kmalloc(sizeof(struct saa5249_device), GFP_KERNEL); + if(t==NULL) + { + kfree(vd); + return -ENOMEM; + } + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) + { + memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); + memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); + t->vdau[pgbuf].expire = 0; + t->vdau[pgbuf].clrfound = TRUE; + t->vdau[pgbuf].stopped = TRUE; + t->is_searching[pgbuf] = FALSE; + } + vd->priv=t; + device->data=vd; + + /* + * Register it + */ + + if((err=video_register_device(vd, VFL_TYPE_VTX))<0) + { + kfree(t); + kfree(vd); + return err; + } + t->bus = device->bus; + return 0; +} + +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); + return 0; +} + +static int saa5249_command(struct i2c_device *device, + unsigned int cmd, void *arg) +{ + printk(KERN_DEBUG "saa5249_command\n"); + return -EINVAL; +} + +/* new I2C driver support */ + +static struct i2c_driver i2c_driver_videotext = +{ + IF_NAME, /* name */ + I2C_DRIVERID_VIDEOTEXT, /* in i2c.h */ + 34, 35, /* Addr range */ + saa5249_attach, + saa5249_detach, + saa5249_command +}; + +/* + * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual + * delay may be longer. + */ + +static void jdelay(unsigned long delay) +{ + sigset_t oldblocked = current->blocked; + + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + delay; + schedule(); + + spin_lock_irq(¤t->sigmask_lock); + current->blocked = oldblocked; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); +} + + +/* Send arbitrary number of bytes to I²C-bus. Start & stop handshaking is done by this routine. + * adr should be address of I²C-device, varargs-list of values to send must be terminated by -1 + * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise + */ + +static int i2c_senddata(struct saa5249_device *t, int adr, ...) +{ + int val, loop; + va_list argp; + + for (loop = 0; loop <= NOACK_REPEAT; loop++) + { + i2c_start(t->bus); + if (i2c_sendbyte(t->bus, adr, 0) != 0) + goto loopend; + + va_start(argp, adr); + while ((val = va_arg(argp, int)) != -1) + { + if (val < 0 || val > 255) + { + printk(KERN_ERR "vtx: internal error in i2c_senddata\n"); + break; + } + if (i2c_sendbyte(t->bus, val, 0) != 0) + goto loopend; + } + va_end(argp); + i2c_stop(t->bus); + return 0; +loopend: + i2c_stop(t->bus); + } + va_end(argp); + return -1; +} + + +/* Send count number of bytes from buffer buf to I²C-device adr. Start & stop handshaking is + * done by this routine. If uaccess is TRUE, data is read from user-space with get_user. + * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise + */ + +static int i2c_sendbuf(struct saa5249_device *t, int adr, int reg, int count, u8 *buf, int uaccess) +{ + int pos, loop; + u8 val; + + for (loop = 0; loop <= NOACK_REPEAT; loop++) + { + i2c_start(t->bus); + if (i2c_sendbyte(t->bus, adr, 0) != 0 || i2c_sendbyte(t->bus, reg, 0) != 0) + goto loopend; + for (pos = 0; pos < count; pos++) + { + /* FIXME: FAULT WITH CLI/SPINLOCK ?? */ + if (uaccess) + get_user(val, buf + pos); + else + val = buf[pos]; + if (i2c_sendbyte(t->bus, val, 0) != 0) + goto loopend; + RESCHED; + } + i2c_stop(t->bus); + return 0; +loopend: + i2c_stop(t->bus); + } + return -1; +} + + +/* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop + * handshaking is done by this routine, ack will be sent after the last byte to inhibit further + * sending of data. If uaccess is TRUE, data is written to user-space with put_user. + * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise + */ + +static int i2c_getdata(struct saa5249_device *t, int adr, int count, u8 *buf, int uaccess) +{ + int pos, loop, val; + + for (loop = 0; loop <= NOACK_REPEAT; loop++) + { + i2c_start(t->bus); + if (i2c_sendbyte(t->bus, adr, 1) != 0) + goto loopend; + for (pos = 0; pos < count; pos++) + { + val = i2c_readbyte(t->bus, (pos==count-1) ? 1 : 0); + if (uaccess) + put_user(val, buf + pos); + else + buf[pos] = val; + RESCHED; + } + i2c_stop(t->bus); + return 0; +loopend: + i2c_stop(t->bus); + } + return -1; +} + + +/* + * Standard character-device-driver functions + */ + +static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg) +{ + struct saa5249_device *t=vd->priv; + static int virtual_mode = FALSE; + + switch(cmd) + { + case VTXIOCGETINFO: + { + vtx_info_t info; + info.version_major = VTX_VER_MAJ; + info.version_minor = VTX_VER_MIN; + info.numpages = NUM_DAUS; + /*info.cct_type = CCT_TYPE;*/ + if(copy_to_user((void*)arg, &info, sizeof(vtx_info_t))) + return -EFAULT; + return 0; + } + + case VTXIOCCLRPAGE: + { + vtx_pagereq_t req; + + if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t))) + return -EFAULT; + if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) + return -EINVAL; + memset(t->vdau[req.pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + t->vdau[req.pgbuf].clrfound = TRUE; + return 0; + } + + case VTXIOCCLRFOUND: + { + vtx_pagereq_t req; + + if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t))) + return -EFAULT; + if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req.pgbuf].clrfound = TRUE; + return 0; + } + + case VTXIOCPAGEREQ: + { + vtx_pagereq_t req; + if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t))) + return -EFAULT; + if (!(req.pagemask & PGMASK_PAGE)) + req.page = 0; + if (!(req.pagemask & PGMASK_HOUR)) + req.hour = 0; + if (!(req.pagemask & PGMASK_MINUTE)) + req.minute = 0; + if (req.page < 0 || req.page > 0x8ff) /* 7FF ?? */ + return -EINVAL; + req.page &= 0x7ff; + if (req.hour < 0 || req.hour > 0x3f || req.minute < 0 || req.minute > 0x7f || + req.pagemask < 0 || req.pagemask >= PGMASK_MAX || req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req.pgbuf].sregs[0] = (req.pagemask & PG_HUND ? 0x10 : 0) | (req.page / 0x100); + t->vdau[req.pgbuf].sregs[1] = (req.pagemask & PG_TEN ? 0x10 : 0) | ((req.page / 0x10) & 0xf); + t->vdau[req.pgbuf].sregs[2] = (req.pagemask & PG_UNIT ? 0x10 : 0) | (req.page & 0xf); + t->vdau[req.pgbuf].sregs[3] = (req.pagemask & HR_TEN ? 0x10 : 0) | (req.hour / 0x10); + t->vdau[req.pgbuf].sregs[4] = (req.pagemask & HR_UNIT ? 0x10 : 0) | (req.hour & 0xf); + t->vdau[req.pgbuf].sregs[5] = (req.pagemask & MIN_TEN ? 0x10 : 0) | (req.minute / 0x10); + t->vdau[req.pgbuf].sregs[6] = (req.pagemask & MIN_UNIT ? 0x10 : 0) | (req.minute & 0xf); + t->vdau[req.pgbuf].stopped = FALSE; + t->vdau[req.pgbuf].clrfound = TRUE; + t->is_searching[req.pgbuf] = TRUE; + return 0; + } + + case VTXIOCGETSTAT: + { + vtx_pagereq_t req; + u8 infobits[10]; + vtx_pageinfo_t info; + int a; + + if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t))) + return -EFAULT; + if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) + return -EINVAL; + if (!t->vdau[req.pgbuf].stopped) + { + if (i2c_senddata(t, CCTWR, 2, 0, -1) || + i2c_sendbuf(t, CCTWR, 3, sizeof(t->vdau[0].sregs), t->vdau[req.pgbuf].sregs, FALSE) || + i2c_senddata(t, CCTWR, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || + i2c_senddata(t, CCTWR, 2, 0, t->vdau[req.pgbuf].sregs[0] | 8, -1) || + i2c_senddata(t, CCTWR, 8, 0, 25, 0, -1)) + return -EIO; + jdelay(PAGE_WAIT); + if (i2c_getdata(t, CCTRD, 10, infobits, FALSE)) + return -EIO; + + if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ + (memcmp(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits)) || + jiffies >= t->vdau[req.pgbuf].expire)) + { /* check if new page arrived */ + if (i2c_senddata(t, CCTWR, 8, 0, 0, 0, -1) || + i2c_getdata(t, CCTRD, VTX_PAGESIZE, t->vdau[req.pgbuf].pgbuf, FALSE)) + return -EIO; + t->vdau[req.pgbuf].expire = jiffies + PGBUF_EXPIRE; + memset(t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); + if (t->virtual_mode) + { + /* Packet X/24 */ + if (i2c_senddata(t, CCTWR, 8, 0, 0x20, 0, -1) || + i2c_getdata(t, CCTRD, 40, t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40, FALSE)) + return -EIO; + /* Packet X/27/0 */ + if (i2c_senddata(t, CCTWR, 8, 0, 0x21, 0, -1) || + i2c_getdata(t, CCTRD, 40, t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40, FALSE)) + return -EIO; + /* Packet 8/30/0...8/30/15 + * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, + * so we should undo this here. + */ + if (i2c_senddata(t, CCTWR, 8, 0, 0x22, 0, -1) || + i2c_getdata(t, CCTRD, 40, t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40, FALSE)) + return -EIO; + } + t->vdau[req.pgbuf].clrfound = FALSE; + memcpy(t->vdau[req.pgbuf].laststat, infobits, sizeof(infobits)); + } + else + { + memcpy(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits)); + } + } + else + { + memcpy(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits)); + } + + info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); + if (info.pagenum < 0x100) + info.pagenum += 0x800; + info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); + info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); + info.charset = ((infobits[7] >> 1) & 7); + info.delete = !!(infobits[3] & 8); + info.headline = !!(infobits[5] & 4); + info.subtitle = !!(infobits[5] & 8); + info.supp_header = !!(infobits[6] & 1); + info.update = !!(infobits[6] & 2); + info.inter_seq = !!(infobits[6] & 4); + info.dis_disp = !!(infobits[6] & 8); + info.serial = !!(infobits[7] & 1); + info.notfound = !!(infobits[8] & 0x10); + info.pblf = !!(infobits[9] & 0x20); + info.hamming = 0; + for (a = 0; a <= 7; a++) + { + if (infobits[a] & 0xf0) + { + info.hamming = 1; + break; + } + } + if (t->vdau[req.pgbuf].clrfound) + info.notfound = 1; + if(copy_to_user(req.buffer, &info, sizeof(vtx_pageinfo_t))) + return -EFAULT; + if (!info.hamming && !info.notfound) + { + t->is_searching[req.pgbuf] = FALSE; + } + return 0; + } + + case VTXIOCGETPAGE: + { + vtx_pagereq_t req; + int start, end; + + if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t))) + return -EFAULT; + if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS || req.start < 0 || + req.start > req.end || req.end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) + return -EINVAL; + if(copy_to_user(req.buffer, &t->vdau[req.pgbuf].pgbuf[req.start], req.end - req.start + 1)) + return -EFAULT; + + /* + * Always read the time directly from SAA5249 + */ + + if (req.start <= 39 && req.end >= 32) + { + start = MAX(req.start, 32); + end = MIN(req.end, 39); + if (i2c_senddata(t, CCTWR, 8, 0, 0, start, -1) || + i2c_getdata(t, CCTRD, end - start + 1, req.buffer + start - req.start, TRUE)) + return -EIO; + } + /* Insert the current header if DAU is still searching for a page */ + if (req.start <= 31 && req.end >= 7 && t->is_searching[req.pgbuf]) + { + start = MAX(req.start, 7); + end = MIN(req.end, 31); + if (i2c_senddata(t, CCTWR, 8, 0, 0, start, -1) || + i2c_getdata(t, CCTRD, end - start + 1, req.buffer + start - req.start, TRUE)) + return -EIO; + } + return 0; + } + + case VTXIOCSTOPDAU: + { + vtx_pagereq_t req; + + if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t))) + return -EFAULT; + if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req.pgbuf].stopped = TRUE; + t->is_searching[req.pgbuf] = FALSE; + return 0; + } + + case VTXIOCPUTPAGE: + case VTXIOCSETDISP: + case VTXIOCPUTSTAT: + return 0; + + case VTXIOCCLRCACHE: + { + if (i2c_senddata(t ,CCTWR, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, CCTWR, 11, + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) + return -EIO; + if (i2c_senddata(t, CCTWR, 3, 0x20, -1)) + return -EIO; + jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ + return 0; + } + + case VTXIOCSETVIRT: + { + /* The SAA5249 has virtual-row reception turned on always */ + t->virtual_mode = (int)arg; + return 0; + } + } + return -EINVAL; +} + + +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) || + /* Display TV-picture, no virtual rows */ + i2c_senddata(t, CCTWR, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */ + + { + return -EIO; + } + + printk("clean\n"); + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) + { + memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); + memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); + t->vdau[pgbuf].expire = 0; + t->vdau[pgbuf].clrfound = TRUE; + t->vdau[pgbuf].stopped = TRUE; + t->is_searching[pgbuf] = FALSE; + } + t->virtual_mode=FALSE; + printk("Go\n"); + MOD_INC_USE_COUNT; + return 0; +} + + + +static void saa5249_release(struct video_device *vd) +{ + struct saa5249_device *t=vd->priv; + i2c_senddata(t, CCTWR, 1, 0x20, -1); /* Turn off CCT */ + i2c_senddata(t, CCTWR, 5, 3, 3, -1); /* Turn off TV-display */ + MOD_DEC_USE_COUNT; + return; +} + +static long saa5249_write(struct video_device *v, const char *buf, unsigned long l, int nb) +{ + return -EINVAL; +} + +static long saa5249_read(struct video_device *v, char *buf, unsigned long l, int nb) +{ + return -EINVAL; +} + +static struct video_device saa_template= +{ + IF_NAME, + VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */ + VID_HARDWARE_SAA5249, + saa5249_open, + saa5249_release, + saa5249_read, + saa5249_write, + saa5249_ioctl, + NULL, + NULL, + NULL, + 0, + 0 +}; + +/* + * 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); + return 0; +} + + +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_videotext); +} diff --git a/drivers/char/selection.c b/drivers/char/selection.c index 0a326a55d..3910d0e30 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -22,6 +22,7 @@ #include <linux/vt_kern.h> #include <linux/consolemap.h> +#include <linux/console_struct.h> #include <linux/selection.h> #ifndef MIN @@ -41,8 +42,6 @@ static int sel_end; static int sel_buffer_lth = 0; static char *sel_buffer = NULL; -#define sel_pos(n) inverse_translate(scrw2glyph(screen_word(sel_cons, n, 1))) - /* clear_selection, highlight and highlight_pointer can be called from interrupt (via scrollback/front) */ @@ -58,6 +57,12 @@ highlight_pointer(const int where) { complement_pos(sel_cons, where); } +static unsigned char +sel_pos(int n) +{ + return inverse_translate(vc_cons[sel_cons].d, screen_glyph(sel_cons, n)); +} + /* remove the current selection highlight, if any, from the console holding the selection. */ void @@ -91,12 +96,11 @@ static inline int inword(const unsigned char c) { /* set inwordLut contents. Invoked by ioctl(). */ int sel_loadlut(const unsigned long arg) { - int err; + int err = -EFAULT; - err = copy_from_user(inwordLut, (u32 *)(arg+4), 32); - if (err) - return -EFAULT; - return 0; + if (!copy_from_user(inwordLut, (u32 *)(arg+4), 32)) + err = 0; + return err; } /* does screen address p correspond to character at LH/RH edge of screen? */ @@ -108,10 +112,7 @@ static inline int atedge(const int p, int size_row) /* constrain v such that v <= u */ static inline unsigned short limit(const unsigned short v, const unsigned short u) { -/* gcc miscompiles the ?: operator, so don't use it.. */ - if (v > u) - return u; - return v; + return (v > u) ? u : v; } /* set the current selection. Invoked by ioctl() or by kernel code. */ @@ -120,15 +121,11 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) int sel_mode, new_sel_start, new_sel_end, spc; char *bp, *obp; int i, ps, pe; - unsigned long num_lines, num_columns, size_row; + unsigned int currcons = fg_console; do_unblank_screen(); poke_blanked_console(); - num_lines = get_video_num_lines(fg_console); - num_columns = get_video_num_columns(fg_console); - size_row = get_video_size_row(fg_console); - { unsigned short *args, xs, ys, xe, ye; args = (unsigned short *)(arg + 1); @@ -150,12 +147,12 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) sel_mode = *args; } xs--; ys--; xe--; ye--; - xs = limit(xs, num_columns - 1); - ys = limit(ys, num_lines - 1); - xe = limit(xe, num_columns - 1); - ye = limit(ye, num_lines - 1); - ps = ys * size_row + (xs << 1); - pe = ye * size_row + (xe << 1); + xs = limit(xs, video_num_columns - 1); + ys = limit(ys, video_num_lines - 1); + xe = limit(xe, video_num_columns - 1); + ye = limit(ye, video_num_lines - 1); + ps = ys * video_size_row + (xs << 1); + pe = ye * video_size_row + (xe << 1); if (sel_mode == 4) { /* useful for screendump without selection highlights */ @@ -195,7 +192,7 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) (!spc && !inword(sel_pos(ps)))) break; new_sel_start = ps; - if (!(ps % size_row)) + if (!(ps % video_size_row)) break; } spc = isspace(sel_pos(pe)); @@ -205,14 +202,14 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) (!spc && !inword(sel_pos(pe)))) break; new_sel_end = pe; - if (!((pe + 2) % size_row)) + if (!((pe + 2) % video_size_row)) break; } break; case 2: /* line-by-line selection */ - new_sel_start = ps - ps % size_row; - new_sel_end = pe + size_row - - pe % size_row - 2; + new_sel_start = ps - ps % video_size_row; + new_sel_end = pe + video_size_row + - pe % video_size_row - 2; break; case 3: highlight_pointer(pe); @@ -226,9 +223,11 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) /* select to end of line if on trailing space */ if (new_sel_end > new_sel_start && - !atedge(new_sel_end, size_row) && isspace(sel_pos(new_sel_end))) { + !atedge(new_sel_end, video_size_row) && + isspace(sel_pos(new_sel_end))) { for (pe = new_sel_end + 2; ; pe += 2) - if (!isspace(sel_pos(pe)) || atedge(pe, size_row)) + if (!isspace(sel_pos(pe)) || + atedge(pe, video_size_row)) break; if (isspace(sel_pos(pe))) new_sel_end = pe; @@ -259,21 +258,23 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) sel_start = new_sel_start; sel_end = new_sel_end; - if (sel_buffer) - kfree(sel_buffer); - sel_buffer = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); - if (!sel_buffer) { - printk("selection: kmalloc() failed\n"); + /* Allocate a new buffer before freeing the old one ... */ + bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); + if (!bp) { + printk(KERN_WARNING "selection: kmalloc() failed\n"); clear_selection(); return -ENOMEM; } + if (sel_buffer) + kfree(sel_buffer); + sel_buffer = bp; - obp = bp = sel_buffer; + obp = bp; for (i = sel_start; i <= sel_end; i += 2) { *bp = sel_pos(i); if (!isspace(*bp++)) obp = bp; - if (! ((i + 2) % size_row)) { + if (! ((i + 2) % video_size_row)) { /* strip trailing blanks from line and add newline, unless non-space at end of line. */ if (obp != bp) { @@ -287,31 +288,29 @@ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) return 0; } -/* Insert the contents of the selection buffer into the queue of the - tty associated with the current console. Invoked by ioctl(). */ +/* Insert the contents of the selection buffer into the + * queue of the tty associated with the current console. + * Invoked by ioctl(). + */ int paste_selection(struct tty_struct *tty) { - struct wait_queue wait = { current, NULL }; - char *bp = sel_buffer; - int c = sel_buffer_lth; - int l; struct vt_struct *vt = (struct vt_struct *) tty->driver_data; + int pasted = 0, count; + struct wait_queue wait = { current, NULL }; - if (!bp || !c) - return 0; poke_blanked_console(); add_wait_queue(&vt->paste_wait, &wait); - do { + while (sel_buffer && sel_buffer_lth > pasted) { current->state = TASK_INTERRUPTIBLE; if (test_bit(TTY_THROTTLED, &tty->flags)) { schedule(); continue; } - l = MIN(c, tty->ldisc.receive_room(tty)); - tty->ldisc.receive_buf(tty, bp, 0, l); - c -= l; - bp += l; - } while (c); + count = sel_buffer_lth - pasted; + count = MIN(count, tty->ldisc.receive_room(tty)); + tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count); + pasted += count; + } remove_wait_queue(&vt->paste_wait, &wait); current->state = TASK_RUNNING; return 0; diff --git a/drivers/char/serial.c b/drivers/char/serial.c index b5e41a2d2..c25e24057 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -64,7 +64,7 @@ * ever possible. */ -#define SERIAL_PARANOIA_CHECK +#undef SERIAL_PARANOIA_CHECK #define CONFIG_SERIAL_NOPAUSE_IO #define SERIAL_DO_RESTART @@ -156,7 +156,7 @@ #endif static char *serial_name = "Serial driver"; -static char *serial_version = "4.25"; +static char *serial_version = "4.26"; static DECLARE_TASK_QUEUE(tq_serial); @@ -852,13 +852,14 @@ static void rs_timer(void) static unsigned long last_strobe = 0; struct async_struct *info; unsigned int i; + unsigned long flags; if ((jiffies - last_strobe) >= RS_STROBE_TIME) { for (i=1; i < NR_IRQS; i++) { info = IRQ_ports[i]; if (!info) continue; - cli(); + save_flags(flags); cli(); #ifdef CONFIG_SERIAL_SHARE_IRQ if (info->next_port) { do { @@ -876,7 +877,7 @@ static void rs_timer(void) } else #endif /* CONFIG_SERIAL_SHARE_IRQ */ rs_interrupt_single(i, NULL, NULL); - sti(); + restore_flags(flags); } } last_strobe = jiffies; @@ -884,13 +885,13 @@ static void rs_timer(void) timer_active |= 1 << RS_TIMER; if (IRQ_ports[0]) { - cli(); + save_flags(flags); cli(); #ifdef CONFIG_SERIAL_SHARE_IRQ rs_interrupt(0, NULL, NULL); #else rs_interrupt_single(0, NULL, NULL); #endif - sti(); + restore_flags(flags); timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2; } @@ -1523,12 +1524,13 @@ static int rs_chars_in_buffer(struct tty_struct *tty) static void rs_flush_buffer(struct tty_struct *tty) { struct async_struct *info = (struct async_struct *)tty->driver_data; - + unsigned long flags; + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; - cli(); + save_flags(flags); cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti(); + restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1565,6 +1567,7 @@ static void rs_send_xchar(struct tty_struct *tty, char ch) static void rs_throttle(struct tty_struct * tty) { struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; @@ -1581,14 +1584,15 @@ static void rs_throttle(struct tty_struct * tty) if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS; - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); } static void rs_unthrottle(struct tty_struct * tty) { struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; @@ -1607,9 +1611,9 @@ static void rs_unthrottle(struct tty_struct * tty) } if (tty->termios->c_cflag & CRTSCTS) info->MCR |= UART_MCR_RTS; - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); } /* @@ -1678,10 +1682,16 @@ 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.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX)) { return -EINVAL; } + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[state->type].dfl_xmit_fifo_size; + /* Make sure address is not already in use */ if (new_serial.type) { for (i = 0 ; i < NR_PORTS; i++) @@ -1730,9 +1740,6 @@ static int set_serial_info(struct async_struct * info, check_and_exit: if (!state->port || !state->type) return 0; - if (state->type != old_state.type) - info->xmit_fifo_size = state->xmit_fifo_size = - uart_config[state->type].dfl_xmit_fifo_size; if (state->flags & ASYNC_INITIALIZED) { if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || @@ -1767,10 +1774,11 @@ static int get_lsr_info(struct async_struct * info, unsigned int *value) { unsigned char status; unsigned int result; + unsigned long flags; - cli(); + save_flags(flags); cli(); status = serial_in(info, UART_LSR); - sti(); + restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value); } @@ -1780,11 +1788,12 @@ static int get_modem_info(struct async_struct * info, unsigned int *value) { unsigned char control, status; unsigned int result; + unsigned long flags; control = info->MCR; - cli(); + save_flags(flags); cli(); status = serial_in(info, UART_MSR); - sti(); + restore_flags(flags); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) #ifdef TIOCM_OUT1 @@ -1803,6 +1812,7 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd, { int error; unsigned int arg; + unsigned long flags; error = get_user(arg, value); if (error) @@ -1849,9 +1859,9 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd, default: return -EINVAL; } - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); return 0; } @@ -1868,7 +1878,9 @@ static int do_autoconfig(struct async_struct * info) shutdown(info); autoconfig(info->state); - if ((info->state->flags & ASYNC_AUTO_IRQ) && (info->state->port != 0)) + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0) && + (info->state->type != PORT_UNKNOWN)) info->state->irq = detect_uart_irq(info->state); retval = startup(info); @@ -2022,7 +2034,8 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, struct async_struct * info = (struct async_struct *)tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ - + unsigned long flags; + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) return -ENODEV; @@ -2074,18 +2087,18 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: - cli(); + save_flags(flags); cli(); /* note the counters on entry */ cprev = info->state->icount; - sti(); + restore_flags(flags); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - cli(); + save_flags(flags); cli(); cnow = info->state->icount; /* atomic copy */ - sti(); + restore_flags(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -2106,9 +2119,9 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, * RI where only 0->1 is counted. */ case TIOCGICOUNT: - cli(); + save_flags(flags); cli(); cnow = info->state->icount; - sti(); + restore_flags(flags); p_cuser = (struct serial_icounter_struct *) arg; error = put_user(cnow.cts, &p_cuser->cts); if (error) return error; @@ -2118,6 +2131,26 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if (error) return error; error = put_user(cnow.dcd, &p_cuser->dcd); if (error) return error; + error = put_user(cnow.rx, &p_cuser->rx); + if (error) return error; + error = put_user(cnow.tx, &p_cuser->tx); + if (error) return error; + error = put_user(cnow.frame, &p_cuser->frame); + if (error) return error; + error = put_user(cnow.overrun, &p_cuser->overrun); + if (error) return error; + error = put_user(cnow.parity, &p_cuser->parity); + if (error) return error; + error = put_user(cnow.brk, &p_cuser->brk); + if (error) return error; + error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); + if (error) return error; + return 0; + + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; default: @@ -2129,7 +2162,8 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) { struct async_struct *info = (struct async_struct *)tty->driver_data; - + unsigned long flags; + if ( (tty->termios->c_cflag == old_termios->c_cflag) && ( RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) @@ -2141,9 +2175,9 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); } /* Handle transition away from B0 status */ @@ -2154,9 +2188,9 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) !test_bit(TTY_THROTTLED, &tty->flags)) { info->MCR |= UART_MCR_RTS; } - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); } /* Handle turning off CRTSCTS */ @@ -2306,6 +2340,9 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) if (info->state->type == PORT_UNKNOWN) return; + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to @@ -2377,7 +2414,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, struct wait_queue wait = { current, NULL }; struct serial_state *state = info->state; int retval; - int do_clocal = 0; + int do_clocal = 0, extra_count = 0; + unsigned long flags; /* * If the device is in the middle of being closed, then block @@ -2447,19 +2485,21 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready before block: ttys%d, count = %d\n", state->line, state->count); #endif - cli(); - if (!tty_hung_up_p(filp)) + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; state->count--; - sti(); + } + restore_flags(flags); info->blocked_open++; while (1) { - cli(); + save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) serial_out(info, UART_MCR, serial_inp(info, UART_MCR) | (UART_MCR_DTR | UART_MCR_RTS)); - sti(); + restore_flags(flags); current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { @@ -2490,7 +2530,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) + if (extra_count) state->count++; info->blocked_open--; #ifdef SERIAL_DEBUG_OPEN @@ -2549,12 +2589,15 @@ static int rs_open(struct tty_struct *tty, struct file * filp) int retval, line; unsigned long page; + MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_PORTS)) return -ENODEV; retval = get_async_struct(line, &info); if (retval) return retval; + tty->driver_data = info; + info->tty = tty; if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; @@ -2562,8 +2605,6 @@ static int rs_open(struct tty_struct *tty, struct file * filp) printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->state->count); #endif - tty->driver_data = info; - info->tty = tty; info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (!tmp_buf) { @@ -2598,7 +2639,6 @@ static int rs_open(struct tty_struct *tty, struct file * filp) if (retval) return retval; - MOD_INC_USE_COUNT; retval = block_til_ready(tty, filp, info); if (retval) { #ifdef SERIAL_DEBUG_OPEN @@ -2641,6 +2681,7 @@ static inline int line_info(char *buf, struct serial_state *state) struct async_struct *info = state->info, scr_info; char stat_buf[30], control, status; int ret; + unsigned long flags; ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", state->line, uart_config[state->type].name, @@ -2663,10 +2704,10 @@ static inline int line_info(char *buf, struct serial_state *state) info->quot = 0; info->tty = 0; } - cli(); + save_flags(flags); cli(); status = serial_in(info, UART_MSR); control = info ? info->MCR : serial_in(info, UART_MCR); - sti(); + restore_flags(flags); stat_buf[0] = 0; stat_buf[1] = 0; @@ -2713,12 +2754,13 @@ static inline int line_info(char *buf, struct serial_state *state) int rs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int i, len = 0; + int i, len = 0, l; off_t begin = 0; len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); for (i = 0; i < NR_PORTS && len < 4000; i++) { - len += line_info(page + len, &rs_table[i]); + l = line_info(page + len, &rs_table[i]); + len += l; if (len+begin > off+count) goto done; if (len+begin < off) { @@ -2764,10 +2806,11 @@ static _INLINE_ void show_serial_version(void) #endif #ifdef CONFIG_SERIAL_SHARE_IRQ printk(" SHARE_IRQ"); -#endif #define SERIAL_OPT +#endif #ifdef CONFIG_SERIAL_DETECT_IRQ printk(" DETECT_IRQ"); +#define SERIAL_OPT #endif #ifdef SERIAL_OPT printk(" enabled\n"); @@ -2788,7 +2831,7 @@ static unsigned detect_uart_irq (struct serial_state * state) { int irq; unsigned long irqs; - unsigned char save_mcr; + unsigned char save_mcr, save_ier; struct async_struct scr_info; /* serial_{in,out} because HUB6 */ #ifdef CONFIG_SERIAL_MANY_PORTS @@ -2812,15 +2855,30 @@ static unsigned detect_uart_irq (struct serial_state * state) /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); save_mcr = serial_inp(&scr_info, UART_MCR); - + save_ier = serial_inp(&scr_info, UART_IER); serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + irqs = probe_irq_on(); serial_outp(&scr_info, UART_MCR, 0); - udelay (1); + udelay (10); + if (state->flags & ASYNC_FOURPORT) { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(&scr_info, UART_LSR); + (void)serial_inp(&scr_info, UART_RX); + (void)serial_inp(&scr_info, UART_IIR); + (void)serial_inp(&scr_info, UART_MSR); + serial_outp(&scr_info, UART_TX, 0xFF); + udelay (20); irq = probe_irq_off(irqs); serial_outp(&scr_info, UART_MCR, save_mcr); - + serial_outp(&scr_info, UART_IER, save_ier); #ifdef CONFIG_SERIAL_MANY_PORTS if (state->flags & ASYNC_FOURPORT) outb_p(save_ICP, ICP); @@ -2888,11 +2946,9 @@ static void autoconfig(struct serial_state * state) if (!(state->flags & ASYNC_SKIP_TEST)) { scratch = serial_inp(info, UART_MCR); serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch); - scratch2 = serial_inp(info, UART_MSR); serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(info, UART_MSR) & 0xF0; serial_outp(info, UART_MCR, scratch); - serial_outp(info, UART_MSR, scratch2); if (status1 != 0x90) { restore_flags(flags); return; @@ -3105,12 +3161,9 @@ __initfunc(int rs_init(void)) state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; state->irq = irq_cannonicalize(state->irq); - if (check_region(state->port,8)) { - state->type = PORT_UNKNOWN; + if (check_region(state->port,8)) continue; - } - if ( (state->type == PORT_UNKNOWN) - && (state->flags & ASYNC_BOOT_AUTOCONF)) + if (state->flags & ASYNC_BOOT_AUTOCONF) autoconfig(state); } /* @@ -3252,13 +3305,13 @@ void cleanup_module(void) /* * Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct async_struct *info) +static inline void wait_for_xmitr(struct serial_state *ser) { int lsr; unsigned int tmout = 1000000; do { - lsr = serial_inp(info, UART_LSR); + lsr = inb(ser->port + UART_LSR); if (--tmout == 0) break; } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY); } @@ -3273,36 +3326,28 @@ static void serial_console_write(struct console *co, const char *s, struct serial_state *ser; int ier; unsigned i; - struct async_struct scr_info; /* serial_{in,out} because HUB6 */ ser = rs_table + co->index; - scr_info.magic = SERIAL_MAGIC; - scr_info.port = ser->port; - scr_info.flags = ser->flags; -#ifdef CONFIG_HUB6 - scr_info.hub6 = state->hub6; -#endif - /* * First save the IER then disable the interrupts */ - ier = serial_inp(&scr_info, UART_IER); - serial_outp(&scr_info, UART_IER, 0x00); + ier = inb(ser->port + UART_IER); + outb(0x00, ser->port + UART_IER); /* * Now, do each character */ for (i = 0; i < count; i++, s++) { - wait_for_xmitr(&scr_info); + wait_for_xmitr(ser); /* * Send the character out. * If a LF, also do CR... */ - serial_outp(&scr_info, UART_TX, *s); + outb(*s, ser->port + UART_TX); if (*s == 10) { - wait_for_xmitr(&scr_info); - serial_outp(&scr_info, UART_TX, 13); + wait_for_xmitr(ser); + outb(13, ser->port + UART_TX); } } @@ -3310,8 +3355,8 @@ static void serial_console_write(struct console *co, const char *s, * Finally, Wait for transmitter & holding register to empty * and restore the IER */ - wait_for_xmitr(&scr_info); - serial_outp(&scr_info, UART_IER, ier); + wait_for_xmitr(ser); + outb(ier, ser->port + UART_IER); } /* @@ -3323,33 +3368,26 @@ static int serial_console_wait_key(struct console *co) int ier; int lsr; int c; - struct async_struct scr_info; /* serial_{in,out} because HUB6 */ ser = rs_table + co->index; - scr_info.magic = SERIAL_MAGIC; - scr_info.port = ser->port; - scr_info.flags = ser->flags; -#ifdef CONFIG_HUB6 - scr_info.hub6 = state->hub6; -#endif /* * First save the IER then disable the interrupts so * that the real driver for the port does not get the * character. */ - ier = serial_inp(&scr_info, UART_IER); - serial_outp(&scr_info, UART_IER, 0x00); + ier = inb(ser->port + UART_IER); + outb(0x00, ser->port + UART_IER); do { - lsr = serial_inp(&scr_info, UART_LSR); + lsr = inb(ser->port + UART_LSR); } while (!(lsr & UART_LSR_DR)); - c = serial_inp(&scr_info, UART_RX); + c = inb(ser->port + UART_RX); /* * Restore the interrupts */ - serial_outp(&scr_info, UART_IER, ier); + outb(ier, ser->port + UART_IER); return c; } @@ -3375,7 +3413,6 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) int cflag = CREAD | HUPCL | CLOCAL; int quot = 0; char *s; - struct async_struct scr_info; /* serial_{in,out} because HUB6 */ if (options) { baud = simple_strtoul(options, NULL, 10); @@ -3439,12 +3476,6 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) * Divisor, bytesize and parity */ ser = rs_table + co->index; - scr_info.magic = SERIAL_MAGIC; - scr_info.port = ser->port; - scr_info.flags = ser->flags; -#ifdef CONFIG_HUB6 - scr_info.hub6 = state->hub6; -#endif quot = ser->baud_base / baud; cval = cflag & (CSIZE | CSTOPB); #if defined(__powerpc__) || defined(__alpha__) @@ -3461,17 +3492,17 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) * Disable UART interrupts, set DTR and RTS high * and set speed. */ - serial_outp(&scr_info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - serial_outp(&scr_info, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_outp(&scr_info, UART_DLM, quot >> 8); /* MS of divisor */ - serial_outp(&scr_info, UART_LCR, cval); /* reset DLAB */ - serial_outp(&scr_info, UART_IER, 0); - serial_outp(&scr_info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ + outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ + outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ + outb(cval, ser->port + UART_LCR); /* reset DLAB */ + outb(0, ser->port + UART_IER); + outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); /* * If we read 0xff from the LSR, there is no UART here. */ - if (serial_inp(&scr_info, UART_LSR) == 0xff) + if (inb(ser->port + UART_LSR) == 0xff) return -1; return 0; } diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 06795e07a..7239923ec 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -46,6 +46,8 @@ #include <linux/ioport.h> #include <linux/config.h> #include <linux/init.h> +#include <linux/smp_lock.h> + #include <asm/system.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -142,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.5"; +static char *stl_drvversion = "5.4.6"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -1082,7 +1084,6 @@ static int stl_write(struct tty_struct *tty, int from_user, const unsigned char { stlport_t *portp; unsigned int len, stlen; - unsigned long flags; unsigned char *chbuf; char *head, *tail; @@ -1114,12 +1115,9 @@ static int stl_write(struct tty_struct *tty, int from_user, const unsigned char (tail - head - 1); count = MIN(len, count); - save_flags(flags); - cli(); down(&stl_tmpwritesem); copy_from_user(stl_tmpwritebuf, chbuf, count); up(&stl_tmpwritesem); - restore_flags(flags); chbuf = &stl_tmpwritebuf[0]; } @@ -2005,7 +2003,6 @@ static void stl_echpci64intr(stlbrd_t *brdp) /* * Service an off-level request for some channel. */ - static void stl_offintr(void *private) { stlport_t *portp; @@ -2020,10 +2017,12 @@ static void stl_offintr(void *private) if (portp == (stlport_t *) NULL) return; + tty = portp->tty; if (tty == (struct tty_struct *) NULL) return; + lock_kernel(); if (test_bit(ASYI_TXLOW, &portp->istate)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -2045,6 +2044,7 @@ static void stl_offintr(void *private) } } } + unlock_kernel(); } /*****************************************************************************/ @@ -2193,7 +2193,7 @@ static inline int stl_initeio(stlbrd_t *brdp) } /* - * Everything looks OK, so lets go ahead and probe for the hardware. + * Everything looks OK, so let's go ahead and probe for the hardware. */ brdp->clk = CD1400_CLK; brdp->isr = stl_eiointr; @@ -2568,7 +2568,7 @@ static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev) #endif /* - * We have all resources from the board, so lets setup the actual + * We have all resources from the board, so let's setup the actual * board structure now. */ switch (brdtype) { diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index a518f6b6a..c70ec0e1f 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -19,8 +19,10 @@ #include <linux/reboot.h> #include <linux/sysrq.h> #include <linux/kbd_kern.h> +#include <linux/quotaops.h> +#include <linux/smp_lock.h> + #include <asm/ptrace.h> -#include <asm/smp_lock.h> #ifdef CONFIG_APM #include <linux/apm_bios.h> @@ -188,7 +190,7 @@ static void go_sync(kdev_t dev, int remount_flag) printk("R/O\n"); return; } - quota_off(dev, -1); + DQUOT_OFF(dev); fsync_dev(dev); flags = MS_RDONLY; if (sb->s_op && sb->s_op->remount_fs) { diff --git a/drivers/char/tga.c b/drivers/char/tga.c deleted file mode 100644 index 7bab07023..000000000 --- a/drivers/char/tga.c +++ /dev/null @@ -1,1169 +0,0 @@ -/* - * linux/drivers/char/tga.c - * - * Copyright (C) 1995 Jay Estabrook - */ - -/* - * tga.c - * - * This module exports the console io support for DEC's TGA - */ - -#include <linux/config.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/kd.h> -#include <linux/malloc.h> -#include <linux/major.h> -#include <linux/mm.h> -#include <linux/ioport.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/console.h> - -#include <asm/io.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/bitops.h> - -#include <linux/kbd_kern.h> -#include <linux/vt_kern.h> -#include <linux/consolemap.h> -#include <linux/selection.h> -#include <linux/console_struct.h> - -extern struct console vt_console_driver; - -/* TGA hardware description (minimal) */ -/* - * Offsets within Memory Space - */ -#define TGA_ROM_OFFSET 0x0000000 -#define TGA_REGS_OFFSET 0x0100000 -#define TGA_8PLANE_FB_OFFSET 0x0200000 -#define TGA_24PLANE_FB_OFFSET 0x0804000 -#define TGA_24PLUSZ_FB_OFFSET 0x1004000 - -#define TGA_PLANEMASK_REG 0x0028 -#define TGA_MODE_REG 0x0030 -#define TGA_RASTEROP_REG 0x0034 -#define TGA_DEEP_REG 0x0050 -#define TGA_PIXELMASK_REG 0x005c -#define TGA_CURSOR_BASE_REG 0x0060 -#define TGA_HORIZ_REG 0x0064 -#define TGA_VERT_REG 0x0068 -#define TGA_BASE_ADDR_REG 0x006c -#define TGA_VALID_REG 0x0070 -#define TGA_CURSOR_XY_REG 0x0074 -#define TGA_INTR_STAT_REG 0x007c -#define TGA_RAMDAC_SETUP_REG 0x00c0 -#define TGA_BLOCK_COLOR0_REG 0x0140 -#define TGA_BLOCK_COLOR1_REG 0x0144 -#define TGA_CLOCK_REG 0x01e8 -#define TGA_RAMDAC_REG 0x01f0 -#define TGA_CMD_STAT_REG 0x01f8 - -/* - * useful defines for managing the BT485 on the 8-plane TGA - */ -#define BT485_READ_BIT 0x01 -#define BT485_WRITE_BIT 0x00 - -#define BT485_ADDR_PAL_WRITE 0x00 -#define BT485_DATA_PAL 0x02 -#define BT485_PIXEL_MASK 0x04 -#define BT485_ADDR_PAL_READ 0x06 -#define BT485_ADDR_CUR_WRITE 0x08 -#define BT485_DATA_CUR 0x0a -#define BT485_CMD_0 0x0c -#define BT485_ADDR_CUR_READ 0x0e -#define BT485_CMD_1 0x10 -#define BT485_CMD_2 0x12 -#define BT485_STATUS 0x14 -#define BT485_CMD_3 0x14 -#define BT485_CUR_RAM 0x16 -#define BT485_CUR_LOW_X 0x18 -#define BT485_CUR_HIGH_X 0x1a -#define BT485_CUR_LOW_Y 0x1c -#define BT485_CUR_HIGH_Y 0x1e - -/* - * useful defines for managing the BT463 on the 24-plane TGAs - */ -#define BT463_ADDR_LO 0x0 -#define BT463_ADDR_HI 0x1 -#define BT463_REG_ACC 0x2 -#define BT463_PALETTE 0x3 - -#define BT463_CUR_CLR_0 0x0100 -#define BT463_CUR_CLR_1 0x0101 - -#define BT463_CMD_REG_0 0x0201 -#define BT463_CMD_REG_1 0x0202 -#define BT463_CMD_REG_2 0x0203 - -#define BT463_READ_MASK_0 0x0205 -#define BT463_READ_MASK_1 0x0206 -#define BT463_READ_MASK_2 0x0207 -#define BT463_READ_MASK_3 0x0208 - -#define BT463_BLINK_MASK_0 0x0209 -#define BT463_BLINK_MASK_1 0x020a -#define BT463_BLINK_MASK_2 0x020b -#define BT463_BLINK_MASK_3 0x020c - -#define BT463_WINDOW_TYPE_BASE 0x0300 - -/* - * built-in font management constants - * - * NOTE: the built-in font is 8x16, and the video resolution - * is 640x480 @ 60Hz. - * This means we could put 30 rows of text on the screen (480/16). - * However, we wish to make then TGA look just like a VGA, as the - * default, so, we pad the character to 8x18, and leave some scan - * lines at the bottom unused. - */ -#define TGA_F_WIDTH 8 -#define TGA_F_HEIGHT 16 -#define TGA_F_HEIGHT_PADDED 18 - -int tga_type; -unsigned long tga_mem_base; -unsigned long tga_fb_base; -unsigned long tga_regs_base; -unsigned int tga_bpp, tga_fb_width, tga_fb_height, tga_fb_stride; - -static unsigned int fb_offset_presets[4] __initdata = { - TGA_8PLANE_FB_OFFSET, - TGA_24PLANE_FB_OFFSET, - 0xffffffff, - TGA_24PLUSZ_FB_OFFSET -}; - -static unsigned int deep_presets[4] __initdata = { - 0x00014000, - 0x0001440d, - 0xffffffff, - 0x0001441d -}; - -static unsigned int rasterop_presets[4] __initdata = { - 0x00000003, - 0x00000303, - 0xffffffff, - 0x00000303 -}; - -static unsigned int mode_presets[4] __initdata = { - 0x00002000, - 0x00002300, - 0xffffffff, - 0x00002300 -}; - -static unsigned int base_addr_presets[4] __initdata = { - 0x00000000, - 0x00000001, - 0xffffffff, - 0x00000001 -}; - -#define TGA_WRITE_REG(v,r) \ - { writel((v), tga_regs_base+(r)); mb(); } - -#define TGA_READ_REG(r) readl(tga_regs_base+(r)) - -#define BT485_WRITE(v,r) \ - TGA_WRITE_REG((r),TGA_RAMDAC_SETUP_REG); \ - TGA_WRITE_REG(((v)&0xff)|((r)<<8),TGA_RAMDAC_REG); - -#define BT463_LOAD_ADDR(a) \ - TGA_WRITE_REG(BT463_ADDR_LO<<2, TGA_RAMDAC_SETUP_REG); \ - TGA_WRITE_REG((BT463_ADDR_LO<<10)|((a)&0xff), TGA_RAMDAC_REG); \ - TGA_WRITE_REG(BT463_ADDR_HI<<2, TGA_RAMDAC_SETUP_REG); \ - TGA_WRITE_REG((BT463_ADDR_HI<<10)|(((a)>>8)&0xff), TGA_RAMDAC_REG); - -#define BT463_WRITE(m,a,v) \ - BT463_LOAD_ADDR((a)); \ - TGA_WRITE_REG(((m)<<2),TGA_RAMDAC_SETUP_REG); \ - TGA_WRITE_REG(((m)<<10)|((v)&0xff),TGA_RAMDAC_REG); - -extern char tga_builtin_font[]; - -void tga_init_video(void); -void tga_clear_screen(void); - -void -set_palette (void) -{ - int i, j; - - if (console_blanked || vt_cons[fg_console]->vc_mode == KD_GRAPHICS) - return; - - - if (tga_type == 0) { /* 8-plane */ - BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE); - TGA_WRITE_REG(BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG); - - for (i = 0; i < 16; i++) { - j = color_table[i]; - TGA_WRITE_REG(default_red[j]|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG); - TGA_WRITE_REG(default_grn[j]|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG); - TGA_WRITE_REG(default_blu[j]|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG); - } - } else { - BT463_LOAD_ADDR(0x0000); - TGA_WRITE_REG((BT463_PALETTE<<2), TGA_RAMDAC_REG); - - for (i = 0; i < 16; i++) { - j = color_table[i]; - TGA_WRITE_REG(default_red[j]|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(default_grn[j]|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(default_blu[j]|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - } - } -} - -void -__set_origin(unsigned short offset) -{ - /* - * should not be called, but if so, do nothing... - */ -} - -/* - * Hide the cursor from view, during blanking, usually... - */ -void -hide_cursor(void) -{ - unsigned long flags; - save_flags(flags); cli(); - - if (tga_type == 0) { - BT485_WRITE(0x20, BT485_CMD_2); - } else { - TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */ - } - - restore_flags(flags); -} - -void -set_cursor(int currcons) -{ - unsigned int idx, xt, yt, row, col; - unsigned long flags; - - if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) - return; - - if (__real_origin != __origin) - __set_origin(__real_origin); - - save_flags(flags); cli(); - - if (deccm) { - idx = (pos - video_mem_base) >> 1; - col = idx % 80; - row = (idx - col) / 80; - - if (tga_type == 0) { /* 8-plane */ - - xt = col * TGA_F_WIDTH + 64; - yt = row * TGA_F_HEIGHT_PADDED + 64; - - /* make sure it's enabled */ - BT485_WRITE(0x22, BT485_CMD_2); /* WIN cursor type */ - - BT485_WRITE(xt, BT485_CUR_LOW_X); - BT485_WRITE((xt >> 8), BT485_CUR_HIGH_X); - BT485_WRITE(yt, BT485_CUR_LOW_Y); - BT485_WRITE((yt >> 8), BT485_CUR_HIGH_Y); - - } else { - - xt = col * TGA_F_WIDTH + 144; - yt = row * TGA_F_HEIGHT_PADDED + 35; - - TGA_WRITE_REG(0x05, TGA_VALID_REG); /* SCANNING and CURSOR */ - TGA_WRITE_REG(xt | (yt << 12), TGA_CURSOR_XY_REG); - } - - } else - hide_cursor(); - restore_flags(flags); -} - -__initfunc(unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc)) -{ - can_do_color = 1; - - /* - * fake the screen memory with some CPU memory - */ - video_mem_base = kmem_start; - kmem_start += video_screen_size; - video_mem_term = kmem_start; - - video_type = VIDEO_TYPE_TGAC; - - *display_desc = "TGA"; - - return kmem_start; -} - -__initfunc(void -con_type_init_finish(void)) -{ -} - -/* - * NOTE: get_scrmem() and set_scrmem() are here only because - * the VGA version of set_scrmem() has some direct VGA references. - */ -void -get_scrmem(int currcons) -{ - memcpyw((unsigned short *)vc_scrbuf[currcons], - (unsigned short *)origin, video_screen_size); - origin = video_mem_start = (unsigned long)vc_scrbuf[currcons]; - scr_end = video_mem_end = video_mem_start + video_screen_size; - pos = origin + y*video_size_row + (x<<1); -} - -void -set_scrmem(int currcons, long offset) -{ - if (video_mem_term - video_mem_base < offset + video_screen_size) - offset = 0; /* strange ... */ - memcpyw((unsigned short *)(video_mem_base + offset), - (unsigned short *) origin, video_screen_size); - video_mem_start = video_mem_base; - video_mem_end = video_mem_term; - origin = video_mem_base + offset; - scr_end = origin + video_screen_size; - pos = origin + y*video_size_row + (x<<1); -} - -/* - * PIO_FONT support. - * - * for now, we will use/allow *only* our built-in font... - */ -int -set_get_font(char * arg, int set, int ch512) -{ - return -EINVAL; -} - -/* - * Adjust the screen to fit a font of a certain height - * - * Returns < 0 for error, 0 if nothing changed, and the number - * of lines on the adjusted console if changed. - * - * for now, we only support the built-in font... - */ -int -con_adjust_height(unsigned long fontheight) -{ - return -EINVAL; -} - -/* NOTE: - * this is here, and not in console.c, because the VGA version - * tests the controller type to see if color can be done. We *KNOW* - * that we can do color on the TGA... :-) - * - * FIXME? maybe the init codes for VGA and TGA could set - * a flag for (in)ability to do colormap set/get??? - */ - -int -set_get_cmap(unsigned char * arg, int set) { - int i; - - i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, 16*3); - if (i) - return i; - - for (i=0; i<16; i++) { - if (set) { - get_user(default_red[i], arg++) ; - get_user(default_grn[i], arg++) ; - get_user(default_blu[i], arg++) ; - } else { - put_user (default_red[i], arg++) ; - put_user (default_grn[i], arg++) ; - put_user (default_blu[i], arg++) ; - } - } - if (set) { - for (i=0; i<MAX_NR_CONSOLES; i++) - if (vc_cons_allocated(i)) { - int j, k ; - 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() ; - } - - return 0; -} - -/* - * dummy routines for the VESA blanking code, which is VGA only, - * so we don't have to carry that stuff around for the TGA... - */ -void vesa_powerdown(void) -{ -} -void vesa_blank(void) -{ -} -void vesa_unblank(void) -{ -} -void set_vesa_blanking(const unsigned long arg) -{ -} - - -/* - * See if we have a TGA card. - * Just a placeholder at the moment, because of the strange - * way the TGA card is initialized. This has to be enabled when - * the kernel initializes PCI devices before the console. - */ -__initfunc(int con_is_present(void)) -{ -#if 0 - unsigned char pci_bus, pci_devfn; - int status; - - status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, - 0, &pci_bus, &pci_devfn); - return (status == PCIBIOS_DEVICE_NOT_FOUND) ? 0 : 1; -#endif - return 1; -} - -/* - * video init code, called from within the PCI bus probing code; - * when TGA console is configured, at the end of the probing code, - * we call here to look for a TGA device, and proceed... - */ -__initfunc(void -tga_console_init(void)) -{ - struct pci_dev *dev; - - /* - * first, find the TGA among the PCI devices... - */ - if (! (dev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL))) { - /* PANIC!!! */ - printk("tga_console_init: TGA not found!!! :-(\n"); - return; - } - - /* - * read BASE_REG_0 for memory address - */ - tga_mem_base = dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; -#ifdef DEBUG - printk("tga_console_init: mem_base 0x%lx\n", tga_mem_base); -#endif /* DEBUG */ - - tga_type = (readl(tga_mem_base) >> 12) & 0x0f; - if (tga_type != 0 && tga_type != 1 && tga_type != 3) { - printk("TGA type (0x%x) unrecognized!\n", tga_type); - return; - } - tga_init_video(); - tga_clear_screen(); - - /* - * FINALLY, we can register TGA as console (whew!) - */ -#ifdef CONFIG_VT_CONSOLE - register_console(&vt_console_driver); -#endif -} - -unsigned char PLLbits[7] __initdata = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 }; - -const unsigned long bt485_cursor_source[64] __initdata = { - 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, - 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, - 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, - 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - -const unsigned int bt463_cursor_source[256] __initdata = { - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0xffff0000, 0x00000000, 0x00000000, 0x00000000, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - -__initfunc(void -tga_init_video(void)) -{ - int i, j, temp; - unsigned char *cbp; - - tga_regs_base = (tga_mem_base + TGA_REGS_OFFSET); - tga_fb_base = (tga_mem_base + fb_offset_presets[tga_type]); - - /* first, disable video timing */ - TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */ - - /* write the DEEP register */ - while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */ - continue; - - mb(); - TGA_WRITE_REG(deep_presets[tga_type], TGA_DEEP_REG); - while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */ - continue; - mb(); - - /* write some more registers */ - TGA_WRITE_REG(rasterop_presets[tga_type], TGA_RASTEROP_REG); - TGA_WRITE_REG(mode_presets[tga_type], TGA_MODE_REG); - TGA_WRITE_REG(base_addr_presets[tga_type], TGA_BASE_ADDR_REG); - - /* write the PLL for 640x480 @ 60Hz */ - for (i = 0; i <= 6; i++) { - for (j = 0; j <= 7; j++) { - temp = (PLLbits[i] >> (7-j)) & 1; - if (i == 6 && j == 7) - temp |= 2; - TGA_WRITE_REG(temp, TGA_CLOCK_REG); - } - } - - /* write some more registers */ - TGA_WRITE_REG(0xffffffff, TGA_PLANEMASK_REG); - TGA_WRITE_REG(0xffffffff, TGA_PIXELMASK_REG); - TGA_WRITE_REG(0x12345678, TGA_BLOCK_COLOR0_REG); - TGA_WRITE_REG(0x12345678, TGA_BLOCK_COLOR1_REG); - - /* init video timing regs for 640x480 @ 60 Hz */ - TGA_WRITE_REG(0x018608a0, TGA_HORIZ_REG); - TGA_WRITE_REG(0x084251e0, TGA_VERT_REG); - - if (tga_type == 0) { /* 8-plane */ - - tga_bpp = 1; - - /* init BT485 RAMDAC registers */ - BT485_WRITE(0xa2, BT485_CMD_0); - BT485_WRITE(0x01, BT485_ADDR_PAL_WRITE); - BT485_WRITE(0x14, BT485_CMD_3); /* cursor 64x64 */ - BT485_WRITE(0x40, BT485_CMD_1); - BT485_WRITE(0x22, BT485_CMD_2); /* WIN cursor type */ - BT485_WRITE(0xff, BT485_PIXEL_MASK); - - /* fill palette registers */ - BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE); - TGA_WRITE_REG(BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG); - - for (i = 0; i < 16; i++) { - j = color_table[i]; - TGA_WRITE_REG(default_red[j]|(BT485_DATA_PAL<<8), - TGA_RAMDAC_REG); - TGA_WRITE_REG(default_grn[j]|(BT485_DATA_PAL<<8), - TGA_RAMDAC_REG); - TGA_WRITE_REG(default_blu[j]|(BT485_DATA_PAL<<8), - TGA_RAMDAC_REG); - } - for (i = 0; i < 240*3; i += 4) { - TGA_WRITE_REG(0x55|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT485_DATA_PAL<<8), TGA_RAMDAC_REG); - } - - /* initialize RAMDAC cursor colors */ - BT485_WRITE(0, BT485_ADDR_CUR_WRITE); - - BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */ - BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */ - BT485_WRITE(0xaa, BT485_DATA_CUR); /* overscan WHITE */ - - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */ - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */ - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 1 BLACK */ - - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 2 BLACK */ - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 2 BLACK */ - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 2 BLACK */ - - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 3 BLACK */ - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 3 BLACK */ - BT485_WRITE(0x00, BT485_DATA_CUR); /* color 3 BLACK */ - - /* initialize RAMDAC cursor RAM */ - BT485_WRITE(0x00, BT485_ADDR_PAL_WRITE); - cbp = (unsigned char *)bt485_cursor_source; - for (i = 0; i < 512; i++) { - BT485_WRITE(*cbp++, BT485_CUR_RAM); - } - for (i = 0; i < 512; i++) { - BT485_WRITE(0xff, BT485_CUR_RAM); - } - - } else { /* 24-plane or 24plusZ */ - - tga_bpp = 4; - - TGA_WRITE_REG(0x01, TGA_VALID_REG); /* SCANNING */ - - /* - * init some registers - */ - BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_0, 0x40); - BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_1, 0x08); - BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_2, 0x40); - - BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_0, 0xff); - BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_1, 0xff); - BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_2, 0xff); - BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_3, 0x0f); - - BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00); - BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00); - BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00); - BT463_WRITE(BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00); - - /* - * fill the palette - */ - BT463_LOAD_ADDR(0x0000); - TGA_WRITE_REG((BT463_PALETTE<<2), TGA_RAMDAC_REG); - - for (i = 0; i < 16; i++) { - j = color_table[i]; - TGA_WRITE_REG(default_red[j]|(BT463_PALETTE<<10), - TGA_RAMDAC_REG); - TGA_WRITE_REG(default_grn[j]|(BT463_PALETTE<<10), - TGA_RAMDAC_REG); - TGA_WRITE_REG(default_blu[j]|(BT463_PALETTE<<10), - TGA_RAMDAC_REG); - } - for (i = 0; i < 512*3; i += 4) { - TGA_WRITE_REG(0x55|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_PALETTE<<10), TGA_RAMDAC_REG); - } - - /* - * fill window type table after start of vertical retrace - */ - while (!(TGA_READ_REG(TGA_INTR_STAT_REG) & 0x01)) - continue; - TGA_WRITE_REG(0x01, TGA_INTR_STAT_REG); - mb(); - while (!(TGA_READ_REG(TGA_INTR_STAT_REG) & 0x01)) - continue; - TGA_WRITE_REG(0x01, TGA_INTR_STAT_REG); - - BT463_LOAD_ADDR(BT463_WINDOW_TYPE_BASE); - TGA_WRITE_REG((BT463_REG_ACC<<2), TGA_RAMDAC_SETUP_REG); - - for (i = 0; i < 16; i++) { - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x01|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x80|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - } - - /* - * init cursor colors - */ - BT463_LOAD_ADDR(BT463_CUR_CLR_0); - - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* background */ - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* background */ - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* background */ - - TGA_WRITE_REG(0xff|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* foreground */ - TGA_WRITE_REG(0xff|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* foreground */ - TGA_WRITE_REG(0xff|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); /* foreground */ - - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - TGA_WRITE_REG(0x00|(BT463_REG_ACC<<10), TGA_RAMDAC_REG); - - /* - * finally, init the cursor shape - */ - temp = tga_fb_base - 1024; /* this assumes video starts at base - and base is beyond memory start*/ - - for (i = 0; i < 256; i++) { - writel(bt463_cursor_source[i], temp + i*4); - } - TGA_WRITE_REG(temp & 0x000fffff, TGA_CURSOR_BASE_REG); - } - - /* finally, enable video scan & cursor - (and pray for the monitor... :-) */ - TGA_WRITE_REG(0x05, TGA_VALID_REG); /* SCANNING and CURSOR */ - - /* oh, and set the globals describing the resolution... :-) */ - tga_fb_width = 640 * tga_bpp; - tga_fb_height = 480; - tga_fb_stride = tga_fb_width / sizeof(int); -} - -__initfunc(void -tga_clear_screen(void)) -{ - register int i, j; - register unsigned int *dst; - - dst = (unsigned int *) ((unsigned long)tga_fb_base); - for (i = 0; i < tga_fb_height; i++, dst += tga_fb_stride) { - for (j = 0; j < tga_fb_stride; j++) { - writel(0, (dst+j)); - } - } - - /* also clear out the "shadow" screen memory */ - memset((char *)video_mem_base, 0, (video_mem_term - video_mem_base)); -} - -/* - * tga_blitc - * - * Displays an ASCII character at a specified character cell - * position. - * - * Called from scr_writew() when the destination is - * the "shadow" screen - */ -static unsigned int -fontmask_bits[16] = { - 0x00000000, - 0xff000000, - 0x00ff0000, - 0xffff0000, - 0x0000ff00, - 0xff00ff00, - 0x00ffff00, - 0xffffff00, - 0x000000ff, - 0xff0000ff, - 0x00ff00ff, - 0xffff00ff, - 0x0000ffff, - 0xff00ffff, - 0x00ffffff, - 0xffffffff -}; - -int -tga_blitc(unsigned int charattr, unsigned long addr) -{ - int row, col, temp, c, attrib; - register unsigned int fgmask, bgmask, data, rowbits; - register unsigned int *dst; - register unsigned char *font_row; - register int i, j, stride; - - c = charattr & 0x00ff; - attrib = (charattr >> 8) & 0x00ff; - - /* - * extract foreground and background indices - * NOTE: we always treat blink/underline bits as color for now... - */ - fgmask = attrib & 0x0f; - bgmask = (attrib >> 4) & 0x0f; - - i = (c & 0xff) << 4; /* NOTE: assumption of 16 bytes per character bitmap */ - - /* - * calculate (row,col) from addr and video_mem_base - */ - temp = (addr - video_mem_base) >> 1; - col = temp % 80; - row = (temp - col) / 80; - - /* - * calculate destination address - */ - dst = (unsigned int *) ( (unsigned long)tga_fb_base - + ( row * tga_fb_width * TGA_F_HEIGHT_PADDED ) - + ( col * TGA_F_WIDTH * tga_bpp) ); - - font_row = (unsigned char *)&tga_builtin_font[i]; - stride = tga_fb_stride; - - if (tga_type == 0) { /* 8-plane */ - - fgmask = fgmask << 8 | fgmask; - fgmask |= fgmask << 16; - bgmask = bgmask << 8 | bgmask; - bgmask |= bgmask << 16; - - for ( j = 0; j < TGA_F_HEIGHT_PADDED; j++ ) { - if (j < TGA_F_HEIGHT) { - rowbits = font_row[j]; - } else { - /* dup the last n rows only if char > 0x7f */ - if (c & 0x80) - rowbits = font_row[j-(TGA_F_HEIGHT_PADDED-TGA_F_HEIGHT)]; - else - rowbits = 0; - } - data = fontmask_bits[(rowbits>>4)&0xf]; - data = (data & fgmask) | (~data & bgmask); - writel(data, dst); - data = fontmask_bits[rowbits&0xf]; - data = (data & fgmask) | (~data & bgmask); - writel(data, (dst+1)); - dst += stride; - } - } else { /* 24-plane */ - - fgmask = (default_red[fgmask] << 16) | - (default_grn[fgmask] << 8) | - (default_blu[fgmask] << 0); - bgmask = (default_red[bgmask] << 16) | - (default_grn[bgmask] << 8) | - (default_blu[bgmask] << 0); - - for ( i = 0; i < TGA_F_HEIGHT_PADDED; i++ ) { - if (i < TGA_F_HEIGHT) { - rowbits = font_row[i]; - } else { - /* dup the last n rows only if char > 0x7f */ - if (c & 0x80) - rowbits = font_row[i-(TGA_F_HEIGHT_PADDED-TGA_F_HEIGHT)]; - else - rowbits = 0; - } - data = 1 << (TGA_F_WIDTH - 1); - for (j = 0; j < TGA_F_WIDTH; j++, data >>= 1) { - if (rowbits & data) - writel(fgmask, (dst+j)); - else - writel(bgmask, (dst+j)); - } - dst += stride; - } - } - return (0); -} - -/* - * font table of displayable characters. - */ -char tga_builtin_font[]={ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, -0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, -0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, -0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, -0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, -0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, -0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, -0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, -0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, -0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, -0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, -0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, -0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, -0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, -0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, -0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, -0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, -0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, -0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, -0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index bf2e642c4..a26ce1311 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -65,6 +65,7 @@ #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/tty.h> +#include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/devpts_fs.h> #include <linux/file.h> @@ -80,6 +81,7 @@ #include <linux/proc_fs.h> #endif #include <linux/init.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -107,6 +109,10 @@ struct termios tty_std_termios; /* for the benefit of tty drivers */ struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */ +#ifdef CONFIG_UNIX98_PTYS +extern struct tty_driver ptm_driver[]; /* Unix98 pty masters; for /dev/ptmx */ +#endif + /* * redirect is the pseudo-tty that console output * is redirected to if asked by TIOCCONS. @@ -123,6 +129,10 @@ static int tty_release(struct inode *, struct file *); static int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); static int tty_fasync(struct file * filp, int on); +#ifdef CONFIG_8xx +extern long console_8xx_init(void); +extern int rs_8xx_init(void); +#endif /* CONFIG_8xx */ #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -367,17 +377,24 @@ static struct file_operations hung_up_tty_fops = { NULL /* hung_up_tty_fasync */ }; +/* + * This can be called through the "tq_scheduler" + * task-list. That is process synchronous, but + * doesn't hold any locks, so we need to make + * sure we have the appropriate locks for what + * we're doing.. + */ void do_tty_hangup(void *data) { struct tty_struct *tty = (struct tty_struct *) data; struct file * filp; struct task_struct *p; - unsigned long flags; if (!tty) return; - - save_flags(flags); cli(); + + /* inuse_filps is protected by the single kernel lock */ + lock_kernel(); check_tty_count(tty, "do_tty_hangup"); for (filp = inuse_filps; filp; filp = filp->f_next) { @@ -396,13 +413,21 @@ void do_tty_hangup(void *data) filp->f_op = &hung_up_tty_fops; } - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + /* FIXME! What are the locking issues here? This may me overdoing things.. */ + { + unsigned long flags; + + save_flags(flags); cli(); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + restore_flags(flags); + } + wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); @@ -445,7 +470,7 @@ void do_tty_hangup(void *data) tty->ctrl_status = 0; if (tty->driver.hangup) (tty->driver.hangup)(tty); - restore_flags(flags); + unlock_kernel(); } void tty_hangup(struct tty_struct * tty) @@ -455,7 +480,7 @@ void tty_hangup(struct tty_struct * tty) printk("%s hangup...\n", tty_name(tty, buf)); #endif - queue_task(&tty->tq_hangup, &tq_timer); + queue_task(&tty->tq_hangup, &tq_scheduler); } void tty_vhangup(struct tty_struct * tty) @@ -629,7 +654,7 @@ static inline ssize_t do_tty_write( ret = -ERESTARTSYS; if (signal_pending(current)) break; - if (need_resched) + if (current->need_resched) schedule(); } if (written) { @@ -1022,6 +1047,10 @@ static void release_dev(struct file * filp) } } #endif + + if (tty->driver.close) + tty->driver.close(tty, filp); + /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1079,9 +1108,6 @@ static void release_dev(struct file * filp) * block, so it's safe to proceed with closing. */ - if (tty->driver.close) - tty->driver.close(tty, filp); - if (pty_master) { if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", @@ -1153,6 +1179,7 @@ static void release_dev(struct file * filp) * Make sure that the tty's task queue isn't activated. */ run_task_queue(&tq_timer); + run_task_queue(&tq_scheduler); /* * The release_mem function takes care of the details of clearing @@ -1208,34 +1235,32 @@ retry_open: device = c->device(c); noctty = 1; } +#ifdef CONFIG_UNIX98_PTYS if (device == PTMX_DEV) { /* find a free pty. */ - struct tty_driver *driver = tty_drivers; - int minor, line; - - /* find the pty driver */ - for (driver=tty_drivers; driver; driver=driver->next) - if (driver->major == PTY_MASTER_MAJOR) - break; - if (!driver) return -ENODEV; - - /* find a minor device that is not in use. */ - for (minor=driver->minor_start; - minor<driver->minor_start+driver->num; - minor++) { - device = MKDEV(driver->major, minor); - retval = init_dev(device, &tty); - if (retval==0) break; /* success! */ + int major, minor; + struct tty_driver *driver; + + /* find a device that is not in use. */ + retval = -1; + for ( major = 0 ; major < UNIX98_NR_MAJORS ; major++ ) { + driver = &ptm_driver[major]; + for (minor = driver->minor_start ; + minor < driver->minor_start + driver->num ; + minor++) { + device = MKDEV(driver->major, minor); + if (!init_dev(device, &tty)) goto ptmx_found; /* ok! */ + } } - if (minor==driver->minor_start+driver->num) /* no success */ - return -EIO; /* no free ptys */ - + return -EIO; /* no free ptys */ + ptmx_found: set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ - line = minor - driver->minor_start; - devpts_pty_new(line, MKDEV(driver->other->major, line+driver->other->minor_start)); + minor -= driver->minor_start; + devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start)); noctty = 1; goto init_dev_done; } +#endif retval = init_dev(device, &tty); if (retval) @@ -1962,7 +1987,11 @@ long console_init(long kmem_start, long kmem_end) memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS); tty_std_termios.c_iflag = ICRNL | IXON; tty_std_termios.c_oflag = OPOST | ONLCR; +#if CONFIG_COBALT_SERIAL + tty_std_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL; +#else tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL; +#endif tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; @@ -2046,14 +2075,19 @@ __initfunc(int tty_init(void)) if (tty_register_driver(&dev_console_driver)) panic("Couldn't register /dev/tty0 driver\n"); +#ifndef CONFIG_COBALT_MICRO_SERVER kbd_init(); #endif +#endif #ifdef CONFIG_ESPSERIAL /* init ESP before rs, so rs doesn't see the port */ espserial_init(); #endif #ifdef CONFIG_SERIAL rs_init(); #endif +#ifdef CONFIG_MAC_SERIAL + macserial_init(); +#endif #ifdef CONFIG_ROCKETPORT rp_init(); #endif @@ -2078,6 +2112,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_SPECIALIX specialix_init(); #endif +#ifdef CONFIG_8xx + rs_8xx_init(); +#endif /* CONFIG_8xx */ pty_init(); #ifdef CONFIG_VT vcs_init(); diff --git a/drivers/char/tuner.c b/drivers/char/tuner.c index a942985c2..9eb3d3944 100644 --- a/drivers/char/tuner.c +++ b/drivers/char/tuner.c @@ -7,13 +7,13 @@ #include <linux/errno.h> #include <linux/malloc.h> -#include "i2c.h" +#include <linux/i2c.h> #include <linux/videodev.h> #include "tuner.h" -int debug = 0; /* insmod parameter */ -int type = 0; /* tuner type */ +static int debug = 0; /* insmod parameter */ +static int type = 0; /* tuner type */ #define dprintk if (debug) printk @@ -243,10 +243,12 @@ struct i2c_driver i2c_driver_tuner = tuner_command }; +EXPORT_NO_SYMBOLS; + #ifdef MODULE int init_module(void) #else -int msp3400c_init(void) +int i2c_tuner_init(void) #endif { i2c_register_driver(&i2c_driver_tuner); diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index c0a4dc151..30de4d39b 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -7,7 +7,8 @@ * [minor: N] * * /dev/vcsaN: idem, but including attributes, and prefixed with - * the 4 bytes lines,columns,x,y (as screendump used to give) + * the 4 bytes lines,columns,x,y (as screendump used to give). + * Attribute/character pair is in native endianity. * [minor: N+128] * * This replaces screendump and part of selection, so that the system @@ -20,8 +21,6 @@ * - making it shorter - scr_readw are macros which expand in PRETTY long code */ -#include <linux/config.h> - #include <linux/kernel.h> #include <linux/major.h> #include <linux/errno.h> @@ -31,42 +30,29 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/vt_kern.h> +#include <linux/console_struct.h> #include <linux/selection.h> #include <asm/uaccess.h> +#include <asm/byteorder.h> #undef attr #undef org #undef addr #define HEADER_SIZE 4 -static unsigned short -func_scr_readw(unsigned short *org) -{ -return scr_readw( org ); -} - -static void -func_scr_writew(unsigned short val, unsigned short *org) -{ -scr_writew( val, org ); -} - static int vcs_size(struct inode *inode) { int size; -#ifdef CONFIG_FB_CONSOLE - int cons = MINOR(inode->i_rdev) & 127; - - if (cons == 0) - cons = fg_console; + int currcons = MINOR(inode->i_rdev) & 127; + if (currcons == 0) + currcons = fg_console; else - cons--; - if (!vc_cons_allocated(cons)) + currcons--; + if (!vc_cons_allocated(currcons)) return -ENXIO; -#endif - size = get_video_num_lines(cons) * get_video_num_columns(cons); + size = video_num_lines * video_num_columns; if (MINOR(inode->i_rdev) & 128) size = 2*size + HEADER_SIZE; @@ -94,7 +80,7 @@ static long long vcs_lseek(struct file *file, long long offset, int orig) return file->f_pos; } -#define RETURN( x ) { enable_bh( CONSOLE_BH ); return x; } +#define RETURN(x) { enable_bh(CONSOLE_BH); return x; } static ssize_t vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) { @@ -107,7 +93,7 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) attr = (currcons & 128); currcons = (currcons & 127); - disable_bh( CONSOLE_BH ); + disable_bh(CONSOLE_BH); if (currcons == 0) { currcons = fg_console; viewed = 1; @@ -128,12 +114,12 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) if (!attr) { org = screen_pos(currcons, p, viewed); while (count-- > 0) - put_user(func_scr_readw(org++) & 0xff, buf++); + put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); } else { if (p < HEADER_SIZE) { char header[HEADER_SIZE]; - header[0] = (char) get_video_num_lines(currcons); - header[1] = (char) get_video_num_columns(currcons); + header[0] = (char) video_num_lines; + header[1] = (char) video_num_columns; getconsxy(currcons, header+2); while (p < HEADER_SIZE && count > 0) { count--; put_user(header[p++], buf++); } @@ -142,15 +128,23 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) p -= HEADER_SIZE; org = screen_pos(currcons, p/2, viewed); if ((p & 1) && count > 0) - { count--; put_user(func_scr_readw(org++) >> 8, buf++); } +#ifdef __BIG_ENDIAN + { count--; put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); } +#else + { count--; put_user(vcs_scr_readw(currcons, org++) >> 8, buf++); } +#endif } while (count > 1) { - put_user(func_scr_readw(org++), (unsigned short *) buf); + put_user(vcs_scr_readw(currcons, org++), (unsigned short *) buf); buf += 2; count -= 2; } if (count > 0) - put_user(func_scr_readw(org) & 0xff, buf++); +#ifdef __BIG_ENDIAN + put_user(vcs_scr_readw(currcons, org) >> 8, buf++); +#else + put_user(vcs_scr_readw(currcons, org) & 0xff, buf++); +#endif } read = buf - buf0; *ppos += read; @@ -165,11 +159,11 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) long p = *ppos; long viewed, attr, size, written; const char *buf0; - unsigned short *org = NULL; + u16 *org0 = NULL, *org = NULL; attr = (currcons & 128); currcons = (currcons & 127); - disable_bh( CONSOLE_BH ); + disable_bh(CONSOLE_BH); if (currcons == 0) { currcons = fg_console; viewed = 1; @@ -188,12 +182,12 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) buf0 = buf; if (!attr) { - org = screen_pos(currcons, p, viewed); + org0 = org = screen_pos(currcons, p, viewed); while (count > 0) { unsigned char c; count--; get_user(c, (const unsigned char*)buf++); - func_scr_writew((func_scr_readw(org) & 0xff00) | c, org); + vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org); org++; } } else { @@ -206,35 +200,41 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) putconsxy(currcons, header+2); } if (count > 0) { - p -= HEADER_SIZE; - org = screen_pos(currcons, p/2, viewed); + p -= HEADER_SIZE; + org0 = org = screen_pos(currcons, p/2, viewed); if ((p & 1) && count > 0) { char c; count--; get_user(c,buf++); - func_scr_writew((c << 8) | - (func_scr_readw(org) & 0xff), org); +#ifdef __BIG_ENDIAN + vcs_scr_writew(currcons, c | + (vcs_scr_readw(currcons, org) & 0xff00), org); +#else + vcs_scr_writew(currcons, (c << 8) | + (vcs_scr_readw(currcons, org) & 0xff), org); +#endif org++; } } while (count > 1) { unsigned short w; get_user(w, (const unsigned short *) buf); - func_scr_writew(w, org++); + vcs_scr_writew(currcons, w, org++); buf += 2; count -= 2; } if (count > 0) { unsigned char c; get_user(c, (const unsigned char*)buf++); - func_scr_writew((func_scr_readw(org) & 0xff00) | c, org); +#ifdef __BIG_ENDIAN + vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org); +#else + vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org); +#endif } } -#ifdef CONFIG_FB_CONSOLE - if (currcons == fg_console) - /* Horribly inefficient if count < screen size. */ - update_screen(currcons); -#endif + if (org0) + update_region(currcons, (unsigned long)(org0), org-org0); written = buf - buf0; *ppos += written; RETURN( written ); diff --git a/drivers/char/vesa_blank.c b/drivers/char/vesa_blank.c deleted file mode 100644 index eb957180b..000000000 --- a/drivers/char/vesa_blank.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * vesa_blank.c - * - * Exported functions: - * void vesa_blank(void); - * void vesa_unblank(void); - * void vesa_powerdown(void); - * void set_vesa_blanking(const unsigned long arg); - * - * Not all hardware reacts well to this code - activate at your own risk. - * Activation is done using a sufficiently recent version of setterm - * or using a tiny C program like the following. - * ------------------------------------------------------------------------ -|#include <stdio.h> -|#include <linux/termios.h> -|main(int argc, char *argv[]) { -| int fd; -| struct { char ten, onoff; } arg; -| -| if (argc != 2) { -| fprintf(stderr, "usage: setvesablank on|vsync|hsync|powerdown|off\n"); -| exit(1); -| } -| if ((fd = open("/dev/console", 0)) < 0) -| fd = 0; -| arg.ten = 10; -| arg.onoff = 0; -| if (!strcmp(argv[1], "on") || !strcmp(argv[1], "vsync")) -| arg.onoff = 1; -| else if (!strcmp(argv[1], "hsync")) -| arg.onoff = 2; -| else if (!strcmp(argv[1], "powerdown")) -| arg.onoff = 3; -| if (ioctl(fd, TIOCLINUX, &arg)) { -| perror("setvesablank: TIOCLINUX"); -| exit(1); -| } -| exit(0); -|} ------------------------------------------------------------------------ -*/ - -#include <asm/io.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <linux/mm.h> - -extern unsigned short video_port_reg, video_port_val; - -/* - * This file handles the VESA Power Saving Protocol that lets a - * monitor be powered down whenever not needed for a longer time. - * The VESA protocol defines: - * - * Mode/Status HSync VSync Video - * ------------------------------------------- - * "On" on on active (mode 0) - * "Suspend" {either} on off blank (mode 1) - * { or } off on blank (mode 2) - * "Off" off off blank (mode 3) - * - * Original code taken from the Power Management Utility (PMU) of - * Huang shi chao, delivered together with many new monitor models - * capable of the VESA Power Saving Protocol. - * - * Adapted to Linux by Christoph Rimek (chrimek@toppoint.de) 15-may-94. - * A slightly adapted fragment of his README follows. - * - * Two-stage blanking by todd j. derr (tjd@barefoot.org) 10-oct-95. - -Patch (based on Linux Kernel revision 1.0) for handling the Power Saving -feature of the new monitor generation. The code works on all these monitors -(mine is a Smile 1506) and should run on *all* video adapter cards (change -some i/o-addresses), although tested only on two different VGA-cards: a -cheap Cirrus Logic (5428) and a miro Crystal 8S (S3-805). - -You can choose from two options: - -(1) Setting vesa_blanking_mode to 1 or 2. - The code will save the current setting of your video adapters' - register settings and then program the controller to turn off - the vertical synchronization pulse (mode 1) or horizontal - synchronization pulse (mode 2). Mode 1 should work with most - monitors, but the VESA spec allows mode 2, so it's included for - completeness. You may set this blanking interval in minutes by - echoing the escape sequence 'ESC[9;interval]' to the terminal. - By default this interval is set to 10 minutes. - - If you use one of these modes, you can also set a second interval - by echoing the escape sequence 'ESC[14;interval]' to the terminal. - The monitor will be turned off completely (mode 3) after being in - suspend mode for the specified interval. An interval of 0 disables - this feature which is the default. - - Both intervals may be set within the range of 0..60 minutes. - -(2) Setting vesa_blanking_mode to 3. - If your monitor locally has an Off_Mode timer then you should not - force your video card to send the OFF-signal - your monitor will - power down by itself. - If your monitor cannot handle this and needs the Off-signal directly, - or if you like your monitor to power down immediately when the - blank_timer times out, then you choose this option. - -On the other hand I'd recommend to not choose this second option unless -it is absolutely necessary. Powering down a monitor to the Off_State with -an approx. power consumption of 3-5 Watts is a rather strong action for -the CRT and it should not be done every now and then. If the software only -sends the signal to enter Standby mode, you have the chance to interfere -before the monitor powers down. Do not set a too short period, if you love -your hardware :-)) . - -By default vesa_blanking_mode is set to 0, thus not using any power saving -features. -*/ - -#define seq_port_reg (0x3c4) /* Sequencer register select port */ -#define seq_port_val (0x3c5) /* Sequencer register value port */ -#define video_misc_rd (0x3cc) /* Video misc. read port */ -#define video_misc_wr (0x3c2) /* Video misc. write port */ - -/* structure holding original VGA register settings */ -static struct { - unsigned char SeqCtrlIndex; /* Sequencer Index reg. */ - unsigned char CrtCtrlIndex; /* CRT-Contr. Index reg. */ - unsigned char CrtMiscIO; /* Miscellaneous register */ - unsigned char HorizontalTotal; /* CRT-Controller:00h */ - unsigned char HorizDisplayEnd; /* CRT-Controller:01h */ - unsigned char StartHorizRetrace; /* CRT-Controller:04h */ - unsigned char EndHorizRetrace; /* CRT-Controller:05h */ - unsigned char Overflow; /* CRT-Controller:07h */ - unsigned char StartVertRetrace; /* CRT-Controller:10h */ - unsigned char EndVertRetrace; /* CRT-Controller:11h */ - unsigned char ModeControl; /* CRT-Controller:17h */ - unsigned char ClockingMode; /* Seq-Controller:01h */ -} vga; - -#define VESA_NO_BLANKING 0 -#define VESA_VSYNC_SUSPEND 1 -#define VESA_HSYNC_SUSPEND 2 -#define VESA_POWERDOWN (VESA_HSYNC_SUSPEND | VESA_VSYNC_SUSPEND) - -#define DEFAULT_VESA_BLANKING_MODE VESA_NO_BLANKING - -static int vesa_blanking_mode = DEFAULT_VESA_BLANKING_MODE; -static int suspend_vesa_blanking_mode = DEFAULT_VESA_BLANKING_MODE; -static int vesa_blanked = 0; - -/* routine to blank a vesa screen */ -void vesa_blank(void) -{ - int mode; - - if((mode = vesa_blanking_mode) == 0) - return; - - /* save original values of VGA controller registers */ - if(!vesa_blanked) { - cli(); - vga.SeqCtrlIndex = inb_p(seq_port_reg); - vga.CrtCtrlIndex = inb_p(video_port_reg); - vga.CrtMiscIO = inb_p(video_misc_rd); - sti(); - - outb_p(0x00,video_port_reg); /* HorizontalTotal */ - vga.HorizontalTotal = inb_p(video_port_val); - outb_p(0x01,video_port_reg); /* HorizDisplayEnd */ - vga.HorizDisplayEnd = inb_p(video_port_val); - outb_p(0x04,video_port_reg); /* StartHorizRetrace */ - vga.StartHorizRetrace = inb_p(video_port_val); - outb_p(0x05,video_port_reg); /* EndHorizRetrace */ - vga.EndHorizRetrace = inb_p(video_port_val); - outb_p(0x07,video_port_reg); /* Overflow */ - vga.Overflow = inb_p(video_port_val); - outb_p(0x10,video_port_reg); /* StartVertRetrace */ - vga.StartVertRetrace = inb_p(video_port_val); - outb_p(0x11,video_port_reg); /* EndVertRetrace */ - vga.EndVertRetrace = inb_p(video_port_val); - outb_p(0x17,video_port_reg); /* ModeControl */ - vga.ModeControl = inb_p(video_port_val); - outb_p(0x01,seq_port_reg); /* ClockingMode */ - vga.ClockingMode = inb_p(seq_port_val); - } - - /* assure that video is enabled */ - /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */ - cli(); - outb_p(0x01,seq_port_reg); - outb_p(vga.ClockingMode | 0x20,seq_port_val); - - /* test for vertical retrace in process.... */ - if ((vga.CrtMiscIO & 0x80) == 0x80) - outb_p(vga.CrtMiscIO & 0xef,video_misc_wr); - - /* - * Set <End of vertical retrace> to minimum (0) and - * <Start of vertical Retrace> to maximum (incl. overflow) - * Result: turn off vertical sync (VSync) pulse. - */ - if (mode & VESA_VSYNC_SUSPEND) { - outb_p(0x10,video_port_reg); /* StartVertRetrace */ - outb_p(0xff,video_port_val); /* maximum value */ - outb_p(0x11,video_port_reg); /* EndVertRetrace */ - outb_p(0x40,video_port_val); /* minimum (bits 0..3) */ - outb_p(0x07,video_port_reg); /* Overflow */ - outb_p(vga.Overflow | 0x84,video_port_val); /* bits 9,10 of */ - /* vert. retrace */ - } - - if (mode & VESA_HSYNC_SUSPEND) { - /* - * Set <End of horizontal retrace> to minimum (0) and - * <Start of horizontal Retrace> to maximum - * Result: turn off horizontal sync (HSync) pulse. - */ - outb_p(0x04,video_port_reg); /* StartHorizRetrace */ - outb_p(0xff,video_port_val); /* maximum */ - outb_p(0x05,video_port_reg); /* EndHorizRetrace */ - outb_p(0x00,video_port_val); /* minimum (0) */ - } - - /* restore both index registers */ - outb_p(vga.SeqCtrlIndex,seq_port_reg); - outb_p(vga.CrtCtrlIndex,video_port_reg); - sti(); - - vesa_blanked = mode; -} - -/* routine to unblank a vesa screen */ -void vesa_unblank(void) -{ - if (!vesa_blanked) - return; - - /* restore original values of VGA controller registers */ - cli(); - outb_p(vga.CrtMiscIO,video_misc_wr); - - outb_p(0x00,video_port_reg); /* HorizontalTotal */ - outb_p(vga.HorizontalTotal,video_port_val); - outb_p(0x01,video_port_reg); /* HorizDisplayEnd */ - outb_p(vga.HorizDisplayEnd,video_port_val); - outb_p(0x04,video_port_reg); /* StartHorizRetrace */ - outb_p(vga.StartHorizRetrace,video_port_val); - outb_p(0x05,video_port_reg); /* EndHorizRetrace */ - outb_p(vga.EndHorizRetrace,video_port_val); - outb_p(0x07,video_port_reg); /* Overflow */ - outb_p(vga.Overflow,video_port_val); - outb_p(0x10,video_port_reg); /* StartVertRetrace */ - outb_p(vga.StartVertRetrace,video_port_val); - outb_p(0x11,video_port_reg); /* EndVertRetrace */ - outb_p(vga.EndVertRetrace,video_port_val); - outb_p(0x17,video_port_reg); /* ModeControl */ - outb_p(vga.ModeControl,video_port_val); - outb_p(0x01,seq_port_reg); /* ClockingMode */ - outb_p(vga.ClockingMode,seq_port_val); - - /* restore index/control registers */ - outb_p(vga.SeqCtrlIndex,seq_port_reg); - outb_p(vga.CrtCtrlIndex,video_port_reg); - sti(); - - vesa_blanked = 0; -} - -void set_vesa_blanking(const unsigned long arg) -{ - unsigned char *argp = (unsigned char *)(arg + 1); - unsigned int mode; - - if (verify_area(VERIFY_READ, argp, 1)) - return; - - get_user(mode, argp); - vesa_blanking_mode = suspend_vesa_blanking_mode = - ((mode <= VESA_POWERDOWN) ? mode : DEFAULT_VESA_BLANKING_MODE); -} - -void vesa_powerdown(void) -{ - if(vesa_blanking_mode == VESA_VSYNC_SUSPEND - || vesa_blanking_mode == VESA_HSYNC_SUSPEND) - { - vesa_blanking_mode = VESA_POWERDOWN; - vesa_blank(); - vesa_blanking_mode = suspend_vesa_blanking_mode; - } -} diff --git a/drivers/char/vga.c b/drivers/char/vga.c deleted file mode 100644 index 3c932ad7c..000000000 --- a/drivers/char/vga.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * linux/drivers/char/vga.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * 1995 Jay Estabrook - */ - -/* - * vga.c - * - * This module exports the console low-level io support for VGA - * - * 'int con_get_font(char *data)' - * 'int con_set_font(char *data, int ch512)' - * 'int con_adjust_height(int fontheight)' - * - * 'int con_get_cmap(char *)' - * 'int con_set_cmap(char *)' - * - * 'int reset_palette(int currcons)' - * 'void set_palette(void)' - * - * User definable mapping table and font loading by Eugene G. Crosser, - * <crosser@pccross.msk.su> - * - * Improved loadable font/UTF-8 support by H. Peter Anvin - * Feb-Sep 1995 <peter.anvin@linux.org> - * - * Colour palette handling, by Simon Tatham - * 17-Jun-95 <sgt20@cam.ac.uk> - * - * if 512 char mode is already enabled don't re-enable it, - * because it causes screen to flicker, by Mitja Horvat - * 5-May-96 <mitja.horvat@guest.arnes.si> - * - * Use 2 outw instead of 4 outb_p to reduce erroneous text - * flashing on RHS of screen during heavy console scrolling . - * Oct 1996, Paul Gortmaker. - * - */ - -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/config.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/kd.h> -#include <linux/malloc.h> -#include <linux/major.h> -#include <linux/mm.h> -#include <linux/ioport.h> -#include <linux/init.h> - -#ifdef __mips__ -#include <asm/bootinfo.h> -#include <asm/sni.h> -/* - * The video control ports are mapped at virtual address - * 0xe0200000 for the onboard S3 card - */ -#define PORT_BASE video_port_base -unsigned long video_port_base; -#endif - -#include <asm/io.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/bitops.h> - -#include <linux/kbd_kern.h> -#include <linux/vt_kern.h> -#include <linux/consolemap.h> -#include <linux/selection.h> -#include <linux/console_struct.h> - -#define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */ -#define CAN_LOAD_PALETTE /* undefine if the user must not do this */ - -#define dac_reg (0x3c8) -#define dac_val (0x3c9) - -#ifdef __powerpc__ -#define VGA_OFFSET 0xC0000000; -#else -#define VGA_OFFSET 0x0 -#endif - -/* - * By replacing the four outb_p with two back to back outw, we can reduce - * the window of opportunity to see text mislocated to the RHS of the - * console during heavy scrolling activity. However there is the remote - * possibility that some pre-dinosaur hardware won't like the back to back - * I/O. Since the Xservers get away with it, we should be able to as well. - */ -static inline void write_vga(unsigned char reg, unsigned int val) -{ -#ifndef SLOW_VGA - unsigned int v1, v2; - - v1 = reg + (val & 0xff00); - v2 = reg + 1 + ((val << 8) & 0xff00); - outw(v1, video_port_reg); - outw(v2, video_port_reg); -#else - outb_p(reg, video_port_reg); - outb_p(val >> 8, video_port_val); - outb_p(reg+1, video_port_reg); - outb_p(val & 0xff, video_port_val); -#endif -} - -void -set_palette (void) -{ - int i, j ; - - if ((video_type != VIDEO_TYPE_VGAC && video_type != VIDEO_TYPE_PICA_S3 - && video_type != VIDEO_TYPE_SNI_RM) - || console_blanked || vt_cons[fg_console]->vc_mode == KD_GRAPHICS) - return ; - - for (i=j=0; i<16; i++) { - outb_p (color_table[i], dac_reg) ; - outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ; - outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ; - outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ; - } -} - -void -__set_origin(unsigned short offset) -{ - clear_selection(); - - __origin = offset; - write_vga(12, offset); -} - -/* - * Put the cursor just beyond the end of the display adaptor memory. - */ -void -hide_cursor(void) -{ - /* This is inefficient, we could just put the cursor at 0xffff, - but perhaps the delays due to the inefficiency are useful for - some hardware... */ - write_vga(14, (video_mem_term - video_mem_base - 1)>>1); -} - -void -set_cursor(int currcons) -{ - if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) - return; - if (__real_origin != __origin) - __set_origin(__real_origin); - if (deccm) { - write_vga(14, (pos - video_mem_base)>>1); - } else - hide_cursor(); -} - -__initfunc(int con_is_present(void)) -{ - unsigned short saved; - unsigned short *p; - - /* - * Find out if there is a graphics card present. - * Are there smarter methods around? - */ - p = (unsigned short *) video_mem_base; - saved = scr_readw(p); - scr_writew(0xAA55, p); - if (scr_readw(p) != 0xAA55) { - scr_writew(saved, p); - return 0; - } - scr_writew(0x55AA, p); - if (scr_readw(p) != 0x55AA) { - scr_writew(saved, p); - return 0; - } - scr_writew(saved, p); - return 1; -} - -__initfunc(unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc)) -{ -#ifdef CONFIG_ACER_PICA_61 - /* - * This type of video is only available as 64bit on board display for - * MIPS machines based on the PICA chipset. If the loader has detected - * such a machine assume that we use that type of video. - */ - if (mips_machgroup == MACH_GROUP_JAZZ - && mips_machtype == MACH_ACER_PICA_61) - { - can_do_color = 1; - video_port_base = 0xe0200000; - video_port_reg = 0x3d4; - video_port_val = 0x3d5; - video_type = VIDEO_TYPE_PICA_S3; - video_mem_base = mips_vram_base; - video_mem_term = video_mem_base + 0x8000; - *display_desc = "PICA-S3"; - /* - * Don't request a region - the video ports are outside of - * the normal port address range. - * Initialize the cursor; the rest of the console code assumes - * this has already been done by the BIOS ... - */ - outb_p(10, video_port_reg); - outb_p(14, video_port_val); - outb_p(11, video_port_reg); - outb_p(15, video_port_val); - } - else -#endif -#ifdef CONFIG_SNI_RM200_PCI - if (mips_machgroup == MACH_GROUP_SNI_RM - && mips_machtype == MACH_SNI_RM200_PCI) - { - can_do_color = 1; - video_port_base = SNI_PORT_BASE; - video_port_reg = 0x3d4; - video_port_val = 0x3d5; - video_type = VIDEO_TYPE_SNI_RM; - video_mem_base = 0xb00b8000; - video_mem_term = video_mem_base + 0x8000; - *display_desc = "VGA+"; - /* - * The SNI machine is pretty much a simple RISC PC; we - * need to request the ports. - */ - request_region(0x3c0,32,"vga+"); - } - else -#endif - if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ - { - video_mem_base = 0xb0000 + VGA_OFFSET; - video_port_reg = 0x3b4; - video_port_val = 0x3b5; - if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) - { - video_type = VIDEO_TYPE_EGAM; - video_mem_term = 0xb8000 + VGA_OFFSET; - *display_desc = "EGA+"; - request_region(0x3b0,16,"ega"); - } - else - { - video_type = VIDEO_TYPE_MDA; - video_mem_term = 0xb2000 + VGA_OFFSET; - *display_desc = "*MDA"; - request_region(0x3b0,12,"mda"); - request_region(0x3bf, 1,"mda"); - } - } - else /* If not, it is color. */ - { - can_do_color = 1; - video_mem_term = 0xb8000 + VGA_OFFSET; - video_port_reg = 0x3d4; - video_port_val = 0x3d5; - if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) - { - int i ; - - video_mem_term = 0xc0000 + VGA_OFFSET; - - if (!ORIG_VIDEO_ISVGA) { - video_type = VIDEO_TYPE_EGAC; - *display_desc = "EGA"; - request_region(0x3c0,32,"ega"); - } else { - video_type = VIDEO_TYPE_VGAC; - *display_desc = "VGA+"; - request_region(0x3c0,32,"vga+"); - -#ifdef VGA_CAN_DO_64KB - /* - * get 64K rather than 32K of video RAM. - * This doesn't actually work on all "VGA" - * controllers (it seems like setting MM=01 - * and COE=1 isn't necessarily a good idea) - */ - video_mem_base = 0xa0000 + VGA_OFFSET; - video_mem_term = 0xb0000 + VGA_OFFSET; - outb_p (6, 0x3ce) ; - outb_p (6, 0x3cf) ; -#endif - - /* - * Normalise the palette registers, to point - * the 16 screen colours to the first 16 - * DAC entries. - */ - - for (i=0; i<16; i++) { - inb_p (0x3da) ; - outb_p (i, 0x3c0) ; - outb_p (i, 0x3c0) ; - } - outb_p (0x20, 0x3c0) ; - - /* now set the DAC registers back to their - * default values */ - - for (i=0; i<16; i++) { - outb_p (color_table[i], 0x3c8) ; - outb_p (default_red[i], 0x3c9) ; - outb_p (default_grn[i], 0x3c9) ; - outb_p (default_blu[i], 0x3c9) ; - } - } - } - else - { - video_type = VIDEO_TYPE_CGA; - video_mem_term = 0xba000 + VGA_OFFSET; - *display_desc = "*CGA"; - request_region(0x3d4,2,"cga"); - } - } - return kmem_start; -} - -__initfunc(void -con_type_init_finish(void)) -{ -} - -void -get_scrmem(int currcons) -{ - memcpyw((unsigned short *)vc_scrbuf[currcons], - (unsigned short *)origin, video_screen_size); - origin = video_mem_start = (unsigned long)vc_scrbuf[currcons]; - scr_end = video_mem_end = video_mem_start + video_screen_size; - pos = origin + y*video_size_row + (x<<1); -} - -void -set_scrmem(int currcons, long offset) -{ -#ifdef CONFIG_HGA - /* This works with XFree86 1.2, 1.3 and 2.0 - This code could be extended and made more generally useful if we could - determine the actual video mode. It appears that this should be - possible on a genuine Hercules card, but I (WM) haven't been able to - read from any of the required registers on my clone card. - */ - /* This code should work with Hercules and MDA cards. */ - if (video_type == VIDEO_TYPE_MDA) - { - if (vcmode == KD_TEXT) - { - /* Ensure that the card is in text mode. */ - int i; - static char herc_txt_tbl[12] = { - 0x61,0x50,0x52,0x0f,0x19,6,0x19,0x19,2,0x0d,0x0b,0x0c }; - outb_p(0, 0x3bf); /* Back to power-on defaults */ - outb_p(0, 0x3b8); /* Blank the screen, select page 0, etc */ - for ( i = 0 ; i < 12 ; i++ ) - { - outb_p(i, 0x3b4); - outb_p(herc_txt_tbl[i], 0x3b5); - } - } -#define HGA_BLINKER_ON 0x20 -#define HGA_SCREEN_ON 8 - /* Make sure that the hardware is not blanked */ - outb_p(HGA_BLINKER_ON | HGA_SCREEN_ON, 0x3b8); - } -#endif CONFIG_HGA - - if (video_mem_term - video_mem_base < offset + video_screen_size) - offset = 0; /* strange ... */ - memcpyw((unsigned short *)(video_mem_base + offset), - (unsigned short *) origin, video_screen_size); - video_mem_start = video_mem_base; - video_mem_end = video_mem_term; - origin = video_mem_base + offset; - scr_end = origin + video_screen_size; - pos = origin + y*video_size_row + (x<<1); - has_wrapped = 0; -} - -/* - * PIO_FONT support. - * - * The font loading code goes back to the codepage package by - * Joel Hoffman (joel@wam.umd.edu). (He reports that the original - * reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2 - * Video Systems_ by Richard Wilton. 1987. Microsoft Press".) - * - * Change for certain monochrome monitors by Yury Shevchuck - * (sizif@botik.yaroslavl.su). - */ - -#define colourmap ((char *)(0xa0000)) -/* Pauline Middelink <middelin@polyware.iaf.nl> reports that we - should use 0xA0000 for the bwmap as well.. */ -#define blackwmap ((char *)(0xa0000)) -#define cmapsz 8192 -#define attrib_port (0x3c0) -#define seq_port_reg (0x3c4) -#define seq_port_val (0x3c5) -#define gr_port_reg (0x3ce) -#define gr_port_val (0x3cf) - -int -set_get_font(char * arg, int set, int ch512) -{ -#ifdef CAN_LOAD_EGA_FONTS - static int ch512enabled = 0; - int i; - char *charmap; - int beg; - unsigned short video_port_status = video_port_reg + 6; - int font_select = 0x00; - - /* no use to "load" CGA... */ - - if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_VGAC - || video_type == VIDEO_TYPE_PICA_S3 - || video_type == VIDEO_TYPE_SNI_RM) { - charmap = colourmap; - beg = 0x0e; -#ifdef VGA_CAN_DO_64KB - if (video_type == VIDEO_TYPE_VGAC || - video_type == VIDEO_TYPE_PICA_S3 || - video_type == VIDEO_TYPE_SNI_RM) - beg = 0x06; -#endif - } else if (video_type == VIDEO_TYPE_EGAM) { - charmap = blackwmap; - beg = 0x0a; - } else - return -EINVAL; - - if (arg) - { - i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, - ch512 ? 2*cmapsz : cmapsz); - if (i) - return i; - } - else - ch512 = 0; /* Default font is always 256 */ - -#ifdef BROKEN_GRAPHICS_PROGRAMS - /* - * All fonts are loaded in slot 0 (0:1 for 512 ch) - */ - - if (!arg) - return -EINVAL; /* Return to default font not supported */ - - video_font_is_default = 0; - font_select = ch512 ? 0x04 : 0x00; -#else - /* - * The default font is kept in slot 0 and is never touched. - * A custom font is loaded in slot 2 (256 ch) or 2:3 (512 ch) - */ - - if (set) - { - video_font_is_default = !arg; - font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00; - } - - if ( !video_font_is_default ) - charmap += 4*cmapsz; -#endif - - cli(); - outb_p( 0x00, seq_port_reg ); /* First, the sequencer */ - outb_p( 0x01, seq_port_val ); /* Synchronous reset */ - outb_p( 0x02, seq_port_reg ); - outb_p( 0x04, seq_port_val ); /* CPU writes only to map 2 */ - outb_p( 0x04, seq_port_reg ); - outb_p( 0x07, seq_port_val ); /* Sequential addressing */ - outb_p( 0x00, seq_port_reg ); - outb_p( 0x03, seq_port_val ); /* Clear synchronous reset */ - - outb_p( 0x04, gr_port_reg ); /* Now, the graphics controller */ - outb_p( 0x02, gr_port_val ); /* select map 2 */ - outb_p( 0x05, gr_port_reg ); - outb_p( 0x00, gr_port_val ); /* disable odd-even addressing */ - outb_p( 0x06, gr_port_reg ); - outb_p( 0x00, gr_port_val ); /* map start at A000:0000 */ - sti(); - - if (arg) - { - if (set) - for (i=0; i<cmapsz ; i++) { - char c; - get_user(c, arg + i); - scr_writeb(c, charmap + i); - } - else - for (i=0; i<cmapsz ; i++) - put_user(scr_readb(charmap + i), arg + i); - - - /* - * In 512-character mode, the character map is not contiguous if - * we want to remain EGA compatible -- which we do - */ - - if (ch512) - { - charmap += 2*cmapsz; - arg += cmapsz; - if (set) - for (i=0; i<cmapsz ; i++) { - char c; - get_user(c, arg+i); - scr_writeb(c, charmap+i); - } - else - for (i=0; i<cmapsz ; i++) - put_user(scr_readb(charmap+i), arg+i); - } - } - - cli(); - outb_p( 0x00, seq_port_reg ); /* First, the sequencer */ - outb_p( 0x01, seq_port_val ); /* Synchronous reset */ - outb_p( 0x02, seq_port_reg ); - outb_p( 0x03, seq_port_val ); /* CPU writes to maps 0 and 1 */ - outb_p( 0x04, seq_port_reg ); - outb_p( 0x03, seq_port_val ); /* odd-even addressing */ - if (set) - { - outb_p( 0x03, seq_port_reg ); /* Character Map Select */ - outb_p( font_select, seq_port_val ); - } - outb_p( 0x00, seq_port_reg ); - outb_p( 0x03, seq_port_val ); /* clear synchronous reset */ - - outb_p( 0x04, gr_port_reg ); /* Now, the graphics controller */ - outb_p( 0x00, gr_port_val ); /* select map 0 for CPU */ - outb_p( 0x05, gr_port_reg ); - outb_p( 0x10, gr_port_val ); /* enable even-odd addressing */ - outb_p( 0x06, gr_port_reg ); - outb_p( beg, gr_port_val ); /* map starts at b800:0 or b000:0 */ - - /* if 512 char mode is already enabled don't re-enable it. */ - if ((set)&&(ch512!=ch512enabled)) /* attribute controller */ - { - ch512enabled=ch512; - /* 256-char: enable intensity bit - 512-char: disable intensity bit */ - inb_p( video_port_status ); /* clear address flip-flop */ - outb_p ( 0x12, attrib_port ); /* color plane enable register */ - outb_p ( ch512 ? 0x07 : 0x0f, attrib_port ); - /* Wilton (1987) mentions the following; I don't know what - it means, but it works, and it appears necessary */ - inb_p( video_port_status ); - outb_p ( 0x20, attrib_port ); - } - sti(); - - return 0; -#else - return -EINVAL; -#endif -} - -/* - * Adjust the screen to fit a font of a certain height - * - * Returns < 0 for error, 0 if nothing changed, and the number - * of lines on the adjusted console if changed. - */ -int -con_adjust_height(unsigned long fontheight) -{ - int rows, maxscan; - unsigned char ovr, vde, fsr, curs, cure; - - if (fontheight > 32 || (video_type != VIDEO_TYPE_VGAC && - video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM && - video_type != VIDEO_TYPE_PICA_S3 && - video_type != VIDEO_TYPE_SNI_RM)) - return -EINVAL; - - if ( fontheight == video_font_height || fontheight == 0 ) - return 0; - - video_font_height = fontheight; - - rows = video_scan_lines/fontheight; /* Number of video rows we end up with */ - maxscan = rows*fontheight - 1; /* Scan lines to actually display-1 */ - - /* Reprogram the CRTC for the new font size - Note: the attempt to read the overflow register will fail - on an EGA, but using 0xff for the previous value appears to - be OK for EGA text modes in the range 257-512 scan lines, so I - guess we don't need to worry about it. - - The same applies for the spill bits in the font size and cursor - registers; they are write-only on EGA, but it appears that they - are all don't care bits on EGA, so I guess it doesn't matter. */ - - cli(); - outb_p( 0x07, video_port_reg ); /* CRTC overflow register */ - ovr = inb_p(video_port_val); - outb_p( 0x09, video_port_reg ); /* Font size register */ - fsr = inb_p(video_port_val); - outb_p( 0x0a, video_port_reg ); /* Cursor start */ - curs = inb_p(video_port_val); - outb_p( 0x0b, video_port_reg ); /* Cursor end */ - cure = inb_p(video_port_val); - sti(); - - vde = maxscan & 0xff; /* Vertical display end reg */ - ovr = (ovr & 0xbd) + /* Overflow register */ - ((maxscan & 0x100) >> 7) + - ((maxscan & 0x200) >> 3); - fsr = (fsr & 0xe0) + (fontheight-1); /* Font size register */ - curs = (curs & 0xc0) + fontheight - (fontheight < 10 ? 2 : 3); - cure = (cure & 0xe0) + fontheight - (fontheight < 10 ? 1 : 2); - - cli(); - outb_p( 0x07, video_port_reg ); /* CRTC overflow register */ - outb_p( ovr, video_port_val ); - outb_p( 0x09, video_port_reg ); /* Font size */ - outb_p( fsr, video_port_val ); - outb_p( 0x0a, video_port_reg ); /* Cursor start */ - outb_p( curs, video_port_val ); - outb_p( 0x0b, video_port_reg ); /* Cursor end */ - outb_p( cure, video_port_val ); - outb_p( 0x12, video_port_reg ); /* Vertical display limit */ - outb_p( vde, video_port_val ); - sti(); - - if ( rows == video_num_lines ) { - /* Change didn't affect number of lines -- no need to scare - the rest of the world */ - return 0; - } - - vc_resize(rows, 0); /* Adjust console size */ - - return rows; -} - -int -set_get_cmap(unsigned char * arg, int set) { -#ifdef CAN_LOAD_PALETTE - int i; - - /* no use to set colourmaps in less than colour VGA */ - - if (video_type != VIDEO_TYPE_PICA_S3 && - video_type != VIDEO_TYPE_SNI_RM && - video_type != VIDEO_TYPE_VGAC) - return -EINVAL; - - i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, 16*3); - if (i) - return i; - - for (i=0; i<16; i++) { - if (set) { - get_user(default_red[i], arg++) ; - get_user(default_grn[i], arg++) ; - get_user(default_blu[i], arg++) ; - } else { - put_user (default_red[i], arg++) ; - put_user (default_grn[i], arg++) ; - put_user (default_blu[i], arg++) ; - } - } - if (set) { - for (i=0; i<MAX_NR_CONSOLES; i++) - if (vc_cons_allocated(i)) { - int j, k ; - 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() ; - } - - return 0; -#else - return -EINVAL; -#endif -} diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index e9566da53..868ba4eb9 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -27,6 +27,10 @@ #include <asm/uaccess.h> #include <asm/system.h> +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif + #define VIDEO_NUM_DEVICES 256 @@ -38,6 +42,7 @@ static struct video_device *video_device[VIDEO_NUM_DEVICES]; #ifdef CONFIG_VIDEO_BT848 extern int init_bttv_cards(struct video_init *); +extern int i2c_tuner_init(struct video_init *); #endif #ifdef CONFIG_VIDEO_SAA5249 extern int init_saa_5249(struct video_init *); @@ -48,9 +53,19 @@ extern int init_colour_qcams(struct video_init *); #ifdef CONFIG_VIDEO_BWQCAM extern int init_bw_qcams(struct video_init *); #endif +#ifdef CONFIG_RADIO_AZTECH +extern int aztech_init(struct video_init *); +#endif +#ifdef CONFIG_RADIO_RTRACK +extern int rtrack_init(struct video_init *); +#endif +#ifdef CONFIG_RADIO_SF16FMI +extern int fmi_init(struct video_init *); +#endif static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BT848 + {"i2c-tuner", i2c_tuner_init}, {"bttv", init_bttv_cards}, #endif #ifdef CONFIG_VIDEO_SAA5249 @@ -65,6 +80,15 @@ static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_PMS {"PMS", init_pms_cards}, #endif +#ifdef CONFIG_RADIO_AZTECH + {"Aztech", aztech_init}, +#endif +#ifdef CONFIG_RADIO_RTRACK + {"RTrack", rtrack_init}, +#endif +#ifdef CONFIG_RADIO_SF16FMI + {"SF16FMI", fmi_init}, +#endif {"end", NULL} }; @@ -77,11 +101,13 @@ static ssize_t video_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; - return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK); + if(vfl->read) + return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK); + else + return -EINVAL; } - /* * Write for now does nothing. No reason it shouldnt do overlay setting * for some boards I guess.. @@ -91,7 +117,25 @@ static ssize_t video_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; - return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK); + if(vfl->write) + return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK); + else + return 0; +} + + +/* + * Poll to see if we're readable, can probably be used for timing on incoming + * frames, etc.. + */ + +static unsigned int video_poll(struct file *file, poll_table * wait) +{ + struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + if(vfl->poll) + return vfl->poll(vfl, file, wait); + else + return 0; } /* @@ -108,8 +152,17 @@ static int video_open(struct inode *inode, struct file *file) return -ENODEV; vfl=video_device[minor]; - if(vfl==NULL) - return -ENODEV; + if(vfl==NULL) { +#ifdef CONFIG_KMOD + char modname[20]; + + sprintf (modname, "char-major-%d-%d", VIDEO_MAJOR, minor); + request_module(modname); + vfl=video_device[minor]; + if (vfl==NULL) +#endif + return -ENODEV; + } if(vfl->busy) return -EBUSY; vfl->busy=1; /* In case vfl->open sleeps */ @@ -223,12 +276,15 @@ int video_register_device(struct video_device *vfd, int type) /* The init call may sleep so we book the slot out then call */ MOD_INC_USE_COUNT; - err=vfd->initialize(vfd); - if(err<0) + if(vfd->initialize) { - video_device[i]=NULL; - MOD_DEC_USE_COUNT; - return err; + err=vfd->initialize(vfd); + if(err<0) + { + video_device[i]=NULL; + MOD_DEC_USE_COUNT; + return err; + } } return 0; } @@ -255,7 +311,7 @@ static struct file_operations video_fops= video_read, video_write, NULL, /* readdir */ - NULL, /* poll */ + video_poll, /* poll */ video_ioctl, video_mmap, video_open, diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 360ca915f..01c973576 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -22,6 +22,7 @@ #include <linux/malloc.h> #include <linux/major.h> #include <linux/fs.h> +#include <linux/config.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -31,6 +32,10 @@ #include <linux/kbd_diacr.h> #include <linux/selection.h> +#ifdef CONFIG_FB_COMPAT_XPMAC +#include <asm/vc_ioctl.h> +#endif /* CONFIG_FB_COMPAT_XPMAC */ + char vt_dont_switch = 0; extern struct tty_driver console_driver; @@ -56,39 +61,9 @@ struct vt_struct *vt_cons[MAX_NR_CONSOLES]; asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on); #endif -extern int getkeycode(unsigned int scancode); -extern int setkeycode(unsigned int scancode, unsigned int keycode); -extern void compute_shiftstate(void); -extern void complete_change_console(unsigned int new_console); -extern int vt_waitactive(int vt); -extern void do_blank_screen(int nopowersave); - -extern unsigned int keymap_count; - -/* - * routines to load custom translation table, EGA/VGA font and - * VGA colour palette from console.c - */ -extern int con_set_trans_old(unsigned char * table); -extern int con_get_trans_old(unsigned char * table); -extern int con_set_trans_new(unsigned short * table); -extern int con_get_trans_new(unsigned short * table); -extern void con_clear_unimap(struct unimapinit *ui); -extern int con_set_unimap(ushort ct, struct unipair *list); -extern int con_get_unimap(ushort ct, ushort *uct, struct unipair *list); -extern void con_set_default_unimap(void); -extern int con_set_font(char * fontmap, int ch512); -extern int con_get_font(char * fontmap); -extern int con_set_cmap(unsigned char *cmap); -extern int con_get_cmap(unsigned char *cmap); -extern void reset_palette(int currcons); -extern void set_palette(void) ; -extern int con_adjust_height(unsigned long fontheight); - -extern int video_mode_512ch; -extern unsigned long video_font_height; -extern unsigned long default_font_height; -extern unsigned long video_scan_lines; +unsigned int video_font_height; +unsigned int default_font_height; +unsigned int video_scan_lines; /* * these are the valid i/o ports we're allowed to change. they map all the @@ -138,20 +113,19 @@ kd_size_changed(int row, int col) } /* - * Generates sound of some count for some number of clock ticks - * [count = 1193180 / frequency] + * 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. * If msec is 0, will return immediately, else will sleep for msec time, then * turn sound off. * - * We use the BEEP_TIMER vector since we're using the same method to - * generate sound, and we'll overwrite any beep in progress. That may - * be something to fix later, if we like. - * * We also return immediately, which is what was implied within the X * comments - KDMKTONE doesn't put the process to sleep. */ + +#if defined(__i386__) || defined(__alpha__) || defined(__powerpc__) + || (defined(__mips__) && !defined(CONFIG_SGI)) + static void kd_nosound(unsigned long ignored) { @@ -192,13 +166,17 @@ _kd_mksound(unsigned int hz, unsigned int ticks) return; } -#ifdef CONFIG_SGI -void _kd_nullsound(unsigned int hz, unsigned int ticks) { } -void (*kd_mksound)(unsigned int hz, unsigned int ticks) = _kd_nullsound; #else -void (*kd_mksound)(unsigned int hz, unsigned int ticks) = _kd_mksound; + +void +_kd_mksound(unsigned int hz, unsigned int ticks) +{ +} + #endif +void (*kd_mksound)(unsigned int hz, unsigned int ticks) = _kd_mksound; + #define i (tmp.kb_index) #define s (tmp.kb_table) @@ -247,9 +225,12 @@ do_kdsk_ioctl(int cmd, struct kbentry *user_kbe, int perm, struct kbd_struct *kb if (kbd->kbdmode != VC_UNICODE) return -EINVAL; + /* ++Geert: non-PC keyboards may generate keycode zero */ +#if !defined(__mc68000__) && !defined(__powerpc__) /* assignment to entry 0 only tests validity of args */ if (!i) break; +#endif if (!(key_map = key_maps[s])) { int j; @@ -401,44 +382,42 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) static inline int do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm) { - int nchar; struct consolefontdesc cfdarg; - int i = 0; + struct console_font_op op; + int i; if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) return -EFAULT; - if (vt_cons[fg_console]->vc_mode != KD_TEXT) - return -EINVAL; switch (cmd) { case PIO_FONTX: if (!perm) return -EPERM; - if ( cfdarg.charcount == 256 || - cfdarg.charcount == 512 ) { - i = con_set_font(cfdarg.chardata, - cfdarg.charcount == 512); - if (i) - return i; - i = con_adjust_height(cfdarg.charheight); - return (i <= 0) ? i : kd_size_changed(i, 0); - } else - return -EINVAL; - case GIO_FONTX: - i = cfdarg.charcount; - cfdarg.charcount = nchar = video_mode_512ch ? 512 : 256; - cfdarg.charheight = video_font_height; - __copy_to_user(user_cfd, &cfdarg, - sizeof(struct consolefontdesc)); - if ( cfdarg.chardata ) - { - if ( i < nchar ) - return -ENOMEM; - return con_get_font(cfdarg.chardata); - } else - return 0; + op.op = KD_FONT_OP_SET; + op.flags = KD_FONT_FLAG_OLD; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + return con_font_op(fg_console, &op); + case GIO_FONTX: { + op.op = KD_FONT_OP_GET; + op.flags = KD_FONT_FLAG_OLD; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + i = con_font_op(fg_console, &op); + if (i) + return i; + cfdarg.charheight = op.height; + cfdarg.charcount = op.charcount; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) + return -EFAULT; + return 0; + } } - return 0; + return -EINVAL; } static inline int @@ -458,9 +437,9 @@ do_unimap_ioctl(int cmd, struct unimapdesc *user_ud,int perm) case PIO_UNIMAP: if (!perm) return -EPERM; - return con_set_unimap(tmp.entry_ct, tmp.entries); + return con_set_unimap(fg_console, tmp.entry_ct, tmp.entries); case GIO_UNIMAP: - return con_get_unimap(tmp.entry_ct, &(user_ud->entry_ct), tmp.entries); + return con_get_unimap(fg_console, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries); } return 0; } @@ -803,7 +782,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); + i = vc_allocate(arg, 0); if (i) return i; set_console(arg); @@ -855,7 +834,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); + i = vc_allocate(newvt, 0); if (i) return i; /* @@ -915,7 +894,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(ll, cc); + i = vc_resize_all(ll, cc); return i ? i : kd_size_changed(ll, cc); } @@ -964,7 +943,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if ( clin ) video_font_height = clin; - i = vc_resize(ll, cc); + i = vc_resize_all(ll, cc); if (i) return i; @@ -972,30 +951,37 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return 0; } - case PIO_FONT: + case PIO_FONT: { + struct console_font_op op; if (!perm) return -EPERM; - if (vt_cons[fg_console]->vc_mode != KD_TEXT) - return -EINVAL; - return con_set_font((char *)arg, 0); - /* con_set_font() defined in console.c */ + op.op = KD_FONT_OP_SET; + op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */ + op.width = 8; + op.height = 0; + op.charcount = 256; + op.data = (char *) arg; + return con_font_op(fg_console, &op); + } - case GIO_FONT: - if (vt_cons[fg_console]->vc_mode != KD_TEXT || - video_mode_512ch) - return -EINVAL; - return con_get_font((char *)arg); - /* con_get_font() defined in console.c */ + case GIO_FONT: { + struct console_font_op op; + op.op = KD_FONT_OP_GET; + op.flags = KD_FONT_FLAG_OLD; + op.width = 8; + op.height = 32; + op.charcount = 256; + op.data = (char *) arg; + return con_font_op(fg_console, &op); + } case PIO_CMAP: if (!perm) return -EPERM; return con_set_cmap((char *)arg); - /* con_set_cmap() defined in console.c */ case GIO_CMAP: return con_get_cmap((char *)arg); - /* con_get_cmap() defined in console.c */ case PIO_FONTX: case GIO_FONTX: @@ -1005,26 +991,37 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, { if (!perm) return -EPERM; - if (vt_cons[fg_console]->vc_mode != KD_TEXT) - return -EINVAL; #ifdef BROKEN_GRAPHICS_PROGRAMS /* With BROKEN_GRAPHICS_PROGRAMS defined, the default font is not saved. */ return -ENOSYS; #else - - i = con_set_font(NULL, 0); /* Set font to default */ + { + struct console_font_op op; + op.op = KD_FONT_OP_SET_DEFAULT; + op.data = NULL; + i = con_font_op(fg_console, &op); if (i) return i; - - i = con_adjust_height(default_font_height); - if ( i > 0 ) kd_size_changed(i, 0); - con_set_default_unimap(); - + con_set_default_unimap(fg_console); return 0; + } #endif } + case KDFONTOP: { + struct console_font_op op; + if (copy_from_user(&op, (void *) arg, sizeof(op))) + return -EFAULT; + if (!perm && op.op != KD_FONT_OP_GET) + return -EPERM; + i = con_font_op(console, &op); + if (i) return i; + if (copy_to_user((void *) arg, &op, sizeof(op))) + return -EFAULT; + return 0; + } + case PIO_SCRNMAP: if (!perm) return -EPERM; @@ -1047,7 +1044,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return -EPERM; i = copy_from_user(&ui, (void *)arg, sizeof(struct unimapinit)); if (i) return -EFAULT; - con_clear_unimap(&ui); + con_clear_unimap(fg_console, &ui); return 0; } @@ -1065,6 +1062,76 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return -EPERM; vt_dont_switch = 0; return 0; +#ifdef CONFIG_FB_COMPAT_XPMAC + case VC_GETMODE: + { + struct vc_mode mode; + + i = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct vc_mode)); + if (i == 0) + i = console_getmode(&mode); + if (i) + return i; + if (copy_to_user((void *) arg, &mode, sizeof(mode))) + return -EFAULT; + return 0; + } + case VC_SETMODE: + case VC_INQMODE: + { + struct vc_mode mode; + + if (!perm) + return -EPERM; + i = verify_area(VERIFY_READ, (void *) arg, + sizeof(struct vc_mode)); + if (i) + return i; + if (copy_from_user(&mode, (void *) arg, sizeof(mode))) + return -EFAULT; + return console_setmode(&mode, cmd == VC_SETMODE); + } + case VC_SETCMAP: + { + unsigned char cmap[3][256], *p; + int n_entries, cmap_size, i, j; + + if (!perm) + return -EPERM; + if (arg == (unsigned long) VC_POWERMODE_INQUIRY + || arg <= VESA_POWERDOWN) { + /* compatibility hack: VC_POWERMODE + was changed from 0x766a to 0x766c */ + return console_powermode((int) arg); + } + i = verify_area(VERIFY_READ, (void *) arg, + sizeof(int)); + if (i) + return i; + if (get_user(cmap_size, (int *) arg)) + return -EFAULT; + if (cmap_size % 3) + return -EINVAL; + n_entries = cmap_size / 3; + if ((unsigned) n_entries > 256) + return -EINVAL; + p = (unsigned char *) (arg + sizeof(int)); + for (j = 0; j < n_entries; ++j) + for (i = 0; i < 3; ++i) + if (get_user(cmap[i][j], p++)) + return -EFAULT; + return console_setcmap(n_entries, cmap[0], + cmap[1], cmap[2]); + } + case VC_GETCMAP: + /* not implemented yet */ + return -ENOIOCTLCMD; + case VC_POWERMODE: + if (!perm) + return -EPERM; + return console_powermode((int) arg); +#endif /* CONFIG_FB_COMPAT_XPMAC */ default: return -ENOIOCTLCMD; } @@ -1127,10 +1194,6 @@ void complete_change_console(unsigned int new_console) { unsigned char old_vc_mode; - if ((new_console == fg_console) || (vt_dont_switch)) - return; - if (!vc_cons_allocated(new_console)) - return; last_console = fg_console; /* @@ -1185,21 +1248,8 @@ void complete_change_console(unsigned int new_console) /* Set the colour palette for this VT */ if (vt_cons[new_console]->vc_mode == KD_TEXT) set_palette() ; - -#ifdef CONFIG_SUN_CONSOLE - if (old_vc_mode != vt_cons[new_console]->vc_mode) - { - if (old_vc_mode == KD_GRAPHICS) - { - suncons_ops.clear_margin(); - suncons_ops.render_screen(); - suncons_ops.set_cursor(fg_console); - } - else - suncons_ops.hide_cursor(); - } -#endif - /* + + /* * Wake anyone waiting for their VT to activate */ vt_wake_waitactive(); diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c index e48f22a0f..86ce29744 100644 --- a/drivers/char/wdt.c +++ b/drivers/char/wdt.c @@ -184,7 +184,7 @@ static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_ } /* - * Read reports the temperature in farenheit + * Read reports the temperature in degrees Fahrenheit. */ static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr) |