// SPDX-License-Identifier: GPL-1.0+ /* * 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. * * Reduced memory usage for older ARM systems - Russell King. * * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of * the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu> * who actually finally proved there really was a race. * * 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to * waiting writing processes-Sapan Bhatia <sapan@corewars.org>. * Also fixed a bug in BLOCKING mode where n_tty_write returns * EAGAIN
*/
/* * Until this number of characters is queued in the xmit buffer, select will * return "we have room for writes".
*/ #define WAKEUP_CHARS 256
#define N_TTY_BUF_SIZE 4096
/* * 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 128
/* * Special byte codes used in the echo buffer to represent operations * or special handling of characters. Bytes in the echo buffer that * are not part of such special blocks are treated as normal character * codes.
*/ #define ECHO_OP_START 0xff #define ECHO_OP_MOVE_BACK_COL 0x80 #define ECHO_OP_SET_CANON_COL 0x81 #define ECHO_OP_ERASE_TAB 0x82
/* If we are not echoing the data, perhaps this is a secret so erase it */ staticvoid zero_buffer(conststruct tty_struct *tty, u8 *buffer, size_t size)
{ if (L_ICANON(tty) && !L_ECHO(tty))
memset(buffer, 0, size);
}
/** * n_tty_kick_worker - start input worker (if required) * @tty: terminal * * Re-schedules the flip buffer work if it may have stopped. * * Locking: * * Caller holds exclusive %termios_rwsem, or * * n_tty_read()/consumer path: * holds non-exclusive %termios_rwsem
*/ staticvoid n_tty_kick_worker(conststruct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
/* Did the input worker stop? Restart it */ if (unlikely(READ_ONCE(ldata->no_room))) {
WRITE_ONCE(ldata->no_room, 0);
WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); /* see if ldisc has been killed - if so, this means that * even though the ldisc has been halted and ->buf.work * cancelled, ->buf.work is about to be rescheduled
*/
WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), "scheduling buffer work for halted ldisc\n");
tty_buffer_restart_work(tty->port);
}
}
/** * n_tty_write_wakeup - asynchronous I/O notifier * @tty: tty device * * Required for the ptys, serial driver etc. since processes that attach * themselves to the master and rely on ASYNC IO must be woken up.
*/ staticvoid n_tty_write_wakeup(struct tty_struct *tty)
{
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
}
/* * Check the remaining room for the input canonicalization * mode. We don't want to throttle the driver if we're in * canonical mode and don't have a newline yet!
*/ if (ldata->icanon && ldata->canon_head == ldata->read_tail) return;
do {
tty_set_flow_change(tty, TTY_THROTTLE_SAFE); if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE) break;
} while (!tty_throttle_safe(tty));
__tty_set_flow_change(tty, 0);
}
staticvoid n_tty_check_unthrottle(struct tty_struct *tty)
{ if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) return;
n_tty_kick_worker(tty);
tty_wakeup(tty->link); return;
}
/* If there is enough space in the read buffer now, let the * low-level driver know. We use 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.
*/
do {
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) break;
n_tty_kick_worker(tty);
} while (!tty_unthrottle_safe(tty));
__tty_set_flow_change(tty, 0);
}
/** * put_tty_queue - add character to tty * @c: character * @ldata: n_tty data * * Add a character to the tty read_buf queue. * * Locking: * * n_tty_receive_buf()/producer path: * caller holds non-exclusive %termios_rwsem
*/ staticinlinevoid put_tty_queue(u8 c, struct n_tty_data *ldata)
{
*read_buf_addr(ldata, ldata->read_head) = c;
ldata->read_head++;
}
/** * reset_buffer_flags - reset buffer state * @ldata: line disc data to reset * * Reset the read buffer counters and clear the flags. Called from * n_tty_open() and n_tty_flush_buffer(). * * Locking: * * caller holds exclusive %termios_rwsem, or * * (locking is not required)
*/ staticvoid reset_buffer_flags(struct n_tty_data *ldata)
{
ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
ldata->commit_head = 0;
ldata->line_start = 0;
/** * n_tty_flush_buffer - clean input queue * @tty: terminal device * * Flush the input buffer. Called when the tty layer wants the buffer flushed * (eg at hangup) or when the %N_TTY line discipline internally has to clean * the pending queue (for example some signals). * * Holds %termios_rwsem to exclude producer/consumer while buffer indices are * reset. * * Locking: %ctrl.lock, exclusive %termios_rwsem
*/ staticvoid n_tty_flush_buffer(struct tty_struct *tty)
{
down_write(&tty->termios_rwsem);
reset_buffer_flags(tty->disc_data);
n_tty_kick_worker(tty);
if (tty->link)
n_tty_packet_mode_flush(tty);
up_write(&tty->termios_rwsem);
}
/** * is_utf8_continuation - utf8 multibyte check * @c: byte to check * * Returns: true if the utf8 character @c is a multibyte continuation * character. We use this to correctly compute the on-screen size of the * character when printing.
*/ staticinlineint is_utf8_continuation(u8 c)
{ return (c & 0xc0) == 0x80;
}
/** * is_continuation - multibyte check * @c: byte to check * @tty: terminal device * * Returns: true if the utf8 character @c is a multibyte continuation character * and the terminal is in unicode mode.
*/ staticinlineint is_continuation(u8 c, conststruct tty_struct *tty)
{ return I_IUTF8(tty) && is_utf8_continuation(c);
}
/** * do_output_char - output one character * @c: character (or partial unicode symbol) * @tty: terminal device * @space: space available in tty driver write buffer * * This is a helper function that handles one output character (including * special characters like TAB, CR, LF, etc.), doing OPOST processing and * putting the results in the tty driver's write buffer. * * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY. * They simply aren't relevant in the world today. If you ever need them, add * them here. * * Returns: the number of bytes of buffer space used or -1 if no space left. * * Locking: should be called under the %output_lock to protect the column state * and space left in the buffer.
*/ staticint do_output_char(u8 c, struct tty_struct *tty, int space)
{ struct n_tty_data *ldata = tty->disc_data; int spaces;
if (!space) return -1;
switch (c) { case'\n': if (O_ONLRET(tty))
ldata->column = 0; if (O_ONLCR(tty)) { if (space < 2) return -1;
ldata->canon_column = ldata->column = 0;
tty->ops->write(tty, "\r\n", 2); return 2;
}
ldata->canon_column = ldata->column; break; case'\r': if (O_ONOCR(tty) && ldata->column == 0) return 0; if (O_OCRNL(tty)) {
c = '\n'; if (O_ONLRET(tty))
ldata->canon_column = ldata->column = 0; break;
}
ldata->canon_column = ldata->column = 0; break; case'\t':
spaces = 8 - (ldata->column & 7); if (O_TABDLY(tty) == XTABS) { if (space < spaces) return -1;
ldata->column += spaces;
tty->ops->write(tty, " ", spaces); return spaces;
}
ldata->column += spaces; break; case'\b': if (ldata->column > 0)
ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty))
c = toupper(c); if (!is_continuation(c, tty))
ldata->column++;
} break;
}
tty_put_char(tty, c); return 1;
}
/** * process_output - output post processor * @c: character (or partial unicode symbol) * @tty: terminal device * * Output one character with OPOST processing. * * Returns: -1 when the output device is full and the character must be * retried. * * Locking: %output_lock to protect column state and space left (also, this is *called from n_tty_write() under the tty layer write lock).
*/ staticint process_output(u8 c, struct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
guard(mutex)(&ldata->output_lock);
if (do_output_char(c, tty, tty_write_room(tty)) < 0) return -1;
return 0;
}
/** * process_output_block - block post processor * @tty: terminal device * @buf: character buffer * @nr: number of bytes to output * * Output a block of characters with OPOST processing. * * This path is used to speed up block console writes, among other things when * processing blocks of output data. It handles only the simple cases normally * found and helps to generate blocks of symbols for the console driver and * thus improve performance. * * Returns: the number of characters output. * * Locking: %output_lock to protect column state and space left (also, this is * called from n_tty_write() under the tty layer write lock).
*/ static ssize_t process_output_block(struct tty_struct *tty, const u8 *buf, unsignedint nr)
{ struct n_tty_data *ldata = tty->disc_data; unsignedint space, i; const u8 *cp;
guard(mutex)(&ldata->output_lock);
space = tty_write_room(tty); if (space == 0) return 0;
if (nr > space)
nr = space;
for (i = 0, cp = buf; i < nr; i++, cp++) {
u8 c = *cp;
switch (c) { case'\n': if (O_ONLRET(tty))
ldata->column = 0; if (O_ONLCR(tty)) goto do_write;
ldata->canon_column = ldata->column; break; case'\r': if (O_ONOCR(tty) && ldata->column == 0) goto do_write; if (O_OCRNL(tty)) goto do_write;
ldata->canon_column = ldata->column = 0; break; case'\t': goto do_write; case'\b': if (ldata->column > 0)
ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) goto do_write; if (!is_continuation(c, tty))
ldata->column++;
} break;
}
}
do_write: return tty->ops->write(tty, buf, i);
}
/* * Since add_echo_byte() is called without holding output_lock, we * might see only portion of multi-byte operation.
*/ if (MASK(ldata->echo_commit) == MASK(*tail + 1)) return -ENODATA;
/* * If the buffer byte is the start of a multi-byte operation, get the * next byte, which is either the op code or a control character value.
*/
op = echo_buf(ldata, *tail + 1);
switch (op) { case ECHO_OP_ERASE_TAB: { unsignedint num_chars, num_bs;
if (MASK(ldata->echo_commit) == MASK(*tail + 2)) return -ENODATA;
num_chars = echo_buf(ldata, *tail + 2);
/* * Determine how many columns to go back in order to erase the * tab. This depends on the number of columns used by other * characters within the tab area. If this (modulo 8) count is * from the start of input rather than from a previous tab, we * offset by canon column. Otherwise, tab spacing is normal.
*/ if (!(num_chars & 0x80))
num_chars += ldata->canon_column;
num_bs = 8 - (num_chars & 7);
if (num_bs > space) return -ENOSPC;
space -= num_bs; while (num_bs--) {
tty_put_char(tty, '\b'); if (ldata->column > 0)
ldata->column--;
}
*tail += 3; break;
} case ECHO_OP_SET_CANON_COL:
ldata->canon_column = ldata->column;
*tail += 2; break;
case ECHO_OP_MOVE_BACK_COL: if (ldata->column > 0)
ldata->column--;
*tail += 2; break;
case ECHO_OP_START: /* This is an escaped echo op start code */ if (!space) return -ENOSPC;
default: /* * If the op is not a special byte code, it is a ctrl char * tagged to be echoed as "^X" (where X is the letter * representing the control char). Note that we must ensure * there is enough space for the whole ctrl pair.
*/ if (space < 2) return -ENOSPC;
tty_put_char(tty, '^');
tty_put_char(tty, op ^ 0100);
ldata->column += 2;
space -= 2;
*tail += 2; break;
}
return space;
}
/** * __process_echoes - write pending echo characters * @tty: terminal device * * Write previously buffered echo (and other ldisc-generated) characters to the * tty. * * Characters generated by the ldisc (including echoes) need to be buffered * because the driver's write buffer can fill during heavy program output. * Echoing straight to the driver will often fail under these conditions, * causing lost characters and resulting mismatches of ldisc state information. * * Since the ldisc state must represent the characters actually sent to the * driver at the time of the write, operations like certain changes in column * state are also saved in the buffer and executed here. * * A circular fifo buffer is used so that the most recent characters are * prioritized. Also, when control characters are echoed with a prefixed "^", * the pair is treated atomically and thus not separated. * * Locking: callers must hold %output_lock.
*/ static size_t __process_echoes(struct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data; unsignedint space, old_space;
size_t tail;
u8 c;
old_space = space = tty_write_room(tty);
tail = ldata->echo_tail; while (MASK(ldata->echo_commit) != MASK(tail)) {
c = echo_buf(ldata, tail); if (c == ECHO_OP_START) { int ret = n_tty_process_echo_ops(tty, &tail, space); if (ret == -ENODATA) goto not_yet_stored; if (ret < 0) break;
space = ret;
} else { if (O_OPOST(tty)) { int retval = do_output_char(c, tty, space); if (retval < 0) break;
space -= retval;
} else { if (!space) break;
tty_put_char(tty, c);
space -= 1;
}
tail += 1;
}
}
/* If the echo buffer is nearly full (so that the possibility exists * of echo overrun before the next commit), then discard enough
* data at the tail to prevent a subsequent overrun */ while (ldata->echo_commit > tail &&
ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { if (echo_buf(ldata, tail) == ECHO_OP_START) { if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
tail += 3; else
tail += 2;
} else
tail++;
}
mutex_lock(&ldata->output_lock);
head = ldata->echo_head;
ldata->echo_mark = head;
old = ldata->echo_commit - ldata->echo_tail;
/* Process committed echoes if the accumulated # of bytes * is over the threshold (and try again each time another
* block is accumulated) */
nr = head - ldata->echo_tail; if (nr < ECHO_COMMIT_WATERMARK ||
(nr % ECHO_BLOCK > old % ECHO_BLOCK)) {
mutex_unlock(&ldata->output_lock); return;
}
if (echoed && tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
/* NB: echo_mark and echo_head should be equivalent here */ staticvoid flush_echoes(struct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
if ((!L_ECHO(tty) && !L_ECHONL(tty)) ||
ldata->echo_commit == ldata->echo_head) return;
/** * add_echo_byte - add a byte to the echo buffer * @c: unicode byte to echo * @ldata: n_tty data * * Add a character or operation byte to the echo buffer.
*/ staticinlinevoid add_echo_byte(u8 c, struct n_tty_data *ldata)
{
*echo_buf_addr(ldata, ldata->echo_head) = c;
smp_wmb(); /* Matches smp_rmb() in echo_buf(). */
ldata->echo_head++;
}
/** * echo_move_back_col - add operation to move back a column * @ldata: n_tty data * * Add an operation to the echo buffer to move back one column.
*/ staticvoid echo_move_back_col(struct n_tty_data *ldata)
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
}
/** * echo_set_canon_col - add operation to set the canon column * @ldata: n_tty data * * Add an operation to the echo buffer to set the canon column to the current * column.
*/ staticvoid echo_set_canon_col(struct n_tty_data *ldata)
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
}
/** * echo_erase_tab - add operation to erase a tab * @num_chars: number of character columns already used * @after_tab: true if num_chars starts after a previous tab * @ldata: n_tty data * * Add an operation to the echo buffer to erase a tab. * * Called by the eraser function, which knows how many character columns have * been used since either a previous tab or the start of input. This * information will be used later, along with canon column (if applicable), to * go back the correct number of columns.
*/ staticvoid echo_erase_tab(unsignedint num_chars, int after_tab, struct n_tty_data *ldata)
{
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
/* We only need to know this modulo 8 (tab spacing) */
num_chars &= 7;
/* Set the high bit as a flag if num_chars is after a previous tab */ if (after_tab)
num_chars |= 0x80;
add_echo_byte(num_chars, ldata);
}
/** * echo_char_raw - echo a character raw * @c: unicode byte to echo * @ldata: line disc data * * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path. * * This variant does not treat control characters specially.
*/ staticvoid echo_char_raw(u8 c, struct n_tty_data *ldata)
{ if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
} else {
add_echo_byte(c, ldata);
}
}
/** * echo_char - echo a character * @c: unicode byte to echo * @tty: terminal device * * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the &tty_driver.receive_buf() path. * * This variant tags control characters to be echoed as "^X" (where X is the * letter representing the control char).
*/ staticvoid echo_char(u8 c, conststruct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
if (c == ECHO_OP_START) {
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(ECHO_OP_START, ldata);
} else { if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
add_echo_byte(ECHO_OP_START, ldata);
add_echo_byte(c, ldata);
}
}
/** * eraser - handle erase function * @c: character input * @tty: terminal device * * Perform erase and necessary output when an erase character is present in the * stream from the driver layer. Handles the complexities of UTF-8 multibyte * symbols. * * Locking: n_tty_receive_buf()/producer path: * caller holds non-exclusive %termios_rwsem
*/ staticvoid eraser(u8 c, conststruct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data; enum { ERASE, WERASE, KILL } kill_type;
size_t head;
size_t cnt; int seen_alnums;
if (ldata->read_head == ldata->canon_head) { /* process_output('\a', tty); */ /* what do you think? */ return;
} if (c == ERASE_CHAR(tty))
kill_type = ERASE; elseif (c == WERASE_CHAR(tty))
kill_type = WERASE; else { if (!L_ECHO(tty)) {
ldata->read_head = ldata->canon_head; return;
} if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
ldata->read_head = ldata->canon_head;
finish_erasing(ldata);
echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ if (L_ECHOK(tty))
echo_char_raw('\n', ldata); return;
}
kill_type = KILL;
}
seen_alnums = 0; while (MASK(ldata->read_head) != MASK(ldata->canon_head)) {
head = ldata->read_head;
/* erase a single possibly multibyte character */ do {
head--;
c = read_buf(ldata, head);
} while (is_continuation(c, tty) &&
MASK(head) != MASK(ldata->canon_head));
/* do not partially erase */ if (is_continuation(c, tty)) break;
if (kill_type == WERASE) { /* Equivalent to BSD's ALTWERASE. */ if (isalnum(c) || c == '_')
seen_alnums++; elseif (seen_alnums) break;
}
cnt = ldata->read_head - head;
ldata->read_head = head; if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!ldata->erasing) {
echo_char_raw('\\', ldata);
ldata->erasing = 1;
} /* if cnt > 1, output a multi-byte character */
echo_char(c, tty); while (--cnt > 0) {
head++;
echo_char_raw(read_buf(ldata, head), ldata);
echo_move_back_col(ldata);
}
} elseif (kill_type == ERASE && !L_ECHOE(tty)) {
echo_char(ERASE_CHAR(tty), tty);
} elseif (c == '\t') { unsignedint num_chars = 0; int after_tab = 0;
size_t tail = ldata->read_head;
/* * Count the columns used for characters * since the start of input or after a * previous tab. * This info is used to go back the correct * number of columns.
*/ while (MASK(tail) != MASK(ldata->canon_head)) {
tail--;
c = read_buf(ldata, tail); if (c == '\t') {
after_tab = 1; break;
} elseif (iscntrl(c)) { if (L_ECHOCTL(tty))
num_chars += 2;
} elseif (!is_continuation(c, tty)) {
num_chars++;
}
}
echo_erase_tab(num_chars, after_tab, ldata);
} else { if (iscntrl(c) && L_ECHOCTL(tty)) {
echo_char_raw('\b', ldata);
echo_char_raw(' ', ldata);
echo_char_raw('\b', ldata);
} if (!iscntrl(c) || L_ECHOCTL(tty)) {
echo_char_raw('\b', ldata);
echo_char_raw(' ', ldata);
echo_char_raw('\b', ldata);
}
}
} if (kill_type == ERASE) break;
} if (ldata->read_head == ldata->canon_head && L_ECHO(tty))
finish_erasing(ldata);
}
/** * isig - handle the ISIG optio * @sig: signal * @tty: terminal * * Called when a signal is being sent due to terminal input. Called from the * &tty_driver.receive_buf() path, so serialized. * * Performs input and output flush if !NOFLSH. In this context, the echo * buffer is 'output'. The signal is processed first to alert any current * readers or writers to discontinue and exit their i/o loops. * * Locking: %ctrl.lock
*/ staticvoid isig(int sig, struct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
if (L_NOFLSH(tty)) { /* signal only */
__isig(sig, tty);
} else { /* signal and flush */
up_read(&tty->termios_rwsem);
down_write(&tty->termios_rwsem);
/** * n_tty_receive_break - handle break * @tty: terminal * * An RS232 break event has been hit in the incoming bitstream. This can cause * a variety of events depending upon the termios settings. * * Locking: n_tty_receive_buf()/producer path: * caller holds non-exclusive termios_rwsem * * Note: may get exclusive %termios_rwsem if flushing input buffer
*/ staticvoid n_tty_receive_break(struct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) {
isig(SIGINT, tty); return;
} if (I_PARMRK(tty)) {
put_tty_queue('\377', ldata);
put_tty_queue('\0', ldata);
}
put_tty_queue('\0', ldata);
}
/** * n_tty_receive_overrun - handle overrun reporting * @tty: terminal * * Data arrived faster than we could process it. While the tty driver has * flagged this the bits that were missed are gone forever. * * Called from the receive_buf path so single threaded. Does not need locking * as num_overrun and overrun_time are function private.
*/ staticvoid n_tty_receive_overrun(conststruct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
/** * n_tty_receive_parity_error - error notifier * @tty: terminal device * @c: character * * Process a parity error and queue the right data to indicate the error case * if necessary. * * Locking: n_tty_receive_buf()/producer path: * caller holds non-exclusive %termios_rwsem
*/ staticvoid n_tty_receive_parity_error(conststruct tty_struct *tty,
u8 c)
{ struct n_tty_data *ldata = tty->disc_data;
if (I_INPCK(tty)) { if (I_IGNPAR(tty)) return; if (I_PARMRK(tty)) {
put_tty_queue('\377', ldata);
put_tty_queue('\0', ldata);
put_tty_queue(c, ldata);
} else
put_tty_queue('\0', ldata);
} else
put_tty_queue(c, ldata);
}
staticvoid
n_tty_receive_signal_char(struct tty_struct *tty, int signal, u8 c)
{
isig(signal, tty); if (I_IXON(tty))
start_tty(tty); if (L_ECHO(tty)) {
echo_char(c, tty);
commit_echoes(tty);
} else
process_echoes(tty);
}
staticbool n_tty_is_char_flow_ctrl(struct tty_struct *tty, u8 c)
{ return c == START_CHAR(tty) || c == STOP_CHAR(tty);
}
/** * n_tty_receive_char_flow_ctrl - receive flow control chars * @tty: terminal device * @c: character * @lookahead_done: lookahead has processed this character already * * Receive and process flow control character actions. * * In case lookahead for flow control chars already handled the character in * advance to the normal receive, the actions are skipped during normal * receive. * * Returns true if @c is consumed as flow-control character, the character * must not be treated as normal character.
*/ staticbool n_tty_receive_char_flow_ctrl(struct tty_struct *tty, u8 c, bool lookahead_done)
{ if (!n_tty_is_char_flow_ctrl(tty, c)) returnfalse;
if (lookahead_done) returntrue;
if (c == START_CHAR(tty)) {
start_tty(tty);
process_echoes(tty); returntrue;
}
if (c == '\n') { if (L_ECHO(tty) || L_ECHONL(tty)) {
echo_char_raw('\n', ldata);
commit_echoes(tty);
}
n_tty_receive_handle_newline(tty, c);
returntrue;
}
if (c == EOF_CHAR(tty)) {
c = __DISABLED_CHAR;
n_tty_receive_handle_newline(tty, c);
returntrue;
}
if ((c == EOL_CHAR(tty)) ||
(c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
*/ if (L_ECHO(tty)) { /* Record the column of first canon char. */ if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
commit_echoes(tty);
} /* * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR?
*/ if (c == '\377' && I_PARMRK(tty))
put_tty_queue(c, ldata);
if (c == '\r') { if (I_IGNCR(tty)) return; if (I_ICRNL(tty))
c = '\n';
} elseif (c == '\n' && I_INLCR(tty))
c = '\r';
if (ldata->icanon && n_tty_receive_char_canon(tty, c)) return;
if (L_ECHO(tty)) {
finish_erasing(ldata); if (c == '\n')
echo_char_raw('\n', ldata); else { /* Record the column of first canon char. */ if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
}
commit_echoes(tty);
}
/** * n_tty_receive_char - perform processing * @tty: terminal device * @c: character * * Process an individual character of input received from the driver. This is * serialized with respect to itself by the rules for the driver above. * * Locking: n_tty_receive_buf()/producer path: * caller holds non-exclusive %termios_rwsem * publishes canon_head if canonical mode is active
*/ staticvoid n_tty_receive_char(struct tty_struct *tty, u8 c)
{ struct n_tty_data *ldata = tty->disc_data;
if (tty->flow.stopped && !tty->flow.tco_stopped && I_IXON(tty) && I_IXANY(tty)) {
start_tty(tty);
process_echoes(tty);
} if (L_ECHO(tty)) {
finish_erasing(ldata); /* Record the column of first canon char. */ if (ldata->canon_head == ldata->read_head)
echo_set_canon_col(ldata);
echo_char(c, tty);
commit_echoes(tty);
} /* PARMRK doubling check */ if (c == '\377' && I_PARMRK(tty))
put_tty_queue(c, ldata);
put_tty_queue(c, ldata);
}
staticvoid n_tty_receive_char_closing(struct tty_struct *tty, u8 c, bool lookahead_done)
{ if (I_ISTRIP(tty))
c &= 0x7f; if (I_IUCLC(tty) && L_IEXTEN(tty))
c = tolower(c);
if (I_IXON(tty)) { if (!n_tty_receive_char_flow_ctrl(tty, c, lookahead_done) &&
tty->flow.stopped && !tty->flow.tco_stopped && I_IXANY(tty) &&
c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) &&
c != SUSP_CHAR(tty)) {
start_tty(tty);
process_echoes(tty);
}
}
}
staticvoid
n_tty_receive_char_flagged(struct tty_struct *tty, u8 c, u8 flag)
{ switch (flag) { case TTY_BREAK:
n_tty_receive_break(tty); break; case TTY_PARITY: case TTY_FRAME:
n_tty_receive_parity_error(tty, c); break; case TTY_OVERRUN:
n_tty_receive_overrun(tty); break; default:
tty_err(tty, "unknown flag %u\n", flag); break;
}
}
/* handle buffer wrap-around by a loop */ for (unsignedint i = 0; i < 2; i++) {
size_t head = MASK(ldata->read_head);
size_t n = min(count, N_TTY_BUF_SIZE - head);
while (count--) { if (fp)
flag = *fp++; if (likely(flag == TTY_NORMAL))
put_tty_queue(*cp++, ldata); else
n_tty_receive_char_flagged(tty, *cp++, flag);
}
}
/** * n_tty_receive_buf_common - process input * @tty: device to receive input * @cp: input chars * @fp: flags for each char (if %NULL, all chars are %TTY_NORMAL) * @count: number of input chars in @cp * @flow: enable flow control * * Called by the terminal driver when a block of characters has been received. * This function must be called from soft contexts not from interrupt context. * The driver is responsible for making calls one at a time and in order (or * using flush_to_ldisc()). * * Returns: the # of input chars from @cp which were processed. * * In canonical mode, the maximum line length is 4096 chars (including the line * termination char); lines longer than 4096 chars are truncated. After 4095 * chars, input data is still processed but not stored. Overflow processing * ensures the tty can always receive more input until at least one line can be * read. * * In non-canonical mode, the read buffer will only accept 4095 chars; this * provides the necessary space for a newline char if the input mode is * switched to canonical. * * Note it is possible for the read buffer to _contain_ 4096 chars in * non-canonical mode: the read buffer could already contain the maximum canon * line of 4096 chars when the mode is switched to non-canonical. * * Locking: n_tty_receive_buf()/producer path: * claims non-exclusive %termios_rwsem * publishes commit_head or canon_head
*/ static size_t
n_tty_receive_buf_common(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t count, bool flow)
{ struct n_tty_data *ldata = tty->disc_data;
size_t n, rcvd = 0; int room, overflow;
down_read(&tty->termios_rwsem);
do { /* * When PARMRK is set, each input char may take up to 3 chars * in the read buf; reduce the buffer space avail by 3x * * If we are doing input canonicalization, and there are no * pending newlines, let characters through without limit, so * that erase characters will be handled. Other excess * characters will be beeped. * * paired with store in *_copy_from_read_buf() -- guarantees * the consumer has loaded the data in read_buf up to the new * read_tail (so this producer will not overwrite unread data)
*/
size_t tail = smp_load_acquire(&ldata->read_tail);
/* ignore parity errors if handling overflow */ if (!overflow || !fp || *fp != TTY_PARITY)
__receive_buf(tty, cp, fp, n);
cp += n; if (fp)
fp += n;
count -= n;
rcvd += n;
} while (!test_bit(TTY_LDISC_CHANGING, &tty->flags));
tty->receive_room = room;
/* Unthrottle if handling overflow on pty */ if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { if (overflow) {
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
tty_unthrottle_safe(tty);
__tty_set_flow_change(tty, 0);
}
} else
n_tty_check_throttle(tty);
if (unlikely(ldata->no_room)) { /* * Barrier here is to ensure to read the latest read_tail in * chars_in_buffer() and to make sure that read_tail is not loaded * before ldata->no_room is set.
*/
smp_mb(); if (!chars_in_buffer(tty))
n_tty_kick_worker(tty);
}
/** * n_tty_set_termios - termios data changed * @tty: terminal * @old: previous data * * Called by the tty layer when the user changes termios flags so that the line * discipline can plan ahead. This function cannot sleep and is protected from * re-entry by the tty layer. The user is guaranteed that this function will * not be re-entered or in progress when the ldisc is closed. * * Locking: Caller holds @tty->termios_rwsem
*/ staticvoid n_tty_set_termios(struct tty_struct *tty, conststruct ktermios *old)
{ struct n_tty_data *ldata = tty->disc_data;
if (I_IGNCR(tty) || I_ICRNL(tty))
set_bit('\r', ldata->char_map); if (I_INLCR(tty))
set_bit('\n', ldata->char_map);
if (L_ICANON(tty)) {
set_bit(ERASE_CHAR(tty), ldata->char_map);
set_bit(KILL_CHAR(tty), ldata->char_map);
set_bit(EOF_CHAR(tty), ldata->char_map);
set_bit('\n', ldata->char_map);
set_bit(EOL_CHAR(tty), ldata->char_map); if (L_IEXTEN(tty)) {
set_bit(WERASE_CHAR(tty), ldata->char_map);
set_bit(LNEXT_CHAR(tty), ldata->char_map);
set_bit(EOL2_CHAR(tty), ldata->char_map); if (L_ECHO(tty))
set_bit(REPRINT_CHAR(tty),
ldata->char_map);
}
} if (I_IXON(tty)) {
set_bit(START_CHAR(tty), ldata->char_map);
set_bit(STOP_CHAR(tty), ldata->char_map);
} if (L_ISIG(tty)) {
set_bit(INTR_CHAR(tty), ldata->char_map);
set_bit(QUIT_CHAR(tty), ldata->char_map);
set_bit(SUSP_CHAR(tty), ldata->char_map);
}
clear_bit(__DISABLED_CHAR, ldata->char_map);
ldata->raw = 0;
ldata->real_raw = 0;
} else {
ldata->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))
ldata->real_raw = 1; else
ldata->real_raw = 0;
} /* * Fix tty hang when I_IXON(tty) is cleared, but the tty * been stopped by STOP_CHAR(tty) before it.
*/ if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow.tco_stopped) {
start_tty(tty);
process_echoes(tty);
}
/* The termios change make the tty ready for I/O */
wake_up_interruptible(&tty->write_wait);
wake_up_interruptible(&tty->read_wait);
}
/** * n_tty_close - close the ldisc for this tty * @tty: device * * Called from the terminal layer when this line discipline is being shut down, * either because of a close or becsuse of a discipline change. The function * will not be called while other ldisc methods are in progress.
*/ staticvoid n_tty_close(struct tty_struct *tty)
{ struct n_tty_data *ldata = tty->disc_data;
/** * n_tty_open - open an ldisc * @tty: terminal to open * * Called when this line discipline is being attached to the terminal device. * Can sleep. Called serialized so that no other events will occur in parallel. * No further open will occur until a close.
*/ staticint n_tty_open(struct tty_struct *tty)
{ struct n_tty_data *ldata;
/* Currently a malloc failure here can panic */
ldata = vzalloc(sizeof(*ldata)); if (!ldata) return -ENOMEM;
/** * copy_from_read_buf - copy read data directly * @tty: terminal device * @kbp: data * @nr: size of data * * Helper function to speed up n_tty_read(). It is only called when %ICANON is * off; it copies characters straight from the tty queue. * * Returns: true if it successfully copied data, but there is still more data * to be had. * * Locking: * * called under the @ldata->atomic_read_lock sem * * n_tty_read()/consumer path: * caller holds non-exclusive %termios_rwsem; * read_tail published
*/ staticbool copy_from_read_buf(conststruct tty_struct *tty, u8 **kbp,
size_t *nr)
/* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
head == ldata->read_tail) returnfalse;
*kbp += n;
*nr -= n;
/* If we have more to copy, let the caller know */ return head != ldata->read_tail;
}
/** * canon_copy_from_read_buf - copy read data in canonical mode * @tty: terminal device * @kbp: data * @nr: size of data * * Helper function for n_tty_read(). It is only called when %ICANON is on; it * copies one line of input up to and including the line-delimiting character * into the result buffer. * * Note: When termios is changed from non-canonical to canonical mode and the * read buffer contains data, n_tty_set_termios() simulates an EOF push (as if * C-d were input) _without_ the %DISABLED_CHAR in the buffer. This causes data * already processed as input to be immediately available as input although a * newline has not been received. * * Locking: * * called under the %atomic_read_lock mutex * * n_tty_read()/consumer path: * caller holds non-exclusive %termios_rwsem; * read_tail published
*/ staticbool canon_copy_from_read_buf(conststruct tty_struct *tty, u8 **kbp,
size_t *nr)
{ struct n_tty_data *ldata = tty->disc_data;
size_t n, size, more, c;
size_t eol;
size_t tail, canon_head; int found = 0;
/* N.B. avoid overrun if nr == 0 */ if (!*nr) returnfalse;
canon_head = smp_load_acquire(&ldata->canon_head);
n = min(*nr, canon_head - ldata->read_tail);
tail = MASK(ldata->read_tail);
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
eol = find_next_bit(ldata->read_flags, size, tail);
more = n - (size - tail); if (eol == N_TTY_BUF_SIZE && more) { /* scan wrapped without finding set bit */
eol = find_first_bit(ldata->read_flags, more);
found = eol != more;
} else
found = eol != size;
n = eol - tail; if (n > N_TTY_BUF_SIZE)
n += N_TTY_BUF_SIZE;
c = n + found;
if (!found || read_buf(ldata, eol) != __DISABLED_CHAR)
n = c;
if (found)
clear_bit(eol, ldata->read_flags);
smp_store_release(&ldata->read_tail, ldata->read_tail + c);
if (found) { if (!ldata->push)
ldata->line_start = ldata->read_tail; else
ldata->push = 0;
tty_audit_push(); returnfalse;
}
/* No EOL found - do a continuation retry if there is more data */ return ldata->read_tail != canon_head;
}
/* * If we finished a read at the exact location of an * EOF (special EOL character that's a __DISABLED_CHAR) * in the stream, silently eat the EOF.
*/ staticvoid canon_skip_eof(struct n_tty_data *ldata)
{
size_t tail, canon_head;
// See if the tail position is EOF in the circular buffer
tail &= (N_TTY_BUF_SIZE - 1); if (!test_bit(tail, ldata->read_flags)) return; if (read_buf(ldata, tail) != __DISABLED_CHAR) return;
// Clear the EOL bit, skip the EOF char.
clear_bit(tail, ldata->read_flags);
smp_store_release(&ldata->read_tail, ldata->read_tail + 1);
}
/** * job_control - check job control * @tty: tty * @file: file handle * * Perform job control management checks on this @file/@tty descriptor and if * appropriate send any needed signals and return a negative error code if * action should be taken. * * Locking: * * redirected write test is safe * * current->signal->tty check is safe * * ctrl.lock to safely reference @tty->ctrl.pgrp
*/ staticint job_control(struct tty_struct *tty, struct file *file)
{ /* 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_op->write_iter == redirected_tty_write) return 0;
return __tty_check_change(tty, SIGTTIN);
}
/* * We still hold the atomic_read_lock and the termios_rwsem, and can just * continue to copy data.
*/ static ssize_t n_tty_continue_cookie(struct tty_struct *tty, u8 *kbuf,
size_t nr, void **cookie)
{ struct n_tty_data *ldata = tty->disc_data;
u8 *kb = kbuf;
if (ldata->icanon && !L_EXTPROC(tty)) { /* * If we have filled the user buffer, see if we should skip an * EOF character before releasing the lock and returning done.
*/ if (!nr)
canon_skip_eof(ldata); elseif (canon_copy_from_read_buf(tty, &kb, &nr)) return kb - kbuf;
} else { if (copy_from_read_buf(tty, &kb, &nr)) return kb - kbuf;
}
/* No more data - release locks and stop retries */
n_tty_kick_worker(tty);
n_tty_check_unthrottle(tty);
up_read(&tty->termios_rwsem);
mutex_unlock(&ldata->atomic_read_lock);
*cookie = NULL;
return kb - kbuf;
}
staticint n_tty_wait_for_input(struct tty_struct *tty, struct file *file, struct wait_queue_entry *wait, long *timeout)
{ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) return -EIO; if (tty_hung_up_p(file)) return 0; /* * Abort readers for ttys which never actually get hung up. * See __tty_hangup().
*/ if (test_bit(TTY_HUPPING, &tty->flags)) return 0; if (!*timeout) return 0; if (tty_io_nonblock(tty, file)) return -EAGAIN; if (signal_pending(current)) return -ERESTARTSYS;
/** * n_tty_read - read function for tty * @tty: tty device * @file: file object * @kbuf: kernelspace buffer pointer * @nr: size of I/O * @cookie: if non-%NULL, this is a continuation read * @offset: where to continue reading from (unused in n_tty) * * Perform reads for the line discipline. We are guaranteed that the line * discipline will not be closed under us but we may get multiple parallel * readers and must handle this ourselves. We may also get a hangup. Always * called in user context, may sleep. * * This code must be sure never to sleep through a hangup. * * Locking: n_tty_read()/consumer path: * claims non-exclusive termios_rwsem; * publishes read_tail
*/ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, u8 *kbuf,
size_t nr, void **cookie, unsignedlong offset)
{ struct n_tty_data *ldata = tty->disc_data;
u8 *kb = kbuf;
DEFINE_WAIT_FUNC(wait, woken_wake_function); int minimum, time;
ssize_t retval; long timeout; bool packet;
size_t old_tail;
/* Is this a continuation of a read started earlier? */ if (*cookie) return n_tty_continue_cookie(tty, kbuf, nr, cookie);
retval = job_control(tty, file); if (retval < 0) return retval;
/* * Internal serialization of reads.
*/ if (file->f_flags & O_NONBLOCK) { if (!mutex_trylock(&ldata->atomic_read_lock)) return -EAGAIN;
} else { if (mutex_lock_interruptible(&ldata->atomic_read_lock)) return -ERESTARTSYS;
}
down_read(&tty->termios_rwsem);
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT; if (!ldata->icanon) {
minimum = MIN_CHAR(tty); if (minimum) {
time = (HZ / 10) * TIME_CHAR(tty);
} else {
timeout = (HZ / 10) * TIME_CHAR(tty);
minimum = 1;
}
}
if (kb - kbuf >= minimum) break; if (time)
timeout = time;
} if (old_tail != ldata->read_tail) { /* * Make sure no_room is not read in n_tty_kick_worker() * before setting ldata->read_tail in copy_from_read_buf().
*/
smp_mb();
n_tty_kick_worker(tty);
}
up_read(&tty->termios_rwsem);
return retval;
more_to_be_read: /* * There is more to be had and we have nothing more to wait for, so * let's mark us for retries. * * NOTE! We return here with both the termios_sem and atomic_read_lock * still held, the retries will release them when done.
*/
remove_wait_queue(&tty->read_wait, &wait);
*cookie = cookie;
return kb - kbuf;
}
/** * n_tty_write - write function for tty * @tty: tty device * @file: file object * @buf: userspace buffer pointer * @nr: size of I/O * * Write function of the terminal device. This is serialized with respect to * other write callers but not to termios changes, reads and other such events. * Since the receive code will echo characters, thus calling driver write * methods, the %output_lock is used in the output processing functions called * here as well as in the echo processing function to protect the column state * and space left in the buffer. * * This code must be sure never to sleep through a hangup. * * Locking: output_lock to protect column state and space left * (note that the process_output*() functions take this lock themselves)
*/
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ if (L_TOSTOP(tty) && file->f_op->write_iter != redirected_tty_write) {
retval = tty_check_change(tty); if (retval) return retval;
}
down_read(&tty->termios_rwsem);
/* Write out any echoed characters that are still pending */
process_echoes(tty);
add_wait_queue(&tty->write_wait, &wait); while (1) { if (signal_pending(current)) {
retval = -ERESTARTSYS; break;
} if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO; break;
} if (O_OPOST(tty)) { while (nr > 0) {
num = process_output_block(tty, b, nr); if (num < 0) { if (num == -EAGAIN) break;
retval = num; goto break_out;
}
b += num;
nr -= num; if (nr == 0) break; if (process_output(*b, tty) < 0) break;
b++; nr--;
} if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
} else { struct n_tty_data *ldata = tty->disc_data;
while (nr > 0) {
mutex_lock(&ldata->output_lock);
num = tty->ops->write(tty, b, nr);
mutex_unlock(&ldata->output_lock); if (num < 0) {
retval = num; goto break_out;
} if (!num) break;
b += num;
nr -= num;
}
} if (!nr) break; if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN; break;
}
up_read(&tty->termios_rwsem);
/** * n_tty_poll - poll method for N_TTY * @tty: terminal device * @file: file accessing it * @wait: poll table * * Called when the line discipline is asked to poll() for data or for special * events. This code is not serialized with respect to other events save * open/close. * * This code must be sure never to sleep through a hangup. * * Locking: called without the kernel lock held -- fine.
*/ static __poll_t n_tty_poll(struct tty_struct *tty, struct file *file,
poll_table *wait)
{
__poll_t mask = 0;
/** * n_tty_inherit_ops - inherit N_TTY methods * @ops: struct tty_ldisc_ops where to save N_TTY methods * * Enables a 'subclass' line discipline to 'inherit' N_TTY methods.
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.