📄 tty_io.c
字号:
}
}
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 (tty->flags & (1 << TTY_SLAVE_CLOSED))
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;
tty->winsize.ws_row = video_num_lines;
tty->winsize.ws_col = video_num_columns;
} 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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -