// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines * which can be dynamically activated and de-activated by the line * discipline handling modules (like SLIP).
*/
/** * tty_chars_in_buffer - characters pending * @tty: terminal * * Returns: the number of bytes of data in the device private output queue. If * no private method is supplied there is assumed to be no queue on the device.
*/ unsignedint tty_chars_in_buffer(struct tty_struct *tty)
{ if (tty->ops->chars_in_buffer) return tty->ops->chars_in_buffer(tty); return 0;
}
EXPORT_SYMBOL(tty_chars_in_buffer);
/** * tty_write_room - write queue space * @tty: terminal * * Returns: the number of bytes that can be queued to this device at the present * time. The result should be treated as a guarantee and the driver cannot * offer a value it later shrinks by more than the number of bytes written. If * no method is provided, 2K is always returned and data may be lost as there * will be no flow control.
*/ unsignedint tty_write_room(struct tty_struct *tty)
{ if (tty->ops->write_room) return tty->ops->write_room(tty); return 2048;
}
EXPORT_SYMBOL(tty_write_room);
/** * tty_driver_flush_buffer - discard internal buffer * @tty: terminal * * Discard the internal output buffer for this device. If no method is provided, * then either the buffer cannot be hardware flushed or there is no buffer * driver side.
*/ void tty_driver_flush_buffer(struct tty_struct *tty)
{ if (tty->ops->flush_buffer)
tty->ops->flush_buffer(tty);
}
EXPORT_SYMBOL(tty_driver_flush_buffer);
/** * tty_unthrottle - flow control * @tty: terminal * * Indicate that a @tty may continue transmitting data down the stack. Takes * the &tty_struct->termios_rwsem to protect against parallel * throttle/unthrottle and also to ensure the driver can consistently reference * its own termios data at this point when implementing software flow control. * * Drivers should however remember that the stack can issue a throttle, then * change flow control method, then unthrottle.
*/ void tty_unthrottle(struct tty_struct *tty)
{
down_write(&tty->termios_rwsem); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->unthrottle)
tty->ops->unthrottle(tty);
tty->flow_change = TTY_FLOW_NO_CHANGE;
up_write(&tty->termios_rwsem);
}
EXPORT_SYMBOL(tty_unthrottle);
/** * tty_throttle_safe - flow control * @tty: terminal * * Indicate that a @tty should stop transmitting data down the stack. * tty_throttle_safe() will only attempt throttle if @tty->flow_change is * %TTY_THROTTLE_SAFE. Prevents an accidental throttle due to race conditions * when throttling is conditional on factors evaluated prior to throttling. * * Returns: %true if @tty is throttled (or was already throttled)
*/ bool tty_throttle_safe(struct tty_struct *tty)
{
guard(mutex)(&tty->throttle_mutex);
if (tty_throttled(tty)) returntrue;
if (tty->flow_change != TTY_THROTTLE_SAFE) returnfalse;
set_bit(TTY_THROTTLED, &tty->flags); if (tty->ops->throttle)
tty->ops->throttle(tty);
returntrue;
}
/** * tty_unthrottle_safe - flow control * @tty: terminal * * Similar to tty_unthrottle() but will only attempt unthrottle if * @tty->flow_change is %TTY_UNTHROTTLE_SAFE. Prevents an accidental unthrottle * due to race conditions when unthrottling is conditional on factors evaluated * prior to unthrottling. * * Returns: %true if @tty is unthrottled (or was already unthrottled)
*/ bool tty_unthrottle_safe(struct tty_struct *tty)
{
guard(mutex)(&tty->throttle_mutex);
if (!tty_throttled(tty)) returntrue;
if (tty->flow_change != TTY_UNTHROTTLE_SAFE) returnfalse;
clear_bit(TTY_THROTTLED, &tty->flags); if (tty->ops->unthrottle)
tty->ops->unthrottle(tty);
returntrue;
}
/** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for * @timeout: how long we will wait * * Wait for characters pending in a tty driver to hit the wire, or for a * timeout to occur (eg due to flow control). * * Locking: none
*/
void tty_wait_until_sent(struct tty_struct *tty, long timeout)
{ if (!timeout)
timeout = MAX_SCHEDULE_TIMEOUT;
timeout = wait_event_interruptible_timeout(tty->write_wait,
!tty_chars_in_buffer(tty), timeout); if (timeout <= 0) return;
if (timeout == MAX_SCHEDULE_TIMEOUT)
timeout = 0;
if (tty->ops->wait_until_sent)
tty->ops->wait_until_sent(tty, timeout);
}
EXPORT_SYMBOL(tty_wait_until_sent);
NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
termios->c_line = locked->c_line ? old->c_line : termios->c_line; for (i = 0; i < NCCS; i++)
termios->c_cc[i] = locked->c_cc[i] ?
old->c_cc[i] : termios->c_cc[i]; /* FIXME: What should we do for i/ospeed */
}
/** * tty_termios_copy_hw - copy hardware settings * @new: new termios * @old: old termios * * Propagate the hardware specific terminal setting bits from the @old termios * structure to the @new one. This is used in cases where the hardware does not * support reconfiguration or as a helper in some cases where only minimal * reconfiguration is supported.
*/ void tty_termios_copy_hw(struct ktermios *new, conststruct ktermios *old)
{ /* The bits a dumb device handles in software. Smart devices need
to always provide a set_termios method */
new->c_cflag &= HUPCL | CREAD | CLOCAL;
new->c_cflag |= old->c_cflag & ~(HUPCL | CREAD | CLOCAL);
new->c_ispeed = old->c_ispeed;
new->c_ospeed = old->c_ospeed;
}
EXPORT_SYMBOL(tty_termios_copy_hw);
/** * tty_termios_hw_change - check for setting change * @a: termios * @b: termios to compare * * Check if any of the bits that affect a dumb device have changed between the * two termios structures, or a speed change is needed. * * Returns: %true if change is needed
*/ bool tty_termios_hw_change(conststruct ktermios *a, conststruct ktermios *b)
{ if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed) returntrue; if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL)) returntrue; returnfalse;
}
EXPORT_SYMBOL(tty_termios_hw_change);
/** * tty_get_char_size - get size of a character * @cflag: termios cflag value * * Returns: size (in bits) of a character depending on @cflag's %CSIZE setting
*/ unsignedchar tty_get_char_size(unsignedint cflag)
{ switch (cflag & CSIZE) { case CS5: return 5; case CS6: return 6; case CS7: return 7; case CS8: default: return 8;
}
}
EXPORT_SYMBOL_GPL(tty_get_char_size);
/** * tty_get_frame_size - get size of a frame * @cflag: termios cflag value * * Get the size (in bits) of a frame depending on @cflag's %CSIZE, %CSTOPB, and * %PARENB setting. The result is a sum of character size, start and stop bits * -- one bit each -- second stop bit (if set), and parity bit (if set). * * Returns: size (in bits) of a frame depending on @cflag's setting.
*/ unsignedchar tty_get_frame_size(unsignedint cflag)
{ unsignedchar bits = 2 + tty_get_char_size(cflag);
if (cflag & CSTOPB)
bits++; if (cflag & PARENB)
bits++; if (cflag & ADDRB)
bits++;
/** * tty_set_termios - update termios values * @tty: tty to update * @new_termios: desired new value * * Perform updates to the termios values set on this @tty. A master pty's * termios should never be set. * * Locking: &tty_struct->termios_rwsem
*/ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
{ struct ktermios old_termios; struct tty_ldisc *ld;
WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER); /* * 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_write(&tty->termios_rwsem);
old_termios = tty->termios;
tty->termios = *new_termios;
unset_locked_termios(tty, &old_termios); /* Reset any ADDRB changes, ADDRB is changed through ->rs485_config() */
tty->termios.c_cflag ^= (tty->termios.c_cflag ^ old_termios.c_cflag) & ADDRB;
if (tty->ops->set_termios)
tty->ops->set_termios(tty, &old_termios); else
tty_termios_copy_hw(&tty->termios, &old_termios);
ld = tty_ldisc_ref(tty); if (ld != NULL) { if (ld->ops->set_termios)
ld->ops->set_termios(tty, &old_termios);
tty_ldisc_deref(ld);
}
up_write(&tty->termios_rwsem); return 0;
}
EXPORT_SYMBOL_GPL(tty_set_termios);
/* * Translate a "termio" structure into a "termios". Ugh.
*/
__weak int user_termio_to_kernel_termios(struct ktermios *termios, struct termio __user *termio)
{ struct termio v;
if (copy_from_user(&v, termio, sizeof(struct termio))) return -EFAULT;
/** * set_termios - set termios values for a tty * @tty: terminal device * @arg: user data * @opt: option information * * Helper function to prepare termios data and run necessary other functions * before using tty_set_termios() to do the actual changes. * * Locking: called functions take &tty_struct->ldisc_sem and * &tty_struct->termios_rwsem locks * * Returns: 0 on success, an error otherwise
*/ staticint set_termios(struct tty_struct *tty, void __user *arg, int opt)
{ struct ktermios tmp_termios; struct tty_ldisc *ld; int retval = tty_check_change(tty);
/* If old style Bfoo values are used then load c_ispeed/c_ospeed
* with the real speed so its unconditionally usable */
tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {
retry_write_wait:
retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty)); if (retval < 0) return retval;
if (tty_write_lock(tty, false) < 0) goto retry_write_wait;
/* Racing writer? */ if (tty_chars_in_buffer(tty)) {
tty_write_unlock(tty); goto retry_write_wait;
}
ld = tty_ldisc_ref(tty); if (ld != NULL) { if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
ld->ops->flush_buffer(tty);
tty_ldisc_deref(ld);
}
if ((opt & TERMIOS_WAIT) && tty->ops->wait_until_sent) {
tty->ops->wait_until_sent(tty, 0); if (signal_pending(current)) {
tty_write_unlock(tty); return -ERESTARTSYS;
}
}
/* FIXME: Arguably if tmp_termios == tty->termios AND the actual requested termios was not tmp_termios then we may want to return an error as no user requested change has
succeeded */ return 0;
}
#ifdef TIOCGETP /* * These are deprecated, but there is limited support.. * * The "sg_flags" translation is a joke..
*/ staticint get_sgflags(struct tty_struct *tty)
{ int flags = 0;
if (!L_ICANON(tty)) { if (L_ISIG(tty))
flags |= 0x02; /* cbreak */ else
flags |= 0x20; /* raw */
} if (L_ECHO(tty))
flags |= 0x08; /* echo */ if (O_OPOST(tty)) if (O_ONLCR(tty))
flags |= 0x10; /* crmod */ return flags;
}
if (copy_from_user(&tmp, ltchars, sizeof(tmp))) return -EFAULT;
down_write(&tty->termios_rwsem);
tty->termios.c_cc[VSUSP] = tmp.t_suspc; /* what is dsuspc anyway? */
tty->termios.c_cc[VEOL2] = tmp.t_dsuspc;
tty->termios.c_cc[VREPRINT] = tmp.t_rprntc; /* what is flushc anyway? */
tty->termios.c_cc[VEOL2] = tmp.t_flushc;
tty->termios.c_cc[VWERASE] = tmp.t_werasc;
tty->termios.c_cc[VLNEXT] = tmp.t_lnextc;
up_write(&tty->termios_rwsem); return 0;
} #endif
/** * tty_change_softcar - carrier change ioctl helper * @tty: tty to update * @enable: enable/disable %CLOCAL * * Perform a change to the %CLOCAL state and call into the driver layer to make * it visible. * * Locking: &tty_struct->termios_rwsem. * * Returns: 0 on success, an error otherwise
*/ staticint tty_change_softcar(struct tty_struct *tty, bool enable)
{ int ret = 0; struct ktermios old;
tcflag_t bit = enable ? CLOCAL : 0;
down_write(&tty->termios_rwsem);
old = tty->termios;
tty->termios.c_cflag &= ~CLOCAL;
tty->termios.c_cflag |= bit; if (tty->ops->set_termios)
tty->ops->set_termios(tty, &old); if (C_CLOCAL(tty) != bit)
ret = -EINVAL;
up_write(&tty->termios_rwsem); return ret;
}
/** * tty_mode_ioctl - mode related ioctls * @tty: tty for the ioctl * @cmd: command * @arg: ioctl argument * * Perform non-line discipline specific mode control ioctls. This is designed * to be called by line disciplines to ensure they provide consistent mode * setting.
*/ int tty_mode_ioctl(struct tty_struct *tty, unsignedint cmd, unsignedlong arg)
{ struct tty_struct *real_tty; void __user *p = (void __user *)arg; int ret = 0; struct ktermios kterm;
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.