From 47afa7a5a8a8fb9e60cdb6a3bd612e07c37e9d90 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 13 Oct 2008 10:44:17 +0100 Subject: tty: some ICANON magic is in the wrong places Move the set up on ldisc change into the ldisc Move the INQ/OUTQ cases into the driver not in shared ioctl code where it gives bogus answers for other ldisc values Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/n_tty.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'drivers/char/n_tty.c') diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 708c2b1dbe51..b4f5dccad2a6 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1011,8 +1011,20 @@ int is_ignored(int sig) static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { - if (!tty) - return; + int canon_change = 1; + BUG_ON(!tty); + + if (old) + canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; + if (canon_change) { + memset(&tty->read_flags, 0, sizeof tty->read_flags); + tty->canon_head = tty->read_tail; + tty->canon_data = 0; + tty->erasing = 0; + } + + if (canon_change && !L_ICANON(tty) && tty->read_cnt) + wake_up_interruptible(&tty->read_wait); tty->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { @@ -1573,6 +1585,43 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, return mask; } +static unsigned long inq_canon(struct tty_struct *tty) +{ + int nr, head, tail; + + if (!tty->canon_data || !tty->read_buf) + return 0; + head = tty->canon_head; + tail = tty->read_tail; + nr = (head - tail) & (N_TTY_BUF_SIZE-1); + /* Skip EOF-chars.. */ + while (head != tail) { + if (test_bit(tail, tty->read_flags) && + tty->read_buf[tail] == __DISABLED_CHAR) + nr--; + tail = (tail+1) & (N_TTY_BUF_SIZE-1); + } + return nr; +} + +static int n_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval; + + switch (cmd) { + case TIOCOUTQ: + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); + case TIOCINQ: + retval = tty->read_cnt; + if (L_ICANON(tty)) + retval = inq_canon(tty); + return put_user(retval, (unsigned int __user *) arg); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", -- cgit v1.2.3