static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) { if (!tty) return; tty->icanon = (L_ICANON(tty) != 0); if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { cli(); memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) set_bit('\r', &tty->process_char_map); if (I_INLCR(tty)) set_bit('\n', &tty->process_char_map); if (L_ICANON(tty)) { set_bit(ERASE_CHAR(tty), &tty->process_char_map); set_bit(KILL_CHAR(tty), &tty->process_char_map); set_bit(EOF_CHAR(tty), &tty->process_char_map); set_bit('\n', &tty->process_char_map); set_bit(EOL_CHAR(tty), &tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), &tty->process_char_map); set_bit(LNEXT_CHAR(tty), &tty->process_char_map); set_bit(EOL2_CHAR(tty), &tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), &tty->process_char_map); } } if (I_IXON(tty)) { set_bit(START_CHAR(tty), &tty->process_char_map); set_bit(STOP_CHAR(tty), &tty->process_char_map); } if (L_ISIG(tty)) { set_bit(INTR_CHAR(tty), &tty->process_char_map); set_bit(QUIT_CHAR(tty), &tty->process_char_map); set_bit(SUSP_CHAR(tty), &tty->process_char_map); } clear_bit(__DISABLED_CHAR, &tty->process_char_map); sti(); tty->raw = 0; tty->real_raw = 0; } else { tty->raw = 1; if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && (I_IGNPAR(tty) || !I_INPCK(tty)) && (tty->driver.flags & TTY_DRIVER_REAL_RAW)) tty->real_raw = 1; else tty->real_raw = 0; } }
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 void change_termios(struct tty_struct * tty, struct termios * new_termios) { int canon_change; struct termios old_termios = *tty->termios; struct tty_ldisc *ld; /* * Perform the actual termios internal changes under lock. */ /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ down(&tty->termios_sem); *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); canon_change = (old_termios.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) /* Get characters left over from canonical mode. */ wake_up_interruptible(&tty->read_wait); /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { int old_flow = ((old_termios.c_iflag & IXON) && (old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTART] == '\021')); int new_flow = (I_IXON(tty) && STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); if (old_flow != new_flow) { tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); if (new_flow) tty->ctrl_status |= TIOCPKT_DOSTOP; else tty->ctrl_status |= TIOCPKT_NOSTOP; wake_up_interruptible(&tty->link->read_wait); } } if (tty->driver->set_termios) (*tty->driver->set_termios)(tty, &old_termios); ld = tty_ldisc_ref(tty); if (ld != NULL) { if (ld->set_termios) (ld->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } up(&tty->termios_sem); }
static inline int input_available_p(struct tty_struct *tty, int amt) { if (L_ICANON(tty)) { if (tty->canon_data) return 1; } else if (tty->read_cnt >= (amt ? amt : 1)) return 1; return 0; }
static int set_termios_2(struct tty_struct * tty, struct termios * termios) { struct termios old_termios = *tty->termios; int canon_change; canon_change = (old_termios.c_lflag ^ termios->c_lflag) & ICANON; cli(); *tty->termios = *termios; if (canon_change) { memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags); tty->canon_head = tty->secondary.tail; tty->canon_data = 0; tty->erasing = 0; } sti(); if (canon_change && !L_ICANON(tty) && !EMPTY(&tty->secondary)) /* Get characters left over from canonical mode. */ wake_up_interruptible(&tty->secondary.proc_list); /* see if packet mode change of state */ if (tty->link && tty->link->packet) { int old_flow = ((old_termios.c_iflag & IXON) && (old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTART] == '\021')); int new_flow = (I_IXON(tty) && STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); if (old_flow != new_flow) { tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); if (new_flow) tty->ctrl_status |= TIOCPKT_DOSTOP; else tty->ctrl_status |= TIOCPKT_NOSTOP; wake_up_interruptible(&tty->link->secondary.proc_list); } } unset_locked_termios(tty->termios, &old_termios, termios_locked[tty->line]); if (tty->set_termios) (*tty->set_termios)(tty, &old_termios); return 0; }
static void change_termios(struct tty_struct * tty, struct termios * new_termios) { int canon_change; struct termios old_termios = *tty->termios; local_irq_disable(); // FIXME: is this safe? *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); canon_change = (old_termios.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; } local_irq_enable(); // FIXME: is this safe? if (canon_change && !L_ICANON(tty) && tty->read_cnt) /* Get characters left over from canonical mode. */ wake_up_interruptible(&tty->read_wait); /* see if packet mode change of state */ if (tty->link && tty->link->packet) { int old_flow = ((old_termios.c_iflag & IXON) && (old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTART] == '\021')); int new_flow = (I_IXON(tty) && STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); if (old_flow != new_flow) { tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); if (new_flow) tty->ctrl_status |= TIOCPKT_DOSTOP; else tty->ctrl_status |= TIOCPKT_NOSTOP; wake_up_interruptible(&tty->link->read_wait); } } if (tty->driver->set_termios) (*tty->driver->set_termios)(tty, &old_termios); if (tty->ldisc.set_termios) (*tty->ldisc.set_termios)(tty, &old_termios); }
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); } }
static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { 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)) { tty->raw = 1; tty->real_raw = 1; n_tty_set_room(tty); return; } if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) set_bit('\r', tty->process_char_map); if (I_INLCR(tty)) set_bit('\n', tty->process_char_map); if (L_ICANON(tty)) { set_bit(ERASE_CHAR(tty), tty->process_char_map); set_bit(KILL_CHAR(tty), tty->process_char_map); set_bit(EOF_CHAR(tty), tty->process_char_map); set_bit('\n', tty->process_char_map); set_bit(EOL_CHAR(tty), tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), tty->process_char_map); set_bit(LNEXT_CHAR(tty), tty->process_char_map); set_bit(EOL2_CHAR(tty), tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), tty->process_char_map); } } if (I_IXON(tty)) { set_bit(START_CHAR(tty), tty->process_char_map); set_bit(STOP_CHAR(tty), tty->process_char_map); } if (L_ISIG(tty)) { set_bit(INTR_CHAR(tty), tty->process_char_map); set_bit(QUIT_CHAR(tty), tty->process_char_map); set_bit(SUSP_CHAR(tty), tty->process_char_map); } clear_bit(__DISABLED_CHAR, tty->process_char_map); tty->raw = 0; tty->real_raw = 0; } else { tty->raw = 1; if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && (I_IGNPAR(tty) || !I_INPCK(tty)) && (tty->driver->flags & TTY_DRIVER_REAL_RAW)) tty->real_raw = 1; else tty->real_raw = 0; } n_tty_set_room(tty); wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); }
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; int size; do_it_again: if (!tty->read_buf) { printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); return -EIO; } /* 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) { 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; if (time) tty->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || (tty->minimum_to_wake > minimum)) tty->minimum_to_wake = minimum; } else { if (time) { current->timeout = time + jiffies; time = 0; } else current->timeout = 0; tty->minimum_to_wake = minimum = 1; } } add_wait_queue(&tty->read_wait, &wait); while (1) { /* First test for status change. */ if (tty->packet && tty->link->ctrl_status) { if (b != buf) break; put_user(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 (((minimum - (b - buf)) < tty->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) tty->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { if (tty->flags & (1 << TTY_OTHER_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_user(TIOCPKT_DATA, b++); nr--; } if (L_ICANON(tty)) { while (1) { int eol; disable_bh(TQUEUE_BH); if (!tty->read_cnt) { enable_bh(TQUEUE_BH); break; } eol = clear_bit(tty->read_tail, &tty->read_flags); c = tty->read_buf[tty->read_tail]; tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); tty->read_cnt--; enable_bh(TQUEUE_BH); if (!eol) { put_user(c, b++); if (--nr) continue; break; } if (--tty->canon_data < 0) { tty->canon_data = 0; } if (c != __DISABLED_CHAR) { put_user(c, b++); nr--; } break; } } else { disable_bh(TQUEUE_BH); copy_from_read_buf(tty, &b, &nr); copy_from_read_buf(tty, &b, &nr); enable_bh(TQUEUE_BH); } /* If there is enough space in the read buffer now, let the low-level driver know. */ if (tty->driver.unthrottle && (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE) && clear_bit(TTY_THROTTLED, &tty->flags)) tty->driver.unthrottle(tty); if (b - buf >= minimum || !nr) break; if (time) current->timeout = time + jiffies; } remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = minimum; current->state = TASK_RUNNING; current->timeout = 0; size = b - buf; if (size && nr) clear_bit(TTY_PUSH, &tty->flags); if (!size && clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; if (!size && !retval) clear_bit(TTY_PUSH, &tty->flags); return (size ? size : retval); }
static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { if (tty->raw) { put_tty_queue(c, tty); return; } if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) { start_tty(tty); return; } if (I_ISTRIP(tty)) c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty)) c=tolower(c); if (tty->closing) { if (I_IXON(tty)) { if (c == START_CHAR(tty)) start_tty(tty); else if (c == STOP_CHAR(tty)) stop_tty(tty); } return; } /* * If the previous character was LNEXT, or we know that this * character is not one of the characters that we'll have to * handle specially, do shortcut processing to speed things * up. */ if (!test_bit(c, &tty->process_char_map) || tty->lnext) { finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); /* beep if no space */ return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); put_tty_queue(c, tty); return; } if (c == '\r') { if (I_IGNCR(tty)) return; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) c = '\r'; if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); return; } if (c == STOP_CHAR(tty)) { stop_tty(tty); return; } } if (L_ISIG(tty)) { int signal; signal = SIGINT; if (c == INTR_CHAR(tty)) goto send_signal; signal = SIGQUIT; if (c == QUIT_CHAR(tty)) goto send_signal; signal = SIGTSTP; if (c == SUSP_CHAR(tty)) { send_signal: isig(signal, tty, 0); return; } } if (L_ICANON(tty)) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { tty->lnext = 1; if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { put_char('^', tty); put_char('\b', tty); } } return; } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { unsigned long tail = tty->canon_head; finish_erasing(tty); echo_char(c, tty); opost('\n', tty); while (tail != tty->read_head) { echo_char(tty->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } return; } if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); return; } opost('\n', tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { if (tty->canon_head != tty->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } if ((c == EOL_CHAR(tty)) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */ if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); return; } /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } /* * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR? */ if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); handle_newline: set_bit(tty->read_head, &tty->read_flags); put_tty_queue(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; if (tty->fasync) kill_fasync(tty->fasync, SIGIO); if (tty->read_wait) wake_up_interruptible(&tty->read_wait); return; } } finish_erasing(tty); if (L_ECHO(tty)) { if (tty->read_cnt >= N_TTY_BUF_SIZE-1) { put_char('\a', tty); /* beep if no space */ return; } if (c == '\n') opost('\n', tty); else { /* Record the column of first canon char. */ if (tty->canon_head == tty->read_head) tty->canon_column = tty->column; echo_char(c, tty); } } if (I_PARMRK(tty) && c == (unsigned char) '\377') put_tty_queue(c, tty); put_tty_queue(c, tty); }
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 void copy_to_cooked(struct tty_struct * tty) { int c, special_flag; unsigned long flags; if (!tty) { printk("copy_to_cooked: called with NULL tty\n"); return; } if (!tty->write) { printk("copy_to_cooked: tty %d has null write routine\n", tty->line); } while (1) { /* * Check to see how much room we have left in the * secondary queue. Send a throttle command or abort * if necessary. */ c = LEFT(&tty->secondary); if (tty->throttle && (c < SQ_THRESHOLD_LW) && !set_bit(TTY_SQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_SQ_FULL); if (c == 0) break; save_flags(flags); cli(); if (!EMPTY(&tty->read_q)) { c = tty->read_q.buf[tty->read_q.tail]; special_flag = clear_bit(tty->read_q.tail, &tty->readq_flags); INC(tty->read_q.tail); restore_flags(flags); } else { restore_flags(flags); break; } if (special_flag) { tty->char_error = c; continue; } if (tty->char_error) { if (tty->char_error == TTY_BREAK) { tty->char_error = 0; if (I_IGNBRK(tty)) continue; /* A break is handled by the lower levels. */ if (I_BRKINT(tty)) continue; if (I_PARMRK(tty)) { put_tty_queue('\377', &tty->secondary); put_tty_queue('\0', &tty->secondary); } put_tty_queue('\0', &tty->secondary); continue; } if (tty->char_error == TTY_OVERRUN) { tty->char_error = 0; printk("tty%d: input overrun\n", tty->line); continue; } /* Must be a parity or frame error */ tty->char_error = 0; if (I_IGNPAR(tty)) { continue; } if (I_PARMRK(tty)) { put_tty_queue('\377', &tty->secondary); put_tty_queue('\0', &tty->secondary); put_tty_queue(c, &tty->secondary); } else put_tty_queue('\0', &tty->secondary); continue; } if (I_ISTRIP(tty)) c &= 0x7f; if (!tty->lnext) { if (c == '\r') { if (I_IGNCR(tty)) continue; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) c = '\r'; } if (I_IUCLC(tty) && L_IEXTEN(tty)) c=tolower(c); if (c == __DISABLED_CHAR) tty->lnext = 1; if (L_ICANON(tty) && !tty->lnext) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); continue; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { tty->lnext = 1; if (L_ECHO(tty)) { if (tty->erasing) { opost('/', tty); tty->erasing = 0; } if (L_ECHOCTL(tty)) { opost('^', tty); opost('\b', tty); } } continue; } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { unsigned long tail = tty->canon_head; if (tty->erasing) { opost('/', tty); tty->erasing = 0; } echo_char(c, tty); opost('\n', tty); while (tail != tty->secondary.head) { echo_char(tty->secondary.buf[tail], tty); INC(tail); } continue; } } if (I_IXON(tty) && !tty->lnext) { if ((tty->stopped && I_IXANY(tty) && L_IEXTEN(tty)) || c == START_CHAR(tty)) { start_tty(tty); continue; } if (c == STOP_CHAR(tty)) { stop_tty(tty); continue; } } if (L_ISIG(tty) && !tty->lnext) { if (c == INTR_CHAR(tty)) { isig(SIGINT, tty); continue; } if (c == QUIT_CHAR(tty)) { isig(SIGQUIT, tty); continue; } if (c == SUSP_CHAR(tty)) { if (!is_orphaned_pgrp(tty->pgrp)) isig(SIGTSTP, tty); continue; } } if (tty->erasing) { opost('/', tty); tty->erasing = 0; } if (c == '\n' && !tty->lnext) { if (L_ECHO(tty) || (L_ICANON(tty) && L_ECHONL(tty))) opost('\n', tty); } else if (L_ECHO(tty)) { /* Don't echo the EOF char in canonical mode. Sun handles this differently by echoing the char and then backspacing, but that's a hack. */ if (c != EOF_CHAR(tty) || !L_ICANON(tty) || tty->lnext) { /* Record the column of first canon char. */ if (tty->canon_head == tty->secondary.head) tty->canon_column = tty->column; echo_char(c, tty); } } if (I_PARMRK(tty) && c == (unsigned char) '\377' && (c != EOF_CHAR(tty) || !L_ICANON(tty) || tty->lnext)) put_tty_queue(c, &tty->secondary); if (L_ICANON(tty) && !tty->lnext && (c == '\n' || c == EOF_CHAR(tty) || c == EOL_CHAR(tty) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty)))) { if (c == EOF_CHAR(tty)) c = __DISABLED_CHAR; set_bit(tty->secondary.head, &tty->secondary_flags); put_tty_queue(c, &tty->secondary); tty->canon_head = tty->secondary.head; tty->canon_data++; } else put_tty_queue(c, &tty->secondary); tty->lnext = 0; } if (!EMPTY(&tty->write_q)) TTY_WRITE_FLUSH(tty); if (L_ICANON(tty) ? tty->canon_data : !EMPTY(&tty->secondary)) wake_up_interruptible(&tty->secondary.proc_list); if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW) && clear_bit(TTY_RQ_THROTTLED, &tty->flags)) tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL); }
int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { struct tty_struct * tty; struct tty_struct * other_tty; struct tty_struct * termios_tty; pid_t pgrp; int dev; int termios_dev; int retval; if (MAJOR(file->f_rdev) != TTY_MAJOR) { printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n"); return -EINVAL; } dev = MINOR(file->f_rdev); tty = TTY_TABLE(dev); if (!tty) return -EINVAL; if (IS_A_PTY(dev)) other_tty = tty_table[PTY_OTHER(dev)]; else other_tty = NULL; if (IS_A_PTY_MASTER(dev)) { termios_tty = other_tty; termios_dev = PTY_OTHER(dev); } else { termios_tty = tty; termios_dev = dev; } switch (cmd) { case TCGETS: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct termios)); if (retval) return retval; memcpy_tofs((struct termios *) arg, termios_tty->termios, sizeof (struct termios)); return 0; case TCSETSF: case TCSETSW: case TCSETS: retval = check_change(termios_tty, termios_dev); if (retval) return retval; if (cmd == TCSETSF || cmd == TCSETSW) { if (cmd == TCSETSF) flush_input(termios_tty); wait_until_sent(termios_tty, 0); } return set_termios(termios_tty, (struct termios *) arg, termios_dev); case TCGETA: return get_termio(termios_tty,(struct termio *) arg); case TCSETAF: case TCSETAW: case TCSETA: retval = check_change(termios_tty, termios_dev); if (retval) return retval; if (cmd == TCSETAF || cmd == TCSETAW) { if (cmd == TCSETAF) flush_input(termios_tty); wait_until_sent(termios_tty, 0); } return set_termio(termios_tty, (struct termio *) arg, termios_dev); case TCXONC: retval = check_change(tty, dev); if (retval) return retval; switch (arg) { case TCOOFF: stop_tty(tty); break; case TCOON: start_tty(tty); break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) put_tty_queue(STOP_CHAR(tty), &tty->write_q); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) put_tty_queue(START_CHAR(tty), &tty->write_q); break; default: return -EINVAL; } return 0; case TCFLSH: retval = check_change(tty, dev); if (retval) return retval; switch (arg) { case TCIFLUSH: flush_input(tty); break; case TCIOFLUSH: flush_input(tty); /* fall through */ case TCOFLUSH: flush_output(tty); break; default: return -EINVAL; } return 0; case TIOCEXCL: set_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCNXCL: clear_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCSCTTY: if (current->leader && (current->session == tty->session)) return 0; /* * The process must be a session leader and * not have a controlling tty already. */ if (!current->leader || (current->tty >= 0)) return -EPERM; if (tty->session > 0) { /* * This tty is already the controlling * tty for another session group! */ if ((arg == 1) && suser()) { /* * Steal it away */ struct task_struct *p; for_each_task(p) if (p->tty == dev) p->tty = -1; } else return -EPERM; } current->tty = dev; tty->session = current->session; tty->pgrp = current->pgrp; return 0; case TIOCGPGRP: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (pid_t)); if (retval) return retval; if (current->tty != termios_dev) return -ENOTTY; put_fs_long(termios_tty->pgrp, (pid_t *) arg); return 0; case TIOCSPGRP: retval = check_change(termios_tty, termios_dev); if (retval) return retval; if ((current->tty < 0) || (current->tty != termios_dev) || (termios_tty->session != current->session)) return -ENOTTY; pgrp = get_fs_long((pid_t *) arg); if (pgrp < 0) return -EINVAL; if (session_of_pgrp(pgrp) != current->session) return -EPERM; termios_tty->pgrp = pgrp; return 0; case TIOCOUTQ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (unsigned long)); if (retval) return retval; put_fs_long(CHARS(&tty->write_q), (unsigned long *) arg); return 0; case TIOCINQ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (unsigned long)); if (retval) return retval; if (L_ICANON(tty)) put_fs_long(inq_canon(tty), (unsigned long *) arg); else put_fs_long(CHARS(&tty->secondary), (unsigned long *) arg); return 0; case TIOCSTI: if ((current->tty != dev) && !suser()) return -EPERM; retval = verify_area(VERIFY_READ, (void *) arg, 1); if (retval) return retval; put_tty_queue(get_fs_byte((char *) arg), &tty->read_q); TTY_READ_FLUSH(tty); return 0; case TIOCGWINSZ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct winsize)); if (retval) return retval; memcpy_tofs((struct winsize *) arg, &tty->winsize, sizeof (struct winsize)); return 0; case TIOCSWINSZ: if (IS_A_PTY_MASTER(dev)) set_window_size(other_tty,(struct winsize *) arg); return set_window_size(tty,(struct winsize *) arg); case TIOCLINUX: switch (get_fs_byte((char *)arg)) { case 0: return do_screendump(arg); case 1: return do_get_ps_info(arg); #ifdef CONFIG_SELECTION case 2: return set_selection(arg); case 3: return paste_selection(tty); case 4: unblank_screen(); return 0; #endif /* CONFIG_SELECTION */ default: return -EINVAL; } case TIOCCONS: if (IS_A_CONSOLE(dev)) { if (!suser()) return -EPERM; redirect = NULL; return 0; } if (redirect) return -EBUSY; if (!suser()) return -EPERM; if (IS_A_PTY_MASTER(dev)) redirect = other_tty; else if (IS_A_PTY_SLAVE(dev)) redirect = tty; else return -ENOTTY; return 0; case FIONBIO: arg = get_fs_long((unsigned long *) arg); if (arg) file->f_flags |= O_NONBLOCK; else file->f_flags &= ~O_NONBLOCK; return 0; case TIOCNOTTY: if (current->tty != dev) return -ENOTTY; if (current->leader) disassociate_ctty(0); current->tty = -1; return 0; case TIOCGETD: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (unsigned long)); if (retval) return retval; put_fs_long(tty->disc, (unsigned long *) arg); return 0; case TIOCSETD: retval = check_change(tty, dev); if (retval) return retval; arg = get_fs_long((unsigned long *) arg); return tty_set_ldisc(tty, arg); case TIOCGLCKTRMIOS: arg = get_fs_long((unsigned long *) arg); retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof (struct termios)); if (retval) return retval; memcpy_tofs((struct termios *) arg, &termios_locked[termios_dev], sizeof (struct termios)); return 0; case TIOCSLCKTRMIOS: if (!suser()) return -EPERM; arg = get_fs_long((unsigned long *) arg); memcpy_fromfs(&termios_locked[termios_dev], (struct termios *) arg, sizeof (struct termios)); return 0; case TIOCPKT: if (!IS_A_PTY_MASTER(dev)) return -ENOTTY; retval = verify_area(VERIFY_READ, (void *) arg, sizeof (unsigned long)); if (retval) return retval; if (get_fs_long(arg)) { if (!tty->packet) { tty->packet = 1; tty->link->ctrl_status = 0; } } else tty->packet = 0; return 0; case TCSBRK: case TCSBRKP: retval = check_change(tty, dev); if (retval) return retval; wait_until_sent(tty, 0); if (!tty->ioctl) return 0; tty->ioctl(tty, file, cmd, arg); return 0; default: if (tty->ioctl) { retval = (tty->ioctl)(tty, file, cmd, arg); if (retval != -EINVAL) return retval; } if (ldiscs[tty->disc].ioctl) { retval = (ldiscs[tty->disc].ioctl) (tty, file, cmd, arg); return retval; } return -EINVAL; }
int n_tty_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) { struct tty_struct * real_tty; int retval; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) real_tty = tty->link; else real_tty = tty; switch (cmd) { #ifdef TIOCGETP case TIOCGETP: return get_sgttyb(real_tty, (struct sgttyb *) arg); case TIOCSETP: case TIOCSETN: return set_sgttyb(real_tty, (struct sgttyb *) arg); #endif #ifdef TIOCGETC case TIOCGETC: return get_tchars(real_tty, (struct tchars *) arg); case TIOCSETC: return set_tchars(real_tty, (struct tchars *) arg); #endif #ifdef TIOCGLTC case TIOCGLTC: return get_ltchars(real_tty, (struct ltchars *) arg); case TIOCSLTC: return set_ltchars(real_tty, (struct ltchars *) arg); #endif case TCGETS: if (kernel_termios_to_user_termios((struct termios *)arg, real_tty->termios)) return -EFAULT; return 0; case TCSETSF: return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_WAIT); case TCSETSW: return set_termios(real_tty, arg, TERMIOS_WAIT); case TCSETS: return set_termios(real_tty, arg, 0); case TCGETA: return get_termio(real_tty,(struct termio *) arg); case TCSETAF: return set_termios(real_tty, arg, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO); case TCSETAW: return set_termios(real_tty, arg, TERMIOS_WAIT | TERMIOS_TERMIO); case TCSETA: return set_termios(real_tty, arg, TERMIOS_TERMIO); case TCXONC: retval = tty_check_change(tty); if (retval) return retval; switch (arg) { case TCOOFF: if (!tty->flow_stopped) { tty->flow_stopped = 1; stop_tty(tty); } break; case TCOON: if (tty->flow_stopped) { tty->flow_stopped = 0; start_tty(tty); } break; case TCIOFF: if (STOP_CHAR(tty) != __DISABLED_CHAR) send_prio_char(tty, STOP_CHAR(tty)); break; case TCION: if (START_CHAR(tty) != __DISABLED_CHAR) send_prio_char(tty, START_CHAR(tty)); break; default: return -EINVAL; } return 0; case TCFLSH: retval = tty_check_change(tty); if (retval) return retval; switch (arg) { case TCIFLUSH: if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); break; case TCIOFLUSH: if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); /* fall through */ case TCOFLUSH: if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); break; default: return -EINVAL; } return 0; case TIOCOUTQ: return put_user(tty->driver->chars_in_buffer ? tty->driver->chars_in_buffer(tty) : 0, (int *) arg); case TIOCINQ: retval = tty->read_cnt; if (L_ICANON(tty)) retval = inq_canon(tty); return put_user(retval, (unsigned int *) arg); case TIOCGLCKTRMIOS: if (kernel_termios_to_user_termios((struct termios *)arg, real_tty->termios_locked)) return -EFAULT; return 0; case TIOCSLCKTRMIOS: if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (user_termios_to_kernel_termios(real_tty->termios_locked, (struct termios *) arg)) return -EFAULT; return 0; case TIOCPKT: { int pktmode; if (tty->driver->type != TTY_DRIVER_TYPE_PTY || tty->driver->subtype != PTY_TYPE_MASTER) return -ENOTTY; if (get_user(pktmode, (int *) arg)) return -EFAULT; if (pktmode) { if (!tty->packet) { tty->packet = 1; tty->link->ctrl_status = 0; } } else tty->packet = 0; return 0; } case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); case TIOCSSOFTCAR: if (get_user(arg, (unsigned int *) arg)) return -EFAULT; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; default: return -ENOIOCTLCMD; } }