?? tty_io.c
字號:
/* * 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 256struct 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)) 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)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -