📄 n_tty.c
字号:
/* * n_tty.c --- implements the N_TTY line discipline. * * This code used to be in tty_io.c, but things are getting hairy * enough that it made sense to split things off. (The N_TTY * processing has changed so much that it's hardly recognizable, * anyway...) * * Note that the open routine for N_TTY is guaranteed never to return * an error. This is because Linux will fall back to setting a line * to N_TTY if it can not switch to any other line discipline. * * Written by Theodore Ts'o, Copyright 1994. * * This file also contains code originally written by Linus Torvalds, * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. * * This file may be redistributed under the terms of the GNU Public * License. */#include <linux/types.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fcntl.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/timer.h>#include <linux/ctype.h>#include <linux/kd.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/poll.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/bitops.h>#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* number of characters left in xmit buffer before select has we have room */#define WAKEUP_CHARS 256/* * This defines the low- and high-watermarks for throttling and * unthrottling the TTY driver. These watermarks are used for * controlling the space in the read buffer. */#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */#define TTY_THRESHOLD_UNTHROTTLE 128static inline void put_tty_queue(unsigned char c, struct tty_struct *tty){ if (tty->read_cnt < N_TTY_BUF_SIZE) { tty->read_buf[tty->read_head] = c; tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1); tty->read_cnt++; }}/* * Check whether to call the driver.unthrottle function. * We test the TTY_THROTTLED bit first so that it always * indicates the current state. */static void check_unthrottle(struct tty_struct * tty){ if (tty->count && test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->driver.unthrottle) tty->driver.unthrottle(tty);}/* * Reset the read buffer counters, clear the flags, * and make sure the driver is unthrottled. Called * from n_tty_open() and n_tty_flush_buffer(). */static void reset_buffer_flags(struct tty_struct *tty){ tty->read_head = tty->read_tail = tty->read_cnt = 0; tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); check_unthrottle(tty);}/* * Flush the input buffer */void n_tty_flush_buffer(struct tty_struct * tty){ /* clear everything and unthrottle the driver */ reset_buffer_flags(tty); if (!tty->link) return; if (tty->link->packet) { tty->ctrl_status |= TIOCPKT_FLUSHREAD; wake_up_interruptible(&tty->link->read_wait); }}/* * Return number of characters buffered to be delivered to user */ssize_t n_tty_chars_in_buffer(struct tty_struct *tty){ if (tty->icanon) { if (!tty->canon_data) return 0; return (tty->canon_head > tty->read_tail) ? tty->canon_head - tty->read_tail : tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); } return tty->read_cnt;}/* * Perform OPOST processing. Returns -1 when the output device is * full and the character must be retried. */static int opost(unsigned char c, struct tty_struct *tty){ int space, spaces; space = tty->driver.write_room(tty); if (!space) return -1; if (O_OPOST(tty)) { switch (c) { case '\n': if (O_ONLRET(tty)) tty->column = 0; if (O_ONLCR(tty)) { if (space < 2) return -1; tty->driver.put_char(tty, '\r'); tty->column = 0; } tty->canon_column = tty->column; break; case '\r': if (O_ONOCR(tty) && tty->column == 0) return 0; if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) tty->canon_column = tty->column = 0; break; } tty->canon_column = tty->column = 0; break; case '\t': spaces = 8 - (tty->column & 7); if (O_TABDLY(tty) == XTABS) { if (space < spaces) return -1; tty->column += spaces; tty->driver.write(tty, 0, " ", spaces); return 0; } tty->column += spaces; break; case '\b': if (tty->column > 0) tty->column--; break; default: if (O_OLCUC(tty)) c = toupper(c); if (!iscntrl(c)) tty->column++; break; } } tty->driver.put_char(tty, c); return 0;}/* * opost_block --- to speed up block console writes, among other * things. */static ssize_t opost_block(struct tty_struct * tty, const unsigned char * inbuf, unsigned int nr){ char buf[80]; int space; int i; char *cp; space = tty->driver.write_room(tty); if (!space) return 0; if (nr > space) nr = space; if (nr > sizeof(buf)) nr = sizeof(buf); nr -= copy_from_user(buf, inbuf, nr); if (!nr) return 0; for (i = 0, cp = buf; i < nr; i++, cp++) { switch (*cp) { case '\n': if (O_ONLRET(tty)) tty->column = 0; if (O_ONLCR(tty)) goto break_out; tty->canon_column = tty->column; break; case '\r': if (O_ONOCR(tty) && tty->column == 0) goto break_out; if (O_OCRNL(tty)) { *cp = '\n'; if (O_ONLRET(tty)) tty->canon_column = tty->column = 0; break; } tty->canon_column = tty->column = 0; break; case '\t': goto break_out; case '\b': if (tty->column > 0) tty->column--; break; default: if (O_OLCUC(tty)) *cp = toupper(*cp); if (!iscntrl(*cp)) tty->column++; break; } }break_out: if (tty->driver.flush_chars) tty->driver.flush_chars(tty); i = tty->driver.write(tty, 0, buf, i); return i;}static inline void put_char(unsigned char c, struct tty_struct *tty){ tty->driver.put_char(tty, c);}/* Must be called only when L_ECHO(tty) is true. */static void echo_char(unsigned char c, struct tty_struct *tty){ if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') { put_char('^', tty); put_char(c ^ 0100, tty); tty->column += 2; } else opost(c, tty);}static inline void finish_erasing(struct tty_struct *tty){ if (tty->erasing) { put_char('/', tty); tty->column += 2; tty->erasing = 0; }}static void eraser(unsigned char c, struct tty_struct *tty){ enum { ERASE, WERASE, KILL } kill_type; int head, seen_alnums; if (tty->read_head == tty->canon_head) { /* opost('\a', tty); */ /* what do you think? */ return; } if (c == ERASE_CHAR(tty)) kill_type = ERASE; else if (c == WERASE_CHAR(tty)) kill_type = WERASE; else { if (!L_ECHO(tty)) { tty->read_cnt -= ((tty->read_head - tty->canon_head) & (N_TTY_BUF_SIZE - 1)); tty->read_head = tty->canon_head; return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { tty->read_cnt -= ((tty->read_head - tty->canon_head) & (N_TTY_BUF_SIZE - 1)); tty->read_head = tty->canon_head; finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty)) opost('\n', tty); return; } kill_type = KILL; } seen_alnums = 0; while (tty->read_head != tty->canon_head) { head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1); c = tty->read_buf[head]; if (kill_type == WERASE) { /* Equivalent to BSD's ALTWERASE. */ if (isalnum(c) || c == '_') seen_alnums++; else if (seen_alnums) break; } tty->read_head = head; tty->read_cnt--; if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!tty->erasing) { put_char('\\', tty); tty->column++; tty->erasing = 1; } echo_char(c, tty); } else if (kill_type == ERASE && !L_ECHOE(tty)) { echo_char(ERASE_CHAR(tty), tty); } else if (c == '\t') { unsigned int col = tty->canon_column; unsigned long tail = tty->canon_head; /* Find the column of the last char. */ while (tail != tty->read_head) { c = tty->read_buf[tail]; if (c == '\t') col = (col | 7) + 1; else if (iscntrl(c)) { if (L_ECHOCTL(tty)) col += 2; } else col++; tail = (tail+1) & (N_TTY_BUF_SIZE-1); } /* should never happen */ if (tty->column > 0x80000000) tty->column = 0; /* Now backup to that column. */ while (tty->column > col) { /* Can't use opost here. */ put_char('\b', tty); if (tty->column > 0) tty->column--; } } else { if (iscntrl(c) && L_ECHOCTL(tty)) { put_char('\b', tty); put_char(' ', tty); put_char('\b', tty); if (tty->column > 0) tty->column--; } if (!iscntrl(c) || L_ECHOCTL(tty)) { put_char('\b', tty); put_char(' ', tty); put_char('\b', tty); if (tty->column > 0) tty->column--; } } } if (kill_type == ERASE) break; } if (tty->read_head == tty->canon_head) finish_erasing(tty);}static inline void isig(int sig, struct tty_struct *tty, int flush){ if (tty->pgrp > 0) kill_pg(tty->pgrp, sig, 1); if (flush || !L_NOFLSH(tty)) { n_tty_flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); }}static inline void n_tty_receive_break(struct tty_struct *tty){ if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { isig(SIGINT, tty, 1); return; } if (I_PARMRK(tty)) { put_tty_queue('\377', tty); put_tty_queue('\0', tty); } put_tty_queue('\0', tty); wake_up_interruptible(&tty->read_wait);}static inline void n_tty_receive_overrun(struct tty_struct *tty){ char buf[64]; tty->num_overrun++; if (time_before(tty->overrun_time, jiffies - HZ)) { printk("%s: %d input overrun(s)\n", tty_name(tty, buf), tty->num_overrun); tty->overrun_time = jiffies; tty->num_overrun = 0; }}static inline void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c){ if (I_IGNPAR(tty)) { return; } if (I_PARMRK(tty)) { put_tty_queue('\377', tty); put_tty_queue('\0', tty); put_tty_queue(c, tty); } else if (I_INPCK(tty)) put_tty_queue('\0', tty); else put_tty_queue(c, tty); wake_up_interruptible(&tty->read_wait);}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 && !tty->flow_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 (tty->icanon) { 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -