home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/kernel/tty_io.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
- /*
- * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
- * or rs-channels. It also implements echoing, cooked mode etc.
- *
- * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
- *
- * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
- * tty_struct and tty_queue structures. Previously there was a array
- * of 256 tty_struct's which was statically allocated, and the
- * tty_queue structures were allocated at boot time. Both are now
- * dynamically allocated only when the tty is open.
- *
- * Also restructured routines so that there is more of a separation
- * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
- * the low-level tty routines (serial.c, pty.c, console.c). This
- * makes for cleaner and more compact code. -TYT, 9/17/92
- *
- * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
- * which can be dynamically activated and de-activated by the line
- * discipline handling modules (like SLIP).
- *
- * NOTE: pay no attention to the line discpline code (yet); its
- * interface is still subject to change in this version...
- * -- TYT, 1/31/92
- *
- * Added functionality to the OPOST tty handling. No delays, but all
- * other bits should be there.
- * -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
- *
- * Rewrote canonical mode and added more termios flags.
- * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
- */
-
- #include <linux/types.h>
- #include <linux/major.h>
- #include <linux/errno.h>
- #include <linux/signal.h>
- #include <linux/fcntl.h>
- #include <linux/sched.h>
- #include <linux/tty.h>
- #include <linux/timer.h>
- #include <linux/ctype.h>
- #include <linux/kd.h>
- #include <linux/mm.h>
- #include <linux/string.h>
- #include <linux/malloc.h>
-
- #include <asm/segment.h>
- #include <asm/system.h>
- #include <asm/bitops.h>
-
- #include "kbd_kern.h"
- #include "vt_kern.h"
-
- #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
-
- #define MAX_TTYS 256
-
- #undef __notyet__
-
- struct tty_struct *tty_table[MAX_TTYS];
- struct termios *tty_termios[MAX_TTYS]; /* We need to keep the termios state */
- /* around, even when a tty is closed */
- struct termios *termios_locked[MAX_TTYS]; /* Bitfield of locked termios flags*/
- struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
- int tty_check_write[MAX_TTYS/32]; /* bitfield for the bh handler */
-
- /*
- * fg_console is the current virtual console,
- * redirect is the pseudo-tty that console output
- * is redirected to if asked by TIOCCONS.
- */
- int fg_console = 0;
- struct tty_struct * redirect = NULL;
- struct wait_queue * keypress_wait = NULL;
-
- static void initialize_tty_struct(int line, struct tty_struct *tty);
- static void initialize_termios(int line, struct termios *tp);
-
- static int tty_read(struct inode *, struct file *, char *, int);
- static int tty_write(struct inode *, struct file *, char *, int);
- static int tty_select(struct inode *, struct file *, int, select_table *);
- static int tty_open(struct inode *, struct file *);
- static void tty_release(struct inode *, struct file *);
-
- int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
- {
- if (disc < N_TTY || disc >= NR_LDISCS)
- return -EINVAL;
-
- if (new_ldisc) {
- ldiscs[disc] = *new_ldisc;
- ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
- } else
- memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
-
- return 0;
- }
-
- void put_tty_queue(unsigned char c, struct tty_queue * queue)
- {
- int head;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- head = (queue->head + 1) & (TTY_BUF_SIZE-1);
- if (head != queue->tail) {
- queue->buf[queue->head] = c;
- queue->head = head;
- }
- restore_flags(flags);
- }
-
- int get_tty_queue(struct tty_queue * queue)
- {
- int result = -1;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if (queue->tail != queue->head) {
- result = queue->buf[queue->tail];
- INC(queue->tail);
- }
- restore_flags(flags);
- return result;
- }
-
- /*
- * This routine copies out a maximum of buflen characters from the
- * read_q; it is a convenience for line disciplines so they can grab a
- * large block of data without calling get_tty_char directly. It
- * returns the number of characters actually read. Return terminates
- * if an error character is read from the queue and the return value
- * is negated.
- */
- int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen)
- {
- int result = 0;
- unsigned char *p = bufp;
- unsigned long flags;
- int head, tail;
- int ok = 1;
-
- save_flags(flags);
- cli();
- tail = tty->read_q.tail;
- head = tty->read_q.head;
- while ((result < buflen) && (tail!=head) && ok) {
- ok = !clear_bit (tail, &tty->readq_flags);
- *p++ = tty->read_q.buf[tail++];
- tail &= TTY_BUF_SIZE-1;
- result++;
- }
- tty->read_q.tail = tail;
- restore_flags(flags);
- return (ok) ? result : -result;
- }
-
-
- void tty_write_flush(struct tty_struct * tty)
- {
- if (!tty->write || EMPTY(&tty->write_q))
- return;
- if (set_bit(TTY_WRITE_BUSY,&tty->flags))
- return;
- tty->write(tty);
- if (!clear_bit(TTY_WRITE_BUSY,&tty->flags))
- printk("tty_write_flush: bit already cleared\n");
- }
-
- void tty_read_flush(struct tty_struct * tty)
- {
- if (!tty || EMPTY(&tty->read_q))
- return;
- if (set_bit(TTY_READ_BUSY, &tty->flags)) {
- printk("tty_read_flush: already busy\n" );
- return;
- }
- ldiscs[tty->disc].handler(tty);
- if (!clear_bit(TTY_READ_BUSY, &tty->flags))
- printk("tty_read_flush: bit already cleared\n");
- }
-
- static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count)
- {
- return 0;
- }
-
- static int hung_up_tty_write(struct inode * inode, struct file * file, char * buf, int count)
- {
- return -EIO;
- }
-
- static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
- {
- return 1;
- }
-
- static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
- {
- return -EIO;
- }
-
- static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- {
- return -ESPIPE;
- }
-
- static struct file_operations tty_fops = {
- tty_lseek,
- tty_read,
- tty_write,
- NULL, /* tty_readdir */
- tty_select,
- tty_ioctl,
- NULL, /* tty_mmap */
- tty_open,
- tty_release
- };
-
- static struct file_operations hung_up_tty_fops = {
- tty_lseek,
- hung_up_tty_read,
- hung_up_tty_write,
- NULL, /* hung_up_tty_readdir */
- hung_up_tty_select,
- hung_up_tty_ioctl,
- NULL, /* hung_up_tty_mmap */
- NULL, /* hung_up_tty_open */
- tty_release /* hung_up_tty_release */
- };
-
- void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
- {
- int i;
- struct file * filp;
- struct task_struct *p;
- int dev;
-
- if (!tty)
- return;
- dev = MKDEV(TTY_MAJOR,tty->line);
- for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
- if (!filp->f_count)
- continue;
- if (filp->f_rdev != dev)
- continue;
- if (filp->f_inode && filp->f_inode->i_rdev == CONSOLE_DEV)
- continue;
- if (filp->f_op != &tty_fops)
- continue;
- filp->f_op = fops;
- }
- flush_input(tty);
- flush_output(tty);
- wake_up_interruptible(&tty->secondary.proc_list);
- if (tty->session > 0) {
- kill_sl(tty->session,SIGHUP,1);
- kill_sl(tty->session,SIGCONT,1);
- }
- tty->session = 0;
- tty->pgrp = -1;
- for_each_task(p) {
- if (p->tty == tty->line)
- p->tty = -1;
- }
- if (tty->hangup)
- (tty->hangup)(tty);
- }
-
- void tty_hangup(struct tty_struct * tty)
- {
- #ifdef TTY_DEBUG_HANGUP
- printk("tty%d hangup...\n", tty->line);
- #endif
- do_tty_hangup(tty, &hung_up_tty_fops);
- }
-
- void tty_vhangup(struct tty_struct * tty)
- {
- #ifdef TTY_DEBUG_HANGUP
- printk("tty%d vhangup...\n", tty->line);
- #endif
- do_tty_hangup(tty, &hung_up_tty_fops);
- }
-
- int tty_hung_up_p(struct file * filp)
- {
- return (filp->f_op == &hung_up_tty_fops);
- }
-
- /*
- * This function is typically called only by the session leader, when
- * it wants to dissassociate itself from its controlling tty.
- *
- * It performs the following functions:
- * (1) Sends a SIGHUP and SIGCONT to the foreground process group
- * (2) Clears the tty from being controlling the session
- * (3) Clears the controlling tty for all processes in the
- * session group.
- */
- void disassociate_ctty(int priv)
- {
- struct tty_struct *tty;
- struct task_struct *p;
-
- if (current->tty >= 0) {
- tty = tty_table[current->tty];
- if (tty) {
- if (tty->pgrp > 0) {
- kill_pg(tty->pgrp, SIGHUP, priv);
- kill_pg(tty->pgrp, SIGCONT, priv);
- }
- tty->session = 0;
- tty->pgrp = -1;
- } else
- printk("disassociate_ctty: ctty is NULL?!?");
- }
-
- for_each_task(p)
- if (p->session == current->session)
- p->tty = -1;
- }
-
- /*
- * Sometimes we want to wait until a particular VT has been activated. We
- * do it in a very simple manner. Everybody waits on a single queue and
- * get woken up at once. Those that are satisfied go on with their business,
- * while those not ready go back to sleep. Seems overkill to add a wait
- * to each vt just for this - usually this does nothing!
- */
- static struct wait_queue *vt_activate_queue = NULL;
-
- /*
- * Sleeps until a vt is activated, or the task is interrupted. Returns
- * 0 if activation, -1 if interrupted.
- */
- int vt_waitactive(void)
- {
- interruptible_sleep_on(&vt_activate_queue);
- return (current->signal & ~current->blocked) ? -1 : 0;
- }
-
- #define vt_wake_waitactive() wake_up(&vt_activate_queue)
-
- extern int kill_proc(int pid, int sig, int priv);
-
- /*
- * Performs the back end of a vt switch
- */
- void complete_change_console(unsigned int new_console)
- {
- unsigned char old_vc_mode;
-
- if (new_console == fg_console || new_console >= NR_CONSOLES)
- return;
-
- /*
- * If we're switching, we could be going from KD_GRAPHICS to
- * KD_TEXT mode or vice versa, which means we need to blank or
- * unblank the screen later.
- */
- old_vc_mode = vt_cons[fg_console].vc_mode;
- update_screen(new_console);
-
- /*
- * If this new console is under process control, send it a signal
- * telling it that it has acquired. Also check if it has died and
- * clean up (similar to logic employed in change_console())
- */
- if (vt_cons[new_console].vt_mode.mode == VT_PROCESS)
- {
- /*
- * Send the signal as privileged - kill_proc() will
- * tell us if the process has gone or something else
- * is awry
- */
- if (kill_proc(vt_cons[new_console].vt_pid,
- vt_cons[new_console].vt_mode.acqsig,
- 1) != 0)
- {
- /*
- * The controlling process has died, so we revert back to
- * normal operation. In this case, we'll also change back
- * to KD_TEXT mode. I'm not sure if this is strictly correct
- * but it saves the agony when the X server dies and the screen
- * remains blanked due to KD_GRAPHICS! It would be nice to do
- * this outside of VT_PROCESS but there is no single process
- * to account for and tracking tty count may be undesirable.
- */
- vt_cons[new_console].vc_mode = KD_TEXT;
- clr_vc_kbd_mode(kbd_table + new_console, VC_RAW);
- clr_vc_kbd_mode(kbd_table + new_console, VC_MEDIUMRAW);
- vt_cons[new_console].vt_mode.mode = VT_AUTO;
- vt_cons[new_console].vt_mode.waitv = 0;
- vt_cons[new_console].vt_mode.relsig = 0;
- vt_cons[new_console].vt_mode.acqsig = 0;
- vt_cons[new_console].vt_mode.frsig = 0;
- vt_cons[new_console].vt_pid = -1;
- vt_cons[new_console].vt_newvt = -1;
- }
- }
-
- /*
- * We do this here because the controlling process above may have
- * gone, and so there is now a new vc_mode
- */
- if (old_vc_mode != vt_cons[new_console].vc_mode)
- {
- if (vt_cons[new_console].vc_mode == KD_TEXT)
- unblank_screen();
- else {
- timer_active &= ~(1<<BLANK_TIMER);
- blank_screen();
- }
- }
-
- /*
- * Wake anyone waiting for their VT to activate
- */
- vt_wake_waitactive();
- return;
- }
-
- /*
- * Performs the front-end of a vt switch
- */
- void change_console(unsigned int new_console)
- {
- if (new_console == fg_console || new_console >= NR_CONSOLES)
- return;
-
- /*
- * If this vt is in process mode, then we need to handshake with
- * that process before switching. Essentially, we store where that
- * vt wants to switch to and wait for it to tell us when it's done
- * (via VT_RELDISP ioctl).
- *
- * We also check to see if the controlling process still exists.
- * If it doesn't, we reset this vt to auto mode and continue.
- * This is a cheap way to track process control. The worst thing
- * that can happen is: we send a signal to a process, it dies, and
- * the switch gets "lost" waiting for a response; hopefully, the
- * user will try again, we'll detect the process is gone (unless
- * the user waits just the right amount of time :-) and revert the
- * vt to auto control.
- */
- if (vt_cons[fg_console].vt_mode.mode == VT_PROCESS)
- {
- /*
- * Send the signal as privileged - kill_proc() will
- * tell us if the process has gone or something else
- * is awry
- */
- if (kill_proc(vt_cons[fg_console].vt_pid,
- vt_cons[fg_console].vt_mode.relsig,
- 1) == 0)
- {
- /*
- * It worked. Mark the vt to switch to and
- * return. The process needs to send us a
- * VT_RELDISP ioctl to complete the switch.
- */
- vt_cons[fg_console].vt_newvt = new_console;
- return;
- }
-
- /*
- * The controlling process has died, so we revert back to
- * normal operation. In this case, we'll also change back
- * to KD_TEXT mode. I'm not sure if this is strictly correct
- * but it saves the agony when the X server dies and the screen
- * remains blanked due to KD_GRAPHICS! It would be nice to do
- * this outside of VT_PROCESS but there is no single process
- * to account for and tracking tty count may be undesirable.
- */
- vt_cons[fg_console].vc_mode = KD_TEXT;
- clr_vc_kbd_mode(kbd_table + fg_console, VC_RAW);
- clr_vc_kbd_mode(kbd_table + fg_console, VC_MEDIUMRAW);
- vt_cons[fg_console].vt_mode.mode = VT_AUTO;
- vt_cons[fg_console].vt_mode.waitv = 0;
- vt_cons[fg_console].vt_mode.relsig = 0;
- vt_cons[fg_console].vt_mode.acqsig = 0;
- vt_cons[fg_console].vt_mode.frsig = 0;
- vt_cons[fg_console].vt_pid = -1;
- vt_cons[fg_console].vt_newvt = -1;
- /*
- * Fall through to normal (VT_AUTO) handling of the switch...
- */
- }
-
- /*
- * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
- */
- if (vt_cons[fg_console].vc_mode == KD_GRAPHICS)
- return;
-
- complete_change_console(new_console);
- }
-
- void wait_for_keypress(void)
- {
- sleep_on(&keypress_wait);
- }
-
- void stop_tty(struct tty_struct *tty)
- {
- if (tty->stopped)
- return;
- tty->stopped = 1;
- if (tty->link && tty->link->packet) {
- tty->ctrl_status &= ~TIOCPKT_START;
- tty->ctrl_status |= TIOCPKT_STOP;
- wake_up_interruptible(&tty->link->secondary.proc_list);
- }
- if (tty->stop)
- (tty->stop)(tty);
- if (IS_A_CONSOLE(tty->line)) {
- set_vc_kbd_led(kbd_table + fg_console, VC_SCROLLOCK);
- set_leds();
- }
- }
-
- void start_tty(struct tty_struct *tty)
- {
- if (!tty->stopped)
- return;
- tty->stopped = 0;
- if (tty->link && tty->link->packet) {
- tty->ctrl_status &= ~TIOCPKT_STOP;
- tty->ctrl_status |= TIOCPKT_START;
- wake_up_interruptible(&tty->link->secondary.proc_list);
- }
- if (tty->start)
- (tty->start)(tty);
- TTY_WRITE_FLUSH(tty);
- if (IS_A_CONSOLE(tty->line)) {
- clr_vc_kbd_led(kbd_table + fg_console, VC_SCROLLOCK);
- set_leds();
- }
- }
-
- /* Perform OPOST processing. Returns -1 when the write_q becomes full
- and the character must be retried. */
-
- static int opost(unsigned char c, struct tty_struct *tty)
- {
- if (FULL(&tty->write_q))
- return -1;
- if (O_OPOST(tty)) {
- switch (c) {
- case '\n':
- if (O_ONLRET(tty))
- tty->column = 0;
- if (O_ONLCR(tty)) {
- if (LEFT(&tty->write_q) < 2)
- return -1;
- put_tty_queue('\r', &tty->write_q);
- tty->column = 0;
- }
- tty->canon_column = tty->column;
- break;
- case '\r':
- if (O_ONOCR(tty) && tty->column == 0)
- return 0;
- if (O_OCRNL(tty)) {
- c = '\n';
- if (O_ONLRET(tty))
- tty->canon_column = tty->column = 0;
- break;
- }
- tty->canon_column = tty->column = 0;
- break;
- case '\t':
- if (O_TABDLY(tty) == XTABS) {
- if (LEFT(&tty->write_q) < 8)
- return -1;
- do
- put_tty_queue(' ', &tty->write_q);
- while (++tty->column % 8);
- return 0;
- }
- tty->column = (tty->column | 7) + 1;
- break;
- case '\b':
- if (tty->column > 0)
- tty->column--;
- break;
- default:
- if (O_OLCUC(tty))
- c = toupper(c);
- if (!iscntrl(c))
- tty->column++;
- break;
- }
- }
- put_tty_queue(c, &tty->write_q);
- return 0;
- }
-
- /* Must be called only when L_ECHO(tty) is true. */
-
- static void echo_char(unsigned char c, struct tty_struct *tty)
- {
- if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
- opost('^', tty);
- opost(c ^ 0100, tty);
- } else
- opost(c, tty);
- }
-
- static void eraser(unsigned char c, struct tty_struct *tty)
- {
- enum { ERASE, WERASE, KILL } kill_type;
- int seen_alnums;
-
- if (tty->secondary.head == tty->canon_head) {
- /* opost('\a', tty); */ /* what do you think? */
- return;
- }
- if (c == ERASE_CHAR(tty))
- kill_type = ERASE;
- else if (c == WERASE_CHAR(tty))
- kill_type = WERASE;
- else {
- if (!L_ECHO(tty)) {
- tty->secondary.head = tty->canon_head;
- return;
- }
- if (!L_ECHOK(tty) || !L_ECHOKE(tty)) {
- tty->secondary.head = tty->canon_head;
- if (tty->erasing) {
- opost('/', tty);
- tty->erasing = 0;
- }
- echo_char(KILL_CHAR(tty), tty);
- /* Add a newline if ECHOK is on and ECHOKE is off. */
- if (L_ECHOK(tty))
- opost('\n', tty);
- return;
- }
- kill_type = KILL;
- }
-
- seen_alnums = 0;
- while (tty->secondary.head != tty->canon_head) {
- c = LAST(&tty->secondary);
- if (kill_type == WERASE) {
- /* Equivalent to BSD's ALTWERASE. */
- if (isalnum(c) || c == '_')
- seen_alnums++;
- else if (seen_alnums)
- break;
- }
- DEC(tty->secondary.head);
- if (L_ECHO(tty)) {
- if (L_ECHOPRT(tty)) {
- if (!tty->erasing) {
- opost('\\', tty);
- tty->erasing = 1;
- }
- echo_char(c, tty);
- } else if (!L_ECHOE(tty)) {
- echo_char(ERASE_CHAR(tty), tty);
- } else if (c == '\t') {
- unsigned int col = tty->canon_column;
- unsigned long tail = tty->canon_head;
-
- /* Find the column of the last char. */
- while (tail != tty->secondary.head) {
- c = tty->secondary.buf[tail];
- if (c == '\t')
- col = (col | 7) + 1;
- else if (iscntrl(c)) {
- if (L_ECHOCTL(tty))
- col += 2;
- } else
- col++;
- INC(tail);
- }
-
- /* Now backup to that column. */
- while (tty->column > col) {
- /* Can't use opost here. */
- put_tty_queue('\b', &tty->write_q);
- tty->column--;
- }
- } else {
- if (iscntrl(c) && L_ECHOCTL(tty)) {
- opost('\b', tty);
- opost(' ', tty);
- opost('\b', tty);
- }
- if (!iscntrl(c) || L_ECHOCTL(tty)) {
- opost('\b', tty);
- opost(' ', tty);
- opost('\b', tty);
- }
- }
- }
- if (kill_type == ERASE)
- break;
- }
- if (tty->erasing && tty->secondary.head == tty->canon_head) {
- opost('/', tty);
- tty->erasing = 0;
- }
- }
-
- static void isig(int sig, struct tty_struct *tty)
- {
- kill_pg(tty->pgrp, sig, 1);
- if (!L_NOFLSH(tty)) {
- flush_input(tty);
- flush_output(tty);
- }
- }
-
- static void copy_to_cooked(struct tty_struct * tty)
- {
- int c, special_flag;
- unsigned long flags;
-
- if (!tty) {
- printk("copy_to_cooked: called with NULL tty\n");
- return;
- }
- if (!tty->write) {
- printk("copy_to_cooked: tty %d has null write routine\n",
- tty->line);
- }
- while (1) {
- /*
- * Check to see how much room we have left in the
- * secondary queue. Send a throttle command or abort
- * if necessary.
- */
- c = LEFT(&tty->secondary);
- if (tty->throttle && (c < SQ_THRESHOLD_LW)
- && !set_bit(TTY_SQ_THROTTLED, &tty->flags))
- tty->throttle(tty, TTY_THROTTLE_SQ_FULL);
- if (c == 0)
- break;
- save_flags(flags); cli();
- if (!EMPTY(&tty->read_q)) {
- c = tty->read_q.buf[tty->read_q.tail];
- special_flag = clear_bit(tty->read_q.tail,
- &tty->readq_flags);
- INC(tty->read_q.tail);
- restore_flags(flags);
- } else {
- restore_flags(flags);
- break;
- }
- if (special_flag) {
- tty->char_error = c;
- continue;
- }
- if (tty->char_error) {
- if (tty->char_error == TTY_BREAK) {
- tty->char_error = 0;
- if (I_IGNBRK(tty))
- continue;
- /* A break is handled by the lower levels. */
- if (I_BRKINT(tty))
- continue;
- if (I_PARMRK(tty)) {
- put_tty_queue('\377', &tty->secondary);
- put_tty_queue('\0', &tty->secondary);
- }
- put_tty_queue('\0', &tty->secondary);
- continue;
- }
- if (tty->char_error == TTY_OVERRUN) {
- tty->char_error = 0;
- printk("tty%d: input overrun\n", tty->line);
- continue;
- }
- /* Must be a parity or frame error */
- tty->char_error = 0;
- if (I_IGNPAR(tty)) {
- continue;
- }
- if (I_PARMRK(tty)) {
- put_tty_queue('\377', &tty->secondary);
- put_tty_queue('\0', &tty->secondary);
- put_tty_queue(c, &tty->secondary);
- } else
- put_tty_queue('\0', &tty->secondary);
- continue;
- }
- if (I_ISTRIP(tty))
- c &= 0x7f;
- if (!tty->lnext) {
- if (c == '\r') {
- if (I_IGNCR(tty))
- continue;
- if (I_ICRNL(tty))
- c = '\n';
- } else if (c == '\n' && I_INLCR(tty))
- c = '\r';
- }
- if (I_IUCLC(tty) && L_IEXTEN(tty))
- c=tolower(c);
- if (c == __DISABLED_CHAR)
- tty->lnext = 1;
- if (L_ICANON(tty) && !tty->lnext) {
- if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
- (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
- eraser(c, tty);
- continue;
- }
- if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
- tty->lnext = 1;
- if (L_ECHO(tty)) {
- if (tty->erasing) {
- opost('/', tty);
- tty->erasing = 0;
- }
- if (L_ECHOCTL(tty)) {
- opost('^', tty);
- opost('\b', tty);
- }
- }
- continue;
- }
- if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
- L_IEXTEN(tty)) {
- unsigned long tail = tty->canon_head;
-
- if (tty->erasing) {
- opost('/', tty);
- tty->erasing = 0;
- }
- echo_char(c, tty);
- opost('\n', tty);
- while (tail != tty->secondary.head) {
- echo_char(tty->secondary.buf[tail],
- tty);
- INC(tail);
- }
- continue;
- }
- }
- if (I_IXON(tty) && !tty->lnext) {
- if ((tty->stopped && I_IXANY(tty) && L_IEXTEN(tty)) ||
- c == START_CHAR(tty)) {
- start_tty(tty);
- continue;
- }
- if (c == STOP_CHAR(tty)) {
- stop_tty(tty);
- continue;
- }
- }
- if (L_ISIG(tty) && !tty->lnext) {
- if (c == INTR_CHAR(tty)) {
- isig(SIGINT, tty);
- continue;
- }
- if (c == QUIT_CHAR(tty)) {
- isig(SIGQUIT, tty);
- continue;
- }
- if (c == SUSP_CHAR(tty)) {
- if (!is_orphaned_pgrp(tty->pgrp))
- isig(SIGTSTP, tty);
- continue;
- }
- }
-
- if (tty->erasing) {
- opost('/', tty);
- tty->erasing = 0;
- }
- if (c == '\n' && !tty->lnext) {
- if (L_ECHO(tty) || (L_ICANON(tty) && L_ECHONL(tty)))
- opost('\n', tty);
- } else if (L_ECHO(tty)) {
- /* Don't echo the EOF char in canonical mode. Sun
- handles this differently by echoing the char and
- then backspacing, but that's a hack. */
- if (c != EOF_CHAR(tty) || !L_ICANON(tty) ||
- tty->lnext) {
- /* Record the column of first canon char. */
- if (tty->canon_head == tty->secondary.head)
- tty->canon_column = tty->column;
- echo_char(c, tty);
- }
- }
-
- if (I_PARMRK(tty) && c == (unsigned char) '\377' &&
- (c != EOF_CHAR(tty) || !L_ICANON(tty) || tty->lnext))
- put_tty_queue(c, &tty->secondary);
-
- if (L_ICANON(tty) && !tty->lnext &&
- (c == '\n' || c == EOF_CHAR(tty) || c == EOL_CHAR(tty) ||
- (c == EOL2_CHAR(tty) && L_IEXTEN(tty)))) {
- if (c == EOF_CHAR(tty))
- c = __DISABLED_CHAR;
- set_bit(tty->secondary.head, &tty->secondary_flags);
- put_tty_queue(c, &tty->secondary);
- tty->canon_head = tty->secondary.head;
- tty->canon_data++;
- } else
- put_tty_queue(c, &tty->secondary);
- tty->lnext = 0;
- }
- if (!EMPTY(&tty->write_q))
- TTY_WRITE_FLUSH(tty);
- if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary))
- wake_up_interruptible(&tty->secondary.proc_list);
-
- if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW)
- && clear_bit(TTY_RQ_THROTTLED, &tty->flags))
- tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL);
- }
-
- int is_ignored(int sig)
- {
- return ((current->blocked & (1<<(sig-1))) ||
- (current->sigaction[sig-1].sa_handler == SIG_IGN));
- }
-
- static inline int input_available_p(struct tty_struct *tty)
- {
- /* Avoid calling TTY_READ_FLUSH unnecessarily. */
- if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary))
- return 1;
-
- /* Shuffle any pending data down the queues. */
- TTY_READ_FLUSH(tty);
- if (tty->link)
- TTY_WRITE_FLUSH(tty->link);
-
- if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary))
- return 1;
- return 0;
- }
-
- static int read_chan(struct tty_struct *tty, struct file *file,
- unsigned char *buf, unsigned int nr)
- {
- struct wait_queue wait = { current, NULL };
- int c;
- unsigned char *b = buf;
- int minimum, time;
- int retval = 0;
-
- /* Job control check -- must be done at start and after
- every sleep (POSIX.1 7.1.1.4). */
- /* 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 &&
- current->tty == tty->line) {
- if (tty->pgrp <= 0)
- printk("read_chan: tty->pgrp <= 0!\n");
- else if (current->pgrp != tty->pgrp) {
- if (is_ignored(SIGTTIN) ||
- is_orphaned_pgrp(current->pgrp))
- return -EIO;
- kill_pg(current->pgrp, SIGTTIN, 1);
- return -ERESTARTSYS;
- }
- }
-
- if (L_ICANON(tty)) {
- minimum = time = 0;
- current->timeout = (unsigned long) -1;
- } else {
- time = (HZ / 10) * TIME_CHAR(tty);
- minimum = MIN_CHAR(tty);
- if (minimum)
- current->timeout = (unsigned long) -1;
- else {
- if (time) {
- current->timeout = time + jiffies;
- time = 0;
- } else
- current->timeout = 0;
- minimum = 1;
- }
- }
-
- add_wait_queue(&tty->secondary.proc_list, &wait);
- while (1) {
- /* First test for status change. */
- if (tty->packet && tty->link->ctrl_status) {
- if (b != buf)
- break;
- put_fs_byte(tty->link->ctrl_status, b++);
- tty->link->ctrl_status = 0;
- break;
- }
- /* This statement must be first before checking for input
- so that any interrupt will set the state back to
- TASK_RUNNING. */
- current->state = TASK_INTERRUPTIBLE;
- if (!input_available_p(tty)) {
- if (test_bit( TTY_SLAVE_CLOSED, &tty->flags )) {
- retval = -EIO;
- break;
- }
- if (tty_hung_up_p(file))
- break;
- if (!current->timeout)
- break;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- if (current->signal & ~current->blocked) {
- retval = -ERESTARTSYS;
- break;
- }
- schedule();
- continue;
- }
- current->state = TASK_RUNNING;
-
- /* Deal with packet mode. */
- if (tty->packet && b == buf) {
- put_fs_byte(TIOCPKT_DATA, b++);
- nr--;
- }
-
- while (1) {
- int eol;
-
- cli();
- if (EMPTY(&tty->secondary)) {
- sti();
- break;
- }
- eol = clear_bit(tty->secondary.tail,
- &tty->secondary_flags);
- c = tty->secondary.buf[tty->secondary.tail];
- if (!nr) {
- /* Gobble up an immediately following EOF if
- there is no more room in buf (this can
- happen if the user "pushes" some characters
- using ^D). This prevents the next read()
- from falsely returning EOF. */
- if (eol) {
- if (c == __DISABLED_CHAR) {
- tty->canon_data--;
- INC(tty->secondary.tail);
- } else {
- set_bit(tty->secondary.tail,
- &tty->secondary_flags);
- }
- }
- sti();
- break;
- }
- INC(tty->secondary.tail);
- sti();
- if (eol) {
- if (--tty->canon_data < 0) {
- printk("read_chan: canon_data < 0!\n");
- tty->canon_data = 0;
- }
- if (c == __DISABLED_CHAR)
- break;
- put_fs_byte(c, b++);
- nr--;
- break;
- }
- put_fs_byte(c, b++);
- nr--;
- }
-
- /* If there is enough space in the secondary queue now, let the
- low-level driver know. */
- if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
- && clear_bit(TTY_SQ_THROTTLED, &tty->flags))
- tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
-
- if (b - buf >= minimum || !nr)
- break;
- if (time)
- current->timeout = time + jiffies;
- }
- remove_wait_queue(&tty->secondary.proc_list, &wait);
- current->state = TASK_RUNNING;
- current->timeout = 0;
- return (b - buf) ? b - buf : retval;
- }
-
- static int write_chan(struct tty_struct * tty, struct file * file,
- unsigned char * buf, unsigned int nr)
- {
- struct wait_queue wait = { current, NULL };
- int c;
- 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) {
- retval = check_change(tty, tty->line);
- if (retval)
- return retval;
- }
-
- add_wait_queue(&tty->write_q.proc_list, &wait);
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- if (current->signal & ~current->blocked) {
- retval = -ERESTARTSYS;
- break;
- }
- if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
- retval = -EIO;
- break;
- }
- while (nr > 0) {
- c = get_fs_byte(b);
- /* Care is needed here: opost() can abort even
- if the write_q is not full. */
- if (opost(c, tty) < 0)
- break;
- b++; nr--;
- }
- TTY_WRITE_FLUSH(tty);
- if (!nr)
- break;
- if (EMPTY(&tty->write_q) && !need_resched)
- continue;
- if (file->f_flags & O_NONBLOCK) {
- retval = -EAGAIN;
- break;
- }
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&tty->write_q.proc_list, &wait);
- return (b - buf) ? b - buf : retval;
- }
-
- static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
- {
- int i, dev;
- struct tty_struct * tty;
-
- dev = file->f_rdev;
- if (MAJOR(dev) != TTY_MAJOR) {
- printk("tty_read: bad pseudo-major nr #%d\n", MAJOR(dev));
- return -EINVAL;
- }
- dev = MINOR(dev);
- tty = TTY_TABLE(dev);
- if (!tty || test_bit( TTY_IO_ERROR, &tty->flags ))
- return -EIO;
-
- /* This check not only needs to be done before reading, but also
- whenever read_chan() gets woken up after sleeping, so I've
- moved it to there. This should only be done for the N_TTY
- line discipline, anyway. Same goes for write_chan(). -- jlc. */
- #if 0
- if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
- (tty->pgrp > 0) &&
- (current->tty == dev) &&
- (tty->pgrp != current->pgrp))
- if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
- return -EIO;
- else {
- (void) kill_pg(current->pgrp, SIGTTIN, 1);
- return -ERESTARTSYS;
- }
- #endif
- if (ldiscs[tty->disc].read)
- /* XXX casts are for what kernel-wide prototypes should be. */
- i = (ldiscs[tty->disc].read)(tty,file,(unsigned char *)buf,(unsigned int)count);
- else
- i = -EIO;
- if (i > 0)
- inode->i_atime = CURRENT_TIME;
- return i;
- }
-
- static int tty_write(struct inode * inode, struct file * file, char * buf, int count)
- {
- int dev, i, is_console;
- struct tty_struct * tty;
-
- dev = file->f_rdev;
- is_console = (inode->i_rdev == CONSOLE_DEV);
- if (MAJOR(dev) != TTY_MAJOR) {
- printk("tty_write: pseudo-major != TTY_MAJOR\n");
- return -EINVAL;
- }
- dev = MINOR(dev);
- if (is_console && redirect)
- tty = redirect;
- else
- tty = TTY_TABLE(dev);
- if (!tty || !tty->write || test_bit( TTY_IO_ERROR, &tty->flags ))
- return -EIO;
- #if 0
- if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
- (current->tty == dev) && (tty->pgrp != current->pgrp)) {
- if (is_orphaned_pgrp(current->pgrp))
- return -EIO;
- if (!is_ignored(SIGTTOU)) {
- (void) kill_pg(current->pgrp, SIGTTOU, 1);
- return -ERESTARTSYS;
- }
- }
- #endif
- if (ldiscs[tty->disc].write)
- /* XXX casts are for what kernel-wide prototypes should be. */
- i = (ldiscs[tty->disc].write)(tty,file,(unsigned char *)buf,(unsigned int)count);
- else
- i = -EIO;
- if (i > 0)
- inode->i_mtime = CURRENT_TIME;
- return i;
- }
-
- /*
- * 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.
- */
- static int init_dev(int dev)
- {
- struct tty_struct *tty, *o_tty;
- struct termios *tp, *o_tp, *ltp, *o_ltp;
- int retval;
- int o_dev;
-
- o_dev = PTY_OTHER(dev);
- tty = o_tty = NULL;
- tp = o_tp = NULL;
- ltp = o_ltp = NULL;
- repeat:
- retval = -EAGAIN;
- if (IS_A_PTY_MASTER(dev) && tty_table[dev] && tty_table[dev]->count)
- goto end_init;
- retval = -ENOMEM;
- if (!tty_table[dev] && !tty) {
- if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
- goto end_init;
- initialize_tty_struct(dev, tty);
- goto repeat;
- }
- if (!tty_termios[dev] && !tp) {
- tp = (struct termios *) kmalloc(sizeof(struct termios),
- GFP_KERNEL);
- if (!tp)
- goto end_init;
- initialize_termios(dev, tp);
- goto repeat;
- }
- if (!termios_locked[dev] && !ltp) {
- ltp = (struct termios *) kmalloc(sizeof(struct termios),
- GFP_KERNEL);
- if (!ltp)
- goto end_init;
- memset(ltp, 0, sizeof(struct termios));
- goto repeat;
- }
- if (IS_A_PTY(dev)) {
- if (!tty_table[o_dev] && !o_tty) {
- o_tty = (struct tty_struct *)
- get_free_page(GFP_KERNEL);
- if (!o_tty)
- goto end_init;
- initialize_tty_struct(o_dev, o_tty);
- goto repeat;
- }
- if (!tty_termios[o_dev] && !o_tp) {
- o_tp = (struct termios *)
- kmalloc(sizeof(struct termios), GFP_KERNEL);
- if (!o_tp)
- goto end_init;
- initialize_termios(o_dev, o_tp);
- goto repeat;
- }
- if (!termios_locked[o_dev] && !o_ltp) {
- o_ltp = (struct termios *)
- kmalloc(sizeof(struct termios), GFP_KERNEL);
- if (!o_ltp)
- goto end_init;
- memset(o_ltp, 0, sizeof(struct termios));
- goto repeat;
- }
-
- }
- /* Now we have allocated all the structures: update all the pointers.. */
- if (!tty_termios[dev]) {
- tty_termios[dev] = tp;
- tp = NULL;
- }
- if (!tty_table[dev]) {
- tty->termios = tty_termios[dev];
- tty_table[dev] = tty;
- tty = NULL;
- }
- if (!termios_locked[dev]) {
- termios_locked[dev] = ltp;
- ltp = NULL;
- }
- if (IS_A_PTY(dev)) {
- if (!tty_termios[o_dev]) {
- tty_termios[o_dev] = o_tp;
- o_tp = NULL;
- }
- if (!termios_locked[o_dev]) {
- termios_locked[o_dev] = o_ltp;
- o_ltp = NULL;
- }
- if (!tty_table[o_dev]) {
- o_tty->termios = tty_termios[o_dev];
- tty_table[o_dev] = o_tty;
- o_tty = NULL;
- }
- tty_table[dev]->link = tty_table[o_dev];
- tty_table[o_dev]->link = tty_table[dev];
- }
- tty_table[dev]->count++;
- if (IS_A_PTY_MASTER(dev))
- tty_table[o_dev]->count++;
- retval = 0;
- 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));
- if (o_tp)
- kfree_s(o_tp, sizeof(struct termios));
- if (ltp)
- kfree_s(ltp, sizeof(struct termios));
- if (o_ltp)
- kfree_s(o_ltp, sizeof(struct termios));
- return retval;
- }
-
- /*
- * 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.
- */
- static void release_dev(int dev, struct file * filp)
- {
- struct tty_struct *tty, *o_tty;
- struct termios *tp, *o_tp;
- struct task_struct **p;
-
- tty = tty_table[dev];
- tp = tty_termios[dev];
- o_tty = NULL;
- o_tp = NULL;
- if (!tty) {
- printk("release_dev: tty_table[%d] was NULL\n", dev);
- return;
- }
- if (!tp) {
- printk("release_dev: tty_termios[%d] was NULL\n", dev);
- return;
- }
- #ifdef TTY_DEBUG_HANGUP
- printk("release_dev of tty%d (tty count=%d)...", dev, tty->count);
- #endif
- if (IS_A_PTY(dev)) {
- o_tty = tty_table[PTY_OTHER(dev)];
- o_tp = tty_termios[PTY_OTHER(dev)];
- if (!o_tty) {
- printk("release_dev: pty pair(%d) was NULL\n", dev);
- return;
- }
- if (!o_tp) {
- printk("release_dev: pty pair(%d) termios was NULL\n", dev);
- return;
- }
- if (tty->link != o_tty || o_tty->link != tty) {
- printk("release_dev: bad pty pointers\n");
- return;
- }
- }
- tty->write_data_cnt = 0; /* Clear out pending trash */
- if (tty->close)
- tty->close(tty, filp);
- if (IS_A_PTY_MASTER(dev)) {
- if (--tty->link->count < 0) {
- printk("release_dev: bad tty slave count (dev = %d): %d\n",
- dev, tty->count);
- tty->link->count = 0;
- }
- }
- if (--tty->count < 0) {
- printk("release_dev: bad tty_table[%d]->count: %d\n",
- dev, tty->count);
- tty->count = 0;
- }
- if (tty->count)
- return;
-
- #ifdef TTY_DEBUG_HANGUP
- printk("freeing tty structure...");
- #endif
-
- /*
- * Make sure there aren't any processes that still think this
- * tty is their controlling tty.
- */
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if ((*p) && (*p)->tty == tty->line)
- (*p)->tty = -1;
- }
-
- /*
- * Shutdown the current line discipline, and reset it to
- * N_TTY.
- */
- if (ldiscs[tty->disc].close != NULL)
- ldiscs[tty->disc].close(tty);
- tty->disc = N_TTY;
- tty->termios->c_line = N_TTY;
-
- if (o_tty) {
- if (o_tty->count)
- return;
- else {
- tty_table[PTY_OTHER(dev)] = NULL;
- tty_termios[PTY_OTHER(dev)] = NULL;
- }
- }
- tty_table[dev] = NULL;
- if (IS_A_PTY(dev)) {
- tty_termios[dev] = NULL;
- kfree_s(tp, sizeof(struct termios));
- }
- if (tty == redirect || o_tty == redirect)
- redirect = NULL;
- free_page((unsigned long) tty);
- if (o_tty)
- free_page((unsigned long) o_tty);
- if (o_tp)
- kfree_s(o_tp, sizeof(struct termios));
- }
-
- /*
- * tty_open and tty_release keep up the tty count that contains the
- * number of opens done on a tty. We cannot use the inode-count, as
- * different inodes might point to the same tty.
- *
- * Open-counting is needed for pty masters, as well as for keeping
- * track of serial lines: DTR is dropped when the last close happens.
- * (This is not done solely through tty->count, now. - Ted 1/27/92)
- *
- * The termios state of a pty is reset on first open so that
- * settings don't persist across reuse.
- */
- static int tty_open(struct inode * inode, struct file * filp)
- {
- struct tty_struct *tty;
- int major, minor;
- int noctty, retval;
-
- retry_open:
- minor = MINOR(inode->i_rdev);
- major = MAJOR(inode->i_rdev);
- noctty = filp->f_flags & O_NOCTTY;
- if (major == TTYAUX_MAJOR) {
- if (!minor) {
- major = TTY_MAJOR;
- minor = current->tty;
- }
- /* noctty = 1; */
- } else if (major == TTY_MAJOR) {
- if (!minor) {
- minor = fg_console + 1;
- noctty = 1;
- }
- } else {
- printk("Bad major #%d in tty_open\n", MAJOR(inode->i_rdev));
- return -ENODEV;
- }
- if (minor <= 0)
- return -ENXIO;
- if (IS_A_PTY_MASTER(minor))
- noctty = 1;
- filp->f_rdev = (major << 8) | minor;
- retval = init_dev(minor);
- if (retval)
- return retval;
- tty = tty_table[minor];
- #ifdef TTY_DEBUG_HANGUP
- printk("opening tty%d...", tty->line);
- #endif
- if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
- return -EBUSY;
-
- #if 0
- /* clean up the packet stuff. */
- /*
- * Why is this not done in init_dev? Right here, if another
- * process opens up a tty in packet mode, all the packet
- * variables get cleared. Come to think of it, is anything
- * using the packet mode at all??? - Ted, 1/27/93
- *
- * Not to worry, a pty master can only be opened once.
- * And rlogind and telnetd both use packet mode. -- jrs
- *
- * Not needed. These are cleared in initialize_tty_struct. -- jlc
- */
- tty->ctrl_status = 0;
- tty->packet = 0;
- #endif
-
- if (tty->open) {
- retval = tty->open(tty, filp);
- } else {
- retval = -ENODEV;
- }
- if (retval) {
- #ifdef TTY_DEBUG_HANGUP
- printk("error %d in opening tty%d...", retval, tty->line);
- #endif
-
- release_dev(minor, filp);
- if (retval != -ERESTARTSYS)
- return retval;
- if (current->signal & ~current->blocked)
- return retval;
- schedule();
- goto retry_open;
- }
- if (!noctty &&
- current->leader &&
- current->tty<0 &&
- tty->session==0) {
- current->tty = minor;
- tty->session = current->session;
- tty->pgrp = current->pgrp;
- }
- filp->f_rdev = MKDEV(TTY_MAJOR,minor); /* Set it to something normal */
- 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 void tty_release(struct inode * inode, struct file * filp)
- {
- int dev;
-
- dev = filp->f_rdev;
- if (MAJOR(dev) != TTY_MAJOR) {
- printk("tty_release: tty pseudo-major != TTY_MAJOR\n");
- return;
- }
- dev = MINOR(filp->f_rdev);
- if (!dev) {
- printk("tty_release: bad f_rdev\n");
- return;
- }
- release_dev(dev, filp);
- }
-
- static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
- {
- int dev;
- struct tty_struct * tty;
-
- dev = filp->f_rdev;
- if (MAJOR(dev) != TTY_MAJOR) {
- printk("tty_select: tty pseudo-major != TTY_MAJOR\n");
- return 0;
- }
- dev = MINOR(filp->f_rdev);
- tty = TTY_TABLE(dev);
- if (!tty) {
- printk("tty_select: tty struct for dev %d was NULL\n", dev);
- return 0;
- }
- if (ldiscs[tty->disc].select)
- return (ldiscs[tty->disc].select)(tty, inode, filp,
- sel_type, wait);
- return 0;
- }
-
- static int normal_select(struct tty_struct * tty, struct inode * inode,
- struct file * file, int sel_type, select_table *wait)
- {
- switch (sel_type) {
- case SEL_IN:
- if (input_available_p(tty))
- return 1;
- /* fall through */
- case SEL_EX:
- if (tty->packet && tty->link->ctrl_status)
- return 1;
- if (test_bit( TTY_SLAVE_CLOSED, &tty->flags ))
- return 1;
- if (tty_hung_up_p(file))
- return 1;
- select_wait(&tty->secondary.proc_list, wait);
- return 0;
- case SEL_OUT:
- if (LEFT(&tty->write_q) > WAKEUP_CHARS)
- return 1;
- select_wait(&tty->write_q.proc_list, wait);
- return 0;
- }
- return 0;
- }
-
- /*
- * This implements the "Secure Attention Key" --- the idea is to
- * prevent trojan horses by killing all processes associated with this
- * tty when the user hits the "Secure Attention Key". Required for
- * super-paranoid applications --- see the Orange Book for more details.
- *
- * This code could be nicer; ideally it should send a HUP, wait a few
- * seconds, then send a INT, and then a KILL signal. But you then
- * have to coordinate with the init process, since all processes associated
- * with the current tty must be dead before the new getty is allowed
- * to spawn.
- */
- void do_SAK( struct tty_struct *tty)
- {
- #ifdef TTY_SOFT_SAK
- tty_hangup(tty);
- #else
- struct task_struct **p;
- int line = tty->line;
- int session = tty->session;
- int i;
- struct file *filp;
-
- flush_input(tty);
- flush_output(tty);
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if (!(*p))
- continue;
- if (((*p)->tty == line) ||
- ((session > 0) && ((*p)->session == session)))
- send_sig(SIGKILL, *p, 1);
- else {
- for (i=0; i < NR_OPEN; i++) {
- filp = (*p)->filp[i];
- if (filp && (filp->f_op == &tty_fops) &&
- (MINOR(filp->f_rdev) == line)) {
- send_sig(SIGKILL, *p, 1);
- break;
- }
- }
- }
- }
- #endif
- }
-
- /*
- * This routine allows a kernel routine to send a large chunk of data
- * to a particular tty; if all of the data can be queued up for ouput
- * immediately, tty_write_data() will return 0. If, however, not all
- * of the data can be immediately queued for delivery, the number of
- * bytes left to be queued up will be returned, and the rest of the
- * data will be queued up when there is room. The callback function
- * will be called (with the argument callarg) when the last of the
- * data is finally in the queue.
- *
- * Note that the callback routine will _not_ be called if all of the
- * data could be queued immediately. This is to avoid a problem with
- * the kernel stack getting too deep, which might happen if the
- * callback routine calls tty_write_data with itself as an argument.
- */
- int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
- void (*callback)(void * data), void * callarg)
- {
- int head, tail, count;
- unsigned long flags;
- char *p;
-
- #define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
-
- save_flags(flags);
- cli();
- if (tty->write_data_cnt) {
- restore_flags(flags);
- return -EBUSY;
- }
-
- head = tty->write_q.head;
- tail = tty->write_q.tail;
- count = buflen;
- p = bufp;
-
- while (count && VLEFT > 0) {
- tty->write_q.buf[head++] = *p++;
- head &= TTY_BUF_SIZE-1;
- count--;
- }
- tty->write_q.head = head;
- if (count) {
- tty->write_data_cnt = count;
- tty->write_data_ptr = (unsigned char *) p;
- tty->write_data_callback = callback;
- tty->write_data_arg = callarg;
- }
- restore_flags(flags);
- tty->write(tty);
- return count;
- }
-
- /*
- * This routine routine is called after an interrupt has drained a
- * tty's write queue, so that there is more space for data waiting to
- * be sent in tty->write_data_ptr.
- *
- * tty_check_write[8] is a bitstring which indicates which ttys
- * needs to be processed.
- */
- void tty_bh_routine(void * unused)
- {
- int i, j, line, mask;
- int head, tail, count;
- unsigned char * p;
- struct tty_struct * tty;
-
- for (i = 0, line = 0; i < MAX_TTYS / 32; i++) {
- if (!tty_check_write[i]) {
- line += 32;
- continue;
- }
- for (j=0, mask=0; j < 32; j++, line++, mask <<= 1) {
- if (clear_bit(j, &tty_check_write[i])) {
- tty = tty_table[line];
- if (!tty || !tty->write_data_cnt)
- continue;
- cli();
- head = tty->write_q.head;
- tail = tty->write_q.tail;
- count = tty->write_data_cnt;
- p = tty->write_data_ptr;
-
- while (count && VLEFT > 0) {
- tty->write_q.buf[head++] = *p++;
- head &= TTY_BUF_SIZE-1;
- count--;
- }
- tty->write_q.head = head;
- tty->write_data_ptr = p;
- tty->write_data_cnt = count;
- sti();
- if (!count)
- (tty->write_data_callback)
- (tty->write_data_arg);
- }
- }
- }
-
- }
-
- /*
- * This subroutine initializes a tty structure. We have to set up
- * things correctly for each different type of tty.
- */
- static void initialize_tty_struct(int line, struct tty_struct *tty)
- {
- memset(tty, 0, sizeof(struct tty_struct));
- tty->line = line;
- tty->disc = N_TTY;
- tty->pgrp = -1;
- if (IS_A_CONSOLE(line)) {
- tty->open = con_open;
- #ifdef __notyet__
- tty->winsize.ws_row = video_num_lines;
- tty->winsize.ws_col = video_num_columns;
- #else
- tty->winsize.ws_row = 0;
- tty->winsize.ws_col = 0;
- #endif
- } else if IS_A_SERIAL(line) {
- tty->open = rs_open;
- } else if IS_A_PTY(line) {
- tty->open = pty_open;
- }
- }
-
- static void initialize_termios(int line, struct termios * tp)
- {
- memset(tp, 0, sizeof(struct termios));
- memcpy(tp->c_cc, INIT_C_CC, NCCS);
- if (IS_A_CONSOLE(line) || IS_A_PTY_SLAVE(line)) {
- tp->c_iflag = ICRNL | IXON;
- tp->c_oflag = OPOST | ONLCR;
- tp->c_cflag = B38400 | CS8 | CREAD;
- tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
- ECHOCTL | ECHOKE | IEXTEN;
- } else if (IS_A_SERIAL(line)) {
- tp->c_iflag = ICRNL | IXON;
- tp->c_oflag = OPOST | ONLCR | XTABS;
- tp->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
- ECHOCTL | ECHOKE | IEXTEN;
- } else if (IS_A_PTY_MASTER(line))
- tp->c_cflag = B9600 | CS8 | CREAD;
- }
-
- static struct tty_ldisc tty_ldisc_N_TTY = {
- 0, /* flags */
- NULL, /* open */
- NULL, /* close */
- read_chan, /* read */
- write_chan, /* write */
- NULL, /* ioctl */
- normal_select, /* select */
- copy_to_cooked /* handler */
- };
-
-
- long tty_init(long kmem_start)
- {
- int i;
-
- if (sizeof(struct tty_struct) > PAGE_SIZE)
- panic("size of tty structure > PAGE_SIZE!");
- if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
- panic("unable to get major %d for tty device", TTY_MAJOR);
- if (register_chrdev(TTYAUX_MAJOR,"tty",&tty_fops))
- panic("unable to get major %d for tty device", TTYAUX_MAJOR);
- for (i=0 ; i< MAX_TTYS ; i++) {
- tty_table[i] = 0;
- tty_termios[i] = 0;
- }
- memset(tty_check_write, 0, sizeof(tty_check_write));
- bh_base[TTY_BH].routine = tty_bh_routine;
-
- /* Setup the default TTY line discipline. */
- memset(ldiscs, 0, sizeof(ldiscs));
- (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
-
- kmem_start = kbd_init(kmem_start);
- kmem_start = con_init(kmem_start);
- kmem_start = rs_init(kmem_start);
- return kmem_start;
- }
-