Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Alan Cox and committed by
Linus Torvalds
47afa7a5 fe6e29fd

+56 -45
+1 -1
drivers/bluetooth/hci_ldisc.c
··· 484 484 return -EUNATCH; 485 485 486 486 default: 487 - err = n_tty_ioctl(tty, file, cmd, arg); 487 + err = n_tty_ioctl_helper(tty, file, cmd, arg); 488 488 break; 489 489 }; 490 490
+1 -1
drivers/char/n_hdlc.c
··· 764 764 break; 765 765 766 766 default: 767 - error = n_tty_ioctl (tty, file, cmd, arg); 767 + error = n_tty_ioctl_helper(tty, file, cmd, arg); 768 768 break; 769 769 } 770 770 return error;
+51 -2
drivers/char/n_tty.c
··· 1011 1011 1012 1012 static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) 1013 1013 { 1014 - if (!tty) 1015 - return; 1014 + int canon_change = 1; 1015 + BUG_ON(!tty); 1016 + 1017 + if (old) 1018 + canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; 1019 + if (canon_change) { 1020 + memset(&tty->read_flags, 0, sizeof tty->read_flags); 1021 + tty->canon_head = tty->read_tail; 1022 + tty->canon_data = 0; 1023 + tty->erasing = 0; 1024 + } 1025 + 1026 + if (canon_change && !L_ICANON(tty) && tty->read_cnt) 1027 + wake_up_interruptible(&tty->read_wait); 1016 1028 1017 1029 tty->icanon = (L_ICANON(tty) != 0); 1018 1030 if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { ··· 1583 1571 tty_write_room(tty) > 0) 1584 1572 mask |= POLLOUT | POLLWRNORM; 1585 1573 return mask; 1574 + } 1575 + 1576 + static unsigned long inq_canon(struct tty_struct *tty) 1577 + { 1578 + int nr, head, tail; 1579 + 1580 + if (!tty->canon_data || !tty->read_buf) 1581 + return 0; 1582 + head = tty->canon_head; 1583 + tail = tty->read_tail; 1584 + nr = (head - tail) & (N_TTY_BUF_SIZE-1); 1585 + /* Skip EOF-chars.. */ 1586 + while (head != tail) { 1587 + if (test_bit(tail, tty->read_flags) && 1588 + tty->read_buf[tail] == __DISABLED_CHAR) 1589 + nr--; 1590 + tail = (tail+1) & (N_TTY_BUF_SIZE-1); 1591 + } 1592 + return nr; 1593 + } 1594 + 1595 + static int n_tty_ioctl(struct tty_struct *tty, struct file *file, 1596 + unsigned int cmd, unsigned long arg) 1597 + { 1598 + int retval; 1599 + 1600 + switch (cmd) { 1601 + case TIOCOUTQ: 1602 + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); 1603 + case TIOCINQ: 1604 + retval = tty->read_cnt; 1605 + if (L_ICANON(tty)) 1606 + retval = inq_canon(tty); 1607 + return put_user(retval, (unsigned int __user *) arg); 1608 + default: 1609 + return n_tty_ioctl_helper(tty, file, cmd, arg); 1610 + } 1586 1611 } 1587 1612 1588 1613 struct tty_ldisc_ops tty_ldisc_N_TTY = {
+2 -40
drivers/char/tty_ioctl.c
··· 489 489 490 490 static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) 491 491 { 492 - int canon_change; 493 492 struct ktermios old_termios; 494 493 struct tty_ldisc *ld; 495 494 unsigned long flags; ··· 504 505 old_termios = *tty->termios; 505 506 *tty->termios = *new_termios; 506 507 unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); 507 - canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; 508 - if (canon_change) { 509 - memset(&tty->read_flags, 0, sizeof tty->read_flags); 510 - tty->canon_head = tty->read_tail; 511 - tty->canon_data = 0; 512 - tty->erasing = 0; 513 - } 514 - 515 - /* This bit should be in the ldisc code */ 516 - if (canon_change && !L_ICANON(tty) && tty->read_cnt) 517 - /* Get characters left over from canonical mode. */ 518 - wake_up_interruptible(&tty->read_wait); 519 508 520 509 /* See if packet mode change of state. */ 521 510 if (tty->link && tty->link->packet) { ··· 664 677 665 678 #endif 666 679 667 - static unsigned long inq_canon(struct tty_struct *tty) 668 - { 669 - int nr, head, tail; 670 - 671 - if (!tty->canon_data || !tty->read_buf) 672 - return 0; 673 - head = tty->canon_head; 674 - tail = tty->read_tail; 675 - nr = (head - tail) & (N_TTY_BUF_SIZE-1); 676 - /* Skip EOF-chars.. */ 677 - while (head != tail) { 678 - if (test_bit(tail, tty->read_flags) && 679 - tty->read_buf[tail] == __DISABLED_CHAR) 680 - nr--; 681 - tail = (tail+1) & (N_TTY_BUF_SIZE-1); 682 - } 683 - return nr; 684 - } 685 680 686 681 #ifdef TIOCGETP 687 682 /* ··· 1079 1110 } 1080 1111 EXPORT_SYMBOL_GPL(tty_perform_flush); 1081 1112 1082 - int n_tty_ioctl(struct tty_struct *tty, struct file *file, 1113 + int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, 1083 1114 unsigned int cmd, unsigned long arg) 1084 1115 { 1085 1116 unsigned long flags; ··· 1117 1148 return 0; 1118 1149 case TCFLSH: 1119 1150 return tty_perform_flush(tty, arg); 1120 - case TIOCOUTQ: 1121 - return put_user(tty_chars_in_buffer(tty), (int __user *) arg); 1122 - case TIOCINQ: 1123 - retval = tty->read_cnt; 1124 - if (L_ICANON(tty)) 1125 - retval = inq_canon(tty); 1126 - return put_user(retval, (unsigned int __user *) arg); 1127 1151 case TIOCPKT: 1128 1152 { 1129 1153 int pktmode; ··· 1142 1180 return tty_mode_ioctl(tty, file, cmd, arg); 1143 1181 } 1144 1182 } 1145 - EXPORT_SYMBOL(n_tty_ioctl); 1183 + EXPORT_SYMBOL(n_tty_ioctl_helper);
+1 -1
include/linux/tty.h
··· 466 466 #endif 467 467 468 468 /* tty_ioctl.c */ 469 - extern int n_tty_ioctl(struct tty_struct *tty, struct file *file, 469 + extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, 470 470 unsigned int cmd, unsigned long arg); 471 471 472 472 /* serial.c */