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