2017-11-07 01:11:51 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* 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).
|
|
|
|
*/
|
|
|
|
|
2023-03-09 16:20:29 +08:00
|
|
|
#include <linux/bits.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/termios.h>
|
|
|
|
#include <linux/errno.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/bitops.h>
|
2006-09-29 17:00:43 +08:00
|
|
|
#include <linux/mutex.h>
|
2011-10-06 05:13:13 +08:00
|
|
|
#include <linux/compat.h>
|
2018-08-16 23:47:53 +08:00
|
|
|
#include <linux/termios_internal.h>
|
2021-04-08 20:51:32 +08:00
|
|
|
#include "tty.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <asm/io.h>
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#undef DEBUG
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal flag options for termios setting behavior
|
|
|
|
*/
|
2023-03-09 16:20:29 +08:00
|
|
|
#define TERMIOS_FLUSH BIT(0)
|
|
|
|
#define TERMIOS_WAIT BIT(1)
|
|
|
|
#define TERMIOS_TERMIO BIT(2)
|
|
|
|
#define TERMIOS_OLD BIT(3)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-13 17:41:42 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_chars_in_buffer - characters pending
|
|
|
|
* @tty: terminal
|
2008-10-13 17:41:42 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2008-10-13 17:41:42 +08:00
|
|
|
*/
|
2021-05-05 17:19:19 +08:00
|
|
|
unsigned int tty_chars_in_buffer(struct tty_struct *tty)
|
2008-04-30 15:54:13 +08:00
|
|
|
{
|
|
|
|
if (tty->ops->chars_in_buffer)
|
|
|
|
return tty->ops->chars_in_buffer(tty);
|
2021-03-02 14:22:12 +08:00
|
|
|
return 0;
|
2008-04-30 15:54:13 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_chars_in_buffer);
|
|
|
|
|
2008-10-13 17:41:42 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_write_room - write queue space
|
|
|
|
* @tty: terminal
|
2008-10-13 17:41:42 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2008-10-13 17:41:42 +08:00
|
|
|
*/
|
2021-05-05 17:19:15 +08:00
|
|
|
unsigned int tty_write_room(struct tty_struct *tty)
|
2008-04-30 15:54:13 +08:00
|
|
|
{
|
|
|
|
if (tty->ops->write_room)
|
|
|
|
return tty->ops->write_room(tty);
|
|
|
|
return 2048;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_write_room);
|
|
|
|
|
2008-10-13 17:41:42 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_driver_flush_buffer - discard internal buffer
|
|
|
|
* @tty: terminal
|
2008-10-13 17:41:42 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2008-10-13 17:41:42 +08:00
|
|
|
*/
|
2008-04-30 15:54:13 +08:00
|
|
|
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);
|
|
|
|
|
2008-10-13 17:41:42 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_unthrottle - flow control
|
|
|
|
* @tty: terminal
|
2008-10-13 17:41:42 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2009-06-11 19:44:17 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Drivers should however remember that the stack can issue a throttle, then
|
|
|
|
* change flow control method, then unthrottle.
|
2008-10-13 17:41:42 +08:00
|
|
|
*/
|
2008-04-30 15:54:18 +08:00
|
|
|
void tty_unthrottle(struct tty_struct *tty)
|
|
|
|
{
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&tty->termios_rwsem);
|
2008-04-30 15:54:18 +08:00
|
|
|
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
|
|
|
|
tty->ops->unthrottle)
|
|
|
|
tty->ops->unthrottle(tty);
|
2023-09-19 16:51:53 +08:00
|
|
|
tty->flow_change = TTY_FLOW_NO_CHANGE;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&tty->termios_rwsem);
|
2008-04-30 15:54:18 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_unthrottle);
|
2008-04-30 15:54:13 +08:00
|
|
|
|
2013-03-06 21:20:52 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_throttle_safe - flow control
|
|
|
|
* @tty: terminal
|
2013-03-06 21:20:52 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2013-03-06 21:20:52 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Returns: %true if @tty is throttled (or was already throttled)
|
2013-03-06 21:20:52 +08:00
|
|
|
*/
|
2023-09-19 16:51:47 +08:00
|
|
|
bool tty_throttle_safe(struct tty_struct *tty)
|
2013-03-06 21:20:52 +08:00
|
|
|
{
|
2023-09-19 16:51:48 +08:00
|
|
|
bool ret = true;
|
2013-03-06 21:20:52 +08:00
|
|
|
|
2013-06-15 21:14:31 +08:00
|
|
|
mutex_lock(&tty->throttle_mutex);
|
2016-04-10 08:11:36 +08:00
|
|
|
if (!tty_throttled(tty)) {
|
2013-03-06 21:20:52 +08:00
|
|
|
if (tty->flow_change != TTY_THROTTLE_SAFE)
|
2023-09-19 16:51:48 +08:00
|
|
|
ret = false;
|
2013-03-06 21:20:52 +08:00
|
|
|
else {
|
2013-04-15 23:06:06 +08:00
|
|
|
set_bit(TTY_THROTTLED, &tty->flags);
|
2013-03-06 21:20:52 +08:00
|
|
|
if (tty->ops->throttle)
|
|
|
|
tty->ops->throttle(tty);
|
|
|
|
}
|
|
|
|
}
|
2013-06-15 21:14:31 +08:00
|
|
|
mutex_unlock(&tty->throttle_mutex);
|
2013-03-06 21:20:52 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_unthrottle_safe - flow control
|
|
|
|
* @tty: terminal
|
2013-03-06 21:20:52 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2013-03-06 21:20:52 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Returns: %true if @tty is unthrottled (or was already unthrottled)
|
2013-03-06 21:20:52 +08:00
|
|
|
*/
|
2023-09-19 16:51:47 +08:00
|
|
|
bool tty_unthrottle_safe(struct tty_struct *tty)
|
2013-03-06 21:20:52 +08:00
|
|
|
{
|
2023-09-19 16:51:48 +08:00
|
|
|
bool ret = true;
|
2013-03-06 21:20:52 +08:00
|
|
|
|
2013-06-15 21:14:31 +08:00
|
|
|
mutex_lock(&tty->throttle_mutex);
|
2016-04-10 08:11:36 +08:00
|
|
|
if (tty_throttled(tty)) {
|
2013-03-06 21:20:52 +08:00
|
|
|
if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
|
2023-09-19 16:51:48 +08:00
|
|
|
ret = false;
|
2013-03-06 21:20:52 +08:00
|
|
|
else {
|
2013-04-15 23:06:06 +08:00
|
|
|
clear_bit(TTY_THROTTLED, &tty->flags);
|
2013-03-06 21:20:52 +08:00
|
|
|
if (tty->ops->unthrottle)
|
|
|
|
tty->ops->unthrottle(tty);
|
|
|
|
}
|
|
|
|
}
|
2013-06-15 21:14:31 +08:00
|
|
|
mutex_unlock(&tty->throttle_mutex);
|
2013-03-06 21:20:52 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-08-27 16:24:01 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_wait_until_sent - wait for I/O to finish
|
|
|
|
* @tty: tty we are waiting for
|
|
|
|
* @timeout: how long we will wait
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Wait for characters pending in a tty driver to hit the wire, or for a
|
|
|
|
* timeout to occur (eg due to flow control).
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Locking: none
|
2006-08-27 16:24:01 +08:00
|
|
|
*/
|
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
void tty_wait_until_sent(struct tty_struct *tty, long timeout)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (!timeout)
|
|
|
|
timeout = MAX_SCHEDULE_TIMEOUT;
|
2015-03-04 17:39:06 +08:00
|
|
|
|
2015-03-04 17:39:07 +08:00
|
|
|
timeout = wait_event_interruptible_timeout(tty->write_wait,
|
|
|
|
!tty_chars_in_buffer(tty), timeout);
|
|
|
|
if (timeout <= 0)
|
2015-03-04 17:39:06 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (timeout == MAX_SCHEDULE_TIMEOUT)
|
|
|
|
timeout = 0;
|
|
|
|
|
|
|
|
if (tty->ops->wait_until_sent)
|
|
|
|
tty->ops->wait_until_sent(tty, timeout);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_wait_until_sent);
|
|
|
|
|
2008-10-13 17:41:42 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Termios Helper Methods
|
|
|
|
*/
|
|
|
|
|
2022-08-16 19:57:37 +08:00
|
|
|
static void unset_locked_termios(struct tty_struct *tty, const struct ktermios *old)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-11-09 02:01:16 +08:00
|
|
|
struct ktermios *termios = &tty->termios;
|
|
|
|
struct ktermios *locked = &tty->termios_locked;
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
2008-02-08 20:18:48 +08:00
|
|
|
|
|
|
|
#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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;
|
2008-02-08 20:18:48 +08:00
|
|
|
for (i = 0; i < NCCS; i++)
|
2005-04-17 06:20:36 +08:00
|
|
|
termios->c_cc[i] = locked->c_cc[i] ?
|
|
|
|
old->c_cc[i] : termios->c_cc[i];
|
2006-12-08 18:38:44 +08:00
|
|
|
/* FIXME: What should we do for i/ospeed */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-10-17 14:30:07 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_termios_copy_hw - copy hardware settings
|
|
|
|
* @new: new termios
|
|
|
|
* @old: old termios
|
2007-10-17 14:30:07 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2007-10-17 14:30:07 +08:00
|
|
|
*/
|
2022-08-16 19:57:35 +08:00
|
|
|
void tty_termios_copy_hw(struct ktermios *new, const struct ktermios *old)
|
2007-10-17 14:30:07 +08:00
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
|
2008-01-08 22:55:51 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_termios_hw_change - check for setting change
|
|
|
|
* @a: termios
|
|
|
|
* @b: termios to compare
|
2008-01-08 22:55:51 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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
|
2008-01-08 22:55:51 +08:00
|
|
|
*/
|
2023-01-17 17:03:56 +08:00
|
|
|
bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
|
2008-01-08 22:55:51 +08:00
|
|
|
{
|
|
|
|
if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed)
|
2023-01-17 17:03:56 +08:00
|
|
|
return true;
|
2008-01-08 22:55:51 +08:00
|
|
|
if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL))
|
2023-01-17 17:03:56 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
2008-01-08 22:55:51 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tty_termios_hw_change);
|
|
|
|
|
2021-06-10 17:02:44 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_get_char_size - get size of a character
|
|
|
|
* @cflag: termios cflag value
|
2021-06-10 17:02:44 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Returns: size (in bits) of a character depending on @cflag's %CSIZE setting
|
2021-06-10 17:02:44 +08:00
|
|
|
*/
|
|
|
|
unsigned char tty_get_char_size(unsigned int 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);
|
|
|
|
|
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_get_frame_size - get size of a frame
|
|
|
|
* @cflag: termios cflag value
|
2021-06-10 17:02:44 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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.
|
2021-06-10 17:02:44 +08:00
|
|
|
*/
|
|
|
|
unsigned char tty_get_frame_size(unsigned int cflag)
|
|
|
|
{
|
|
|
|
unsigned char bits = 2 + tty_get_char_size(cflag);
|
|
|
|
|
|
|
|
if (cflag & CSTOPB)
|
|
|
|
bits++;
|
|
|
|
if (cflag & PARENB)
|
|
|
|
bits++;
|
2022-06-25 04:42:09 +08:00
|
|
|
if (cflag & ADDRB)
|
|
|
|
bits++;
|
2021-06-10 17:02:44 +08:00
|
|
|
|
|
|
|
return bits;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tty_get_frame_size);
|
|
|
|
|
2006-08-27 16:24:01 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_set_termios - update termios values
|
|
|
|
* @tty: tty to update
|
|
|
|
* @new_termios: desired new value
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Perform updates to the termios values set on this @tty. A master pty's
|
|
|
|
* termios should never be set.
|
2014-10-17 03:33:22 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Locking: &tty_struct->termios_rwsem
|
2006-08-27 16:24:01 +08:00
|
|
|
*/
|
2015-04-10 21:13:05 +08:00
|
|
|
int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-04-30 15:53:59 +08:00
|
|
|
struct ktermios old_termios;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct tty_ldisc *ld;
|
2008-02-08 20:18:48 +08:00
|
|
|
|
2014-10-17 03:33:22 +08:00
|
|
|
WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
|
|
|
|
tty->driver->subtype == PTY_TYPE_MASTER);
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Perform the actual termios internal changes under lock.
|
|
|
|
*/
|
2008-02-08 20:18:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* FIXME: we need to decide on some locking/ordering semantics
|
|
|
|
for the set_termios notification eventually */
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
old_termios = tty->termios;
|
|
|
|
tty->termios = *new_termios;
|
2015-11-09 02:01:16 +08:00
|
|
|
unset_locked_termios(tty, &old_termios);
|
2022-06-25 04:42:09 +08:00
|
|
|
/* Reset any ADDRB changes, ADDRB is changed through ->rs485_config() */
|
|
|
|
tty->termios.c_cflag ^= (tty->termios.c_cflag ^ old_termios.c_cflag) & ADDRB;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-30 15:54:13 +08:00
|
|
|
if (tty->ops->set_termios)
|
2014-11-06 01:26:25 +08:00
|
|
|
tty->ops->set_termios(tty, &old_termios);
|
2007-10-17 14:30:07 +08:00
|
|
|
else
|
2012-07-14 22:31:47 +08:00
|
|
|
tty_termios_copy_hw(&tty->termios, &old_termios);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ld = tty_ldisc_ref(tty);
|
|
|
|
if (ld != NULL) {
|
2008-07-17 04:53:12 +08:00
|
|
|
if (ld->ops->set_termios)
|
2014-11-06 01:26:25 +08:00
|
|
|
ld->ops->set_termios(tty, &old_termios);
|
2005-04-17 06:20:36 +08:00
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
}
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&tty->termios_rwsem);
|
2011-02-15 00:27:53 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-04-10 21:13:05 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tty_set_termios);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2022-08-21 07:36:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
termios->c_iflag = (0xffff0000 & termios->c_iflag) | v.c_iflag;
|
|
|
|
termios->c_oflag = (0xffff0000 & termios->c_oflag) | v.c_oflag;
|
|
|
|
termios->c_cflag = (0xffff0000 & termios->c_cflag) | v.c_cflag;
|
|
|
|
termios->c_lflag = (0xffff0000 & termios->c_lflag) | v.c_lflag;
|
|
|
|
termios->c_line = (0xffff0000 & termios->c_lflag) | v.c_line;
|
|
|
|
memcpy(termios->c_cc, v.c_cc, NCC);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Translate a "termios" structure into a "termio". Ugh.
|
|
|
|
*/
|
|
|
|
__weak int kernel_termios_to_user_termio(struct termio __user *termio,
|
|
|
|
struct ktermios *termios)
|
|
|
|
{
|
|
|
|
struct termio v;
|
|
|
|
memset(&v, 0, sizeof(struct termio));
|
|
|
|
v.c_iflag = termios->c_iflag;
|
|
|
|
v.c_oflag = termios->c_oflag;
|
|
|
|
v.c_cflag = termios->c_cflag;
|
|
|
|
v.c_lflag = termios->c_lflag;
|
|
|
|
v.c_line = termios->c_line;
|
|
|
|
memcpy(v.c_cc, termios->c_cc, NCC);
|
|
|
|
return copy_to_user(termio, &v, sizeof(struct termio));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TCGETS2
|
|
|
|
__weak int user_termios_to_kernel_termios(struct ktermios *k,
|
|
|
|
struct termios2 __user *u)
|
|
|
|
{
|
|
|
|
return copy_from_user(k, u, sizeof(struct termios2));
|
|
|
|
}
|
|
|
|
__weak int kernel_termios_to_user_termios(struct termios2 __user *u,
|
|
|
|
struct ktermios *k)
|
|
|
|
{
|
|
|
|
return copy_to_user(u, k, sizeof(struct termios2));
|
|
|
|
}
|
|
|
|
__weak int user_termios_to_kernel_termios_1(struct ktermios *k,
|
|
|
|
struct termios __user *u)
|
|
|
|
{
|
|
|
|
return copy_from_user(k, u, sizeof(struct termios));
|
|
|
|
}
|
|
|
|
__weak int kernel_termios_to_user_termios_1(struct termios __user *u,
|
|
|
|
struct ktermios *k)
|
|
|
|
{
|
|
|
|
return copy_to_user(u, k, sizeof(struct termios));
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
__weak int user_termios_to_kernel_termios(struct ktermios *k,
|
|
|
|
struct termios __user *u)
|
|
|
|
{
|
|
|
|
return copy_from_user(k, u, sizeof(struct termios));
|
|
|
|
}
|
|
|
|
__weak int kernel_termios_to_user_termios(struct termios __user *u,
|
|
|
|
struct ktermios *k)
|
|
|
|
{
|
|
|
|
return copy_to_user(u, k, sizeof(struct termios));
|
|
|
|
}
|
|
|
|
#endif /* TCGETS2 */
|
|
|
|
|
2006-08-27 16:24:01 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* set_termios - set termios values for a tty
|
|
|
|
* @tty: terminal device
|
|
|
|
* @arg: user data
|
|
|
|
* @opt: option information
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Helper function to prepare termios data and run necessary other functions
|
|
|
|
* before using tty_set_termios() to do the actual changes.
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Locking: called functions take &tty_struct->ldisc_sem and
|
|
|
|
* &tty_struct->termios_rwsem locks
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, an error otherwise
|
2006-08-27 16:24:01 +08:00
|
|
|
*/
|
2008-02-08 20:18:48 +08:00
|
|
|
static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-12-08 18:38:44 +08:00
|
|
|
struct ktermios tmp_termios;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct tty_ldisc *ld;
|
|
|
|
int retval = tty_check_change(tty);
|
|
|
|
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_read(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
tmp_termios = tty->termios;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_read(&tty->termios_rwsem);
|
2006-12-08 18:38:47 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (opt & TERMIOS_TERMIO) {
|
|
|
|
if (user_termio_to_kernel_termios(&tmp_termios,
|
|
|
|
(struct termio __user *)arg))
|
|
|
|
return -EFAULT;
|
2006-12-08 18:38:44 +08:00
|
|
|
#ifdef TCGETS2
|
|
|
|
} else if (opt & TERMIOS_OLD) {
|
|
|
|
if (user_termios_to_kernel_termios_1(&tmp_termios,
|
2006-12-08 18:38:47 +08:00
|
|
|
(struct termios __user *)arg))
|
2006-12-08 18:38:44 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
if (user_termios_to_kernel_termios(&tmp_termios,
|
2006-12-08 18:38:47 +08:00
|
|
|
(struct termios2 __user *)arg))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
2006-12-08 18:38:47 +08:00
|
|
|
#else
|
|
|
|
} else if (user_termios_to_kernel_termios(&tmp_termios,
|
|
|
|
(struct termios __user *)arg))
|
|
|
|
return -EFAULT;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
/* If old style Bfoo values are used then load c_ispeed/c_ospeed
|
|
|
|
* with the real speed so its unconditionally usable */
|
2006-12-08 18:38:44 +08:00
|
|
|
tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
|
|
|
|
tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
|
|
|
|
|
2023-03-17 19:33:17 +08:00
|
|
|
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;
|
|
|
|
|
2023-08-10 17:14:39 +08:00
|
|
|
if (tty_write_lock(tty, false) < 0)
|
2023-03-17 19:33:17 +08:00
|
|
|
goto retry_write_wait;
|
|
|
|
|
|
|
|
/* Racing writer? */
|
|
|
|
if (tty_chars_in_buffer(tty)) {
|
|
|
|
tty_write_unlock(tty);
|
|
|
|
goto retry_write_wait;
|
|
|
|
}
|
2008-02-08 20:18:48 +08:00
|
|
|
|
2023-03-17 19:33:17 +08:00
|
|
|
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);
|
|
|
|
}
|
2008-02-08 20:18:48 +08:00
|
|
|
|
2023-03-17 19:33:17 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tty_set_termios(tty, &tmp_termios);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2023-03-17 19:33:17 +08:00
|
|
|
tty_write_unlock(tty);
|
|
|
|
} else {
|
|
|
|
tty_set_termios(tty, &tmp_termios);
|
|
|
|
}
|
2007-10-17 14:30:07 +08:00
|
|
|
|
|
|
|
/* 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 */
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-11 21:03:13 +08:00
|
|
|
static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
|
|
|
|
{
|
2013-06-15 21:14:23 +08:00
|
|
|
down_read(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
*kterm = tty->termios;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_read(&tty->termios_rwsem);
|
2009-06-11 21:03:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
|
|
|
|
{
|
2013-06-15 21:14:23 +08:00
|
|
|
down_read(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
*kterm = tty->termios_locked;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_read(&tty->termios_rwsem);
|
2009-06-11 21:03:13 +08:00
|
|
|
}
|
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
static int get_termio(struct tty_struct *tty, struct termio __user *termio)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-06-11 21:03:13 +08:00
|
|
|
struct ktermios kterm;
|
|
|
|
copy_termios(tty, &kterm);
|
|
|
|
if (kernel_termios_to_user_termio(termio, &kterm))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TIOCGETP
|
|
|
|
/*
|
|
|
|
* These are deprecated, but there is limited support..
|
|
|
|
*
|
|
|
|
* The "sg_flags" translation is a joke..
|
|
|
|
*/
|
2008-02-08 20:18:48 +08:00
|
|
|
static int get_sgflags(struct tty_struct *tty)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int flags = 0;
|
|
|
|
|
2016-01-11 12:36:15 +08:00
|
|
|
if (!L_ICANON(tty)) {
|
|
|
|
if (L_ISIG(tty))
|
2005-04-17 06:20:36 +08:00
|
|
|
flags |= 0x02; /* cbreak */
|
|
|
|
else
|
|
|
|
flags |= 0x20; /* raw */
|
|
|
|
}
|
2016-01-11 12:36:15 +08:00
|
|
|
if (L_ECHO(tty))
|
2005-04-17 06:20:36 +08:00
|
|
|
flags |= 0x08; /* echo */
|
2016-01-11 12:36:15 +08:00
|
|
|
if (O_OPOST(tty))
|
|
|
|
if (O_ONLCR(tty))
|
2005-04-17 06:20:36 +08:00
|
|
|
flags |= 0x10; /* crmod */
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sgttyb tmp;
|
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_read(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
tmp.sg_ispeed = tty->termios.c_ispeed;
|
|
|
|
tmp.sg_ospeed = tty->termios.c_ospeed;
|
|
|
|
tmp.sg_erase = tty->termios.c_cc[VERASE];
|
|
|
|
tmp.sg_kill = tty->termios.c_cc[VKILL];
|
2005-04-17 06:20:36 +08:00
|
|
|
tmp.sg_flags = get_sgflags(tty);
|
2013-06-15 21:14:23 +08:00
|
|
|
up_read(&tty->termios_rwsem);
|
2008-02-08 20:18:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
|
|
|
|
}
|
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
static void set_sgflags(struct ktermios *termios, int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-07-18 00:05:40 +08:00
|
|
|
termios->c_iflag = ICRNL | IXON;
|
|
|
|
termios->c_oflag = 0;
|
|
|
|
termios->c_lflag = ISIG | ICANON;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (flags & 0x02) { /* cbreak */
|
2012-07-18 00:05:40 +08:00
|
|
|
termios->c_iflag = 0;
|
|
|
|
termios->c_lflag &= ~ICANON;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (flags & 0x08) { /* echo */
|
2012-07-18 00:05:40 +08:00
|
|
|
termios->c_lflag |= ECHO | ECHOE | ECHOK |
|
2005-04-17 06:20:36 +08:00
|
|
|
ECHOCTL | ECHOKE | IEXTEN;
|
|
|
|
}
|
|
|
|
if (flags & 0x10) { /* crmod */
|
2012-07-18 00:05:40 +08:00
|
|
|
termios->c_oflag |= OPOST | ONLCR;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (flags & 0x20) { /* raw */
|
2012-07-18 00:05:40 +08:00
|
|
|
termios->c_iflag = 0;
|
|
|
|
termios->c_lflag &= ~(ISIG | ICANON);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-07-18 00:05:40 +08:00
|
|
|
if (!(termios->c_lflag & ICANON)) {
|
|
|
|
termios->c_cc[VMIN] = 1;
|
|
|
|
termios->c_cc[VTIME] = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-27 16:24:01 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* set_sgttyb - set legacy terminal values
|
|
|
|
* @tty: tty structure
|
|
|
|
* @sgttyb: pointer to old style terminal structure
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Updates a terminal from the legacy BSD style terminal information structure.
|
2006-08-27 16:24:01 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Locking: &tty_struct->termios_rwsem
|
|
|
|
*
|
|
|
|
* Returns: 0 on success, an error otherwise
|
2006-08-27 16:24:01 +08:00
|
|
|
*/
|
2008-02-08 20:18:48 +08:00
|
|
|
static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
struct sgttyb tmp;
|
2006-12-08 18:38:44 +08:00
|
|
|
struct ktermios termios;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
retval = tty_check_change(tty);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2008-02-08 20:18:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
termios = tty->termios;
|
2005-04-17 06:20:36 +08:00
|
|
|
termios.c_cc[VERASE] = tmp.sg_erase;
|
|
|
|
termios.c_cc[VKILL] = tmp.sg_kill;
|
|
|
|
set_sgflags(&termios, tmp.sg_flags);
|
2006-12-08 18:38:44 +08:00
|
|
|
/* Try and encode into Bfoo format */
|
2008-02-08 20:18:48 +08:00
|
|
|
tty_termios_encode_baud_rate(&termios, termios.c_ispeed,
|
|
|
|
termios.c_ospeed);
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&tty->termios_rwsem);
|
2011-02-15 00:27:53 +08:00
|
|
|
tty_set_termios(tty, &termios);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef TIOCGETC
|
2008-02-08 20:18:48 +08:00
|
|
|
static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct tchars tmp;
|
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_read(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
tmp.t_intrc = tty->termios.c_cc[VINTR];
|
|
|
|
tmp.t_quitc = tty->termios.c_cc[VQUIT];
|
|
|
|
tmp.t_startc = tty->termios.c_cc[VSTART];
|
|
|
|
tmp.t_stopc = tty->termios.c_cc[VSTOP];
|
|
|
|
tmp.t_eofc = tty->termios.c_cc[VEOF];
|
|
|
|
tmp.t_brkc = tty->termios.c_cc[VEOL2]; /* what is brkc anyway? */
|
2013-06-15 21:14:23 +08:00
|
|
|
up_read(&tty->termios_rwsem);
|
2005-04-17 06:20:36 +08:00
|
|
|
return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
|
|
|
|
}
|
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct tchars tmp;
|
|
|
|
|
|
|
|
if (copy_from_user(&tmp, tchars, sizeof(tmp)))
|
|
|
|
return -EFAULT;
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
tty->termios.c_cc[VINTR] = tmp.t_intrc;
|
|
|
|
tty->termios.c_cc[VQUIT] = tmp.t_quitc;
|
|
|
|
tty->termios.c_cc[VSTART] = tmp.t_startc;
|
|
|
|
tty->termios.c_cc[VSTOP] = tmp.t_stopc;
|
|
|
|
tty->termios.c_cc[VEOF] = tmp.t_eofc;
|
|
|
|
tty->termios.c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&tty->termios_rwsem);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef TIOCGLTC
|
2008-02-08 20:18:48 +08:00
|
|
|
static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ltchars tmp;
|
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_read(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
tmp.t_suspc = tty->termios.c_cc[VSUSP];
|
2008-02-08 20:18:48 +08:00
|
|
|
/* what is dsuspc anyway? */
|
2012-07-14 22:31:47 +08:00
|
|
|
tmp.t_dsuspc = tty->termios.c_cc[VSUSP];
|
|
|
|
tmp.t_rprntc = tty->termios.c_cc[VREPRINT];
|
2008-02-08 20:18:48 +08:00
|
|
|
/* what is flushc anyway? */
|
2012-07-14 22:31:47 +08:00
|
|
|
tmp.t_flushc = tty->termios.c_cc[VEOL2];
|
|
|
|
tmp.t_werasc = tty->termios.c_cc[VWERASE];
|
|
|
|
tmp.t_lnextc = tty->termios.c_cc[VLNEXT];
|
2013-06-15 21:14:23 +08:00
|
|
|
up_read(&tty->termios_rwsem);
|
2005-04-17 06:20:36 +08:00
|
|
|
return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
|
|
|
|
}
|
|
|
|
|
2008-02-08 20:18:48 +08:00
|
|
|
static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ltchars tmp;
|
|
|
|
|
|
|
|
if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
tty->termios.c_cc[VSUSP] = tmp.t_suspc;
|
2008-02-08 20:18:48 +08:00
|
|
|
/* what is dsuspc anyway? */
|
2012-07-14 22:31:47 +08:00
|
|
|
tty->termios.c_cc[VEOL2] = tmp.t_dsuspc;
|
|
|
|
tty->termios.c_cc[VREPRINT] = tmp.t_rprntc;
|
2008-02-08 20:18:48 +08:00
|
|
|
/* what is flushc anyway? */
|
2012-07-14 22:31:47 +08:00
|
|
|
tty->termios.c_cc[VEOL2] = tmp.t_flushc;
|
|
|
|
tty->termios.c_cc[VWERASE] = tmp.t_werasc;
|
|
|
|
tty->termios.c_cc[VLNEXT] = tmp.t_lnextc;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&tty->termios_rwsem);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-04-30 15:53:34 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_change_softcar - carrier change ioctl helper
|
|
|
|
* @tty: tty to update
|
|
|
|
* @enable: enable/disable %CLOCAL
|
2008-04-30 15:53:34 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* 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
|
2008-04-30 15:53:34 +08:00
|
|
|
*/
|
2023-08-10 17:14:42 +08:00
|
|
|
static int tty_change_softcar(struct tty_struct *tty, bool enable)
|
2008-04-30 15:53:34 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
2008-04-30 15:54:13 +08:00
|
|
|
struct ktermios old;
|
2023-08-10 17:14:42 +08:00
|
|
|
tcflag_t bit = enable ? CLOCAL : 0;
|
2008-04-30 15:53:34 +08:00
|
|
|
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
old = tty->termios;
|
|
|
|
tty->termios.c_cflag &= ~CLOCAL;
|
|
|
|
tty->termios.c_cflag |= bit;
|
2008-04-30 15:54:13 +08:00
|
|
|
if (tty->ops->set_termios)
|
|
|
|
tty->ops->set_termios(tty, &old);
|
2016-01-11 12:36:15 +08:00
|
|
|
if (C_CLOCAL(tty) != bit)
|
2008-04-30 15:53:34 +08:00
|
|
|
ret = -EINVAL;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&tty->termios_rwsem);
|
2008-04-30 15:53:34 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-11-07 17:24:56 +08:00
|
|
|
/**
|
2023-09-19 16:51:49 +08:00
|
|
|
* tty_mode_ioctl - mode related ioctls
|
|
|
|
* @tty: tty for the ioctl
|
|
|
|
* @cmd: command
|
|
|
|
* @arg: ioctl argument
|
2007-11-07 17:24:56 +08:00
|
|
|
*
|
2023-09-19 16:51:49 +08:00
|
|
|
* Perform non-line discipline specific mode control ioctls. This is designed
|
|
|
|
* to be called by line disciplines to ensure they provide consistent mode
|
|
|
|
* setting.
|
2007-11-07 17:24:56 +08:00
|
|
|
*/
|
2021-09-14 17:11:23 +08:00
|
|
|
int tty_mode_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-02-08 20:18:48 +08:00
|
|
|
struct tty_struct *real_tty;
|
2005-04-17 06:20:36 +08:00
|
|
|
void __user *p = (void __user *)arg;
|
2008-10-13 17:38:46 +08:00
|
|
|
int ret = 0;
|
2009-06-11 21:03:13 +08:00
|
|
|
struct ktermios kterm;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCGETP:
|
|
|
|
return get_sgttyb(real_tty, (struct sgttyb __user *) arg);
|
|
|
|
case TIOCSETP:
|
|
|
|
case TIOCSETN:
|
|
|
|
return set_sgttyb(real_tty, (struct sgttyb __user *) arg);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
#ifdef TIOCGETC
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCGETC:
|
|
|
|
return get_tchars(real_tty, p);
|
|
|
|
case TIOCSETC:
|
|
|
|
return set_tchars(real_tty, p);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
#ifdef TIOCGLTC
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCGLTC:
|
|
|
|
return get_ltchars(real_tty, p);
|
|
|
|
case TIOCSLTC:
|
|
|
|
return set_ltchars(real_tty, p);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCSETSF:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD);
|
|
|
|
case TCSETSW:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD);
|
|
|
|
case TCSETS:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_OLD);
|
2006-12-08 18:38:44 +08:00
|
|
|
#ifndef TCGETS2
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCGETS:
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios(real_tty, &kterm);
|
|
|
|
if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
|
2008-10-13 17:38:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
return ret;
|
2006-12-08 18:38:44 +08:00
|
|
|
#else
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCGETS:
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios(real_tty, &kterm);
|
|
|
|
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
|
2008-10-13 17:38:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
return ret;
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCGETS2:
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios(real_tty, &kterm);
|
|
|
|
if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm))
|
2008-10-13 17:38:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
return ret;
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCSETSF2:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT);
|
|
|
|
case TCSETSW2:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_WAIT);
|
|
|
|
case TCSETS2:
|
|
|
|
return set_termios(real_tty, p, 0);
|
2006-12-08 18:38:44 +08:00
|
|
|
#endif
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCGETA:
|
|
|
|
return get_termio(real_tty, p);
|
|
|
|
case TCSETAF:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO);
|
|
|
|
case TCSETAW:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_TERMIO);
|
|
|
|
case TCSETA:
|
|
|
|
return set_termios(real_tty, p, TERMIOS_TERMIO);
|
2007-11-07 17:24:56 +08:00
|
|
|
#ifndef TCGETS2
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCGLCKTRMIOS:
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios_locked(real_tty, &kterm);
|
|
|
|
if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
|
2008-10-13 17:38:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
return ret;
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCSLCKTRMIOS:
|
2023-12-08 22:36:56 +08:00
|
|
|
if (!checkpoint_restore_ns_capable(&init_user_ns))
|
2008-02-08 20:18:48 +08:00
|
|
|
return -EPERM;
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios_locked(real_tty, &kterm);
|
|
|
|
if (user_termios_to_kernel_termios(&kterm,
|
2008-02-08 20:18:48 +08:00
|
|
|
(struct termios __user *) arg))
|
2009-06-11 21:03:13 +08:00
|
|
|
return -EFAULT;
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&real_tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
real_tty->termios_locked = kterm;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&real_tty->termios_rwsem);
|
2009-06-11 21:03:13 +08:00
|
|
|
return 0;
|
2007-11-07 17:24:56 +08:00
|
|
|
#else
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCGLCKTRMIOS:
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios_locked(real_tty, &kterm);
|
|
|
|
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
|
2008-10-13 17:38:46 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
return ret;
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCSLCKTRMIOS:
|
2023-12-08 22:36:56 +08:00
|
|
|
if (!checkpoint_restore_ns_capable(&init_user_ns))
|
2009-06-11 21:03:13 +08:00
|
|
|
return -EPERM;
|
|
|
|
copy_termios_locked(real_tty, &kterm);
|
|
|
|
if (user_termios_to_kernel_termios_1(&kterm,
|
2008-02-08 20:18:48 +08:00
|
|
|
(struct termios __user *) arg))
|
2009-06-11 21:03:13 +08:00
|
|
|
return -EFAULT;
|
2013-06-15 21:14:23 +08:00
|
|
|
down_write(&real_tty->termios_rwsem);
|
2012-07-14 22:31:47 +08:00
|
|
|
real_tty->termios_locked = kterm;
|
2013-06-15 21:14:23 +08:00
|
|
|
up_write(&real_tty->termios_rwsem);
|
2008-10-13 17:38:46 +08:00
|
|
|
return ret;
|
2007-11-07 17:24:56 +08:00
|
|
|
#endif
|
2008-10-13 17:38:18 +08:00
|
|
|
#ifdef TCGETX
|
2020-12-03 10:03:31 +08:00
|
|
|
case TCGETX:
|
2008-10-13 17:38:18 +08:00
|
|
|
case TCSETX:
|
|
|
|
case TCSETXW:
|
|
|
|
case TCSETXF:
|
2021-04-07 17:52:03 +08:00
|
|
|
return -ENOTTY;
|
|
|
|
#endif
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCGSOFTCAR:
|
2009-06-11 21:03:13 +08:00
|
|
|
copy_termios(real_tty, &kterm);
|
|
|
|
ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
|
2008-02-08 20:18:48 +08:00
|
|
|
(int __user *)arg);
|
2008-10-13 17:38:46 +08:00
|
|
|
return ret;
|
2008-02-08 20:18:48 +08:00
|
|
|
case TIOCSSOFTCAR:
|
|
|
|
if (get_user(arg, (unsigned int __user *) arg))
|
|
|
|
return -EFAULT;
|
2008-08-27 02:52:47 +08:00
|
|
|
return tty_change_softcar(real_tty, arg);
|
2008-02-08 20:18:48 +08:00
|
|
|
default:
|
|
|
|
return -ENOIOCTLCMD;
|
2007-11-07 17:24:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tty_mode_ioctl);
|
|
|
|
|
2013-03-12 04:44:45 +08:00
|
|
|
|
|
|
|
/* Caller guarantees ldisc reference is held */
|
|
|
|
static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg)
|
2007-11-07 17:24:56 +08:00
|
|
|
{
|
2013-03-12 04:44:45 +08:00
|
|
|
struct tty_ldisc *ld = tty->ldisc;
|
2007-11-07 17:24:56 +08:00
|
|
|
|
|
|
|
switch (arg) {
|
|
|
|
case TCIFLUSH:
|
tty: Add driver unthrottle in ioctl(...,TCFLSH,..).
Regression 'tty: fix "IRQ45: nobody cared"'
Regression commit 7b292b4bf9a9d6098440d85616d6ca4c608b8304
Function reset_buffer_flags() also invoked during the ioctl(...,TCFLSH,..).
At the time of request we can have full buffers and throttled driver too.
If we don't unthrottle driver, we can get forever throttled driver, because,
after request, we will have empty buffers and throttled driver and
there is no place to unthrottle driver.
It simple reproduce with "pty" pair then one side sleep on tty->write_wait,
and other side do ioctl(...,TCFLSH,..). Then there is no place to do writers wake up.
Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:07:50 +08:00
|
|
|
if (ld && ld->ops->flush_buffer) {
|
2008-07-17 04:53:12 +08:00
|
|
|
ld->ops->flush_buffer(tty);
|
tty: Add driver unthrottle in ioctl(...,TCFLSH,..).
Regression 'tty: fix "IRQ45: nobody cared"'
Regression commit 7b292b4bf9a9d6098440d85616d6ca4c608b8304
Function reset_buffer_flags() also invoked during the ioctl(...,TCFLSH,..).
At the time of request we can have full buffers and throttled driver too.
If we don't unthrottle driver, we can get forever throttled driver, because,
after request, we will have empty buffers and throttled driver and
there is no place to unthrottle driver.
It simple reproduce with "pty" pair then one side sleep on tty->write_wait,
and other side do ioctl(...,TCFLSH,..). Then there is no place to do writers wake up.
Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:07:50 +08:00
|
|
|
tty_unthrottle(tty);
|
|
|
|
}
|
2007-11-07 17:24:56 +08:00
|
|
|
break;
|
|
|
|
case TCIOFLUSH:
|
tty: Add driver unthrottle in ioctl(...,TCFLSH,..).
Regression 'tty: fix "IRQ45: nobody cared"'
Regression commit 7b292b4bf9a9d6098440d85616d6ca4c608b8304
Function reset_buffer_flags() also invoked during the ioctl(...,TCFLSH,..).
At the time of request we can have full buffers and throttled driver too.
If we don't unthrottle driver, we can get forever throttled driver, because,
after request, we will have empty buffers and throttled driver and
there is no place to unthrottle driver.
It simple reproduce with "pty" pair then one side sleep on tty->write_wait,
and other side do ioctl(...,TCFLSH,..). Then there is no place to do writers wake up.
Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:07:50 +08:00
|
|
|
if (ld && ld->ops->flush_buffer) {
|
2008-07-17 04:53:12 +08:00
|
|
|
ld->ops->flush_buffer(tty);
|
tty: Add driver unthrottle in ioctl(...,TCFLSH,..).
Regression 'tty: fix "IRQ45: nobody cared"'
Regression commit 7b292b4bf9a9d6098440d85616d6ca4c608b8304
Function reset_buffer_flags() also invoked during the ioctl(...,TCFLSH,..).
At the time of request we can have full buffers and throttled driver too.
If we don't unthrottle driver, we can get forever throttled driver, because,
after request, we will have empty buffers and throttled driver and
there is no place to unthrottle driver.
It simple reproduce with "pty" pair then one side sleep on tty->write_wait,
and other side do ioctl(...,TCFLSH,..). Then there is no place to do writers wake up.
Signed-off-by: Ilya Zykov <ilya@ilyx.ru>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-01-16 17:07:50 +08:00
|
|
|
tty_unthrottle(tty);
|
|
|
|
}
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2007-11-07 17:24:56 +08:00
|
|
|
case TCOFLUSH:
|
2008-04-30 15:54:13 +08:00
|
|
|
tty_driver_flush_buffer(tty);
|
2007-11-07 17:24:56 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2013-03-12 04:44:45 +08:00
|
|
|
|
|
|
|
int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct tty_ldisc *ld;
|
|
|
|
int retval = tty_check_change(tty);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
|
retval = __tty_perform_flush(tty, arg);
|
|
|
|
if (ld)
|
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
return retval;
|
|
|
|
}
|
2007-11-07 17:24:56 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tty_perform_flush);
|
|
|
|
|
2021-09-14 17:11:24 +08:00
|
|
|
int n_tty_ioctl_helper(struct tty_struct *tty, unsigned int cmd,
|
|
|
|
unsigned long arg)
|
2007-11-07 17:24:56 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
switch (cmd) {
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCXONC:
|
|
|
|
retval = tty_check_change(tty);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
switch (arg) {
|
|
|
|
case TCOOFF:
|
2021-05-05 17:19:05 +08:00
|
|
|
spin_lock_irq(&tty->flow.lock);
|
|
|
|
if (!tty->flow.tco_stopped) {
|
|
|
|
tty->flow.tco_stopped = true;
|
2014-09-11 03:06:33 +08:00
|
|
|
__stop_tty(tty);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2021-05-05 17:19:05 +08:00
|
|
|
spin_unlock_irq(&tty->flow.lock);
|
2008-02-08 20:18:48 +08:00
|
|
|
break;
|
|
|
|
case TCOON:
|
2021-05-05 17:19:05 +08:00
|
|
|
spin_lock_irq(&tty->flow.lock);
|
|
|
|
if (tty->flow.tco_stopped) {
|
|
|
|
tty->flow.tco_stopped = false;
|
2014-09-11 03:06:33 +08:00
|
|
|
__start_tty(tty);
|
2008-02-08 20:18:48 +08:00
|
|
|
}
|
2021-05-05 17:19:05 +08:00
|
|
|
spin_unlock_irq(&tty->flow.lock);
|
2008-02-08 20:18:48 +08:00
|
|
|
break;
|
|
|
|
case TCIOFF:
|
|
|
|
if (STOP_CHAR(tty) != __DISABLED_CHAR)
|
2014-09-11 03:06:35 +08:00
|
|
|
retval = tty_send_xchar(tty, STOP_CHAR(tty));
|
2008-02-08 20:18:48 +08:00
|
|
|
break;
|
|
|
|
case TCION:
|
|
|
|
if (START_CHAR(tty) != __DISABLED_CHAR)
|
2014-09-11 03:06:35 +08:00
|
|
|
retval = tty_send_xchar(tty, START_CHAR(tty));
|
2008-02-08 20:18:48 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2008-02-08 20:18:48 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2014-09-11 03:06:35 +08:00
|
|
|
return retval;
|
2008-02-08 20:18:48 +08:00
|
|
|
case TCFLSH:
|
2013-09-26 08:13:04 +08:00
|
|
|
retval = tty_check_change(tty);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2013-03-12 04:44:45 +08:00
|
|
|
return __tty_perform_flush(tty, arg);
|
2008-02-08 20:18:48 +08:00
|
|
|
default:
|
|
|
|
/* Try the mode commands */
|
2021-09-14 17:11:23 +08:00
|
|
|
return tty_mode_ioctl(tty, cmd, arg);
|
2008-02-08 20:18:48 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-10-13 17:44:17 +08:00
|
|
|
EXPORT_SYMBOL(n_tty_ioctl_helper);
|