18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
68c2ecf20Sopenharmony_ci * which can be dynamically activated and de-activated by the line
78c2ecf20Sopenharmony_ci * discipline handling modules (like SLIP).
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/termios.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/major.h>
168c2ecf20Sopenharmony_ci#include <linux/tty.h>
178c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/bitops.h>
228c2ecf20Sopenharmony_ci#include <linux/mutex.h>
238c2ecf20Sopenharmony_ci#include <linux/compat.h>
248c2ecf20Sopenharmony_ci#include "tty.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <asm/io.h>
278c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#undef TTY_DEBUG_WAIT_UNTIL_SENT
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
328c2ecf20Sopenharmony_ci# define tty_debug_wait_until_sent(tty, f, args...)    tty_debug(tty, f, ##args)
338c2ecf20Sopenharmony_ci#else
348c2ecf20Sopenharmony_ci# define tty_debug_wait_until_sent(tty, f, args...)    do {} while (0)
358c2ecf20Sopenharmony_ci#endif
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#undef	DEBUG
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Internal flag options for termios setting behavior
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci#define TERMIOS_FLUSH	1
438c2ecf20Sopenharmony_ci#define TERMIOS_WAIT	2
448c2ecf20Sopenharmony_ci#define TERMIOS_TERMIO	4
458c2ecf20Sopenharmony_ci#define TERMIOS_OLD	8
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/**
498c2ecf20Sopenharmony_ci *	tty_chars_in_buffer	-	characters pending
508c2ecf20Sopenharmony_ci *	@tty: terminal
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci *	Return the number of bytes of data in the device private
538c2ecf20Sopenharmony_ci *	output queue. If no private method is supplied there is assumed
548c2ecf20Sopenharmony_ci *	to be no queue on the device.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ciint tty_chars_in_buffer(struct tty_struct *tty)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	if (tty->ops->chars_in_buffer)
608c2ecf20Sopenharmony_ci		return tty->ops->chars_in_buffer(tty);
618c2ecf20Sopenharmony_ci	else
628c2ecf20Sopenharmony_ci		return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_chars_in_buffer);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci *	tty_write_room		-	write queue space
688c2ecf20Sopenharmony_ci *	@tty: terminal
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci *	Return the number of bytes that can be queued to this device
718c2ecf20Sopenharmony_ci *	at the present time. The result should be treated as a guarantee
728c2ecf20Sopenharmony_ci *	and the driver cannot offer a value it later shrinks by more than
738c2ecf20Sopenharmony_ci *	the number of bytes written. If no method is provided 2K is always
748c2ecf20Sopenharmony_ci *	returned and data may be lost as there will be no flow control.
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ciint tty_write_room(struct tty_struct *tty)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	if (tty->ops->write_room)
808c2ecf20Sopenharmony_ci		return tty->ops->write_room(tty);
818c2ecf20Sopenharmony_ci	return 2048;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_write_room);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/**
868c2ecf20Sopenharmony_ci *	tty_driver_flush_buffer	-	discard internal buffer
878c2ecf20Sopenharmony_ci *	@tty: terminal
888c2ecf20Sopenharmony_ci *
898c2ecf20Sopenharmony_ci *	Discard the internal output buffer for this device. If no method
908c2ecf20Sopenharmony_ci *	is provided then either the buffer cannot be hardware flushed or
918c2ecf20Sopenharmony_ci *	there is no buffer driver side.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_civoid tty_driver_flush_buffer(struct tty_struct *tty)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	if (tty->ops->flush_buffer)
968c2ecf20Sopenharmony_ci		tty->ops->flush_buffer(tty);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_driver_flush_buffer);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/**
1018c2ecf20Sopenharmony_ci *	tty_throttle		-	flow control
1028c2ecf20Sopenharmony_ci *	@tty: terminal
1038c2ecf20Sopenharmony_ci *
1048c2ecf20Sopenharmony_ci *	Indicate that a tty should stop transmitting data down the stack.
1058c2ecf20Sopenharmony_ci *	Takes the termios rwsem to protect against parallel throttle/unthrottle
1068c2ecf20Sopenharmony_ci *	and also to ensure the driver can consistently reference its own
1078c2ecf20Sopenharmony_ci *	termios data at this point when implementing software flow control.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_civoid tty_throttle(struct tty_struct *tty)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
1138c2ecf20Sopenharmony_ci	/* check TTY_THROTTLED first so it indicates our state */
1148c2ecf20Sopenharmony_ci	if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
1158c2ecf20Sopenharmony_ci	    tty->ops->throttle)
1168c2ecf20Sopenharmony_ci		tty->ops->throttle(tty);
1178c2ecf20Sopenharmony_ci	tty->flow_change = 0;
1188c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_throttle);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/**
1238c2ecf20Sopenharmony_ci *	tty_unthrottle		-	flow control
1248c2ecf20Sopenharmony_ci *	@tty: terminal
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci *	Indicate that a tty may continue transmitting data down the stack.
1278c2ecf20Sopenharmony_ci *	Takes the termios rwsem to protect against parallel throttle/unthrottle
1288c2ecf20Sopenharmony_ci *	and also to ensure the driver can consistently reference its own
1298c2ecf20Sopenharmony_ci *	termios data at this point when implementing software flow control.
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci *	Drivers should however remember that the stack can issue a throttle,
1328c2ecf20Sopenharmony_ci *	then change flow control method, then unthrottle.
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_civoid tty_unthrottle(struct tty_struct *tty)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
1388c2ecf20Sopenharmony_ci	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
1398c2ecf20Sopenharmony_ci	    tty->ops->unthrottle)
1408c2ecf20Sopenharmony_ci		tty->ops->unthrottle(tty);
1418c2ecf20Sopenharmony_ci	tty->flow_change = 0;
1428c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_unthrottle);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/**
1478c2ecf20Sopenharmony_ci *	tty_throttle_safe	-	flow control
1488c2ecf20Sopenharmony_ci *	@tty: terminal
1498c2ecf20Sopenharmony_ci *
1508c2ecf20Sopenharmony_ci *	Similar to tty_throttle() but will only attempt throttle
1518c2ecf20Sopenharmony_ci *	if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental
1528c2ecf20Sopenharmony_ci *	throttle due to race conditions when throttling is conditional
1538c2ecf20Sopenharmony_ci *	on factors evaluated prior to throttling.
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci *	Returns 0 if tty is throttled (or was already throttled)
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciint tty_throttle_safe(struct tty_struct *tty)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	int ret = 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	mutex_lock(&tty->throttle_mutex);
1638c2ecf20Sopenharmony_ci	if (!tty_throttled(tty)) {
1648c2ecf20Sopenharmony_ci		if (tty->flow_change != TTY_THROTTLE_SAFE)
1658c2ecf20Sopenharmony_ci			ret = 1;
1668c2ecf20Sopenharmony_ci		else {
1678c2ecf20Sopenharmony_ci			set_bit(TTY_THROTTLED, &tty->flags);
1688c2ecf20Sopenharmony_ci			if (tty->ops->throttle)
1698c2ecf20Sopenharmony_ci				tty->ops->throttle(tty);
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	mutex_unlock(&tty->throttle_mutex);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return ret;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/**
1788c2ecf20Sopenharmony_ci *	tty_unthrottle_safe	-	flow control
1798c2ecf20Sopenharmony_ci *	@tty: terminal
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci *	Similar to tty_unthrottle() but will only attempt unthrottle
1828c2ecf20Sopenharmony_ci *	if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental
1838c2ecf20Sopenharmony_ci *	unthrottle due to race conditions when unthrottling is conditional
1848c2ecf20Sopenharmony_ci *	on factors evaluated prior to unthrottling.
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci *	Returns 0 if tty is unthrottled (or was already unthrottled)
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciint tty_unthrottle_safe(struct tty_struct *tty)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	int ret = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	mutex_lock(&tty->throttle_mutex);
1948c2ecf20Sopenharmony_ci	if (tty_throttled(tty)) {
1958c2ecf20Sopenharmony_ci		if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
1968c2ecf20Sopenharmony_ci			ret = 1;
1978c2ecf20Sopenharmony_ci		else {
1988c2ecf20Sopenharmony_ci			clear_bit(TTY_THROTTLED, &tty->flags);
1998c2ecf20Sopenharmony_ci			if (tty->ops->unthrottle)
2008c2ecf20Sopenharmony_ci				tty->ops->unthrottle(tty);
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	mutex_unlock(&tty->throttle_mutex);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return ret;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/**
2098c2ecf20Sopenharmony_ci *	tty_wait_until_sent	-	wait for I/O to finish
2108c2ecf20Sopenharmony_ci *	@tty: tty we are waiting for
2118c2ecf20Sopenharmony_ci *	@timeout: how long we will wait
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci *	Wait for characters pending in a tty driver to hit the wire, or
2148c2ecf20Sopenharmony_ci *	for a timeout to occur (eg due to flow control)
2158c2ecf20Sopenharmony_ci *
2168c2ecf20Sopenharmony_ci *	Locking: none
2178c2ecf20Sopenharmony_ci */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_civoid tty_wait_until_sent(struct tty_struct *tty, long timeout)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	tty_debug_wait_until_sent(tty, "wait until sent, timeout=%ld\n", timeout);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (!timeout)
2248c2ecf20Sopenharmony_ci		timeout = MAX_SCHEDULE_TIMEOUT;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	timeout = wait_event_interruptible_timeout(tty->write_wait,
2278c2ecf20Sopenharmony_ci			!tty_chars_in_buffer(tty), timeout);
2288c2ecf20Sopenharmony_ci	if (timeout <= 0)
2298c2ecf20Sopenharmony_ci		return;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (timeout == MAX_SCHEDULE_TIMEOUT)
2328c2ecf20Sopenharmony_ci		timeout = 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (tty->ops->wait_until_sent)
2358c2ecf20Sopenharmony_ci		tty->ops->wait_until_sent(tty, timeout);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_wait_until_sent);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci *		Termios Helper Methods
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic void unset_locked_termios(struct tty_struct *tty, struct ktermios *old)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct ktermios *termios = &tty->termios;
2478c2ecf20Sopenharmony_ci	struct ktermios *locked  = &tty->termios_locked;
2488c2ecf20Sopenharmony_ci	int	i;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
2538c2ecf20Sopenharmony_ci	NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
2548c2ecf20Sopenharmony_ci	NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
2558c2ecf20Sopenharmony_ci	NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
2568c2ecf20Sopenharmony_ci	termios->c_line = locked->c_line ? old->c_line : termios->c_line;
2578c2ecf20Sopenharmony_ci	for (i = 0; i < NCCS; i++)
2588c2ecf20Sopenharmony_ci		termios->c_cc[i] = locked->c_cc[i] ?
2598c2ecf20Sopenharmony_ci			old->c_cc[i] : termios->c_cc[i];
2608c2ecf20Sopenharmony_ci	/* FIXME: What should we do for i/ospeed */
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/**
2648c2ecf20Sopenharmony_ci *	tty_termios_copy_hw	-	copy hardware settings
2658c2ecf20Sopenharmony_ci *	@new: New termios
2668c2ecf20Sopenharmony_ci *	@old: Old termios
2678c2ecf20Sopenharmony_ci *
2688c2ecf20Sopenharmony_ci *	Propagate the hardware specific terminal setting bits from
2698c2ecf20Sopenharmony_ci *	the old termios structure to the new one. This is used in cases
2708c2ecf20Sopenharmony_ci *	where the hardware does not support reconfiguration or as a helper
2718c2ecf20Sopenharmony_ci *	in some cases where only minimal reconfiguration is supported
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_civoid tty_termios_copy_hw(struct ktermios *new, struct ktermios *old)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	/* The bits a dumb device handles in software. Smart devices need
2778c2ecf20Sopenharmony_ci	   to always provide a set_termios method */
2788c2ecf20Sopenharmony_ci	new->c_cflag &= HUPCL | CREAD | CLOCAL;
2798c2ecf20Sopenharmony_ci	new->c_cflag |= old->c_cflag & ~(HUPCL | CREAD | CLOCAL);
2808c2ecf20Sopenharmony_ci	new->c_ispeed = old->c_ispeed;
2818c2ecf20Sopenharmony_ci	new->c_ospeed = old->c_ospeed;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_termios_copy_hw);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci/**
2868c2ecf20Sopenharmony_ci *	tty_termios_hw_change	-	check for setting change
2878c2ecf20Sopenharmony_ci *	@a: termios
2888c2ecf20Sopenharmony_ci *	@b: termios to compare
2898c2ecf20Sopenharmony_ci *
2908c2ecf20Sopenharmony_ci *	Check if any of the bits that affect a dumb device have changed
2918c2ecf20Sopenharmony_ci *	between the two termios structures, or a speed change is needed.
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciint tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed)
2978c2ecf20Sopenharmony_ci		return 1;
2988c2ecf20Sopenharmony_ci	if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL))
2998c2ecf20Sopenharmony_ci		return 1;
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_termios_hw_change);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/**
3058c2ecf20Sopenharmony_ci *	tty_set_termios		-	update termios values
3068c2ecf20Sopenharmony_ci *	@tty: tty to update
3078c2ecf20Sopenharmony_ci *	@new_termios: desired new value
3088c2ecf20Sopenharmony_ci *
3098c2ecf20Sopenharmony_ci *	Perform updates to the termios values set on this terminal.
3108c2ecf20Sopenharmony_ci *	A master pty's termios should never be set.
3118c2ecf20Sopenharmony_ci *
3128c2ecf20Sopenharmony_ci *	Locking: termios_rwsem
3138c2ecf20Sopenharmony_ci */
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ciint tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct ktermios old_termios;
3188c2ecf20Sopenharmony_ci	struct tty_ldisc *ld;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
3218c2ecf20Sopenharmony_ci		tty->driver->subtype == PTY_TYPE_MASTER);
3228c2ecf20Sopenharmony_ci	/*
3238c2ecf20Sopenharmony_ci	 *	Perform the actual termios internal changes under lock.
3248c2ecf20Sopenharmony_ci	 */
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* FIXME: we need to decide on some locking/ordering semantics
3288c2ecf20Sopenharmony_ci	   for the set_termios notification eventually */
3298c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
3308c2ecf20Sopenharmony_ci	old_termios = tty->termios;
3318c2ecf20Sopenharmony_ci	tty->termios = *new_termios;
3328c2ecf20Sopenharmony_ci	unset_locked_termios(tty, &old_termios);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (tty->ops->set_termios)
3358c2ecf20Sopenharmony_ci		tty->ops->set_termios(tty, &old_termios);
3368c2ecf20Sopenharmony_ci	else
3378c2ecf20Sopenharmony_ci		tty_termios_copy_hw(&tty->termios, &old_termios);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ld = tty_ldisc_ref(tty);
3408c2ecf20Sopenharmony_ci	if (ld != NULL) {
3418c2ecf20Sopenharmony_ci		if (ld->ops->set_termios)
3428c2ecf20Sopenharmony_ci			ld->ops->set_termios(tty, &old_termios);
3438c2ecf20Sopenharmony_ci		tty_ldisc_deref(ld);
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_set_termios);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/**
3518c2ecf20Sopenharmony_ci *	set_termios		-	set termios values for a tty
3528c2ecf20Sopenharmony_ci *	@tty: terminal device
3538c2ecf20Sopenharmony_ci *	@arg: user data
3548c2ecf20Sopenharmony_ci *	@opt: option information
3558c2ecf20Sopenharmony_ci *
3568c2ecf20Sopenharmony_ci *	Helper function to prepare termios data and run necessary other
3578c2ecf20Sopenharmony_ci *	functions before using tty_set_termios to do the actual changes.
3588c2ecf20Sopenharmony_ci *
3598c2ecf20Sopenharmony_ci *	Locking:
3608c2ecf20Sopenharmony_ci *		Called functions take ldisc and termios_rwsem locks
3618c2ecf20Sopenharmony_ci */
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int set_termios(struct tty_struct *tty, void __user *arg, int opt)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct ktermios tmp_termios;
3668c2ecf20Sopenharmony_ci	struct tty_ldisc *ld;
3678c2ecf20Sopenharmony_ci	int retval = tty_check_change(tty);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (retval)
3708c2ecf20Sopenharmony_ci		return retval;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
3738c2ecf20Sopenharmony_ci	tmp_termios = tty->termios;
3748c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (opt & TERMIOS_TERMIO) {
3778c2ecf20Sopenharmony_ci		if (user_termio_to_kernel_termios(&tmp_termios,
3788c2ecf20Sopenharmony_ci						(struct termio __user *)arg))
3798c2ecf20Sopenharmony_ci			return -EFAULT;
3808c2ecf20Sopenharmony_ci#ifdef TCGETS2
3818c2ecf20Sopenharmony_ci	} else if (opt & TERMIOS_OLD) {
3828c2ecf20Sopenharmony_ci		if (user_termios_to_kernel_termios_1(&tmp_termios,
3838c2ecf20Sopenharmony_ci						(struct termios __user *)arg))
3848c2ecf20Sopenharmony_ci			return -EFAULT;
3858c2ecf20Sopenharmony_ci	} else {
3868c2ecf20Sopenharmony_ci		if (user_termios_to_kernel_termios(&tmp_termios,
3878c2ecf20Sopenharmony_ci						(struct termios2 __user *)arg))
3888c2ecf20Sopenharmony_ci			return -EFAULT;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci#else
3918c2ecf20Sopenharmony_ci	} else if (user_termios_to_kernel_termios(&tmp_termios,
3928c2ecf20Sopenharmony_ci					(struct termios __user *)arg))
3938c2ecf20Sopenharmony_ci		return -EFAULT;
3948c2ecf20Sopenharmony_ci#endif
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* If old style Bfoo values are used then load c_ispeed/c_ospeed
3978c2ecf20Sopenharmony_ci	 * with the real speed so its unconditionally usable */
3988c2ecf20Sopenharmony_ci	tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
3998c2ecf20Sopenharmony_ci	tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {
4028c2ecf20Sopenharmony_ciretry_write_wait:
4038c2ecf20Sopenharmony_ci		retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty));
4048c2ecf20Sopenharmony_ci		if (retval < 0)
4058c2ecf20Sopenharmony_ci			return retval;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if (tty_write_lock(tty, false) < 0)
4088c2ecf20Sopenharmony_ci			goto retry_write_wait;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		/* Racing writer? */
4118c2ecf20Sopenharmony_ci		if (tty_chars_in_buffer(tty)) {
4128c2ecf20Sopenharmony_ci			tty_write_unlock(tty);
4138c2ecf20Sopenharmony_ci			goto retry_write_wait;
4148c2ecf20Sopenharmony_ci		}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		ld = tty_ldisc_ref(tty);
4178c2ecf20Sopenharmony_ci		if (ld != NULL) {
4188c2ecf20Sopenharmony_ci			if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
4198c2ecf20Sopenharmony_ci				ld->ops->flush_buffer(tty);
4208c2ecf20Sopenharmony_ci			tty_ldisc_deref(ld);
4218c2ecf20Sopenharmony_ci		}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		if ((opt & TERMIOS_WAIT) && tty->ops->wait_until_sent) {
4248c2ecf20Sopenharmony_ci			tty->ops->wait_until_sent(tty, 0);
4258c2ecf20Sopenharmony_ci			if (signal_pending(current)) {
4268c2ecf20Sopenharmony_ci				tty_write_unlock(tty);
4278c2ecf20Sopenharmony_ci				return -ERESTARTSYS;
4288c2ecf20Sopenharmony_ci			}
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		tty_set_termios(tty, &tmp_termios);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		tty_write_unlock(tty);
4348c2ecf20Sopenharmony_ci	} else {
4358c2ecf20Sopenharmony_ci		tty_set_termios(tty, &tmp_termios);
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* FIXME: Arguably if tmp_termios == tty->termios AND the
4398c2ecf20Sopenharmony_ci	   actual requested termios was not tmp_termios then we may
4408c2ecf20Sopenharmony_ci	   want to return an error as no user requested change has
4418c2ecf20Sopenharmony_ci	   succeeded */
4428c2ecf20Sopenharmony_ci	return 0;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
4488c2ecf20Sopenharmony_ci	*kterm = tty->termios;
4498c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
4558c2ecf20Sopenharmony_ci	*kterm = tty->termios_locked;
4568c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic int get_termio(struct tty_struct *tty, struct termio __user *termio)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct ktermios kterm;
4628c2ecf20Sopenharmony_ci	copy_termios(tty, &kterm);
4638c2ecf20Sopenharmony_ci	if (kernel_termios_to_user_termio(termio, &kterm))
4648c2ecf20Sopenharmony_ci		return -EFAULT;
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci#ifdef TIOCGETP
4698c2ecf20Sopenharmony_ci/*
4708c2ecf20Sopenharmony_ci * These are deprecated, but there is limited support..
4718c2ecf20Sopenharmony_ci *
4728c2ecf20Sopenharmony_ci * The "sg_flags" translation is a joke..
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic int get_sgflags(struct tty_struct *tty)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	int flags = 0;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (!L_ICANON(tty)) {
4798c2ecf20Sopenharmony_ci		if (L_ISIG(tty))
4808c2ecf20Sopenharmony_ci			flags |= 0x02;		/* cbreak */
4818c2ecf20Sopenharmony_ci		else
4828c2ecf20Sopenharmony_ci			flags |= 0x20;		/* raw */
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci	if (L_ECHO(tty))
4858c2ecf20Sopenharmony_ci		flags |= 0x08;			/* echo */
4868c2ecf20Sopenharmony_ci	if (O_OPOST(tty))
4878c2ecf20Sopenharmony_ci		if (O_ONLCR(tty))
4888c2ecf20Sopenharmony_ci			flags |= 0x10;		/* crmod */
4898c2ecf20Sopenharmony_ci	return flags;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct sgttyb tmp;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
4978c2ecf20Sopenharmony_ci	tmp.sg_ispeed = tty->termios.c_ispeed;
4988c2ecf20Sopenharmony_ci	tmp.sg_ospeed = tty->termios.c_ospeed;
4998c2ecf20Sopenharmony_ci	tmp.sg_erase = tty->termios.c_cc[VERASE];
5008c2ecf20Sopenharmony_ci	tmp.sg_kill = tty->termios.c_cc[VKILL];
5018c2ecf20Sopenharmony_ci	tmp.sg_flags = get_sgflags(tty);
5028c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void set_sgflags(struct ktermios *termios, int flags)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	termios->c_iflag = ICRNL | IXON;
5108c2ecf20Sopenharmony_ci	termios->c_oflag = 0;
5118c2ecf20Sopenharmony_ci	termios->c_lflag = ISIG | ICANON;
5128c2ecf20Sopenharmony_ci	if (flags & 0x02) {	/* cbreak */
5138c2ecf20Sopenharmony_ci		termios->c_iflag = 0;
5148c2ecf20Sopenharmony_ci		termios->c_lflag &= ~ICANON;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	if (flags & 0x08) {		/* echo */
5178c2ecf20Sopenharmony_ci		termios->c_lflag |= ECHO | ECHOE | ECHOK |
5188c2ecf20Sopenharmony_ci				    ECHOCTL | ECHOKE | IEXTEN;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	if (flags & 0x10) {		/* crmod */
5218c2ecf20Sopenharmony_ci		termios->c_oflag |= OPOST | ONLCR;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	if (flags & 0x20) {	/* raw */
5248c2ecf20Sopenharmony_ci		termios->c_iflag = 0;
5258c2ecf20Sopenharmony_ci		termios->c_lflag &= ~(ISIG | ICANON);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	if (!(termios->c_lflag & ICANON)) {
5288c2ecf20Sopenharmony_ci		termios->c_cc[VMIN] = 1;
5298c2ecf20Sopenharmony_ci		termios->c_cc[VTIME] = 0;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci/**
5348c2ecf20Sopenharmony_ci *	set_sgttyb		-	set legacy terminal values
5358c2ecf20Sopenharmony_ci *	@tty: tty structure
5368c2ecf20Sopenharmony_ci *	@sgttyb: pointer to old style terminal structure
5378c2ecf20Sopenharmony_ci *
5388c2ecf20Sopenharmony_ci *	Updates a terminal from the legacy BSD style terminal information
5398c2ecf20Sopenharmony_ci *	structure.
5408c2ecf20Sopenharmony_ci *
5418c2ecf20Sopenharmony_ci *	Locking: termios_rwsem
5428c2ecf20Sopenharmony_ci */
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	int retval;
5478c2ecf20Sopenharmony_ci	struct sgttyb tmp;
5488c2ecf20Sopenharmony_ci	struct ktermios termios;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	retval = tty_check_change(tty);
5518c2ecf20Sopenharmony_ci	if (retval)
5528c2ecf20Sopenharmony_ci		return retval;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
5558c2ecf20Sopenharmony_ci		return -EFAULT;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
5588c2ecf20Sopenharmony_ci	termios = tty->termios;
5598c2ecf20Sopenharmony_ci	termios.c_cc[VERASE] = tmp.sg_erase;
5608c2ecf20Sopenharmony_ci	termios.c_cc[VKILL] = tmp.sg_kill;
5618c2ecf20Sopenharmony_ci	set_sgflags(&termios, tmp.sg_flags);
5628c2ecf20Sopenharmony_ci	/* Try and encode into Bfoo format */
5638c2ecf20Sopenharmony_ci#ifdef BOTHER
5648c2ecf20Sopenharmony_ci	tty_termios_encode_baud_rate(&termios, termios.c_ispeed,
5658c2ecf20Sopenharmony_ci						termios.c_ospeed);
5668c2ecf20Sopenharmony_ci#endif
5678c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
5688c2ecf20Sopenharmony_ci	tty_set_termios(tty, &termios);
5698c2ecf20Sopenharmony_ci	return 0;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci#endif
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci#ifdef TIOCGETC
5748c2ecf20Sopenharmony_cistatic int get_tchars(struct tty_struct *tty, struct tchars __user *tchars)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct tchars tmp;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
5798c2ecf20Sopenharmony_ci	tmp.t_intrc = tty->termios.c_cc[VINTR];
5808c2ecf20Sopenharmony_ci	tmp.t_quitc = tty->termios.c_cc[VQUIT];
5818c2ecf20Sopenharmony_ci	tmp.t_startc = tty->termios.c_cc[VSTART];
5828c2ecf20Sopenharmony_ci	tmp.t_stopc = tty->termios.c_cc[VSTOP];
5838c2ecf20Sopenharmony_ci	tmp.t_eofc = tty->termios.c_cc[VEOF];
5848c2ecf20Sopenharmony_ci	tmp.t_brkc = tty->termios.c_cc[VEOL2];	/* what is brkc anyway? */
5858c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
5868c2ecf20Sopenharmony_ci	return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int set_tchars(struct tty_struct *tty, struct tchars __user *tchars)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct tchars tmp;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	if (copy_from_user(&tmp, tchars, sizeof(tmp)))
5948c2ecf20Sopenharmony_ci		return -EFAULT;
5958c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
5968c2ecf20Sopenharmony_ci	tty->termios.c_cc[VINTR] = tmp.t_intrc;
5978c2ecf20Sopenharmony_ci	tty->termios.c_cc[VQUIT] = tmp.t_quitc;
5988c2ecf20Sopenharmony_ci	tty->termios.c_cc[VSTART] = tmp.t_startc;
5998c2ecf20Sopenharmony_ci	tty->termios.c_cc[VSTOP] = tmp.t_stopc;
6008c2ecf20Sopenharmony_ci	tty->termios.c_cc[VEOF] = tmp.t_eofc;
6018c2ecf20Sopenharmony_ci	tty->termios.c_cc[VEOL2] = tmp.t_brkc;	/* what is brkc anyway? */
6028c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci#endif
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci#ifdef TIOCGLTC
6088c2ecf20Sopenharmony_cistatic int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct ltchars tmp;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	down_read(&tty->termios_rwsem);
6138c2ecf20Sopenharmony_ci	tmp.t_suspc = tty->termios.c_cc[VSUSP];
6148c2ecf20Sopenharmony_ci	/* what is dsuspc anyway? */
6158c2ecf20Sopenharmony_ci	tmp.t_dsuspc = tty->termios.c_cc[VSUSP];
6168c2ecf20Sopenharmony_ci	tmp.t_rprntc = tty->termios.c_cc[VREPRINT];
6178c2ecf20Sopenharmony_ci	/* what is flushc anyway? */
6188c2ecf20Sopenharmony_ci	tmp.t_flushc = tty->termios.c_cc[VEOL2];
6198c2ecf20Sopenharmony_ci	tmp.t_werasc = tty->termios.c_cc[VWERASE];
6208c2ecf20Sopenharmony_ci	tmp.t_lnextc = tty->termios.c_cc[VLNEXT];
6218c2ecf20Sopenharmony_ci	up_read(&tty->termios_rwsem);
6228c2ecf20Sopenharmony_ci	return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct ltchars tmp;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
6308c2ecf20Sopenharmony_ci		return -EFAULT;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
6338c2ecf20Sopenharmony_ci	tty->termios.c_cc[VSUSP] = tmp.t_suspc;
6348c2ecf20Sopenharmony_ci	/* what is dsuspc anyway? */
6358c2ecf20Sopenharmony_ci	tty->termios.c_cc[VEOL2] = tmp.t_dsuspc;
6368c2ecf20Sopenharmony_ci	tty->termios.c_cc[VREPRINT] = tmp.t_rprntc;
6378c2ecf20Sopenharmony_ci	/* what is flushc anyway? */
6388c2ecf20Sopenharmony_ci	tty->termios.c_cc[VEOL2] = tmp.t_flushc;
6398c2ecf20Sopenharmony_ci	tty->termios.c_cc[VWERASE] = tmp.t_werasc;
6408c2ecf20Sopenharmony_ci	tty->termios.c_cc[VLNEXT] = tmp.t_lnextc;
6418c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
6428c2ecf20Sopenharmony_ci	return 0;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci#endif
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci/**
6478c2ecf20Sopenharmony_ci *	tty_change_softcar	-	carrier change ioctl helper
6488c2ecf20Sopenharmony_ci *	@tty: tty to update
6498c2ecf20Sopenharmony_ci *	@arg: enable/disable CLOCAL
6508c2ecf20Sopenharmony_ci *
6518c2ecf20Sopenharmony_ci *	Perform a change to the CLOCAL state and call into the driver
6528c2ecf20Sopenharmony_ci *	layer to make it visible. All done with the termios rwsem
6538c2ecf20Sopenharmony_ci */
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic int tty_change_softcar(struct tty_struct *tty, int arg)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	int ret = 0;
6588c2ecf20Sopenharmony_ci	int bit = arg ? CLOCAL : 0;
6598c2ecf20Sopenharmony_ci	struct ktermios old;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	down_write(&tty->termios_rwsem);
6628c2ecf20Sopenharmony_ci	old = tty->termios;
6638c2ecf20Sopenharmony_ci	tty->termios.c_cflag &= ~CLOCAL;
6648c2ecf20Sopenharmony_ci	tty->termios.c_cflag |= bit;
6658c2ecf20Sopenharmony_ci	if (tty->ops->set_termios)
6668c2ecf20Sopenharmony_ci		tty->ops->set_termios(tty, &old);
6678c2ecf20Sopenharmony_ci	if (C_CLOCAL(tty) != bit)
6688c2ecf20Sopenharmony_ci		ret = -EINVAL;
6698c2ecf20Sopenharmony_ci	up_write(&tty->termios_rwsem);
6708c2ecf20Sopenharmony_ci	return ret;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/**
6748c2ecf20Sopenharmony_ci *	tty_mode_ioctl		-	mode related ioctls
6758c2ecf20Sopenharmony_ci *	@tty: tty for the ioctl
6768c2ecf20Sopenharmony_ci *	@file: file pointer for the tty
6778c2ecf20Sopenharmony_ci *	@cmd: command
6788c2ecf20Sopenharmony_ci *	@arg: ioctl argument
6798c2ecf20Sopenharmony_ci *
6808c2ecf20Sopenharmony_ci *	Perform non line discipline specific mode control ioctls. This
6818c2ecf20Sopenharmony_ci *	is designed to be called by line disciplines to ensure they provide
6828c2ecf20Sopenharmony_ci *	consistent mode setting.
6838c2ecf20Sopenharmony_ci */
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ciint tty_mode_ioctl(struct tty_struct *tty, struct file *file,
6868c2ecf20Sopenharmony_ci			unsigned int cmd, unsigned long arg)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct tty_struct *real_tty;
6898c2ecf20Sopenharmony_ci	void __user *p = (void __user *)arg;
6908c2ecf20Sopenharmony_ci	int ret = 0;
6918c2ecf20Sopenharmony_ci	struct ktermios kterm;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	BUG_ON(file == NULL);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
6968c2ecf20Sopenharmony_ci	    tty->driver->subtype == PTY_TYPE_MASTER)
6978c2ecf20Sopenharmony_ci		real_tty = tty->link;
6988c2ecf20Sopenharmony_ci	else
6998c2ecf20Sopenharmony_ci		real_tty = tty;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	switch (cmd) {
7028c2ecf20Sopenharmony_ci#ifdef TIOCGETP
7038c2ecf20Sopenharmony_ci	case TIOCGETP:
7048c2ecf20Sopenharmony_ci		return get_sgttyb(real_tty, (struct sgttyb __user *) arg);
7058c2ecf20Sopenharmony_ci	case TIOCSETP:
7068c2ecf20Sopenharmony_ci	case TIOCSETN:
7078c2ecf20Sopenharmony_ci		return set_sgttyb(real_tty, (struct sgttyb __user *) arg);
7088c2ecf20Sopenharmony_ci#endif
7098c2ecf20Sopenharmony_ci#ifdef TIOCGETC
7108c2ecf20Sopenharmony_ci	case TIOCGETC:
7118c2ecf20Sopenharmony_ci		return get_tchars(real_tty, p);
7128c2ecf20Sopenharmony_ci	case TIOCSETC:
7138c2ecf20Sopenharmony_ci		return set_tchars(real_tty, p);
7148c2ecf20Sopenharmony_ci#endif
7158c2ecf20Sopenharmony_ci#ifdef TIOCGLTC
7168c2ecf20Sopenharmony_ci	case TIOCGLTC:
7178c2ecf20Sopenharmony_ci		return get_ltchars(real_tty, p);
7188c2ecf20Sopenharmony_ci	case TIOCSLTC:
7198c2ecf20Sopenharmony_ci		return set_ltchars(real_tty, p);
7208c2ecf20Sopenharmony_ci#endif
7218c2ecf20Sopenharmony_ci	case TCSETSF:
7228c2ecf20Sopenharmony_ci		return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD);
7238c2ecf20Sopenharmony_ci	case TCSETSW:
7248c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD);
7258c2ecf20Sopenharmony_ci	case TCSETS:
7268c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, TERMIOS_OLD);
7278c2ecf20Sopenharmony_ci#ifndef TCGETS2
7288c2ecf20Sopenharmony_ci	case TCGETS:
7298c2ecf20Sopenharmony_ci		copy_termios(real_tty, &kterm);
7308c2ecf20Sopenharmony_ci		if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
7318c2ecf20Sopenharmony_ci			ret = -EFAULT;
7328c2ecf20Sopenharmony_ci		return ret;
7338c2ecf20Sopenharmony_ci#else
7348c2ecf20Sopenharmony_ci	case TCGETS:
7358c2ecf20Sopenharmony_ci		copy_termios(real_tty, &kterm);
7368c2ecf20Sopenharmony_ci		if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
7378c2ecf20Sopenharmony_ci			ret = -EFAULT;
7388c2ecf20Sopenharmony_ci		return ret;
7398c2ecf20Sopenharmony_ci	case TCGETS2:
7408c2ecf20Sopenharmony_ci		copy_termios(real_tty, &kterm);
7418c2ecf20Sopenharmony_ci		if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm))
7428c2ecf20Sopenharmony_ci			ret = -EFAULT;
7438c2ecf20Sopenharmony_ci		return ret;
7448c2ecf20Sopenharmony_ci	case TCSETSF2:
7458c2ecf20Sopenharmony_ci		return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT);
7468c2ecf20Sopenharmony_ci	case TCSETSW2:
7478c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, TERMIOS_WAIT);
7488c2ecf20Sopenharmony_ci	case TCSETS2:
7498c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, 0);
7508c2ecf20Sopenharmony_ci#endif
7518c2ecf20Sopenharmony_ci	case TCGETA:
7528c2ecf20Sopenharmony_ci		return get_termio(real_tty, p);
7538c2ecf20Sopenharmony_ci	case TCSETAF:
7548c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO);
7558c2ecf20Sopenharmony_ci	case TCSETAW:
7568c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_TERMIO);
7578c2ecf20Sopenharmony_ci	case TCSETA:
7588c2ecf20Sopenharmony_ci		return set_termios(real_tty, p, TERMIOS_TERMIO);
7598c2ecf20Sopenharmony_ci#ifndef TCGETS2
7608c2ecf20Sopenharmony_ci	case TIOCGLCKTRMIOS:
7618c2ecf20Sopenharmony_ci		copy_termios_locked(real_tty, &kterm);
7628c2ecf20Sopenharmony_ci		if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
7638c2ecf20Sopenharmony_ci			ret = -EFAULT;
7648c2ecf20Sopenharmony_ci		return ret;
7658c2ecf20Sopenharmony_ci	case TIOCSLCKTRMIOS:
7668c2ecf20Sopenharmony_ci		if (!checkpoint_restore_ns_capable(&init_user_ns))
7678c2ecf20Sopenharmony_ci			return -EPERM;
7688c2ecf20Sopenharmony_ci		copy_termios_locked(real_tty, &kterm);
7698c2ecf20Sopenharmony_ci		if (user_termios_to_kernel_termios(&kterm,
7708c2ecf20Sopenharmony_ci					       (struct termios __user *) arg))
7718c2ecf20Sopenharmony_ci			return -EFAULT;
7728c2ecf20Sopenharmony_ci		down_write(&real_tty->termios_rwsem);
7738c2ecf20Sopenharmony_ci		real_tty->termios_locked = kterm;
7748c2ecf20Sopenharmony_ci		up_write(&real_tty->termios_rwsem);
7758c2ecf20Sopenharmony_ci		return 0;
7768c2ecf20Sopenharmony_ci#else
7778c2ecf20Sopenharmony_ci	case TIOCGLCKTRMIOS:
7788c2ecf20Sopenharmony_ci		copy_termios_locked(real_tty, &kterm);
7798c2ecf20Sopenharmony_ci		if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
7808c2ecf20Sopenharmony_ci			ret = -EFAULT;
7818c2ecf20Sopenharmony_ci		return ret;
7828c2ecf20Sopenharmony_ci	case TIOCSLCKTRMIOS:
7838c2ecf20Sopenharmony_ci		if (!checkpoint_restore_ns_capable(&init_user_ns))
7848c2ecf20Sopenharmony_ci			return -EPERM;
7858c2ecf20Sopenharmony_ci		copy_termios_locked(real_tty, &kterm);
7868c2ecf20Sopenharmony_ci		if (user_termios_to_kernel_termios_1(&kterm,
7878c2ecf20Sopenharmony_ci					       (struct termios __user *) arg))
7888c2ecf20Sopenharmony_ci			return -EFAULT;
7898c2ecf20Sopenharmony_ci		down_write(&real_tty->termios_rwsem);
7908c2ecf20Sopenharmony_ci		real_tty->termios_locked = kterm;
7918c2ecf20Sopenharmony_ci		up_write(&real_tty->termios_rwsem);
7928c2ecf20Sopenharmony_ci		return ret;
7938c2ecf20Sopenharmony_ci#endif
7948c2ecf20Sopenharmony_ci#ifdef TCGETX
7958c2ecf20Sopenharmony_ci	case TCGETX:
7968c2ecf20Sopenharmony_ci	case TCSETX:
7978c2ecf20Sopenharmony_ci	case TCSETXW:
7988c2ecf20Sopenharmony_ci	case TCSETXF:
7998c2ecf20Sopenharmony_ci		return -ENOTTY;
8008c2ecf20Sopenharmony_ci#endif
8018c2ecf20Sopenharmony_ci	case TIOCGSOFTCAR:
8028c2ecf20Sopenharmony_ci		copy_termios(real_tty, &kterm);
8038c2ecf20Sopenharmony_ci		ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
8048c2ecf20Sopenharmony_ci						(int __user *)arg);
8058c2ecf20Sopenharmony_ci		return ret;
8068c2ecf20Sopenharmony_ci	case TIOCSSOFTCAR:
8078c2ecf20Sopenharmony_ci		if (get_user(arg, (unsigned int __user *) arg))
8088c2ecf20Sopenharmony_ci			return -EFAULT;
8098c2ecf20Sopenharmony_ci		return tty_change_softcar(real_tty, arg);
8108c2ecf20Sopenharmony_ci	default:
8118c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_mode_ioctl);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/* Caller guarantees ldisc reference is held */
8188c2ecf20Sopenharmony_cistatic int __tty_perform_flush(struct tty_struct *tty, unsigned long arg)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct tty_ldisc *ld = tty->ldisc;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	switch (arg) {
8238c2ecf20Sopenharmony_ci	case TCIFLUSH:
8248c2ecf20Sopenharmony_ci		if (ld && ld->ops->flush_buffer) {
8258c2ecf20Sopenharmony_ci			ld->ops->flush_buffer(tty);
8268c2ecf20Sopenharmony_ci			tty_unthrottle(tty);
8278c2ecf20Sopenharmony_ci		}
8288c2ecf20Sopenharmony_ci		break;
8298c2ecf20Sopenharmony_ci	case TCIOFLUSH:
8308c2ecf20Sopenharmony_ci		if (ld && ld->ops->flush_buffer) {
8318c2ecf20Sopenharmony_ci			ld->ops->flush_buffer(tty);
8328c2ecf20Sopenharmony_ci			tty_unthrottle(tty);
8338c2ecf20Sopenharmony_ci		}
8348c2ecf20Sopenharmony_ci		fallthrough;
8358c2ecf20Sopenharmony_ci	case TCOFLUSH:
8368c2ecf20Sopenharmony_ci		tty_driver_flush_buffer(tty);
8378c2ecf20Sopenharmony_ci		break;
8388c2ecf20Sopenharmony_ci	default:
8398c2ecf20Sopenharmony_ci		return -EINVAL;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci	return 0;
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ciint tty_perform_flush(struct tty_struct *tty, unsigned long arg)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct tty_ldisc *ld;
8478c2ecf20Sopenharmony_ci	int retval = tty_check_change(tty);
8488c2ecf20Sopenharmony_ci	if (retval)
8498c2ecf20Sopenharmony_ci		return retval;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ld = tty_ldisc_ref_wait(tty);
8528c2ecf20Sopenharmony_ci	retval = __tty_perform_flush(tty, arg);
8538c2ecf20Sopenharmony_ci	if (ld)
8548c2ecf20Sopenharmony_ci		tty_ldisc_deref(ld);
8558c2ecf20Sopenharmony_ci	return retval;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_perform_flush);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ciint n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
8608c2ecf20Sopenharmony_ci		       unsigned int cmd, unsigned long arg)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	int retval;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	switch (cmd) {
8658c2ecf20Sopenharmony_ci	case TCXONC:
8668c2ecf20Sopenharmony_ci		retval = tty_check_change(tty);
8678c2ecf20Sopenharmony_ci		if (retval)
8688c2ecf20Sopenharmony_ci			return retval;
8698c2ecf20Sopenharmony_ci		switch (arg) {
8708c2ecf20Sopenharmony_ci		case TCOOFF:
8718c2ecf20Sopenharmony_ci			spin_lock_irq(&tty->flow_lock);
8728c2ecf20Sopenharmony_ci			if (!tty->flow_stopped) {
8738c2ecf20Sopenharmony_ci				tty->flow_stopped = 1;
8748c2ecf20Sopenharmony_ci				__stop_tty(tty);
8758c2ecf20Sopenharmony_ci			}
8768c2ecf20Sopenharmony_ci			spin_unlock_irq(&tty->flow_lock);
8778c2ecf20Sopenharmony_ci			break;
8788c2ecf20Sopenharmony_ci		case TCOON:
8798c2ecf20Sopenharmony_ci			spin_lock_irq(&tty->flow_lock);
8808c2ecf20Sopenharmony_ci			if (tty->flow_stopped) {
8818c2ecf20Sopenharmony_ci				tty->flow_stopped = 0;
8828c2ecf20Sopenharmony_ci				__start_tty(tty);
8838c2ecf20Sopenharmony_ci			}
8848c2ecf20Sopenharmony_ci			spin_unlock_irq(&tty->flow_lock);
8858c2ecf20Sopenharmony_ci			break;
8868c2ecf20Sopenharmony_ci		case TCIOFF:
8878c2ecf20Sopenharmony_ci			if (STOP_CHAR(tty) != __DISABLED_CHAR)
8888c2ecf20Sopenharmony_ci				retval = tty_send_xchar(tty, STOP_CHAR(tty));
8898c2ecf20Sopenharmony_ci			break;
8908c2ecf20Sopenharmony_ci		case TCION:
8918c2ecf20Sopenharmony_ci			if (START_CHAR(tty) != __DISABLED_CHAR)
8928c2ecf20Sopenharmony_ci				retval = tty_send_xchar(tty, START_CHAR(tty));
8938c2ecf20Sopenharmony_ci			break;
8948c2ecf20Sopenharmony_ci		default:
8958c2ecf20Sopenharmony_ci			return -EINVAL;
8968c2ecf20Sopenharmony_ci		}
8978c2ecf20Sopenharmony_ci		return retval;
8988c2ecf20Sopenharmony_ci	case TCFLSH:
8998c2ecf20Sopenharmony_ci		retval = tty_check_change(tty);
9008c2ecf20Sopenharmony_ci		if (retval)
9018c2ecf20Sopenharmony_ci			return retval;
9028c2ecf20Sopenharmony_ci		return __tty_perform_flush(tty, arg);
9038c2ecf20Sopenharmony_ci	default:
9048c2ecf20Sopenharmony_ci		/* Try the mode commands */
9058c2ecf20Sopenharmony_ci		return tty_mode_ioctl(tty, file, cmd, arg);
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(n_tty_ioctl_helper);
909