static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { unsigned int mask = 0; poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty))) mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) tty->minimum_to_wake = MIN_CHAR(tty); else tty->minimum_to_wake = 1; } if (tty->ops->write && !tty_is_writelocked(tty) && tty_chars_in_buffer(tty) < WAKEUP_CHARS && tty_write_room(tty) > 0) mask |= POLLOUT | POLLWRNORM; return mask; }
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, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty))) return 1; /* fall through */ case SEL_EX: if (tty->packet && tty->link->ctrl_status) return 1; if (tty->flags & (1 << TTY_OTHER_CLOSED)) return 1; if (tty_hung_up_p(file)) return 1; if (!waitqueue_active(&tty->read_wait)) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) tty->minimum_to_wake = MIN_CHAR(tty); else tty->minimum_to_wake = 1; } select_wait(&tty->read_wait, wait); return 0; case SEL_OUT: if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS) return 1; select_wait(&tty->write_wait, wait); return 0; } return 0; }
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { unsigned char __user *b = buf; DECLARE_WAITQUEUE(wait, current); int c; int minimum, time; ssize_t retval = 0; ssize_t size; long timeout; unsigned long flags; int packet; do_it_again: BUG_ON(!tty->read_buf); c = job_control(tty, file); if (c < 0) return c; minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!tty->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { 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 { timeout = 0; if (time) { timeout = time; time = 0; } tty->minimum_to_wake = minimum = 1; } } if (file->f_flags & O_NONBLOCK) { if (!mutex_trylock(&tty->atomic_read_lock)) return -EAGAIN; } else { if (mutex_lock_interruptible(&tty->atomic_read_lock)) return -ERESTARTSYS; } packet = tty->packet; add_wait_queue(&tty->read_wait, &wait); while (nr) { if (packet && tty->link->ctrl_status) { unsigned char cs; if (b != buf) break; spin_lock_irqsave(&tty->link->ctrl_lock, flags); cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); if (tty_put_user(tty, cs, b++)) { retval = -EFAULT; b--; break; } nr--; break; } set_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 (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; break; } if (tty_hung_up_p(file)) break; if (!timeout) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } if (signal_pending(current)) { retval = -ERESTARTSYS; break; } n_tty_set_room(tty); timeout = schedule_timeout(timeout); BUG_ON(!tty->read_buf); continue; } __set_current_state(TASK_RUNNING); if (packet && b == buf) { if (tty_put_user(tty, TIOCPKT_DATA, b++)) { retval = -EFAULT; b--; break; } nr--; } if (tty->icanon && !L_EXTPROC(tty)) { while (nr && tty->read_cnt) { int eol; eol = test_and_clear_bit(tty->read_tail, tty->read_flags); c = tty->read_buf[tty->read_tail]; spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); tty->read_cnt--; if (eol) { if (--tty->canon_data < 0) tty->canon_data = 0; } spin_unlock_irqrestore(&tty->read_lock, flags); if (!eol || (c != __DISABLED_CHAR)) { if (tty_put_user(tty, c, b++)) { retval = -EFAULT; b--; break; } nr--; } if (eol) { tty_audit_push(tty); break; } } if (retval) break; } else { int uncopied; uncopied = copy_from_read_buf(tty, &b, &nr); uncopied += copy_from_read_buf(tty, &b, &nr); if (uncopied) { retval = -EFAULT; break; } } if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { n_tty_set_room(tty); check_unthrottle(tty); } if (b - buf >= minimum) break; if (time) timeout = time; } mutex_unlock(&tty->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = minimum; __set_current_state(TASK_RUNNING); size = b - buf; if (size) { retval = size; if (nr) clear_bit(TTY_PUSH, &tty->flags); } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; n_tty_set_room(tty); return retval; }
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 ssize_t read_chan(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) { unsigned char *b = buf; struct wait_queue wait = { current, NULL }; int c; int minimum, time; ssize_t retval = 0; ssize_t size; long timeout; 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_dentry->d_inode->i_rdev != CONSOLE_DEV && file->f_dentry->d_inode->i_rdev != SYSCONS_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; } } minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!tty->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { 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 { timeout = 0; if (time) { timeout = time; time = 0; } tty->minimum_to_wake = minimum = 1; } } if (file->f_flags & O_NONBLOCK) { if (down_trylock(&tty->atomic_read)) return -EAGAIN; } else { if (down_interruptible(&tty->atomic_read)) return -ERESTARTSYS; } add_wait_queue(&tty->read_wait, &wait); set_bit(TTY_DONT_FLIP, &tty->flags); while (nr) { /* First test for status change. */ if (tty->packet && tty->link->ctrl_status) { unsigned char cs; if (b != buf) break; cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; put_user(cs, b++); nr--; 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 (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; break; } if (tty_hung_up_p(file)) break; if (!timeout) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } if (signal_pending(current)) { retval = -ERESTARTSYS; break; } clear_bit(TTY_DONT_FLIP, &tty->flags); timeout = schedule_timeout(timeout); set_bit(TTY_DONT_FLIP, &tty->flags); continue; } current->state = TASK_RUNNING; /* Deal with packet mode. */ if (tty->packet && b == buf) { put_user(TIOCPKT_DATA, b++); nr--; } if (tty->icanon) { /* N.B. avoid overrun if nr == 0 */ while (nr && tty->read_cnt) { int eol; eol = test_and_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--; if (!eol || (c != __DISABLED_CHAR)) { put_user(c, b++); nr--; } if (eol) { /* this test should be redundant: * we shouldn't be reading data if * canon_data is 0 */ if (--tty->canon_data < 0) tty->canon_data = 0; break; } } } else { int uncopied; uncopied = copy_from_read_buf(tty, &b, &nr); uncopied += copy_from_read_buf(tty, &b, &nr); if (uncopied) { retval = -EFAULT; break; } } /* If there is enough space in the read buffer now, let the * low-level driver know. We use n_tty_chars_in_buffer() to * check the buffer, as it now knows about canonical mode. * Otherwise, if the driver is throttled and the line is * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) check_unthrottle(tty); if (b - buf >= minimum) break; if (time) timeout = time; } clear_bit(TTY_DONT_FLIP, &tty->flags); up(&tty->atomic_read); remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = minimum; current->state = TASK_RUNNING; size = b - buf; if (size) { retval = size; if (nr) clear_bit(TTY_PUSH, &tty->flags); } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; return retval; }
static ssize_t read_chan(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { unsigned char __user *b = buf; DECLARE_WAITQUEUE(wait, current); int c; int minimum, time; ssize_t retval = 0; ssize_t size; long timeout; unsigned long flags; do_it_again: if (!tty->read_buf) { printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); return -EIO; } c = job_control(tty, file); if(c < 0) return c; minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!tty->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { 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 { timeout = 0; if (time) { timeout = time; time = 0; } tty->minimum_to_wake = minimum = 1; } } /* * Internal serialization of reads. */ if (tx_cache_get_file_ro(file)->f_flags & O_NONBLOCK) { if (!mutex_trylock(&tty->atomic_read_lock)) return -EAGAIN; } else { if (mutex_lock_interruptible(&tty->atomic_read_lock)) return -ERESTARTSYS; } add_wait_queue(&tty->read_wait, &wait); while (nr) { /* First test for status change. */ if (tty->packet && tty->link->ctrl_status) { unsigned char cs; if (b != buf) break; cs = tty->link->ctrl_status; tty->link->ctrl_status = 0; if (put_user(cs, b++)) { retval = -EFAULT; b--; break; } nr--; break; } /* This statement must be first before checking for input so that any interrupt will set the state back to TASK_RUNNING. */ set_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 (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; break; } if (tty_hung_up_p(file)) break; if (!timeout) break; if (tx_cache_get_file_ro(file)->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } if (signal_pending(current)) { retval = -ERESTARTSYS; break; } n_tty_set_room(tty); timeout = schedule_timeout(timeout); continue; } __set_current_state(TASK_RUNNING); /* Deal with packet mode. */ if (tty->packet && b == buf) { if (put_user(TIOCPKT_DATA, b++)) { retval = -EFAULT; b--; break; } nr--; } if (tty->icanon) { /* N.B. avoid overrun if nr == 0 */ while (nr && tty->read_cnt) { int eol; eol = test_and_clear_bit(tty->read_tail, tty->read_flags); c = tty->read_buf[tty->read_tail]; spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); tty->read_cnt--; if (eol) { /* this test should be redundant: * we shouldn't be reading data if * canon_data is 0 */ if (--tty->canon_data < 0) tty->canon_data = 0; } spin_unlock_irqrestore(&tty->read_lock, flags); if (!eol || (c != __DISABLED_CHAR)) { if (put_user(c, b++)) { retval = -EFAULT; b--; break; } nr--; } if (eol) break; } if (retval) break; } else { int uncopied; uncopied = copy_from_read_buf(tty, &b, &nr); uncopied += copy_from_read_buf(tty, &b, &nr); if (uncopied) { retval = -EFAULT; break; } } /* If there is enough space in the read buffer now, let the * low-level driver know. We use n_tty_chars_in_buffer() to * check the buffer, as it now knows about canonical mode. * Otherwise, if the driver is throttled and the line is * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, * we won't get any more characters. */ if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { n_tty_set_room(tty); check_unthrottle(tty); } if (b - buf >= minimum) break; if (time) timeout = time; } mutex_unlock(&tty->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = minimum; __set_current_state(TASK_RUNNING); size = b - buf; if (size) { retval = size; if (nr) clear_bit(TTY_PUSH, &tty->flags); } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; n_tty_set_room(tty); return retval; }
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; }
/** * @brief Reads data from a TTY device. * * @details Reads @p n bytes data from the TTY device, which minor device number * is @p minor, to the buffer pointed to by @p buf. * * @param minor Minor device number of target TTY device. * @param buf Buffer where data shall be placed. * @param n Number of bytes to be read. * * @returns The number of bytes actually read to the TTY device. */ PRIVATE ssize_t tty_read(unsigned minor, char *buf, size_t n) { size_t i; /* # bytes read. */ unsigned char ch; /* Working character. */ unsigned char *p; /* Write pointer. */ UNUSED(minor); i = n; p = (unsigned char *)buf; /* Read characters. */ disable_interrupts(); while (i > 0) { /* Canonical mode. */ if (tty.term.c_lflag & ICANON) { /* Wait for data to become available. */ if (tty_sleep_empty(&tty)) { enable_interrupts(); return (-EINTR); } KBUFFER_GET(tty.rinput, ch); /* Erase. */ if (ch == ERASE_CHAR(tty)) { if (!KBUFFER_EMPTY(tty.cinput)) { KBUFFER_TAKEOUT(tty.cinput); console_put(ch, WHITE); } } /* Kill. */ else if (ch == KILL_CHAR(tty)) { while (!KBUFFER_EMPTY(tty.cinput)) { i = n; p = (unsigned char *)buf; KBUFFER_TAKEOUT(tty.cinput); console_put('\b', WHITE); } } /* End of line. */ else if (ch == EOL_CHAR(tty)) { console_put('\n', WHITE); continue; } else { /* End of file. */ if (ch == EOF_CHAR(tty)) ch = '\0'; KBUFFER_PUT(tty.cinput, ch); /* Copy data to input buffer. */ if ((ch == '\n') || (KBUFFER_FULL(tty.cinput)) || (ch == '\0')) { /* Copy data from input buffer. */ while ((i > 0) && (!KBUFFER_EMPTY(tty.cinput))) { KBUFFER_GET(tty.cinput, ch); /* EOF. */ if (ch == '\0') goto out; i--; *p++ = ch; /* Done reading. */ if (ch == '\n') goto out; } } } } /* Non canonical mode. */ else { if (MIN_CHAR(tty) > 0) { /* Case A: MIN>0, TIME>0 */ if (TIME_CHAR(tty) > 0) { kprintf("tty: MIN>0, TIME>0"); goto out; } /* Case B: MIN>0, TIME=0 */ else { /* Wait for data to become available. */ if (tty_sleep_empty(&tty)) { enable_interrupts(); return (-EINTR); } /* Copy data from input buffer. */ while ((i > 0) && (!KBUFFER_EMPTY(tty.cinput))) { KBUFFER_GET(tty.rinput, ch); i--; *p++ = ch; } } } else { /* Case C: MIN=0, TIME>0 */ if (TIME_CHAR(tty) > 0) { kprintf("tty: MIN=0, TIME>0"); goto out; } /* Case D: MIN=0, TIME=0 */ else { /* Done reading. */ if (KBUFFER_EMPTY(tty.cinput)) goto out; KBUFFER_GET(tty.rinput, ch); i--; *p++ = ch; } } } } out: enable_interrupts(); return ((ssize_t)((char *)p - buf)); }