diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
commit | e308faf24f68e262d92d294a01ddca7a17e76762 (patch) | |
tree | 22c47cb315811834861f013067878ff664e95abd /drivers/char | |
parent | 30c6397ce63178fcb3e7963ac247f0a03132aca9 (diff) |
Sync with Linux 2.1.46.
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ChangeLog | 24 | ||||
-rw-r--r-- | drivers/char/Config.in | 2 | ||||
-rw-r--r-- | drivers/char/Makefile | 18 | ||||
-rw-r--r-- | drivers/char/console.c | 58 | ||||
-rw-r--r-- | drivers/char/cyclades.c | 353 | ||||
-rw-r--r-- | drivers/char/joystick.c | 376 | ||||
-rw-r--r-- | drivers/char/lp.c | 4 | ||||
-rw-r--r-- | drivers/char/mem.c | 11 | ||||
-rw-r--r-- | drivers/char/misc.c | 3 | ||||
-rw-r--r-- | drivers/char/n_tty.c | 93 | ||||
-rw-r--r-- | drivers/char/pc110pad.c | 690 | ||||
-rw-r--r-- | drivers/char/pc110pad.h | 31 | ||||
-rw-r--r-- | drivers/char/psaux.c | 32 | ||||
-rw-r--r-- | drivers/char/pty.c | 97 | ||||
-rw-r--r-- | drivers/char/random.c | 150 | ||||
-rw-r--r-- | drivers/char/rtc.c | 10 | ||||
-rw-r--r-- | drivers/char/serial.c | 40 | ||||
-rw-r--r-- | drivers/char/sysrq.c | 4 | ||||
-rw-r--r-- | drivers/char/tty_io.c | 594 | ||||
-rw-r--r-- | drivers/char/vc_screen.c | 5 |
20 files changed, 2134 insertions, 461 deletions
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog index 6a0009511..3083c0c54 100644 --- a/drivers/char/ChangeLog +++ b/drivers/char/ChangeLog @@ -1,3 +1,27 @@ +Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * serial.c (begin_break, end_break, rs_ioctl): Applied patch + to support BSD ioctls to set and clear the break + condition explicitly. + + * console.c (scrup, scrdown, insert_line, delete_line): Applied + fix suggested by Aaron Tiensivu to speed up block scrolls + up and down. + + * n_tty.c (opost_block, write_chan): Added a modified "fast + console" patch which processes a block of text via + "cooking" efficiently. + +Wed Jun 18 15:25:50 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * tty_io.c (init_dev, release_dev): Applied fix suggested by Bill + Hawes to prevent race conditions in the tty code. + + * n_tty.c (n_tty_chars_in_buffer): Applied fix suggested by Bill + Hawes so that n_tty_chars_in_buffer returns the correct + value in the case when the tty is in cannonical mode. (To + avoid a pty deadlock with telnetd.) + 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 diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 4216b3154..c02b376e8 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -52,6 +52,7 @@ if [ "$CONFIG_MOUSE" = "y" ]; then if [ "$CONFIG_PSMOUSE" != "n" ]; then bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE fi + tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD fi if [ "$CONFIG_MODULES" = "y" ]; then @@ -97,4 +98,5 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG fi bool 'Enhanced Real Time Clock Support' CONFIG_RTC +tristate 'PC joystick support' CONFIG_JOYSTICK endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 14efcf554..73c1ac599 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -145,6 +145,14 @@ else endif endif +ifeq ($(CONFIG_JOYSTICK),y) +L_OBJS += joystick.o +else + ifeq ($(CONFIG_JOYSTICK),m) + M_OBJS += joystick.o + endif +endif + ifeq ($(CONFIG_MS_BUSMOUSE),y) M = y L_OBJS += msbusmouse.o @@ -218,6 +226,16 @@ ifdef CONFIG_SUN_MOUSE M = y endif +ifeq ($(CONFIG_PC110_PAD),y) +M = y +L_OBJS += pc110pad.o +else + ifeq ($(CONFIG_PC110_PAD),m) + M_OBJS += pc110pad.o + MM = m + endif +endif + ifeq ($(CONFIG_SUN_OPENPROMIO),y) M = y else diff --git a/drivers/char/console.c b/drivers/char/console.c index 61ab64aac..b24def4a2 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -514,13 +514,15 @@ static void set_origin(int currcons) __set_origin(__real_origin); } -static void scrup(int currcons, unsigned int t, unsigned int b) +static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr) { int hardscroll = hardscroll_enabled; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - if (t || b != video_num_lines) + if (t || b != video_num_lines || nr > 1) hardscroll = 0; if (hardscroll) { origin += video_size_row; @@ -575,31 +577,35 @@ static void scrup(int currcons, unsigned int t, unsigned int b) set_origin(currcons); } else { unsigned short * d = (unsigned short *) (origin+video_size_row*t); - unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1)); + unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr)); - memcpyw(d, s, (b-t-1) * video_size_row); - memsetw(d + (b-t-1) * video_num_columns, video_erase_char, video_size_row); + 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) +scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr) { unsigned short *s; unsigned int count; + unsigned int step; - if (b > video_num_lines || t >= b) + 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-2)); - if (b >= t + 1) { - count = b - t - 1; - while (count) { - count--; - memcpyw(s + video_num_columns, s, video_size_row); - s -= video_num_columns; - } + 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); } - memsetw(s + video_num_columns, video_erase_char, video_size_row); has_scrolled = 1; } @@ -609,7 +615,7 @@ static void lf(int currcons) * if below scrolling region */ if (y+1 == bottom) - scrup(currcons,top,bottom); + scrup(currcons,top,bottom, 1); else if (y < video_num_lines-1) { y++; pos += video_size_row; @@ -623,7 +629,7 @@ static void ri(int currcons) * if above scrolling region */ if (y == top) - scrdown(currcons,top,bottom); + scrdown(currcons,top,bottom,1); else if (y > 0) { y--; pos -= video_size_row; @@ -1148,9 +1154,9 @@ static void insert_char(int currcons) need_wrap = 0; } -static void insert_line(int currcons) +static void insert_line(int currcons, unsigned int nr) { - scrdown(currcons,y,bottom); + scrdown(currcons,y,bottom,nr); need_wrap = 0; } @@ -1167,9 +1173,9 @@ static void delete_char(int currcons) need_wrap = 0; } -static void delete_line(int currcons) +static void delete_line(int currcons, unsigned int nr) { - scrup(currcons,y,bottom); + scrup(currcons,y,bottom,nr); need_wrap = 0; } @@ -1189,8 +1195,7 @@ static void csi_L(int currcons, unsigned int nr) nr = video_num_lines; else if (!nr) nr = 1; - while (nr--) - insert_line(currcons); + insert_line(currcons, nr); } static void csi_P(int currcons, unsigned int nr) @@ -1209,8 +1214,7 @@ static void csi_M(int currcons, unsigned int nr) nr = video_num_lines; else if (!nr) nr=1; - while (nr--) - delete_line(currcons); + delete_line(currcons, nr); } static void save_cur(int currcons) diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index efa895a69..94969bf28 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,6 +1,6 @@ #define BLOCKMOVE static char rcsid[] = -"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $"; +"$Revision: 1.1.1.1 $$Date: 1997/06/01 03:17:29 $"; /* * linux/drivers/char/cyclades.c @@ -29,6 +29,9 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 1.1.1.1 1997/06/01 03:17:29 ralf + * Initial import of Linux/MIPS pre-2.1.40. + * * Revision 1.36.4.27 1997/03/26 10:30:00 daniel * Changed for suport linux versions 2.1.X. * Backward compatible with linux versions 2.0.X. @@ -98,7 +101,7 @@ static char rcsid[] = * after -Y stuff (to make changes clearer) * * Revision 1.36.4.12 1996/07/11 15:40:55 bentson - * Add code to poll Cyclom-Z. Add code to get & set RS-232 control. + * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. * Add code to send break. Clear firmware ID word at startup (so * that other code won't talk to inactive board). * @@ -391,7 +394,13 @@ static char rcsid[] = constant in the definition below. No other change is necessary to support more boards/ports. */ -#define NR_PORTS 64 +//#define NR_PORTS 64 +#define NR_PORTS 128 + +#define ZE_V1_NPORTS 64 +#define ZO_V1 0 +#define ZO_V2 1 +#define ZE_V1 2 #define SERIAL_PARANOIA_CHECK #undef SERIAL_DEBUG_OPEN @@ -453,35 +462,51 @@ static char rcsid[] = #include <linux/kernel.h> #include <linux/bios32.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/version.h> -#if LINUX_VERSION_CODE >= 131328 +#if (LINUX_VERSION_CODE >= 0x020100) #include <asm/uaccess.h> +#include <linux/init.h> -#define memcpy_fromfs copy_from_user -#define memcpy_tofs copy_to_user -#define put_fs_long put_user -#define vremap ioremap +#define cy_put_user put_user -static unsigned long get_fs_long(unsigned long *addr) +static unsigned long cy_get_user(unsigned long *addr) { unsigned long result = 0; int error = get_user (result, addr); if (error) - printk ("cyclades: get_fs_long: error == %d\n", error); + printk ("cyclades: cy_get_user: error == %d\n", error); return result; } +#else + +#define __initfunc(__arginit) __arginit +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define cy_get_user get_fs_long +#define cy_put_user put_fs_long +#define ioremap vremap + #endif #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif + #define IS_CYC_Z(card) ((card).num_chips == 1) +#define Z_FPGA_CHECK(card) \ + ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0) + +#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->mail_box_0) || \ + Z_FPGA_CHECK(card)) && \ + (ZFIRM_ID==((struct FIRM_ID *) \ + ((card).base_addr+ID_ADDRESS))->signature)) + #define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) @@ -548,7 +573,7 @@ static struct cyclades_card *IRQ_cards[16]; /* * 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, + * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one @@ -698,10 +723,12 @@ 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 /* @@ -758,6 +785,7 @@ do_softint(void *private_) if (!tty) return; +#if (LINUX_VERSION_CODE >= 0x020125) if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); @@ -774,6 +802,24 @@ do_softint(void *private_) } wake_up_interruptible(&tty->write_wait); } +#else + if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } +#endif } /* do_softint */ @@ -1351,7 +1397,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ -/******** Start of block of Cyclom-Z specific code *********/ +/******** Start of block of Cyclades-Z specific code *********/ /***********************************************************/ @@ -1365,7 +1411,7 @@ cyz_fetch_msg( struct cyclades_card *cinfo, unsigned long loc_doorbell; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) @@ -1397,7 +1443,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, int index; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) @@ -1408,7 +1454,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, pci_doorbell = &((struct RUNTIME_9060 *) (cinfo->ctl_addr))->pci_doorbell; while( (*pci_doorbell & 0xff) != 0){ - if (index++ == 100){ + if (index++ == 1000){ return(-1); } udelay(50L); @@ -1421,6 +1467,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, } /* cyz_issue_cmd */ +#if 0 static int cyz_update_channel( struct cyclades_card *cinfo, u_long channel, u_char mode, u_char cmd) @@ -1430,7 +1477,7 @@ cyz_update_channel( struct cyclades_card *cinfo, struct ZFW_CTRL *zfw_ctrl; struct CH_CTRL *ch_ctrl; - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = @@ -1442,6 +1489,7 @@ cyz_update_channel( struct cyclades_card *cinfo, return cyz_issue_cmd(cinfo, channel, cmd, 0L); } /* cyz_update_channel */ +#endif static void @@ -1467,6 +1515,7 @@ cyz_poll(unsigned long arg) u_long channel; u_char cmd; u_long *param; + u_long hw_ver, fw_ver; cyz_timerlist.expires = jiffies + 100; @@ -1476,7 +1525,7 @@ cyz_poll(unsigned long arg) firm_id = (struct FIRM_ID *) (cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ continue; } @@ -1484,6 +1533,8 @@ cyz_poll(unsigned long arg) (struct ZFW_CTRL *) (cinfo->base_addr + firm_id->zfwctrl_addr); board_ctrl = &zfw_ctrl->board_ctrl; + fw_ver = board_ctrl->fw_version; + hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0; while( cyz_fetch_msg( cinfo, &channel, &cmd, ¶m) == 1){ char_count = 0; @@ -1514,7 +1565,9 @@ cyz_poll(unsigned long arg) break; case C_CM_MDCD: if (info->flags & ASYNC_CHECK_CD){ - if( ch_ctrl[channel].rs_status & C_RS_DCD){ + if (((hw_ver != 0 || fw_ver > 241) + ? ((u_long)param) + : ch_ctrl[channel].rs_status) & C_RS_DCD) { /* SP("Open Wakeup\n"); */ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); @@ -1709,7 +1762,7 @@ cyz_poll(unsigned long arg) } /* cyz_poll */ -/********** End of block of Cyclom-Z specific code *********/ +/********** End of block of Cyclades-Z specific code *********/ /***********************************************************/ @@ -1794,7 +1847,7 @@ startup(struct cyclades_port * info) base_addr = (unsigned char*) (cy_card[card].base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])){ return -ENODEV; } @@ -1951,7 +2004,7 @@ shutdown(struct cyclades_port * info) #endif firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])) { return; } @@ -2143,7 +2196,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, base_addr = (char *)(cinfo->base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return -EINVAL; } @@ -2237,11 +2290,17 @@ cy_open(struct tty_struct *tty, struct file * filp) will make the user pay attention. */ if (IS_CYC_Z(cy_card[info->card])) { - struct FIRM_ID *firm_id = - (struct FIRM_ID *) - (cy_card[info->card].base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ - printk("Cyclom-Z firmware not yet loaded\n"); + if (!ISZLOADED(cy_card[info->card])) { + if (((ZE_V1 ==((struct RUNTIME_9060 *) + ((cy_card[info->card]).ctl_addr))->mail_box_0) && + Z_FPGA_CHECK(cy_card[info->card])) && + (ZFIRM_HLT==((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"); + } else { + printk("Cyclades-Z firmware not yet loaded\n"); + } return -ENODEV; } } @@ -2448,7 +2507,7 @@ cy_write(struct tty_struct * tty, int from_user, } if (from_user) { - memcpy_fromfs(tmp_buf, buf, c); + 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); @@ -2816,7 +2875,7 @@ set_line_char(struct cyclades_port * info) firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])) { return; } @@ -2956,7 +3015,7 @@ get_serial_info(struct cyclades_port * info, tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ @@ -2970,7 +3029,7 @@ set_serial_info(struct cyclades_port * info, if (!new_info) return -EFAULT; - memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); + copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { @@ -3051,7 +3110,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature == ZFIRM_ID){ + if (ISZLOADED(cy_card[card])) { zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + firm_id->zfwctrl_addr); @@ -3070,7 +3129,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) } } - put_fs_long(result,(unsigned long *) value); + cy_put_user(result,(unsigned long *) value); return 0; } /* get_modem_info */ @@ -3082,7 +3141,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, int card,chip,channel,index; unsigned char *base_addr; unsigned long flags; - unsigned int arg = get_fs_long((unsigned long *) value); + unsigned int arg = cy_get_user((unsigned long *) value); struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; struct BOARD_CTRL *board_ctrl; @@ -3180,7 +3239,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature == ZFIRM_ID){ + if (ISZLOADED(cy_card[card])) { zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + firm_id->zfwctrl_addr); @@ -3281,7 +3340,7 @@ static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); info->mon.int_count = 0; info->mon.char_count = 0; info->mon.char_max = 0; @@ -3335,7 +3394,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; - put_fs_long(tmp,value); + cy_put_user(tmp,value); } else { // Nothing to do! } @@ -3354,7 +3413,7 @@ set_default_threshold(struct cyclades_port * info, unsigned long value) static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_threshold,value); + cy_put_user(info->default_threshold,value); return 0; }/* get_default_threshold */ @@ -3401,7 +3460,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = base_addr[CyRTPR<<index]; - put_fs_long(tmp,value); + cy_put_user(tmp,value); } else { // Nothing to do! } @@ -3420,7 +3479,7 @@ set_default_timeout(struct cyclades_port * info, unsigned long value) static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_timeout,value); + cy_put_user(info->default_timeout,value); return 0; }/* get_default_timeout */ @@ -3530,7 +3589,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - put_fs_long(C_CLOCAL(tty) ? 1 : 0, + cy_put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); break; case TIOCSSOFTCAR: @@ -3541,7 +3600,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, break; } - arg = get_fs_long((unsigned long *) arg); + arg = cy_get_user((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -3967,8 +4026,8 @@ cy_detect_isa(void)) } /* probe for CD1400... */ -#if LINUX_VERSION_CODE >= 131328 - cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000); +#if (LINUX_VERSION_CODE >= 0x020100) + cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000); #endif cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0); if (cy_isa_nchan == 0) { @@ -3988,6 +4047,7 @@ cy_detect_isa(void)) printk("Cyclom-Y/ISA found at 0x%x ", (unsigned int) cy_isa_address); printk("but no more channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(nboard); } /* fill the next cy_card structure available */ @@ -3998,6 +4058,7 @@ cy_detect_isa(void)) printk("Cyclom-Y/ISA found at 0x%x ", (unsigned int) cy_isa_address); printk("but no more cards can be used .\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(nboard); } @@ -4051,6 +4112,8 @@ cy_detect_pci(void)) unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; unsigned short i,j,cy_pci_nchan; unsigned short device_id,dev_index = 0,board_index = 0; + unsigned long mailbox; + unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; if(pcibios_present() == 0) { /* PCI bus not present */ return(0); @@ -4070,6 +4133,9 @@ cy_detect_pci(void)) } } + if (device_id == 0) + break; + /* read PCI configuration area */ pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_INTERRUPT_LINE, &cy_pci_irq); @@ -4081,9 +4147,7 @@ cy_detect_pci(void)) PCI_BASE_ADDRESS_2, &cy_pci_addr2); pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_REVISION_ID, &cyy_rev_id); - if (device_id == 0){ - break; - }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + 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, ", @@ -4096,11 +4160,11 @@ cy_detect_pci(void)) cy_pci_addr1 &= 0xfffffffc; cy_pci_addr2 &= 0xfffffff0; -#if LINUX_VERSION_CODE < 131328 +#if (LINUX_VERSION_CODE < 0x020100) if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ #endif cy_pci_addr2 = - (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin); + (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin); #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n", @@ -4112,12 +4176,14 @@ cy_detect_pci(void)) printk("Cyclom-Y PCI host card with "); printk("no Serial-Modules at 0x%x.\n", (unsigned int) cy_pci_addr2); + i--; continue; } if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { printk("Cyclom-Y/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(i); } /* fill the next cy_card structure available */ @@ -4128,6 +4194,7 @@ cy_detect_pci(void)) printk("Cyclom-Y/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); } @@ -4167,65 +4234,101 @@ cy_detect_pci(void)) cy_next_channel += cy_pci_nchan; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ /* print message */ - printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); - printk("Cyclom-Z/PCI not supported for low addresses\n"); + printk("Cyclades-Z/PCI not supported for low addresses\n"); break; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ #ifdef CY_PCI_DEBUG - printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); #endif - cy_pci_addr2 &= 0xfffffff0; - cy_pci_addr2 = (unsigned int) vremap( - cy_pci_addr2 & PAGE_MASK, - PAGE_ALIGN(CyPCI_Zwin)) - + (cy_pci_addr2 & (PAGE_SIZE-1)); cy_pci_addr0 &= 0xfffffff0; - cy_pci_addr0 = (unsigned int) vremap( + cy_pci_addr0 = (unsigned int) ioremap( cy_pci_addr0 & PAGE_MASK, PAGE_ALIGN(CyPCI_Zctl)) + (cy_pci_addr0 & (PAGE_SIZE-1)); + + mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; + cy_pci_addr2 &= 0xfffffff0; + if (mailbox == ZE_V1) { + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Ze_win)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + if (ZeIndex == NR_CARDS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } else { + Ze_addr0[ZeIndex] = cy_pci_addr0; + Ze_addr2[ZeIndex] = cy_pci_addr2; + ZeIndex++; + } + i--; + continue; + } else { + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + } + #ifdef CY_PCI_DEBUG - printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) + if (mailbox == ZO_V1) { + ((struct RUNTIME_9060 *)(cy_pci_addr0)) ->loc_addr_base = WIN_CREG; - PAUSE - printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n", - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) - ->loc_addr_base = WIN_RAM; + PAUSE + printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n", + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_RAM; + } else { + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); + } #endif /* The following clears the firmware id word. This ensures that the driver will not attempt to talk to the board until it has been properly initialized. */ PAUSE - *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; + if (mailbox == ZO_V1) + *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; - /* This must be a Cyclom-8Zo/PCI. The extendable + /* This must be a Cyclades-8Zo/PCI. The extendable version will have a different device_id and will be allocated its maximum number of ports. */ cy_pci_nchan = 8; + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + /* fill the next cy_card structure available */ for (j = 0 ; j < NR_CARDS ; j++) { if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Z/PCI found at 0x%x ", + printk("Cyclades-Z/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); } @@ -4235,9 +4338,9 @@ cy_detect_pci(void)) SA_INTERRUPT,"cyclomZ",NULL)) { printk("Could not allocate IRQ%d ", - (unsigned int) cy_pci_addr2); - printk("for Cyclom-Z/PCI at 0x%x.\n", cy_pci_irq); + printk("for Cyclades-Z/PCI at 0x%x.\n", + (unsigned int) cy_pci_addr2); return(i); } } @@ -4254,12 +4357,12 @@ cy_detect_pci(void)) /* print message */ /* don't report IRQ if board is no IRQ */ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { - printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", j+1,cy_pci_addr2, (cy_pci_addr2 + CyPCI_Zwin - 1), (int)cy_pci_irq); }else{ - printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ", + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", j+1,cy_pci_addr2, (cy_pci_addr2 + CyPCI_Zwin - 1)); } @@ -4268,6 +4371,93 @@ cy_detect_pci(void)) cy_next_channel += cy_pci_nchan; } } + + for (; ZeIndex != 0 && i < NR_CARDS; i++) { + cy_pci_addr0 = Ze_addr0[0]; + cy_pci_addr2 = Ze_addr2[0]; + for (j = 0 ; j < ZeIndex-1 ; j++) { + Ze_addr0[j] = Ze_addr0[j+1]; + Ze_addr2[j] = Ze_addr2[j+1]; + } + ZeIndex--; + mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + /* This must be the new Cyclades-Ze/PCI. */ + cy_pci_nchan = ZE_V1_NPORTS; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-Z/PCI at 0x%x.\n", + (unsigned int) cy_pci_addr2); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + 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; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Ze_win - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Ze_win - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + if (ZeIndex != 0) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) Ze_addr2[0]); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } return(i); #else return(0); @@ -4319,6 +4509,8 @@ cy_init(void)) struct cyclades_card *cinfo; int number_z_boards = 0; int board,port,i; + unsigned long mailbox; + int nports; show_version(); @@ -4416,10 +4608,13 @@ 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){ /* Cyclom-8Zo/PCI */ + if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */ number_z_boards++; + mailbox = ((struct RUNTIME_9060 *) + cy_card[board].ctl_addr)->mail_box_0; + nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; for (port = cinfo->first_line ; - port < cinfo->first_line + 8; + port < cinfo->first_line + nports; port++) { info = &cy_port[port]; @@ -4523,7 +4718,7 @@ cy_init(void)) cyz_timerlist.expires = jiffies + 1; add_timer(&cyz_timerlist); #ifdef CY_PCI_DEBUG - printk("Cyclom-Z polling initialized\n"); + printk("Cyclades-Z polling initialized\n"); #endif } diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c new file mode 100644 index 000000000..e85d36a98 --- /dev/null +++ b/drivers/char/joystick.c @@ -0,0 +1,376 @@ +/* + + linux/drivers/char/joystick.c + Copyright (C) 1992, 1993 Arthur C. Smith + Joystick driver for Linux running on an IBM compatible computer. + +VERSION INFO: +01/08/93 ACS 0.1: Works but needs multi-joystick support +01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1) + Added delay between measuring joystick axis + Added scaling ioctl +02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel + panics 8-) +02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read. + After looking at a schematic of a joystick card + it became apparent that any write to the joystick + port started ALL the joystick one shots. If the + one that we are reading is short enough and the + first one to be read, the second one will return + bad data if it's one shot has not expired when + the joystick port is written for the second time. + Thus solves the mystery delay problem in 0.2! +05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added + joystick support to the make config options, + updated the driver to return the buttons as + positive logic, and read both axis at once + (thanks Eyal!), and added some new ioctls. +02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15 + kernel (and hopefully 1.0). Also did some + cleanup: indented code, fixed some typos, wrote + man page, etc... +05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code +04/03/96 Matt Rhoten 0.8: many minor changes: + new read loop from Hal Maney <maney@norden.com> + cleaned up #includes to allow #include of + joystick.h with gcc -Wall and from g++ + made js_init fail if it finds zero joysticks + general source/comment cleanup + use of MOD_(INC|DEC)_USE_COUNT + changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE> + to compile correctly under 1.3 in kernel or as module +06/30/97 Alan Cox 0.9: Ported to 2.1.x + Reformatted to resemble Linux coding standard + Removed semaphore bug (we can dump the lot I think) + Fixed xntp timer adjust during joystick timer0 bug + Changed variable names to lower case. Kept binary + compatibility. + Better ioctl names. Kept binary compatibility. + Removed 'save_busy'. Just set busy to 1. +*/ + +#include <linux/module.h> +#include <linux/joystick.h> +#include <linux/mm.h> +#include <linux/major.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +static struct js_config js_data[JS_MAX]; /* misc data */ +static int js_exist; /* which joysticks' axis exist? */ +static int js_read_semaphore; /* to prevent two processes from trying + to read different joysticks at the + same time */ + +/* + * get_timer0(): + * returns the current value of timer 0. This is a 16 bit counter that starts + * at LATCH and counts down to 0 + */ + +extern inline int get_timer0(void) +{ + unsigned long flags; + int t0, t1; + save_flags(flags); + cli(); + outb (0, PIT_MODE); + t0 = (int) inb (PIT_COUNTER_0); + t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0; + restore_flags(flags); + return (t1); +} + +/* + * find_axes(): + * + * returns which axes are hooked up, in a bitfield. 2^n is set if + * axis n is hooked up, for 0 <= n < 4. + * + * REVIEW: should update this to handle eight-axis (four-stick) game port + * cards. anyone have one of these to test on? mattrh 3/23/96 + */ + +extern inline int find_axes(void) +{ + int j; + outb (0xff, JS_PORT); /* trigger oneshots */ + /* and see what happens */ + for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--); + /* do nothing; wait for the timeout */ + js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */ + js_exist = (~js_exist) & 0x0f; +/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/ + return js_exist; +} + +static int js_ioctl (struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned int minor = MINOR (inode->i_rdev); + if (minor >= JS_MAX) + return -ENODEV; + + if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/ + return -ENODEV; + switch (cmd) + { + + case JSIOCSCAL: /*from struct *arg to js_data[minor]*/ + if(copy_from_user(&js_data[minor].js_corr, + (void *)arg, sizeof(struct js_status))) + return -EFAULT; + break; + case JSIOCGCAL: /*to struct *arg from js_data[minor]*/ + if(copy_to_user((void *) arg, &js_data[minor].js_corr, + sizeof(struct js_status))) + return -EFAULT; + break; + case JSIOCSTIMEOUT: + if(copy_from_user(&js_data[minor].js_timeout, + (void *)arg, sizeof(js_data[0].js_timeout))) + return -EFAULT; + break; + case JSIOCGTIMEOUT: + if(copy_to_user((void *)arg, &js_data[minor].js_timeout, + sizeof(js_data[0].js_timeout))) + return -EFAULT; + break; + case JSIOCSTIMELIMIT: + if(copy_from_user(&js_data[minor].js_timelimit, + (void *)arg, sizeof(js_data[0].js_timelimit))) + return -EFAULT; + break; + case JSIOCGTIMELIMIT: + if(copy_to_user((void *)arg, &js_data[minor].js_timelimit, + sizeof(js_data[0].js_timelimit))) + return -EFAULT; + break; + case JSIOCGCONFIG: + if(copy_to_user((void *)arg, &js_data[minor], + sizeof(struct js_config))) + return -EFAULT; + break; + case JSIOCSCONFIG: + if(copy_from_user(&js_data[minor], (void *)arg, + sizeof(struct js_config))) + return -EFAULT; + /* Must be busy to do this ioctl! */ + js_data[minor].busy = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * js_open(): + * device open routine. increments module usage count, initializes + * data for that joystick. + * + * returns: 0 or + * -ENODEV: asked for joystick other than #0 or #1 + * -ENODEV: asked for joystick on axis where there is none + * -EBUSY: attempt to open joystick already open + */ + +static int js_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR (inode->i_rdev); + int j; + + if (minor >= JS_MAX) + return -ENODEV; /*check for joysticks*/ + + for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--); + cli(); /*block js_read while js_exist is being modified*/ + /*js minor exists?*/ + if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) { + js_exist = (~js_exist) & 0x0f; + sti(); + return -ENODEV; + } + js_exist = (~js_exist) & 0x0f; + sti(); + + if (js_data[minor].busy) + return -EBUSY; + js_data[minor].busy = JS_TRUE; + js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/ + js_data[minor].js_corr.y = JS_DEF_CORR; + js_data[minor].js_timeout = JS_DEF_TIMEOUT; + js_data[minor].js_timelimit = JS_DEF_TIMELIMIT; + js_data[minor].js_expiretime = jiffies; + + MOD_INC_USE_COUNT; + return 0; +} + +static int js_release (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR (inode->i_rdev); + inode->i_atime = CURRENT_TIME; + js_data[minor].busy = JS_FALSE; + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_read() reads the buttons x, and y axis from both joysticks if a + * given interval has expired since the last read or is equal to + * -1l. The buttons are in port 0x201 in the high nibble. The axis are + * read by writing to 0x201 and then measuring the time it takes the + * one shots to clear. + */ + +static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count) +{ + int j, chk, jsmask; + int t0, t_x0, t_y0, t_x1, t_y1; + unsigned int minor, minor2; + int buttons; + + if (count != JS_RETURN) + return -EINVAL; + minor = MINOR (inode->i_rdev); + inode->i_atime = CURRENT_TIME; + if (jiffies >= js_data[minor].js_expiretime) + { + minor2 = minor << 1; + j = js_data[minor].js_timeout; + for (; (js_exist & inb (JS_PORT)) && j; j--); + if (j == 0) + return -ENODEV; /*no joystick here*/ + /*Make sure no other proc is using port*/ + + cli(); + js_read_semaphore++; + sti(); + + buttons = ~(inb (JS_PORT) >> 4); + js_data[0].js_save.buttons = buttons & 0x03; + js_data[1].js_save.buttons = (buttons >> 2) & 0x03; + j = js_data[minor].js_timeout; + jsmask = 0; + + cli(); /*no interrupts!*/ + outb (0xff, JS_PORT); /*trigger one-shots*/ + /*get init timestamp*/ + t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 (); + /*wait for an axis' bit to clear or timeout*/ + while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask)) + { + if (!(chk & JS_X_0)) { + t_x0 = get_timer0(); + jsmask |= JS_X_0; + } + if (!(chk & JS_Y_0)) { + t_y0 = get_timer0(); + jsmask |= JS_Y_0; + } + if (!(chk & JS_X_1)) { + t_x1 = get_timer0(); + jsmask |= JS_X_1; + } + if (!(chk & JS_Y_1)) { + t_y1 = get_timer0(); + jsmask |= JS_Y_1; + } + } + sti(); /* allow interrupts */ + + js_read_semaphore = 0; /* allow other reads to progress */ + if (j == 0) + return -ENODEV; /*read timed out*/ + js_data[0].js_expiretime = jiffies + + js_data[0].js_timelimit; /*update data*/ + js_data[1].js_expiretime = jiffies + + js_data[1].js_timelimit; + js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >> + js_data[0].js_corr.x; + js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >> + js_data[0].js_corr.y; + js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >> + js_data[1].js_corr.x; + js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >> + js_data[1].js_corr.y; + } + + if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN)) + return -EFAULT; + return JS_RETURN; +} + + +static struct file_operations js_fops = +{ + NULL, /* js_lseek*/ + js_read, /* js_read */ + NULL, /* js_write*/ + NULL, /* js_readaddr*/ + NULL, /* js_select */ + js_ioctl, /* js_ioctl*/ + NULL, /* js_mmap */ + js_open, /* js_open*/ + js_release, /* js_release*/ + NULL /* js_fsync */ +}; + +#ifdef MODULE + +#define joystick_init init_module + +void cleanup_module (void) +{ + if (unregister_chrdev (JOYSTICK_MAJOR, "joystick")) + printk ("joystick: cleanup_module failed\n"); + release_region(JS_PORT, 1); +} + +#endif /* MODULE */ + +int joystick_init(void) +{ + int js_num; + int js_count; + + if (check_region(JS_PORT, 1)) { + printk("js_init: port already in use\n"); + return -EBUSY; + } + + js_num = find_axes(); + js_count = !!(js_num & 0x3) + !!(js_num & 0xC); + + + if (js_count == 0) + { + printk("No joysticks found.\n"); + return -ENODEV; + /* if the user boots the machine, which runs insmod, and THEN + decides to hook up the joystick, well, then we do the wrong + thing. But it's a good idea to avoid giving out a false sense + of security by letting the module load otherwise. */ + } + + if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) { + printk ("Unable to get major=%d for joystick\n", + JOYSTICK_MAJOR); + return -EBUSY; + } + request_region(JS_PORT, 1, "joystick"); + + for (js_num = 0; js_num < JS_MAX; js_num++) + js_data[js_num].busy = JS_FALSE; + js_read_semaphore = 0; + + printk (KERN_INFO "Found %d joystick%c.\n", + js_count, + (js_num == 1) ? ' ' : 's'); + return 0; +} + diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 5eb52752d..9f9e5a992 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -724,7 +724,11 @@ __initfunc(int lp_init(void)) return 0; printk(KERN_INFO "lp: driver loaded but no devices found\n"); +#ifdef MODULE + return 0; +#else return 1; +#endif } #ifdef MODULE diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 499132bf8..063503595 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -20,6 +20,7 @@ #include <linux/mm.h> #include <linux/random.h> #include <linux/init.h> +#include <linux/joystick.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -134,8 +135,7 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str #endif if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -532,6 +532,13 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_SOUND soundcard_init(); #endif +#ifdef CONFIG_JOYSTICK + /* + * Some joysticks only appear when the soundcard they are + * connected too is confgured. Keep the sound/joystick ordering. + */ + joystick_init(); +#endif #if CONFIG_QIC02_TAPE qic02_tape_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 90ff2026f..6262792b6 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -217,6 +217,9 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_SUN_MOUSE sun_mouse_init(); #endif +#ifdef CONFIG_PC110_PAD + pc110pad_init(); +#endif /* * Only one watchdog can succeed. We probe the pcwatchdog first, * then the wdt cards and finally the software watchdog which always diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 8db4e1443..32dc3a51d 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -88,9 +88,17 @@ void n_tty_flush_buffer(struct tty_struct * tty) /* * Return number of characters buffered to be delivered to user + * */ int n_tty_chars_in_buffer(struct tty_struct *tty) { + if (tty->icanon) { + if (!tty->canon_data) return 0; + + return (tty->canon_head > tty->read_tail) ? + tty->canon_head - tty->read_tail : + tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + } return tty->read_cnt; } @@ -157,6 +165,72 @@ static int opost(unsigned char c, struct tty_struct *tty) return 0; } +/* + * opost_block --- to speed up block console writes, among other + * things. + */ +static int opost_block(struct tty_struct * tty, + const unsigned char * inbuf, unsigned int nr) +{ + char buf[80]; + int space; + int i; + char *cp; + + space = tty->driver.write_room(tty); + if (!space) + return 0; + if (nr > space) + nr = space; + if (nr > sizeof(buf)) + nr = sizeof(buf); + nr -= copy_from_user(buf, inbuf, nr); + if (!nr) + return 0; + + for (i = 0, cp = buf; i < nr; i++, cp++) { + switch (*cp) { + case '\n': + if (O_ONLRET(tty)) + tty->column = 0; + if (O_ONLCR(tty)) + goto break_out; + tty->canon_column = tty->column; + break; + case '\r': + if (O_ONOCR(tty) && tty->column == 0) + goto break_out; + if (O_OCRNL(tty)) { + *cp = '\n'; + if (O_ONLRET(tty)) + tty->canon_column = tty->column = 0; + break; + } + tty->canon_column = tty->column = 0; + break; + case '\t': + goto break_out; + case '\b': + if (tty->column > 0) + tty->column--; + break; + default: + if (O_OLCUC(tty)) + *cp = toupper(*cp); + if (!iscntrl(*cp)) + tty->column++; + break; + } + } +break_out: + if (tty->driver.flush_chars) + tty->driver.flush_chars(tty); + i = tty->driver.write(tty, 0, buf, i); + return i; +} + + + static inline void put_char(unsigned char c, struct tty_struct *tty) { tty->driver.put_char(tty, c); @@ -632,7 +706,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) return; tty->icanon = (L_ICANON(tty) != 0); - if (tty->flags & (1<<TTY_HW_COOK_IN)) { + if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; return; @@ -780,7 +854,7 @@ do_it_again: /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_inode->i_rdev != CONSOLE_DEV && + if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && current->tty == tty) { if (tty->pgrp <= 0) printk("read_chan: tty->pgrp <= 0!\n"); @@ -838,7 +912,7 @@ do_it_again: tty->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { - if (tty->flags & (1 << TTY_OTHER_CLOSED)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; break; } @@ -934,12 +1008,12 @@ static int write_chan(struct tty_struct * tty, struct file * file, const unsigned char * buf, unsigned int nr) { struct wait_queue wait = { current, NULL }; - int c; + int c, num; const unsigned char *b = buf; int retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) { + if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) { retval = tty_check_change(tty); if (retval) return retval; @@ -956,8 +1030,13 @@ static int write_chan(struct tty_struct * tty, struct file * file, retval = -EIO; break; } - if (O_OPOST(tty) && !(tty->flags & (1<<TTY_HW_COOK_OUT))) { + if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { while (nr > 0) { + num = opost_block(tty, b, nr); + b += num; + nr -= num; + if (nr == 0) + break; get_user(c, b); if (opost(c, tty) < 0) break; @@ -993,7 +1072,7 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; - if (tty->flags & (1 << TTY_OTHER_CLOSED)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c new file mode 100644 index 000000000..7a6b8cde0 --- /dev/null +++ b/drivers/char/pc110pad.c @@ -0,0 +1,690 @@ +/* + * Linux driver for the PC110 pad + * + * The pad provides triples of data. The first byte has + * 0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down + * The second byte is bits 0-6 X + * The third is bits 0-6 Y + * + * This is read internally and used to synthesize a stream of + * triples in the form expected from a PS/2 device. + * + * 0.0 1997-05-16 Alan Cox <alan@cymru.net> - Pad reader + * 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation + * 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture + * 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/busmouse.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/ptrace.h> +#include <linux/poll.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> + +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include "pc110pad.h" + + +static struct pc110pad_params default_params = { + PC110PAD_PS2, /* read mode */ + 50 MS, /* bounce interval */ + 200 MS, /* tap interval */ + 10, /* IRQ */ + 0x15E0, /* I/O port */ +}; + + +static struct pc110pad_params current_params; + + +/* driver/filesystem interface management */ +static struct wait_queue *queue; +static struct fasync_struct *asyncptr; +static int active=0; /* number of concurrent open()s */ + + +/* + * Utility to reset a timer to go off some time in the future. + */ + +static void set_timer_callback(struct timer_list *timer, int ticks) +{ + del_timer(timer); + timer->expires = jiffies+ticks; + add_timer(timer); +} + + +/* + * Take care of letting any waiting processes know that + * now would be a good time to do a read(). Called + * whenever a state transition occurs, real or synthetic. + */ + +static void wake_readers(void) +{ + wake_up_interruptible(&queue); + if(asyncptr) + kill_fasync(asyncptr, SIGIO); +} + + +/*****************************************************************************/ +/* + * Deal with the messy business of synthesizing button tap and drag + * events. + * + * Exports: + * notify_pad_up_down() + * Must be called whenever debounced pad up/down state changes. + * button_pending + * Flag is set whenever read_button() has new values + * to return. + * read_button() + * Obtains the current synthetic mouse button state. + */ + +/* + * These keep track of up/down transitions needed to generate the + * synthetic mouse button events. While recent_transition is set, + * up/down events cause transition_count to increment. tap_timer + * turns off the recent_transition flag and may cause some synthetic + * up/down mouse events to be created by incrementing synthesize_tap. + */ + +static int button_pending=0; +static int recent_transition=0; +static int transition_count=0; +static int synthesize_tap=0; +static void tap_timeout(unsigned long data); +static struct timer_list tap_timer = { NULL, NULL, 0, 0, tap_timeout }; + + +/* + * This callback goes off a short time after an up/down transition; + * before it goes off, transitions will be considered part of a + * single PS/2 event and counted in transition_count. Once the + * timeout occurs the recent_transition flag is cleared and + * any synthetic mouse up/down events are generated. + */ + +static void tap_timeout(unsigned long data) +{ + if(!recent_transition) + { + printk("pc110pad: tap_timeout but no recent transition!\n"); + } + if( transition_count==2 || transition_count==4 || transition_count==6 ) + { + synthesize_tap+=transition_count; + button_pending = 1; + wake_readers(); + } + recent_transition=0; +} + + +/* + * Called by the raw pad read routines when a (debounced) up/down + * transition is detected. + */ + +void notify_pad_up_down(void) +{ + if(recent_transition) + { + transition_count++; + } + else + { + transition_count=1; + recent_transition=1; + } + set_timer_callback(&tap_timer, current_params.tap_interval); + + /* changes to transition_count can cause reported button to change */ + button_pending = 1; + wake_readers(); +} + + +static void read_button(int *b) +{ + if(synthesize_tap) + { + *b=--synthesize_tap & 1; + } + else + { + *b=(!recent_transition && transition_count==3); /* drag */ + } + button_pending=(synthesize_tap>0); +} + + +/*****************************************************************************/ +/* + * Read pad absolute co-ordinates and debounced up/down state. + * + * Exports: + * pad_irq() + * Function to be called whenever the pad signals + * that it has new data available. + * read_raw_pad() + * Returns the most current pad state. + * xy_pending + * Flag is set whenever read_raw_pad() has new values + * to return. + * Imports: + * wake_readers() + * Called when movement occurs. + * notify_pad_up_down() + * Called when debounced up/down status changes. + */ + +/* + * These are up/down state and absolute co-ords read directly from pad + */ + +static int raw_data[3]; +static int raw_data_count=0; +static int raw_x=0, raw_y=0; /* most recent absolute co-ords read */ +static int raw_down=0; /* raw up/down state */ +static int debounced_down=0; /* up/down state after debounce processing */ +static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE; + /* set just after an up/down transition */ +static int xy_pending=0; /* set if new data have not yet been read */ + +/* + * Timer goes off a short while after an up/down transition and copies + * the value of raw_down to debounced_down. + */ + +static void bounce_timeout(unsigned long data); +static struct timer_list bounce_timer = { NULL, NULL, 0, 0, bounce_timeout }; + + +static void bounce_timeout(unsigned long data) +{ + /* + * No further up/down transitions happened within the + * bounce period, so treat this as a genuine transition. + */ + switch(bounce) + { + case NO_BOUNCE: + { + /* + * Strange; the timer callback should only go off if + * we were expecting to do bounce processing! + */ + printk("pc110pad, bounce_timeout: bounce flag not set!\n"); + break; + } + case JUST_GONE_UP: + { + /* + * The last up we spotted really was an up, so set + * debounced state the same as raw state. + */ + bounce=NO_BOUNCE; + if(debounced_down==raw_down) + { + printk("pc110pad, bounce_timeout: raw already debounced!\n"); + } + debounced_down=raw_down; + + notify_pad_up_down(); + break; + } + case JUST_GONE_DOWN: + { + /* + * We don't debounce down events, but we still time + * out soon after one occurs so we can avoid the (x,y) + * skittering that sometimes happens. + */ + bounce=NO_BOUNCE; + break; + } + } +} + + +/* + * Callback when pad's irq goes off; copies values in to raw_* globals; + * initiates debounce processing. + */ +static void pad_irq(int irq, void *ptr, struct pt_regs *regs) +{ + + /* Obtain byte from pad and prime for next byte */ + { + int value=inb_p(current_params.io); + int handshake=inb_p(current_params.io+2); + outb_p(handshake | 1, current_params.io+2); + outb_p(handshake &~1, current_params.io+2); + inb_p(0x64); + + raw_data[raw_data_count++]=value; + } + + if(raw_data_count==3) + { + int new_down=raw_data[0]&0x01; + int new_x=raw_data[1]; + int new_y=raw_data[2]; + if(raw_data[0]&0x10) new_x+=128; + if(raw_data[0]&0x80) new_x+=256; + if(raw_data[0]&0x08) new_y+=128; + + if( (raw_x!=new_x) || (raw_y!=new_y) ) + { + raw_x=new_x; + raw_y=new_y; + xy_pending=1; + } + + if(new_down != raw_down) + { + /* Down state has changed. raw_down always holds + * the most recently observed state. + */ + raw_down=new_down; + + /* Forget any earlier bounce processing */ + if(bounce) + { + del_timer(&bounce_timer); + bounce=NO_BOUNCE; + } + + if(new_down) + { + if(debounced_down) + { + /* pad gone down, but we were reporting + * it down anyway because we suspected + * (correctly) that the last up was just + * a bounce + */ + } + else + { + bounce=JUST_GONE_DOWN; + set_timer_callback(&bounce_timer, + current_params.bounce_interval); + /* start new stroke/tap */ + debounced_down=new_down; + notify_pad_up_down(); + } + } + else /* just gone up */ + { + if(recent_transition) + { + /* early bounces are probably part of + * a multi-tap gesture, so process + * immediately + */ + debounced_down=new_down; + notify_pad_up_down(); + } + else + { + /* don't trust it yet */ + bounce=JUST_GONE_UP; + set_timer_callback(&bounce_timer, + current_params.bounce_interval); + } + } + } + wake_readers(); + raw_data_count=0; + } +} + + +static void read_raw_pad(int *down, int *debounced, int *x, int *y) +{ + disable_irq(current_params.irq); + { + *down=raw_down; + *debounced=debounced_down; + *x=raw_x; + *y=raw_y; + xy_pending = 0; + } + enable_irq(current_params.irq); +} + +/*****************************************************************************/ +/* + * Filesystem interface + */ + +/* + * Read returns byte triples, so we need to keep track of + * how much of a triple has been read. This is shared across + * all processes which have this device open---not that anything + * will make much sense in that case. + */ +static int read_bytes[3]; +static int read_byte_count=0; + + + +static void sample_raw(int d[3]) +{ + d[0]=raw_data[0]; + d[1]=raw_data[1]; + d[2]=raw_data[2]; +} + + +static void sample_rare(int d[3]) +{ + int thisd, thisdd, thisx, thisy; + + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + + d[0]=(thisd?0x80:0) + | (thisx/256)<<4 + | (thisdd?0x08:0) + | (thisy/256) + ; + d[1]=thisx%256; + d[2]=thisy%256; +} + + +static void sample_debug(int d[3]) +{ + int thisd, thisdd, thisx, thisy; + int b; + cli(); + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce; + d[1]=(recent_transition?0x80:0)+transition_count; + read_button(&b); + d[2]=(synthesize_tap<<4) | (b?0x01:0); + sti(); +} + + +static void sample_ps2(int d[3]) +{ + static int lastx, lasty, lastd; + + int thisd, thisdd, thisx, thisy; + int dx, dy, b; + + /* + * Obtain the current mouse parameters and limit as appropriate for + * the return data format. Interrupts are only disabled while + * obtaining the parameters, NOT during the puts_fs_byte() calls, + * so paging in put_user() does not affect mouse tracking. + */ + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + read_button(&b); + + /* Now compare with previous readings. Note that we use the + * raw down flag rather than the debounced one. + */ + if( (thisd && !lastd) /* new stroke */ + || (bounce!=NO_BOUNCE) ) + { + dx=0; + dy=0; + } + else + { + dx = (thisx-lastx); + dy = -(thisy-lasty); + } + lastx=thisx; + lasty=thisy; + lastd=thisd; + +/* + d[0]= ((dy<0)?0x20:0) + | ((dx<0)?0x10:0) + | 0x08 + | (b? 0x01:0x00) + ; +*/ + d[0]= ((dy<0)?0x20:0) + | ((dx<0)?0x10:0) + | (b? 0x00:0x08) + ; + d[1]=dx; + d[2]=dy; +} + + + +static int fasync_pad(struct inode *inode, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(inode, filp, on, &asyncptr); + if (retval < 0) + return retval; + return 0; +} + + +/* + * close access to the pad + */ +static int close_pad(struct inode * inode, struct file * file) +{ + fasync_pad(inode, file, 0); + if (--active) + return; + outb(0x30, current_params.io+2); /* switch off digitiser */ + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * open access to the pad + */ +static int open_pad(struct inode * inode, struct file * file) +{ + if (active++) + return 0; + MOD_INC_USE_COUNT; + + cli(); + outb(0x30, current_params.io+2); /* switch off digitiser */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + outb(0x38, current_params.io+2); /* switch on digitiser */ + current_params = default_params; + raw_data_count=0; /* re-sync input byte counter */ + read_byte_count=0; /* re-sync output byte counter */ + button_pending=0; + recent_transition=0; + transition_count=0; + synthesize_tap=0; + del_timer(&bounce_timer); + del_timer(&tap_timer); + sti(); + + return 0; +} + + +/* + * writes are disallowed + */ +static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count) +{ + return -EINVAL; +} + + +void new_sample(int d[3]) +{ + switch(current_params.mode) + { + case PC110PAD_RAW: sample_raw(d); break; + case PC110PAD_RARE: sample_rare(d); break; + case PC110PAD_DEBUG: sample_debug(d); break; + case PC110PAD_PS2: sample_ps2(d); break; + } +} + + +/* + * Read pad data. Currently never blocks. + */ +static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count) +{ + int r; + + for(r=0; r<count; r++) + { + if(!read_byte_count) + new_sample(read_bytes); + if(put_user(read_bytes[read_byte_count], buffer+r)) + return -EFAULT; + read_byte_count = (read_byte_count+1)%3; + } + return r; +} + + +/* + * select for pad input + */ + +static unsigned int pad_poll(struct file *file, poll_table * wait) +{ + poll_wait(&queue, wait); + if(button_pending || xy_pending) + return POLLIN | POLLRDNORM; + return 0; +} + + +static int pad_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct pc110pad_params new; + + if (!inode) + return -EINVAL; + + switch (cmd) { + case PC110PADIOCGETP: + new = current_params; + if(copy_to_user((void *)arg, &new, sizeof(new))) + return -EFAULT; + return 0; + + case PC110PADIOCSETP: + if(copy_from_user(&new, (void *)arg, sizeof(new))) + return -EFAULT; + + if( (new.mode<PC110PAD_RAW) + || (new.mode>PC110PAD_PS2) + || (new.bounce_interval<0) + || (new.tap_interval<0) + ) + return -EINVAL; + + current_params.mode = new.mode; + current_params.bounce_interval = new.bounce_interval; + current_params.tap_interval = new.tap_interval; + return 0; + } + return -ENOIOCTLCMD; +} + + +static struct file_operations pad_fops = { + NULL, /* pad_seek */ + read_pad, + write_pad, + NULL, /* pad_readdir */ + pad_poll, + pad_ioctl, + NULL, /* pad_mmap */ + open_pad, + close_pad, + NULL, /* fsync */ + fasync_pad, + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + + +static struct miscdevice pc110_pad = { + PC110PAD_MINOR, "pc110 pad", &pad_fops +}; + + +static int pc110pad_init(void) +{ + current_params = default_params; + + if(request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0)) + { + printk("pc110pad: Unable to get IRQ.\n"); + return -EBUSY; + } + if(check_region(current_params.io, 4)) + { + printk("pc110pad: I/O area in use.\n"); + free_irq(current_params.irq,0); + return -EBUSY; + } + request_region(current_params.io, 4, "pc110pad"); + printk("PC110 digitizer pad at 0x%X, irq %d.\n", + current_params.io,current_params.irq); + misc_register(&pc110_pad); + outb(0x30, current_params.io+2); /* switch off digitiser */ + + return 0; +} + +#ifdef MODULE + +static void pc110pad_unload(void) +{ + outb(0x30, current_params.io+2); /* switch off digitiser */ + if(current_params.irq) + free_irq(current_params.irq, 0); + current_params.irq=0; + release_region(current_params.io, 4); + misc_deregister(&pc110_pad); +} + + + +int init_module(void) +{ + return pc110pad_init(); +} + +void cleanup_module(void) +{ + pc110pad_unload(); +} +#endif diff --git a/drivers/char/pc110pad.h b/drivers/char/pc110pad.h new file mode 100644 index 000000000..56d8d82e0 --- /dev/null +++ b/drivers/char/pc110pad.h @@ -0,0 +1,31 @@ +#ifndef _PC110PAD_H +#define _PC110PAD_H + +#include <linux/ioctl.h> + +enum pc110pad_mode { + PC110PAD_RAW, /* bytes as they come out of the hardware */ + PC110PAD_RARE, /* debounced up/down and absolute x,y */ + PC110PAD_DEBUG, /* up/down, debounced, transitions, button */ + PC110PAD_PS2, /* ps2 relative (default) */ +}; + + +struct pc110pad_params { + enum pc110pad_mode mode; + int bounce_interval; + int tap_interval; + int irq; + int io; +}; + +#define MS *HZ/1000 + +/* Appears as device major=10 (MISC), minor=PC110_PAD */ + +#define PC110PAD_IOCTL_TYPE 0x9a + +#define PC110PADIOCGETP _IOR(PC110PAD_IOCTL_TYPE, 0, struct pc110pad_params) +#define PC110PADIOCSETP _IOR(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params) + +#endif /* _PC110PAD_H */ diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index 006c60ddf..eea857b8a 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -53,6 +53,8 @@ #include <asm/uaccess.h> #include <asm/system.h> +#include "pc_keyb.h" + #ifdef CONFIG_SGI #include <asm/segment.h> #include <asm/sgihpc.h> @@ -80,17 +82,6 @@ #define AUX_DISABLE 0xa7 /* disable aux */ #define AUX_ENABLE 0xa8 /* enable aux */ -/* aux device commands */ -#define AUX_SET_RES 0xe8 /* set resolution */ -#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */ -#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */ -#define AUX_GET_SCALE 0xe9 /* get scaling factor */ -#define AUX_SET_STREAM 0xea /* set stream mode */ -#define AUX_SET_SAMPLE 0xf3 /* set sample rate */ -#define AUX_ENABLE_DEV 0xf4 /* enable aux device */ -#define AUX_DISABLE_DEV 0xf5 /* disable aux device */ -#define AUX_RESET 0xff /* reset aux device */ - #define MAX_RETRIES 60 /* some aux operations take long time*/ #if defined(__alpha__) && !defined(CONFIG_PCI) # define AUX_IRQ 9 /* Jensen is odd indeed */ @@ -212,7 +203,16 @@ static void aux_write_dev(int val) /* * Write to device & handle returned ack */ -#if defined INITIALIZE_DEVICE + +#ifdef INITIALIZE_DEVICE +__initfunc(static void aux_write_dev_nosleep(int val)) +{ + poll_aux_status_nosleep(); + ps2_outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); + poll_aux_status_nosleep(); + ps2_outb_p(val, KBD_DATA_REG); +} + static int aux_write_ack(int val) { int retries = 0; @@ -663,11 +663,11 @@ __initfunc(int psaux_init(void)) aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ poll_aux_status_nosleep(); #endif /* INITIALIZE_DEVICE */ - ps2_outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ + ps2_outb_p(KBD_CCMD_MOUSE_DISABLE, AUX_COMMAND); /* Disable Aux device */ + poll_aux_status_nosleep(); + ps2_outb_p(KBD_CCMD_WRITE_MODE, AUX_COMMAND); poll_aux_status_nosleep(); - ps2_outb_p(AUX_CMD_WRITE,AUX_COMMAND); - poll_aux_status_nosleep(); /* Disable interrupts */ - ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */ + ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); } return 0; } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 8f1015397..929bb2f85 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -26,20 +26,6 @@ struct pty_struct { #define PTY_MAGIC 0x5001 -#define PTY_BUF_SIZE PAGE_SIZE/2 - -/* - * tmp_buf is used as a temporary buffer by pty_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a pty 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 PTY's, since it significantly saves memory if - * large numbers of PTY's are open. - */ -static unsigned char *tmp_buf; -static struct semaphore tmp_buf_sem = MUTEX; - static struct tty_driver pty_driver, pty_slave_driver; static struct tty_driver old_pty_driver, old_pty_slave_driver; static int pty_refcount; @@ -104,37 +90,51 @@ static void pty_unthrottle(struct tty_struct * tty) set_bit(TTY_THROTTLED, &tty->flags); } +/* + * WSH 05/24/97: modified to + * (1) use space in tty->flip instead of a shared temp buffer + * The flip buffers aren't being used for a pty, so there's lots + * of space available. The buffer is protected by a per-pty + * semaphore that should almost never come under contention. + * (2) avoid redundant copying for cases where count >> receive_room + * N.B. Calls from user space may now return an error code instead of + * a count. + */ static int pty_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c=0, n, r; + int c=0, n; char *temp_buffer; if (!to || tty->stopped) return 0; - + if (from_user) { - down(&tmp_buf_sem); - temp_buffer = tmp_buf + - ((tty->driver.subtype-1) * PTY_BUF_SIZE); + down(&tty->flip.pty_sem); + temp_buffer = &tty->flip.char_buf[0]; while (count > 0) { - n = MIN(count, PTY_BUF_SIZE); + /* check space so we don't copy needlessly */ + n = MIN(count, to->ldisc.receive_room(to)); + if (!n) break; + + n = MIN(n, PTY_BUF_SIZE); n -= copy_from_user(temp_buffer, buf, n); if (!n) { if (!c) c = -EFAULT; break; } - r = to->ldisc.receive_room(to); - if (r <= 0) - break; - n = MIN(n, r); - to->ldisc.receive_buf(to, temp_buffer, 0, n); - buf += n; c+= n; + + /* check again in case the buffer filled up */ + n = MIN(n, to->ldisc.receive_room(to)); + if (!n) break; + buf += n; + c += n; count -= n; + to->ldisc.receive_buf(to, temp_buffer, 0, n); } - up(&tmp_buf_sem); + up(&tty->flip.pty_sem); } else { c = MIN(count, to->ldisc.receive_room(to)); to->ldisc.receive_buf(to, buf, 0, c); @@ -153,14 +153,42 @@ static int pty_write_room(struct tty_struct *tty) return to->ldisc.receive_room(to); } +/* + * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior + * The chars_in_buffer() value is used by the ldisc select() function + * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). + * The pty driver chars_in_buffer() Master/Slave must behave differently: + * + * The Master side needs to allow typed-ahead commands to accumulate + * while being canonicalized, so we report "our buffer" as empty until + * some threshold is reached, and then report the count. (Any count > + * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock + * the count returned must be 0 if no canonical data is available to be + * read. (The N_TTY ldisc.chars_in_buffer now knows this.) + * + * The Slave side passes all characters in raw mode to the Master side's + * buffer where they can be read immediately, so in this case we can + * return the true count in the buffer. + */ static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; + int count; if (!to || !to->ldisc.chars_in_buffer) return 0; - return to->ldisc.chars_in_buffer(to); + /* The ldisc must report 0 if no characters available to be read */ + count = to->ldisc.chars_in_buffer(to); + + if (tty->driver.subtype == PTY_TYPE_SLAVE) return count; + + /* Master side driver ... if the other side's read buffer is less than + * half full, return 0 to allow writers to proceed; otherwise return + * the count. This leaves a comfortable margin to avoid overflow, + * and still allows half a buffer's worth of typed-ahead commands. + */ + return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); } static void pty_flush_buffer(struct tty_struct *tty) @@ -194,17 +222,6 @@ static int pty_open(struct tty_struct *tty, struct file * filp) pty = pty_state + line; tty->driver_data = pty; - if (!tmp_buf) { - unsigned long page = __get_free_page(GFP_KERNEL); - if (!tmp_buf) { - retval = -ENOMEM; - if (!page) - goto out; - tmp_buf = (unsigned char *) page; - memset((void *) page, 0, PAGE_SIZE); - } else - free_page(page); - } retval = -EIO; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) goto out; @@ -288,8 +305,6 @@ __initfunc(int pty_init(void)) old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS; old_pty_slave_driver.other = &old_pty_driver; - tmp_buf = 0; - if (tty_register_driver(&pty_driver)) panic("Couldn't register pty driver"); if (tty_register_driver(&pty_slave_driver)) diff --git a/drivers/char/random.c b/drivers/char/random.c index 5f7619391..527ac8609 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,7 +1,7 @@ /* * random.c -- A strong random number generator * - * Version 1.02, last modified 15-Apr-97 + * Version 1.03, last modified 26-Apr-97 * * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved. * @@ -227,6 +227,7 @@ */ #include <linux/utsname.h> +#include <linux/config.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/string.h> @@ -1124,8 +1125,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long * update the access time. */ if (inode && count != 0) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + UPDATE_ATIME(inode); } return (count ? count : retval); @@ -1181,7 +1181,7 @@ random_write(struct inode * inode, struct file * file, } if ((ret > 0) && inode) { inode->i_mtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } return ret; } @@ -1335,11 +1335,15 @@ struct file_operations urandom_fops = { * starting point for each pair of TCP endpoints. This defeats * attacks which rely on guessing the initial TCP sequence number. * This algorithm was suggested by Steve Bellovin. + * + * Using a very strong hash was taking an appreciable amount of the total + * TCP connection establishment time, so this is a weaker hash, + * compensated for by changing the secret periodically. */ /* F, G and H are basic MD4 functions: selection, majority, parity */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#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 ) ) ) @@ -1357,9 +1361,9 @@ struct file_operations urandom_fops = { (a) = ROTL ((s), (a));} /* - * Basic cut-down MD4 transform + * Basic cut-down MD4 transform. Returns only 32 bits of result. */ -static void halfMD4Transform (__u32 buf[4], __u32 in[8]) +static __u32 halfMD4Transform (__u32 const buf[4], __u32 const in[8]) { __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; @@ -1376,77 +1380,141 @@ static void halfMD4Transform (__u32 buf[4], __u32 in[8]) /* Round 2 */ GG (a, b, c, d, in[ 0], 3); GG (d, a, b, c, in[ 4], 5); - GG (a, b, c, d, in[ 1], 9); - GG (d, a, b, c, in[ 5], 13); + 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 (a, b, c, d, in[ 3], 9); - GG (d, a, b, c, in[ 7], 13); + GG (c, d, a, b, in[ 3], 9); + GG (b, c, d, a, in[ 7], 13); /* Round 3 */ HH (a, b, c, d, in[ 0], 3); - HH (c, d, a, b, in[ 4], 9); - HH (a, b, c, d, in[ 2], 11); - HH (c, d, a, b, in[ 6], 15); + 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 (c, d, a, b, in[ 5], 9); - HH (a, b, c, d, in[ 3], 11); - HH (c, d, a, b, in[ 7], 15); + HH (d, a, b, c, in[ 5], 9); + HH (c, d, a, b, in[ 3], 11); + HH (b, c, d, a, in[ 7], 15); - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; + return buf[1] + b; /* "most hashed" word */ + /* Alternative: return sum of all words? */ } +/* This should not be decreased so low that ISNs wrap too fast. */ #define REKEY_INTERVAL 300 +#define HASH_BITS 24 __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport) { static __u32 rekey_time = 0; + static __u32 count = 0; static __u32 secret[12]; - static char count = 0; struct timeval tv; - __u32 tmp[12]; __u32 seq; /* - * Pick a random secret every REKEY_INTERVAL seconds + * Pick a random secret every REKEY_INTERVAL seconds. */ - do_gettimeofday(&tv); + do_gettimeofday(&tv); /* We need the usecs below... */ + if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { - get_random_bytes(&secret, sizeof(secret)); rekey_time = tv.tv_sec; - count++; + /* First three words are overwritten below. */ + get_random_bytes(&secret+3, sizeof(secret)-12); + count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS; } - memcpy(tmp, secret, sizeof(tmp)); /* - * Pick a unique starting offset for each - * TCP connection endpoints (saddr, daddr, sport, dport) + * Pick a unique starting offset for each TCP connection endpoints + * (saddr, daddr, sport, dport). + * Note that the words are placed into the first words to be + * mixed in with the halfMD4. This is because the starting + * vector is also a random secret (at secret+8), and further + * hashing fixed data into it isn't going to improve anything, + * so we should get started with the variable data. */ - tmp[8]=saddr; - tmp[9]=daddr; - tmp[10]=(sport << 16) + dport; - halfMD4Transform(tmp, tmp+4); - + secret[0]=saddr; + secret[1]=daddr; + secret[2]=(sport << 16) + dport; + + seq = (halfMD4Transform(secret+8, secret) & + ((1<<HASH_BITS)-1)) + (count << HASH_BITS); + /* * 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. + * Further reading shows this assumes 2Mb/s networks. + * For 10Mb/s ethernet, a 1MHz clock is appropriate. * That's funny, Linux has one built in! Use it! + * (Networks are faster now - should this be increased?) */ - seq = (tmp[1]&0xFFFFFF) + (tv.tv_usec+tv.tv_sec*1000000) + - (count << 24); + seq += tv.tv_usec + tv.tv_sec*1000000; #if 0 printk("init_seq(%lx, %lx, %d, %d) = %d\n", saddr, daddr, sport, dport, seq); #endif - return (seq); + return seq; +} + +#ifdef CONFIG_SYN_COOKIES +/* + * Secure SYN cookie computation. This is the algorithm worked out by + * 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; + * + */ +__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, + __u16 sport, __u16 dport, __u32 sseq, __u32 count) +{ + static int is_init = 0; + static __u32 secret[2][16]; + __u32 tmp[16]; + __u32 seq; + + /* + * Pick two random secret the first time we open a TCP connection. + */ + if (is_init == 0) { + get_random_bytes(&secret[0], sizeof(secret[0])); + get_random_bytes(&secret[1], sizeof(secret[1])); + is_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. + */ + + 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); + + /* Zap lower 3 bits to leave room for the MSS representation */ + return (seq & 0xfffff8); } +#endif + #ifdef RANDOM_BENCHMARK /* diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index a8614999d..5f03f8887 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -152,7 +152,7 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, unsigned long count) { struct wait_queue wait = { current, NULL }; - int retval; + int retval = 0; if (count < sizeof(unsigned long)) return -EINVAL; @@ -180,7 +180,9 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, data = rtc_irq_data; rtc_irq_data = 0; restore_flags(flags); - retval = put_user(data, (unsigned long *)buf)) ?: sizeof(unsigned long); + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); } current->state = TASK_RUNNING; @@ -262,7 +264,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, * "don't care" or "match all". Only the tm_hour, * tm_min and tm_sec are used. */ - int retval; unsigned char hrs, min, sec; struct rtc_time alm_tm; @@ -305,7 +306,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } case RTC_SET_TIME: /* Set the RTC */ { - int retval; struct rtc_time rtc_tm; unsigned char mon, day, hrs, min, sec, leap_yr; unsigned char save_control, save_freq_select; @@ -418,7 +418,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: return -EINVAL; } - return copy_to_user(arg, &wtime, sizeof wtime) ? -EFAULT : 0; + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; } /* diff --git a/drivers/char/serial.c b/drivers/char/serial.c index e2a93a0c3..ca561c4e9 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1421,6 +1421,9 @@ static void shutdown(struct async_struct * info) info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; #endif + /* disable break condition */ + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); @@ -2089,6 +2092,30 @@ static void send_break( struct async_struct * info, int duration) } /* + * This routine sets the break condition on the serial port. + */ +static void begin_break(struct async_struct * info) +{ + if (!info->port) + return; + cli(); + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); + sti(); +} + +/* + * This routine clears the break condition on the serial port. + */ +static void end_break(struct async_struct * info) +{ + if (!info->port) + return; + cli(); + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + sti(); +} + +/* * This routine returns a bitfield of "wild interrupts". Basically, * any unclaimed interrupts which is flapping around. */ @@ -2292,6 +2319,19 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if (current->signal & ~current->blocked) return -EINTR; return 0; + case TIOCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + begin_break(info); + return 0; + case TIOCCBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + end_break(info); + return 0; case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); case TIOCSSOFTCAR: diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index e2044c086..1e0e1a2a0 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $ + * $Id: sysrq.c,v 1.1 1997/06/17 13:24:07 ralf Exp $ * * Linux Magic System Request Key Hacks * @@ -112,7 +112,7 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, show_mem(); break; case 2 ... 11: /* 0-9 -- set console logging level */ - key -= 2; + key--; if (key == 10) key = 0; orig_log_level = key; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index c08e44a27..7a31e162d 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -45,9 +45,12 @@ * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 * - * Move console and virtual terminal code to more apropriate files, + * Move console and virtual terminal code to more appropriate files, * implement CONFIG_VT and generalize console device interface. * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 + * + * Rewrote init_dev and release_dev to eliminate races. + * -- Bill Hawes <whawes@star.net>, June 97 */ #include <linux/config.h> @@ -90,8 +93,8 @@ #undef TTY_DEBUG_HANGUP -#define TTY_PARANOIA_CHECK -#define CHECK_TTY_COUNT +#define TTY_PARANOIA_CHECK 1 +#define CHECK_TTY_COUNT 1 struct termios tty_std_termios; /* for the benefit of tty drivers */ struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */ @@ -370,13 +373,15 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) for (filp = inuse_filps; filp; filp = filp->f_next) { if (filp->private_data != tty) continue; - if (!filp->f_inode) + if (!filp->f_dentry) + continue; + if (!filp->f_dentry->d_inode) continue; - if (filp->f_inode->i_rdev == CONSOLE_DEV) + if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV) continue; if (filp->f_op != &tty_fops) continue; - tty_fasync(filp->f_inode, filp, 0); + tty_fasync(filp->f_dentry->d_inode, filp, 0); filp->f_op = fops; } @@ -384,7 +389,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); @@ -533,7 +538,7 @@ void start_tty(struct tty_struct *tty) } if (tty->driver.start) (tty->driver.start)(tty); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); @@ -548,7 +553,7 @@ static long tty_read(struct inode * inode, struct file * file, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_read")) return -EIO; - if (!tty || (tty->flags & (1 << TTY_IO_ERROR))) + if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; /* This check not only needs to be done before reading, but also @@ -630,7 +635,7 @@ static long tty_write(struct inode * inode, struct file * file, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_write")) return -EIO; - if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR))) + if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; #if 0 if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) && @@ -651,18 +656,31 @@ static long tty_write(struct inode * inode, struct file * file, (unsigned int)count); } +/* Semaphore to protect creating and releasing a tty */ +static struct semaphore tty_sem = MUTEX; +static void down_tty_sem(int index) +{ + down(&tty_sem); +} +static void up_tty_sem(int index) +{ + up(&tty_sem); +} +static void release_mem(struct tty_struct *tty, int idx); + /* - * This is so ripe with races that you should *really* not touch this - * unless you know exactly what you are doing. All the changes have to be - * made atomically, or there may be incorrect pointers all over the place. + * WSH 06/09/97: Rewritten to remove races and properly clean up after a + * failed open. The new code protects the open with a semaphore, so it's + * really quite straightforward. The semaphore locking can probably be + * relaxed for the (most common) case of reopening a tty. */ static int init_dev(kdev_t device, struct tty_struct **ret_tty) { - struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc; + struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; struct tty_driver *driver; - int retval; + int retval=0; int idx; driver = get_tty_driver(device); @@ -670,189 +688,251 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) return -ENODEV; idx = MINOR(device) - driver->minor_start; - tty = o_tty = NULL; + tty = driver->table[idx]; + + /* + * Check whether we need to acquire the tty semaphore to avoid + * race conditions. For now, play it safe. + */ + down_tty_sem(idx); + + /* check whether we're reopening an existing tty */ + if(tty) goto fast_track; + + /* + * First time open is complex, especially for PTY devices. + * This code guarantees that either everything succeeds and the + * TTY is ready for operation, or else the table slots are vacated + * and the allocated memory released. (Except that the termios + * and locked termios may be retained.) + */ + + o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; - o_tty_loc = NULL; - o_tp_loc = o_ltp_loc = NULL; - tty_loc = &driver->table[idx]; - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; + tty = (struct tty_struct*) get_free_page(GFP_KERNEL); + if(!tty) + goto fail_no_mem; + initialize_tty_struct(tty); + tty->device = device; + tty->driver = *driver; -repeat: - retval = -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER && - *tty_loc && (*tty_loc)->count) - goto end_init; - retval = -ENOMEM; - if (!*tty_loc && !tty) { - if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) - goto end_init; - initialize_tty_struct(tty); - tty->device = device; - tty->driver = *driver; - goto repeat; - } - if (!*tp_loc && !tp) { + tp_loc = &driver->termios[idx]; + if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) - goto end_init; + goto free_mem_out; *tp = driver->init_termios; - goto repeat; } - if (!*ltp_loc && !ltp) { + + ltp_loc = &driver->termios_locked[idx]; + if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) - goto end_init; + goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); - goto repeat; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty_loc = &driver->other->table[idx]; - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; - if (!*o_tty_loc && !o_tty) { - kdev_t o_device; - - o_tty = (struct tty_struct *) - get_free_page(GFP_KERNEL); - if (!o_tty) - goto end_init; - o_device = MKDEV(driver->other->major, - driver->other->minor_start + idx); - initialize_tty_struct(o_tty); - o_tty->device = o_device; - o_tty->driver = *driver->other; - goto repeat; - } - if (!*o_tp_loc && !o_tp) { + if (driver->type == TTY_DRIVER_TYPE_PTY) { + o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + if (!o_tty) + goto free_mem_out; + initialize_tty_struct(o_tty); + o_tty->device = (kdev_t) MKDEV(driver->other->major, + driver->other->minor_start + idx); + o_tty->driver = *driver->other; + + o_tp_loc = &driver->other->termios[idx]; + if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) - goto end_init; + goto free_mem_out; *o_tp = driver->other->init_termios; - goto repeat; } - if (!*o_ltp_loc && !o_ltp) { + + o_ltp_loc = &driver->other->termios_locked[idx]; + if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) - goto end_init; + goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); - goto repeat; } - + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->table[idx] = o_tty; + if (!*o_tp_loc) + *o_tp_loc = o_tp; + if (!*o_ltp_loc) + *o_ltp_loc = o_ltp; + o_tty->termios = *o_tp_loc; + o_tty->termios_locked = *o_ltp_loc; + (*driver->other->refcount)++; + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; } - /* Now we have allocated all the structures: update all the pointers.. */ - if (!*tp_loc) { + + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_mem to clean up, so + * there's no need to null out the local pointers. + */ + driver->table[idx] = tty; + if (!*tp_loc) *tp_loc = tp; - tp = NULL; - } - if (!*ltp_loc) { + if (!*ltp_loc) *ltp_loc = ltp; - ltp = NULL; + tty->termios = *tp_loc; + tty->termios_locked = *ltp_loc; + (*driver->refcount)++; + tty->count++; + + /* + * Structures all installed ... call the ldisc open routines. + * If we fail here just call release_mem to clean up. No need + * to decrement the use counts, as release_mem doesn't care. + */ + if (tty->ldisc.open) { + retval = (tty->ldisc.open)(tty); + if (retval) + goto release_mem_out; } - if (!*tty_loc) { - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - *tty_loc = tty; - (*driver->refcount)++; - (*tty_loc)->count++; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); - if (retval < 0) { - (*tty_loc)->count--; - tty = NULL; - goto end_init; - } - } - tty = NULL; - } else { - if ((*tty_loc)->flags & (1 << TTY_CLOSING)) { - printk("Attempt to open closing tty %s.\n", - tty_name(*tty_loc)); - printk("Ack!!!! This should never happen!!\n"); - return -EINVAL; + if (o_tty && o_tty->ldisc.open) { + retval = (o_tty->ldisc.open)(o_tty); + if (retval) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + goto release_mem_out; } - (*tty_loc)->count++; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - if (!*o_tp_loc) { - *o_tp_loc = o_tp; - o_tp = NULL; - } - if (!*o_ltp_loc) { - *o_ltp_loc = o_ltp; - o_ltp = NULL; - } - if (!*o_tty_loc) { - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - *o_tty_loc = o_tty; - (*driver->other->refcount)++; - if (o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); - if (retval < 0) { - (*tty_loc)->count--; - o_tty = NULL; - goto end_init; - } - } - o_tty = NULL; + goto success; + + /* + * This fast open can be used if the tty is already open. + * No memory is allocated, and the only failures are from + * attempting to open a closing tty or attempting multiple + * opens on a pty master. + */ +fast_track: + if (test_bit(TTY_CLOSING, &tty->flags)) { + retval = -EIO; + goto end_init; + } + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) { + retval = -EIO; + goto end_init; } - (*tty_loc)->link = *o_tty_loc; - (*o_tty_loc)->link = *tty_loc; - if (driver->subtype == PTY_TYPE_MASTER) - (*o_tty_loc)->count++; + tty->link->count++; } - (*tty_loc)->driver = *driver; - *ret_tty = *tty_loc; - retval = 0; + tty->count++; + tty->driver = *driver; /* N.B. why do this every time?? */ + +success: + *ret_tty = tty; + + /* All paths come through here to release the semaphore */ end_init: - if (tty) - free_page((unsigned long) tty); - if (o_tty) - free_page((unsigned long) o_tty); - if (tp) - kfree_s(tp, sizeof(struct termios)); + up_tty_sem(idx); + return retval; + + /* Release locally allocated memory ... nothing placed in slots */ +free_mem_out: if (o_tp) kfree_s(o_tp, sizeof(struct termios)); + if (o_tty) + free_page((unsigned long) o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); - if (o_ltp) - kfree_s(o_ltp, sizeof(struct termios)); - return retval; + if (tp) + kfree_s(tp, sizeof(struct termios)); + free_page((unsigned long) tty); + +fail_no_mem: + retval = -ENOMEM; + goto end_init; + + /* call the tty release_mem routine to clean out this slot */ +release_mem_out: + printk("init_dev: ldisc open failed, clearing slot %d\n", idx); + release_mem(tty, idx); + goto end_init; +} + +/* + * Releases memory associated with a tty structure, and clears out the + * driver table slots. + */ +static void release_mem(struct tty_struct *tty, int idx) +{ + struct tty_struct *o_tty; + struct termios *tp; + + if ((o_tty = tty->link) != NULL) { + o_tty->driver.table[idx] = NULL; + if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = o_tty->driver.termios[idx]; + o_tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + o_tty->magic = 0; + (*o_tty->driver.refcount)--; + free_page((unsigned long) o_tty); + } + + tty->driver.table[idx] = NULL; + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = tty->driver.termios[idx]; + tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + tty->magic = 0; + (*tty->driver.refcount)--; + free_page((unsigned long) tty); } /* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. + * + * WSH 09/09/97: rewritten to avoid some nasty race conditions that could + * lead to double frees or releasing memory still in use. */ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; - struct termios *tp, *o_tp, *ltp, *o_ltp; - struct task_struct *p; + int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev")) + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev")) return; check_tty_count(tty, "release_dev"); - tty_fasync(filp->f_inode, filp, 0); - - tp = tty->termios; - ltp = tty->termios_locked; + tty_fasync(filp->f_dentry->d_inode, filp, 0); idx = MINOR(tty->device) - tty->driver.minor_start; + pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && + tty->driver.subtype == PTY_TYPE_MASTER); + o_tty = tty->link; + #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver.num) { printk("release_dev: bad idx when trying to free (%s)\n", @@ -864,15 +944,15 @@ static void release_dev(struct file * filp) idx, kdevname(tty->device)); return; } - if (tp != tty->driver.termios[idx]) { - printk("release_dev: driver.termios[%d] not termios for (" - "%s)\n", + if (tty->termios != tty->driver.termios[idx]) { + printk("release_dev: driver.termios[%d] not termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (ltp != tty->driver.termios_locked[idx]) { - printk("release_dev: driver.termios_locked[%d] not termios_locked for (" - "%s)\n", + if (tty->termios_locked != tty->driver.termios_locked[idx]) { + printk("release_dev: driver.termios_locked[%d] not " + "termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } @@ -883,10 +963,6 @@ static void release_dev(struct file * filp) tty->count); #endif - o_tty = tty->link; - o_tp = (o_tty) ? o_tty->termios : NULL; - o_ltp = (o_tty) ? o_tty->termios_locked : NULL; - #ifdef TTY_PARANOIA_CHECK if (tty->driver.other) { if (o_tty != tty->driver.other->table[idx]) { @@ -895,34 +971,90 @@ static void release_dev(struct file * filp) idx, kdevname(tty->device)); return; } - if (o_tp != tty->driver.other->termios[idx]) { - printk("release_dev: other->termios[%d] not o_termios for (" - "%s)\n", + if (o_tty->termios != tty->driver.other->termios[idx]) { + printk("release_dev: other->termios[%d] not o_termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_ltp != tty->driver.other->termios_locked[idx]) { - printk("release_dev: other->termios_locked[%d] not o_termios_locked for (" - "%s)\n", + if (o_tty->termios_locked != + tty->driver.other->termios_locked[idx]) { + printk("release_dev: other->termios_locked[%d] not " + "o_termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_tty->link != tty) { printk("release_dev: bad pty pointers\n"); return; } } #endif - + /* + * 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 + * wait queues and kick everyone out _before_ actually starting to + * close. This ensures that we won't block while releasing the tty + * structure. + * + * The test for the o_tty closing is necessary, since the master and + * slave sides may close in any order. If the slave side closes out + * first, its count will be one, since the master side holds an open. + * Thus this test wouldn't be triggered at the time the slave closes, + * so we do it now. + * + * Note that it's possible for the tty to be opened again while we're + * flushing out waiters. By recalculating the closing flags before + * each iteration we avoid any problems. + */ + while (1) { + tty_closing = tty->count <= 1; + o_tty_closing = o_tty && + (o_tty->count <= (pty_master ? 1 : 0)); + do_sleep = 0; + + if (tty_closing) { + if (waitqueue_active(&tty->read_wait)) { + wake_up(&tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&tty->write_wait)) { + wake_up(&tty->write_wait); + do_sleep++; + } + } + if (o_tty_closing) { + if (waitqueue_active(&o_tty->read_wait)) { + wake_up(&o_tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&o_tty->write_wait)) { + wake_up(&o_tty->write_wait); + do_sleep++; + } + } + if (!do_sleep) + break; + + printk("release_dev: %s: read/write wait queue active!\n", + tty_name(tty)); + schedule(); + } + + /* + * The closing flags are now consistent with the open counts on + * both sides, and we've completed the last operation that could + * block, so it's safe to proceed with closing. + */ + if (tty->driver.close) tty->driver.close(tty, filp); - if (tty->driver.type == TTY_DRIVER_TYPE_PTY && - tty->driver.subtype == PTY_TYPE_MASTER) { - if (--tty->link->count < 0) { + + if (pty_master) { + if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", - tty->count, tty_name(tty)); - tty->link->count = 0; + o_tty->count, tty_name(o_tty)); + o_tty->count = 0; } } if (--tty->count < 0) { @@ -930,60 +1062,50 @@ static void release_dev(struct file * filp) tty->count, tty_name(tty)); tty->count = 0; } - if (tty->count) - return; /* - * Sanity check --- if tty->count is zero, there shouldn't be - * any waiters on tty->read_wait or tty->write_wait. But just - * in case.... + * Perform some housekeeping before deciding whether to return. + * + * Set the TTY_CLOSING flag if this was the last open. In the + * case of a pty we may have to wait around for the other side + * to close, and TTY_CLOSING makes sure we can't be reopened. */ - while (1) { - if (waitqueue_active(&tty->read_wait)) { - printk("release_dev: %s: read_wait active?!?\n", - tty_name(tty)); - wake_up(&tty->read_wait); - } else if (waitqueue_active(&tty->write_wait)) { - printk("release_dev: %s: write_wait active?!?\n", - tty_name(tty)); - wake_up(&tty->write_wait); - } else - break; - schedule(); - } - + if(tty_closing) + set_bit(TTY_CLOSING, &tty->flags); + if(o_tty_closing) + set_bit(TTY_CLOSING, &o_tty->flags); + /* - * We're committed; at this point, we must not block! + * If _either_ side is closing, make sure there aren't any + * processes that still think tty or o_tty is their controlling + * tty. Also, clear redirect if it points to either tty. */ - if (o_tty) { - if (o_tty->count) - return; - tty->driver.other->table[idx] = NULL; - tty->driver.other->termios[idx] = NULL; - kfree_s(o_tp, sizeof(struct termios)); + if (tty_closing || o_tty_closing) { + struct task_struct *p; + + read_lock(&tasklist_lock); + for_each_task(p) { + if (p->tty == tty || (o_tty && p->tty == o_tty)) + p->tty = NULL; + } + read_unlock(&tasklist_lock); + + if (redirect == tty || (o_tty && redirect == o_tty)) + redirect = NULL; } + + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) + return; + filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure..."); #endif - tty->flags |= (1 << TTY_CLOSING); - - /* - * Make sure there aren't any processes that still think this - * tty is their controlling tty. - */ - read_lock(&tasklist_lock); - for_each_task(p) { - if (p->tty == tty) - p->tty = NULL; - if (o_tty && p->tty == o_tty) - p->tty = NULL; - } - read_unlock(&tasklist_lock); /* - * Shutdown the current line discipline, and reset it to - * N_TTY. + * Shutdown the current line discipline, and reset it to N_TTY. + * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -995,41 +1117,34 @@ static void release_dev(struct file * filp) o_tty->ldisc = ldiscs[N_TTY]; } - tty->driver.table[idx] = NULL; - if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { - tty->driver.termios[idx] = NULL; - kfree_s(tp, sizeof(struct termios)); - } - if (tty == redirect || o_tty == redirect) - redirect = NULL; /* * Make sure that the tty's task queue isn't activated. If it - * is, take it out of the linked list. + * is, take it out of the linked list. The tqueue isn't used by + * pty's, so skip the test for them. */ - spin_lock_irq(&tqueue_lock); - if (tty->flip.tqueue.sync) { - struct tq_struct *tq, *prev; - - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if (tq == &tty->flip.tqueue) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; + if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { + spin_lock_irq(&tqueue_lock); + if (tty->flip.tqueue.sync) { + struct tq_struct *tq, *prev; + + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if (tq == &tty->flip.tqueue) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } } } + spin_unlock_irq(&tqueue_lock); } - spin_unlock_irq(&tqueue_lock); - tty->magic = 0; - (*tty->driver.refcount)--; - free_page((unsigned long) tty); - filp->private_data = 0; - if (o_tty) { - o_tty->magic = 0; - (*o_tty->driver.refcount)--; - free_page((unsigned long) o_tty); - } + + /* + * The release_mem function takes care of the details of clearing + * the slots and preserving the termios structure. + */ + release_mem(tty, idx); } /* @@ -1077,6 +1192,7 @@ retry_open: retval = init_dev(device, &tty); if (retval) return retval; + /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */ filp->private_data = tty; check_tty_count(tty, "tty_open"); if (tty->driver.type == TTY_DRIVER_TYPE_PTY && @@ -1123,11 +1239,6 @@ retry_open: return 0; } -/* - * Note that releasing a pty master also releases the child, so - * we have to make the redirection checks after that and on both - * sides of a pty. - */ static int tty_release(struct inode * inode, struct file * filp) { release_dev(filp); @@ -1139,7 +1250,7 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait) struct tty_struct * tty; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "tty_poll")) + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll")) return 0; if (tty->ldisc.poll) @@ -1545,6 +1656,7 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->flip.flag_buf_ptr = tty->flip.flag_buf; tty->flip.tqueue.routine = flush_to_ldisc; tty->flip.tqueue.data = tty; + tty->flip.pty_sem = MUTEX; } /* diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 603250b81..c0d7440c3 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -237,6 +237,11 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long func_scr_writew((func_scr_readw(org) & 0xff00) | c, org); } } +#ifdef CONFIG_FB_CONSOLE + if (currcons == fg_console) + /* Horribly inefficient if count < screen size. */ + update_screen(currcons); +#endif written = buf - buf0; file->f_pos += written; RETURN( written ); |