xref: /linux/drivers/tty/tty_ioctl.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
296fd7ce5SGreg Kroah-Hartman /*
396fd7ce5SGreg Kroah-Hartman  *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
496fd7ce5SGreg Kroah-Hartman  *
596fd7ce5SGreg Kroah-Hartman  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
696fd7ce5SGreg Kroah-Hartman  * which can be dynamically activated and de-activated by the line
796fd7ce5SGreg Kroah-Hartman  * discipline handling modules (like SLIP).
896fd7ce5SGreg Kroah-Hartman  */
996fd7ce5SGreg Kroah-Hartman 
100b05223bSIlpo Järvinen #include <linux/bits.h>
1196fd7ce5SGreg Kroah-Hartman #include <linux/types.h>
1296fd7ce5SGreg Kroah-Hartman #include <linux/termios.h>
1396fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
14174cd4b1SIngo Molnar #include <linux/sched/signal.h>
1596fd7ce5SGreg Kroah-Hartman #include <linux/kernel.h>
1696fd7ce5SGreg Kroah-Hartman #include <linux/major.h>
1796fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
1896fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
1996fd7ce5SGreg Kroah-Hartman #include <linux/string.h>
2096fd7ce5SGreg Kroah-Hartman #include <linux/mm.h>
2196fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
2296fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
2396fd7ce5SGreg Kroah-Hartman #include <linux/mutex.h>
248193c429SThomas Meyer #include <linux/compat.h>
25c9874d3fSAl Viro #include <linux/termios_internal.h>
269f72cab1SGreg Kroah-Hartman #include "tty.h"
2796fd7ce5SGreg Kroah-Hartman 
2896fd7ce5SGreg Kroah-Hartman #include <asm/io.h>
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
3096fd7ce5SGreg Kroah-Hartman 
3196fd7ce5SGreg Kroah-Hartman #undef	DEBUG
3296fd7ce5SGreg Kroah-Hartman 
3396fd7ce5SGreg Kroah-Hartman /*
3496fd7ce5SGreg Kroah-Hartman  * Internal flag options for termios setting behavior
3596fd7ce5SGreg Kroah-Hartman  */
360b05223bSIlpo Järvinen #define TERMIOS_FLUSH	BIT(0)
370b05223bSIlpo Järvinen #define TERMIOS_WAIT	BIT(1)
380b05223bSIlpo Järvinen #define TERMIOS_TERMIO	BIT(2)
390b05223bSIlpo Järvinen #define TERMIOS_OLD	BIT(3)
4096fd7ce5SGreg Kroah-Hartman 
4196fd7ce5SGreg Kroah-Hartman /**
4296fd7ce5SGreg Kroah-Hartman  * tty_chars_in_buffer - characters pending
4396fd7ce5SGreg Kroah-Hartman  * @tty: terminal
4496fd7ce5SGreg Kroah-Hartman  *
453b1a696bSJiri Slaby (SUSE)  * Returns: the number of bytes of data in the device private output queue. If
463b1a696bSJiri Slaby (SUSE)  * no private method is supplied there is assumed to be no queue on the device.
4796fd7ce5SGreg Kroah-Hartman  */
tty_chars_in_buffer(struct tty_struct * tty)48fff4ef17SJiri Slaby unsigned int tty_chars_in_buffer(struct tty_struct *tty)
4996fd7ce5SGreg Kroah-Hartman {
5096fd7ce5SGreg Kroah-Hartman 	if (tty->ops->chars_in_buffer)
5196fd7ce5SGreg Kroah-Hartman 		return tty->ops->chars_in_buffer(tty);
5296fd7ce5SGreg Kroah-Hartman 	return 0;
5396fd7ce5SGreg Kroah-Hartman }
5496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_chars_in_buffer);
5596fd7ce5SGreg Kroah-Hartman 
5696fd7ce5SGreg Kroah-Hartman /**
5796fd7ce5SGreg Kroah-Hartman  * tty_write_room - write queue space
5896fd7ce5SGreg Kroah-Hartman  * @tty: terminal
5996fd7ce5SGreg Kroah-Hartman  *
603b1a696bSJiri Slaby (SUSE)  * Returns: the number of bytes that can be queued to this device at the present
613b1a696bSJiri Slaby (SUSE)  * time. The result should be treated as a guarantee and the driver cannot
623b1a696bSJiri Slaby (SUSE)  * offer a value it later shrinks by more than the number of bytes written. If
633b1a696bSJiri Slaby (SUSE)  * no method is provided, 2K is always returned and data may be lost as there
643b1a696bSJiri Slaby (SUSE)  * will be no flow control.
6596fd7ce5SGreg Kroah-Hartman  */
tty_write_room(struct tty_struct * tty)6603b3b1a2SJiri Slaby unsigned int tty_write_room(struct tty_struct *tty)
6796fd7ce5SGreg Kroah-Hartman {
6896fd7ce5SGreg Kroah-Hartman 	if (tty->ops->write_room)
6996fd7ce5SGreg Kroah-Hartman 		return tty->ops->write_room(tty);
7096fd7ce5SGreg Kroah-Hartman 	return 2048;
7196fd7ce5SGreg Kroah-Hartman }
7296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_write_room);
7396fd7ce5SGreg Kroah-Hartman 
7496fd7ce5SGreg Kroah-Hartman /**
7596fd7ce5SGreg Kroah-Hartman  * tty_driver_flush_buffer - discard internal buffer
7696fd7ce5SGreg Kroah-Hartman  * @tty: terminal
7796fd7ce5SGreg Kroah-Hartman  *
783b1a696bSJiri Slaby (SUSE)  * Discard the internal output buffer for this device. If no method is provided,
793b1a696bSJiri Slaby (SUSE)  * then either the buffer cannot be hardware flushed or there is no buffer
803b1a696bSJiri Slaby (SUSE)  * driver side.
8196fd7ce5SGreg Kroah-Hartman  */
tty_driver_flush_buffer(struct tty_struct * tty)8296fd7ce5SGreg Kroah-Hartman void tty_driver_flush_buffer(struct tty_struct *tty)
8396fd7ce5SGreg Kroah-Hartman {
8496fd7ce5SGreg Kroah-Hartman 	if (tty->ops->flush_buffer)
8596fd7ce5SGreg Kroah-Hartman 		tty->ops->flush_buffer(tty);
8696fd7ce5SGreg Kroah-Hartman }
8796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_driver_flush_buffer);
8896fd7ce5SGreg Kroah-Hartman 
8996fd7ce5SGreg Kroah-Hartman /**
9096fd7ce5SGreg Kroah-Hartman  * tty_unthrottle - flow control
9196fd7ce5SGreg Kroah-Hartman  * @tty: terminal
9296fd7ce5SGreg Kroah-Hartman  *
933b1a696bSJiri Slaby (SUSE)  * Indicate that a @tty may continue transmitting data down the stack. Takes
943b1a696bSJiri Slaby (SUSE)  * the &tty_struct->termios_rwsem to protect against parallel
953b1a696bSJiri Slaby (SUSE)  * throttle/unthrottle and also to ensure the driver can consistently reference
963b1a696bSJiri Slaby (SUSE)  * its own termios data at this point when implementing software flow control.
9796fd7ce5SGreg Kroah-Hartman  *
983b1a696bSJiri Slaby (SUSE)  * Drivers should however remember that the stack can issue a throttle, then
993b1a696bSJiri Slaby (SUSE)  * change flow control method, then unthrottle.
10096fd7ce5SGreg Kroah-Hartman  */
tty_unthrottle(struct tty_struct * tty)10196fd7ce5SGreg Kroah-Hartman void tty_unthrottle(struct tty_struct *tty)
10296fd7ce5SGreg Kroah-Hartman {
1036a1c0680SPeter Hurley 	down_write(&tty->termios_rwsem);
10496fd7ce5SGreg Kroah-Hartman 	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
10596fd7ce5SGreg Kroah-Hartman 	    tty->ops->unthrottle)
10696fd7ce5SGreg Kroah-Hartman 		tty->ops->unthrottle(tty);
107e5d0424aSJiri Slaby (SUSE) 	tty->flow_change = TTY_FLOW_NO_CHANGE;
1086a1c0680SPeter Hurley 	up_write(&tty->termios_rwsem);
10996fd7ce5SGreg Kroah-Hartman }
11096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_unthrottle);
11196fd7ce5SGreg Kroah-Hartman 
11296fd7ce5SGreg Kroah-Hartman /**
11370bc1264SPeter Hurley  * tty_throttle_safe - flow control
11470bc1264SPeter Hurley  * @tty: terminal
11570bc1264SPeter Hurley  *
1163b1a696bSJiri Slaby (SUSE)  * Indicate that a @tty should stop transmitting data down the stack.
1173b1a696bSJiri Slaby (SUSE)  * tty_throttle_safe() will only attempt throttle if @tty->flow_change is
1183b1a696bSJiri Slaby (SUSE)  * %TTY_THROTTLE_SAFE. Prevents an accidental throttle due to race conditions
1193b1a696bSJiri Slaby (SUSE)  * when throttling is conditional on factors evaluated prior to throttling.
12070bc1264SPeter Hurley  *
1213b1a696bSJiri Slaby (SUSE)  * Returns: %true if @tty is throttled (or was already throttled)
12270bc1264SPeter Hurley  */
tty_throttle_safe(struct tty_struct * tty)123c2a36609SJiri Slaby (SUSE) bool tty_throttle_safe(struct tty_struct *tty)
12470bc1264SPeter Hurley {
1255b4f9cf3SJiri Slaby (SUSE) 	bool ret = true;
12670bc1264SPeter Hurley 
127d8c1f929SPeter Hurley 	mutex_lock(&tty->throttle_mutex);
12897ef38b8SPeter Hurley 	if (!tty_throttled(tty)) {
12970bc1264SPeter Hurley 		if (tty->flow_change != TTY_THROTTLE_SAFE)
1305b4f9cf3SJiri Slaby (SUSE) 			ret = false;
13170bc1264SPeter Hurley 		else {
132579a00a5SPeter Hurley 			set_bit(TTY_THROTTLED, &tty->flags);
13370bc1264SPeter Hurley 			if (tty->ops->throttle)
13470bc1264SPeter Hurley 				tty->ops->throttle(tty);
13570bc1264SPeter Hurley 		}
13670bc1264SPeter Hurley 	}
137d8c1f929SPeter Hurley 	mutex_unlock(&tty->throttle_mutex);
13870bc1264SPeter Hurley 
13970bc1264SPeter Hurley 	return ret;
14070bc1264SPeter Hurley }
14170bc1264SPeter Hurley 
14270bc1264SPeter Hurley /**
14370bc1264SPeter Hurley  * tty_unthrottle_safe - flow control
14470bc1264SPeter Hurley  * @tty: terminal
14570bc1264SPeter Hurley  *
1463b1a696bSJiri Slaby (SUSE)  * Similar to tty_unthrottle() but will only attempt unthrottle if
1473b1a696bSJiri Slaby (SUSE)  * @tty->flow_change is %TTY_UNTHROTTLE_SAFE. Prevents an accidental unthrottle
1483b1a696bSJiri Slaby (SUSE)  * due to race conditions when unthrottling is conditional on factors evaluated
1493b1a696bSJiri Slaby (SUSE)  * prior to unthrottling.
15070bc1264SPeter Hurley  *
1513b1a696bSJiri Slaby (SUSE)  * Returns: %true if @tty is unthrottled (or was already unthrottled)
15270bc1264SPeter Hurley  */
tty_unthrottle_safe(struct tty_struct * tty)153c2a36609SJiri Slaby (SUSE) bool tty_unthrottle_safe(struct tty_struct *tty)
15470bc1264SPeter Hurley {
1555b4f9cf3SJiri Slaby (SUSE) 	bool ret = true;
15670bc1264SPeter Hurley 
157d8c1f929SPeter Hurley 	mutex_lock(&tty->throttle_mutex);
15897ef38b8SPeter Hurley 	if (tty_throttled(tty)) {
15970bc1264SPeter Hurley 		if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
1605b4f9cf3SJiri Slaby (SUSE) 			ret = false;
16170bc1264SPeter Hurley 		else {
162579a00a5SPeter Hurley 			clear_bit(TTY_THROTTLED, &tty->flags);
16370bc1264SPeter Hurley 			if (tty->ops->unthrottle)
16470bc1264SPeter Hurley 				tty->ops->unthrottle(tty);
16570bc1264SPeter Hurley 		}
16670bc1264SPeter Hurley 	}
167d8c1f929SPeter Hurley 	mutex_unlock(&tty->throttle_mutex);
16870bc1264SPeter Hurley 
16970bc1264SPeter Hurley 	return ret;
17070bc1264SPeter Hurley }
17170bc1264SPeter Hurley 
17270bc1264SPeter Hurley /**
17396fd7ce5SGreg Kroah-Hartman  * tty_wait_until_sent - wait for I/O to finish
17496fd7ce5SGreg Kroah-Hartman  * @tty: tty we are waiting for
17596fd7ce5SGreg Kroah-Hartman  * @timeout: how long we will wait
17696fd7ce5SGreg Kroah-Hartman  *
1773b1a696bSJiri Slaby (SUSE)  * Wait for characters pending in a tty driver to hit the wire, or for a
1783b1a696bSJiri Slaby (SUSE)  * timeout to occur (eg due to flow control).
17996fd7ce5SGreg Kroah-Hartman  *
18096fd7ce5SGreg Kroah-Hartman  * Locking: none
18196fd7ce5SGreg Kroah-Hartman  */
18296fd7ce5SGreg Kroah-Hartman 
tty_wait_until_sent(struct tty_struct * tty,long timeout)18396fd7ce5SGreg Kroah-Hartman void tty_wait_until_sent(struct tty_struct *tty, long timeout)
18496fd7ce5SGreg Kroah-Hartman {
18596fd7ce5SGreg Kroah-Hartman 	if (!timeout)
18696fd7ce5SGreg Kroah-Hartman 		timeout = MAX_SCHEDULE_TIMEOUT;
18779fbf4a5SJohan Hovold 
188c37bc682SJohan Hovold 	timeout = wait_event_interruptible_timeout(tty->write_wait,
189c37bc682SJohan Hovold 			!tty_chars_in_buffer(tty), timeout);
190c37bc682SJohan Hovold 	if (timeout <= 0)
19179fbf4a5SJohan Hovold 		return;
19279fbf4a5SJohan Hovold 
19379fbf4a5SJohan Hovold 	if (timeout == MAX_SCHEDULE_TIMEOUT)
19479fbf4a5SJohan Hovold 		timeout = 0;
19579fbf4a5SJohan Hovold 
19696fd7ce5SGreg Kroah-Hartman 	if (tty->ops->wait_until_sent)
19796fd7ce5SGreg Kroah-Hartman 		tty->ops->wait_until_sent(tty, timeout);
19896fd7ce5SGreg Kroah-Hartman }
19996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_wait_until_sent);
20096fd7ce5SGreg Kroah-Hartman 
20196fd7ce5SGreg Kroah-Hartman 
20296fd7ce5SGreg Kroah-Hartman /*
20396fd7ce5SGreg Kroah-Hartman  *		Termios Helper Methods
20496fd7ce5SGreg Kroah-Hartman  */
20596fd7ce5SGreg Kroah-Hartman 
unset_locked_termios(struct tty_struct * tty,const struct ktermios * old)206bec5b814SIlpo Järvinen static void unset_locked_termios(struct tty_struct *tty, const struct ktermios *old)
20796fd7ce5SGreg Kroah-Hartman {
208d97ba9cdSPeter Hurley 	struct ktermios *termios = &tty->termios;
209d97ba9cdSPeter Hurley 	struct ktermios *locked  = &tty->termios_locked;
21096fd7ce5SGreg Kroah-Hartman 	int	i;
21196fd7ce5SGreg Kroah-Hartman 
21296fd7ce5SGreg Kroah-Hartman #define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
21396fd7ce5SGreg Kroah-Hartman 
21496fd7ce5SGreg Kroah-Hartman 	NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
21596fd7ce5SGreg Kroah-Hartman 	NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
21696fd7ce5SGreg Kroah-Hartman 	NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
21796fd7ce5SGreg Kroah-Hartman 	NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
21896fd7ce5SGreg Kroah-Hartman 	termios->c_line = locked->c_line ? old->c_line : termios->c_line;
21996fd7ce5SGreg Kroah-Hartman 	for (i = 0; i < NCCS; i++)
22096fd7ce5SGreg Kroah-Hartman 		termios->c_cc[i] = locked->c_cc[i] ?
22196fd7ce5SGreg Kroah-Hartman 			old->c_cc[i] : termios->c_cc[i];
22296fd7ce5SGreg Kroah-Hartman 	/* FIXME: What should we do for i/ospeed */
22396fd7ce5SGreg Kroah-Hartman }
22496fd7ce5SGreg Kroah-Hartman 
22596fd7ce5SGreg Kroah-Hartman /**
22696fd7ce5SGreg Kroah-Hartman  * tty_termios_copy_hw - copy hardware settings
2273b1a696bSJiri Slaby (SUSE)  * @new: new termios
2283b1a696bSJiri Slaby (SUSE)  * @old: old termios
22996fd7ce5SGreg Kroah-Hartman  *
2303b1a696bSJiri Slaby (SUSE)  * Propagate the hardware specific terminal setting bits from the @old termios
2313b1a696bSJiri Slaby (SUSE)  * structure to the @new one. This is used in cases where the hardware does not
2323b1a696bSJiri Slaby (SUSE)  * support reconfiguration or as a helper in some cases where only minimal
2333b1a696bSJiri Slaby (SUSE)  * reconfiguration is supported.
23496fd7ce5SGreg Kroah-Hartman  */
tty_termios_copy_hw(struct ktermios * new,const struct ktermios * old)235d15f89d9SIlpo Järvinen void tty_termios_copy_hw(struct ktermios *new, const struct ktermios *old)
23696fd7ce5SGreg Kroah-Hartman {
23796fd7ce5SGreg Kroah-Hartman 	/* The bits a dumb device handles in software. Smart devices need
23896fd7ce5SGreg Kroah-Hartman 	   to always provide a set_termios method */
23996fd7ce5SGreg Kroah-Hartman 	new->c_cflag &= HUPCL | CREAD | CLOCAL;
24096fd7ce5SGreg Kroah-Hartman 	new->c_cflag |= old->c_cflag & ~(HUPCL | CREAD | CLOCAL);
24196fd7ce5SGreg Kroah-Hartman 	new->c_ispeed = old->c_ispeed;
24296fd7ce5SGreg Kroah-Hartman 	new->c_ospeed = old->c_ospeed;
24396fd7ce5SGreg Kroah-Hartman }
24496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_termios_copy_hw);
24596fd7ce5SGreg Kroah-Hartman 
24696fd7ce5SGreg Kroah-Hartman /**
24796fd7ce5SGreg Kroah-Hartman  * tty_termios_hw_change - check for setting change
24896fd7ce5SGreg Kroah-Hartman  * @a: termios
24996fd7ce5SGreg Kroah-Hartman  * @b: termios to compare
25096fd7ce5SGreg Kroah-Hartman  *
2513b1a696bSJiri Slaby (SUSE)  * Check if any of the bits that affect a dumb device have changed between the
2523b1a696bSJiri Slaby (SUSE)  * two termios structures, or a speed change is needed.
2533b1a696bSJiri Slaby (SUSE)  *
2543b1a696bSJiri Slaby (SUSE)  * Returns: %true if change is needed
25596fd7ce5SGreg Kroah-Hartman  */
tty_termios_hw_change(const struct ktermios * a,const struct ktermios * b)25687f22db4SIlpo Järvinen bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
25796fd7ce5SGreg Kroah-Hartman {
25896fd7ce5SGreg Kroah-Hartman 	if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed)
25987f22db4SIlpo Järvinen 		return true;
26096fd7ce5SGreg Kroah-Hartman 	if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL))
26187f22db4SIlpo Järvinen 		return true;
26287f22db4SIlpo Järvinen 	return false;
26396fd7ce5SGreg Kroah-Hartman }
26496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_termios_hw_change);
26596fd7ce5SGreg Kroah-Hartman 
26696fd7ce5SGreg Kroah-Hartman /**
267654ee49bSJiri Slaby  * tty_get_char_size - get size of a character
268654ee49bSJiri Slaby  * @cflag: termios cflag value
269654ee49bSJiri Slaby  *
2703b1a696bSJiri Slaby (SUSE)  * Returns: size (in bits) of a character depending on @cflag's %CSIZE setting
271654ee49bSJiri Slaby  */
tty_get_char_size(unsigned int cflag)272654ee49bSJiri Slaby unsigned char tty_get_char_size(unsigned int cflag)
273654ee49bSJiri Slaby {
274654ee49bSJiri Slaby 	switch (cflag & CSIZE) {
275654ee49bSJiri Slaby 	case CS5:
276654ee49bSJiri Slaby 		return 5;
277654ee49bSJiri Slaby 	case CS6:
278654ee49bSJiri Slaby 		return 6;
279654ee49bSJiri Slaby 	case CS7:
280654ee49bSJiri Slaby 		return 7;
281654ee49bSJiri Slaby 	case CS8:
282654ee49bSJiri Slaby 	default:
283654ee49bSJiri Slaby 		return 8;
284654ee49bSJiri Slaby 	}
285654ee49bSJiri Slaby }
286654ee49bSJiri Slaby EXPORT_SYMBOL_GPL(tty_get_char_size);
287654ee49bSJiri Slaby 
288654ee49bSJiri Slaby /**
289654ee49bSJiri Slaby  * tty_get_frame_size - get size of a frame
290654ee49bSJiri Slaby  * @cflag: termios cflag value
291654ee49bSJiri Slaby  *
2923b1a696bSJiri Slaby (SUSE)  * Get the size (in bits) of a frame depending on @cflag's %CSIZE, %CSTOPB, and
2933b1a696bSJiri Slaby (SUSE)  * %PARENB setting. The result is a sum of character size, start and stop bits
2943b1a696bSJiri Slaby (SUSE)  * -- one bit each -- second stop bit (if set), and parity bit (if set).
2953b1a696bSJiri Slaby (SUSE)  *
2963b1a696bSJiri Slaby (SUSE)  * Returns: size (in bits) of a frame depending on @cflag's setting.
297654ee49bSJiri Slaby  */
tty_get_frame_size(unsigned int cflag)298654ee49bSJiri Slaby unsigned char tty_get_frame_size(unsigned int cflag)
299654ee49bSJiri Slaby {
300654ee49bSJiri Slaby 	unsigned char bits = 2 + tty_get_char_size(cflag);
301654ee49bSJiri Slaby 
302654ee49bSJiri Slaby 	if (cflag & CSTOPB)
303654ee49bSJiri Slaby 		bits++;
304654ee49bSJiri Slaby 	if (cflag & PARENB)
305654ee49bSJiri Slaby 		bits++;
3064f768e94SIlpo Järvinen 	if (cflag & ADDRB)
3074f768e94SIlpo Järvinen 		bits++;
308654ee49bSJiri Slaby 
309654ee49bSJiri Slaby 	return bits;
310654ee49bSJiri Slaby }
311654ee49bSJiri Slaby EXPORT_SYMBOL_GPL(tty_get_frame_size);
312654ee49bSJiri Slaby 
313654ee49bSJiri Slaby /**
3148d075b19SAlan Cox  * tty_set_termios - update termios values
31596fd7ce5SGreg Kroah-Hartman  * @tty: tty to update
31696fd7ce5SGreg Kroah-Hartman  * @new_termios: desired new value
31796fd7ce5SGreg Kroah-Hartman  *
3183b1a696bSJiri Slaby (SUSE)  * Perform updates to the termios values set on this @tty. A master pty's
3193b1a696bSJiri Slaby (SUSE)  * termios should never be set.
3206460fbbfSPeter Hurley  *
3213b1a696bSJiri Slaby (SUSE)  * Locking: &tty_struct->termios_rwsem
32296fd7ce5SGreg Kroah-Hartman  */
tty_set_termios(struct tty_struct * tty,struct ktermios * new_termios)323b00f5c2dSFrederic Danis int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
32496fd7ce5SGreg Kroah-Hartman {
32596fd7ce5SGreg Kroah-Hartman 	struct ktermios old_termios;
32696fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
32796fd7ce5SGreg Kroah-Hartman 
3286460fbbfSPeter Hurley 	WARN_ON(tty->driver->type == TTY_DRIVER_TYPE_PTY &&
3296460fbbfSPeter Hurley 		tty->driver->subtype == PTY_TYPE_MASTER);
33096fd7ce5SGreg Kroah-Hartman 	/*
33196fd7ce5SGreg Kroah-Hartman 	 *	Perform the actual termios internal changes under lock.
33296fd7ce5SGreg Kroah-Hartman 	 */
33396fd7ce5SGreg Kroah-Hartman 
33496fd7ce5SGreg Kroah-Hartman 
33596fd7ce5SGreg Kroah-Hartman 	/* FIXME: we need to decide on some locking/ordering semantics
33696fd7ce5SGreg Kroah-Hartman 	   for the set_termios notification eventually */
3376a1c0680SPeter Hurley 	down_write(&tty->termios_rwsem);
338adc8d746SAlan Cox 	old_termios = tty->termios;
339adc8d746SAlan Cox 	tty->termios = *new_termios;
340d97ba9cdSPeter Hurley 	unset_locked_termios(tty, &old_termios);
3414f768e94SIlpo Järvinen 	/* Reset any ADDRB changes, ADDRB is changed through ->rs485_config() */
3424f768e94SIlpo Järvinen 	tty->termios.c_cflag ^= (tty->termios.c_cflag ^ old_termios.c_cflag) & ADDRB;
34396fd7ce5SGreg Kroah-Hartman 
34496fd7ce5SGreg Kroah-Hartman 	if (tty->ops->set_termios)
345c961bfb1SPeter Hurley 		tty->ops->set_termios(tty, &old_termios);
34696fd7ce5SGreg Kroah-Hartman 	else
347adc8d746SAlan Cox 		tty_termios_copy_hw(&tty->termios, &old_termios);
34896fd7ce5SGreg Kroah-Hartman 
34996fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref(tty);
35096fd7ce5SGreg Kroah-Hartman 	if (ld != NULL) {
35196fd7ce5SGreg Kroah-Hartman 		if (ld->ops->set_termios)
352c961bfb1SPeter Hurley 			ld->ops->set_termios(tty, &old_termios);
35396fd7ce5SGreg Kroah-Hartman 		tty_ldisc_deref(ld);
35496fd7ce5SGreg Kroah-Hartman 	}
3556a1c0680SPeter Hurley 	up_write(&tty->termios_rwsem);
3568d075b19SAlan Cox 	return 0;
35796fd7ce5SGreg Kroah-Hartman }
358b00f5c2dSFrederic Danis EXPORT_SYMBOL_GPL(tty_set_termios);
35996fd7ce5SGreg Kroah-Hartman 
3601d5d6682SAl Viro 
3611d5d6682SAl Viro /*
3621d5d6682SAl Viro  * Translate a "termio" structure into a "termios". Ugh.
3631d5d6682SAl Viro  */
user_termio_to_kernel_termios(struct ktermios * termios,struct termio __user * termio)3641d5d6682SAl Viro __weak int user_termio_to_kernel_termios(struct ktermios *termios,
3651d5d6682SAl Viro 						struct termio __user *termio)
3661d5d6682SAl Viro {
3671d5d6682SAl Viro 	struct termio v;
3681d5d6682SAl Viro 
3691d5d6682SAl Viro 	if (copy_from_user(&v, termio, sizeof(struct termio)))
3701d5d6682SAl Viro 		return -EFAULT;
3711d5d6682SAl Viro 
3721d5d6682SAl Viro 	termios->c_iflag = (0xffff0000 & termios->c_iflag) | v.c_iflag;
3731d5d6682SAl Viro 	termios->c_oflag = (0xffff0000 & termios->c_oflag) | v.c_oflag;
3741d5d6682SAl Viro 	termios->c_cflag = (0xffff0000 & termios->c_cflag) | v.c_cflag;
3751d5d6682SAl Viro 	termios->c_lflag = (0xffff0000 & termios->c_lflag) | v.c_lflag;
3761d5d6682SAl Viro 	termios->c_line = (0xffff0000 & termios->c_lflag) | v.c_line;
3771d5d6682SAl Viro 	memcpy(termios->c_cc, v.c_cc, NCC);
3781d5d6682SAl Viro 	return 0;
3791d5d6682SAl Viro }
3801d5d6682SAl Viro 
3811d5d6682SAl Viro /*
3821d5d6682SAl Viro  * Translate a "termios" structure into a "termio". Ugh.
3831d5d6682SAl Viro  */
kernel_termios_to_user_termio(struct termio __user * termio,struct ktermios * termios)3841d5d6682SAl Viro __weak int kernel_termios_to_user_termio(struct termio __user *termio,
3851d5d6682SAl Viro 						struct ktermios *termios)
3861d5d6682SAl Viro {
3871d5d6682SAl Viro 	struct termio v;
3881d5d6682SAl Viro 	memset(&v, 0, sizeof(struct termio));
3891d5d6682SAl Viro 	v.c_iflag = termios->c_iflag;
3901d5d6682SAl Viro 	v.c_oflag = termios->c_oflag;
3911d5d6682SAl Viro 	v.c_cflag = termios->c_cflag;
3921d5d6682SAl Viro 	v.c_lflag = termios->c_lflag;
3931d5d6682SAl Viro 	v.c_line = termios->c_line;
3941d5d6682SAl Viro 	memcpy(v.c_cc, termios->c_cc, NCC);
3951d5d6682SAl Viro 	return copy_to_user(termio, &v, sizeof(struct termio));
3961d5d6682SAl Viro }
3971d5d6682SAl Viro 
3981d5d6682SAl Viro #ifdef TCGETS2
user_termios_to_kernel_termios(struct ktermios * k,struct termios2 __user * u)3991d5d6682SAl Viro __weak int user_termios_to_kernel_termios(struct ktermios *k,
4001d5d6682SAl Viro 						 struct termios2 __user *u)
4011d5d6682SAl Viro {
4021d5d6682SAl Viro 	return copy_from_user(k, u, sizeof(struct termios2));
4031d5d6682SAl Viro }
kernel_termios_to_user_termios(struct termios2 __user * u,struct ktermios * k)4041d5d6682SAl Viro __weak int kernel_termios_to_user_termios(struct termios2 __user *u,
4051d5d6682SAl Viro 						 struct ktermios *k)
4061d5d6682SAl Viro {
4071d5d6682SAl Viro 	return copy_to_user(u, k, sizeof(struct termios2));
4081d5d6682SAl Viro }
user_termios_to_kernel_termios_1(struct ktermios * k,struct termios __user * u)4091d5d6682SAl Viro __weak int user_termios_to_kernel_termios_1(struct ktermios *k,
4101d5d6682SAl Viro 						   struct termios __user *u)
4111d5d6682SAl Viro {
4121d5d6682SAl Viro 	return copy_from_user(k, u, sizeof(struct termios));
4131d5d6682SAl Viro }
kernel_termios_to_user_termios_1(struct termios __user * u,struct ktermios * k)4141d5d6682SAl Viro __weak int kernel_termios_to_user_termios_1(struct termios __user *u,
4151d5d6682SAl Viro 						   struct ktermios *k)
4161d5d6682SAl Viro {
4171d5d6682SAl Viro 	return copy_to_user(u, k, sizeof(struct termios));
4181d5d6682SAl Viro }
4191d5d6682SAl Viro 
4201d5d6682SAl Viro #else
4211d5d6682SAl Viro 
user_termios_to_kernel_termios(struct ktermios * k,struct termios __user * u)4221d5d6682SAl Viro __weak int user_termios_to_kernel_termios(struct ktermios *k,
4231d5d6682SAl Viro 						 struct termios __user *u)
4241d5d6682SAl Viro {
4251d5d6682SAl Viro 	return copy_from_user(k, u, sizeof(struct termios));
4261d5d6682SAl Viro }
kernel_termios_to_user_termios(struct termios __user * u,struct ktermios * k)4271d5d6682SAl Viro __weak int kernel_termios_to_user_termios(struct termios __user *u,
4281d5d6682SAl Viro 						 struct ktermios *k)
4291d5d6682SAl Viro {
4301d5d6682SAl Viro 	return copy_to_user(u, k, sizeof(struct termios));
4311d5d6682SAl Viro }
4321d5d6682SAl Viro #endif /* TCGETS2 */
4331d5d6682SAl Viro 
43496fd7ce5SGreg Kroah-Hartman /**
43596fd7ce5SGreg Kroah-Hartman  * set_termios - set termios values for a tty
43696fd7ce5SGreg Kroah-Hartman  * @tty: terminal device
43796fd7ce5SGreg Kroah-Hartman  * @arg: user data
43896fd7ce5SGreg Kroah-Hartman  * @opt: option information
43996fd7ce5SGreg Kroah-Hartman  *
4403b1a696bSJiri Slaby (SUSE)  * Helper function to prepare termios data and run necessary other functions
4413b1a696bSJiri Slaby (SUSE)  * before using tty_set_termios() to do the actual changes.
44296fd7ce5SGreg Kroah-Hartman  *
4433b1a696bSJiri Slaby (SUSE)  * Locking: called functions take &tty_struct->ldisc_sem and
4443b1a696bSJiri Slaby (SUSE)  * &tty_struct->termios_rwsem locks
4453b1a696bSJiri Slaby (SUSE)  *
4463b1a696bSJiri Slaby (SUSE)  * Returns: 0 on success, an error otherwise
44796fd7ce5SGreg Kroah-Hartman  */
set_termios(struct tty_struct * tty,void __user * arg,int opt)44896fd7ce5SGreg Kroah-Hartman static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
44996fd7ce5SGreg Kroah-Hartman {
45096fd7ce5SGreg Kroah-Hartman 	struct ktermios tmp_termios;
45196fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
45296fd7ce5SGreg Kroah-Hartman 	int retval = tty_check_change(tty);
45396fd7ce5SGreg Kroah-Hartman 
45496fd7ce5SGreg Kroah-Hartman 	if (retval)
45596fd7ce5SGreg Kroah-Hartman 		return retval;
45696fd7ce5SGreg Kroah-Hartman 
4576a1c0680SPeter Hurley 	down_read(&tty->termios_rwsem);
458adc8d746SAlan Cox 	tmp_termios = tty->termios;
4596a1c0680SPeter Hurley 	up_read(&tty->termios_rwsem);
46096fd7ce5SGreg Kroah-Hartman 
46196fd7ce5SGreg Kroah-Hartman 	if (opt & TERMIOS_TERMIO) {
46296fd7ce5SGreg Kroah-Hartman 		if (user_termio_to_kernel_termios(&tmp_termios,
46396fd7ce5SGreg Kroah-Hartman 						(struct termio __user *)arg))
46496fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
46596fd7ce5SGreg Kroah-Hartman #ifdef TCGETS2
46696fd7ce5SGreg Kroah-Hartman 	} else if (opt & TERMIOS_OLD) {
46796fd7ce5SGreg Kroah-Hartman 		if (user_termios_to_kernel_termios_1(&tmp_termios,
46896fd7ce5SGreg Kroah-Hartman 						(struct termios __user *)arg))
46996fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
47096fd7ce5SGreg Kroah-Hartman 	} else {
47196fd7ce5SGreg Kroah-Hartman 		if (user_termios_to_kernel_termios(&tmp_termios,
47296fd7ce5SGreg Kroah-Hartman 						(struct termios2 __user *)arg))
47396fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
47496fd7ce5SGreg Kroah-Hartman 	}
47596fd7ce5SGreg Kroah-Hartman #else
47696fd7ce5SGreg Kroah-Hartman 	} else if (user_termios_to_kernel_termios(&tmp_termios,
47796fd7ce5SGreg Kroah-Hartman 					(struct termios __user *)arg))
47896fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
47996fd7ce5SGreg Kroah-Hartman #endif
48096fd7ce5SGreg Kroah-Hartman 
48196fd7ce5SGreg Kroah-Hartman 	/* If old style Bfoo values are used then load c_ispeed/c_ospeed
48296fd7ce5SGreg Kroah-Hartman 	 * with the real speed so its unconditionally usable */
48396fd7ce5SGreg Kroah-Hartman 	tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
48496fd7ce5SGreg Kroah-Hartman 	tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
48596fd7ce5SGreg Kroah-Hartman 
486094fb49aSIlpo Järvinen 	if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {
487094fb49aSIlpo Järvinen retry_write_wait:
488094fb49aSIlpo Järvinen 		retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty));
489094fb49aSIlpo Järvinen 		if (retval < 0)
490094fb49aSIlpo Järvinen 			return retval;
49196fd7ce5SGreg Kroah-Hartman 
492af815336SJiri Slaby (SUSE) 		if (tty_write_lock(tty, false) < 0)
493094fb49aSIlpo Järvinen 			goto retry_write_wait;
494094fb49aSIlpo Järvinen 
495094fb49aSIlpo Järvinen 		/* Racing writer? */
496094fb49aSIlpo Järvinen 		if (tty_chars_in_buffer(tty)) {
497094fb49aSIlpo Järvinen 			tty_write_unlock(tty);
498094fb49aSIlpo Järvinen 			goto retry_write_wait;
499094fb49aSIlpo Järvinen 		}
500094fb49aSIlpo Järvinen 
501094fb49aSIlpo Järvinen 		ld = tty_ldisc_ref(tty);
50296fd7ce5SGreg Kroah-Hartman 		if (ld != NULL) {
50396fd7ce5SGreg Kroah-Hartman 			if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
50496fd7ce5SGreg Kroah-Hartman 				ld->ops->flush_buffer(tty);
50596fd7ce5SGreg Kroah-Hartman 			tty_ldisc_deref(ld);
50696fd7ce5SGreg Kroah-Hartman 		}
50796fd7ce5SGreg Kroah-Hartman 
508094fb49aSIlpo Järvinen 		if ((opt & TERMIOS_WAIT) && tty->ops->wait_until_sent) {
509094fb49aSIlpo Järvinen 			tty->ops->wait_until_sent(tty, 0);
510094fb49aSIlpo Järvinen 			if (signal_pending(current)) {
511094fb49aSIlpo Järvinen 				tty_write_unlock(tty);
512183d95cdSOleg Nesterov 				return -ERESTARTSYS;
51396fd7ce5SGreg Kroah-Hartman 			}
514094fb49aSIlpo Järvinen 		}
51596fd7ce5SGreg Kroah-Hartman 
5168d075b19SAlan Cox 		tty_set_termios(tty, &tmp_termios);
51796fd7ce5SGreg Kroah-Hartman 
518094fb49aSIlpo Järvinen 		tty_write_unlock(tty);
519094fb49aSIlpo Järvinen 	} else {
520094fb49aSIlpo Järvinen 		tty_set_termios(tty, &tmp_termios);
521094fb49aSIlpo Järvinen 	}
522094fb49aSIlpo Järvinen 
52396fd7ce5SGreg Kroah-Hartman 	/* FIXME: Arguably if tmp_termios == tty->termios AND the
52496fd7ce5SGreg Kroah-Hartman 	   actual requested termios was not tmp_termios then we may
52596fd7ce5SGreg Kroah-Hartman 	   want to return an error as no user requested change has
52696fd7ce5SGreg Kroah-Hartman 	   succeeded */
52796fd7ce5SGreg Kroah-Hartman 	return 0;
52896fd7ce5SGreg Kroah-Hartman }
52996fd7ce5SGreg Kroah-Hartman 
copy_termios(struct tty_struct * tty,struct ktermios * kterm)53096fd7ce5SGreg Kroah-Hartman static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
53196fd7ce5SGreg Kroah-Hartman {
5326a1c0680SPeter Hurley 	down_read(&tty->termios_rwsem);
533adc8d746SAlan Cox 	*kterm = tty->termios;
5346a1c0680SPeter Hurley 	up_read(&tty->termios_rwsem);
53596fd7ce5SGreg Kroah-Hartman }
53696fd7ce5SGreg Kroah-Hartman 
copy_termios_locked(struct tty_struct * tty,struct ktermios * kterm)53796fd7ce5SGreg Kroah-Hartman static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
53896fd7ce5SGreg Kroah-Hartman {
5396a1c0680SPeter Hurley 	down_read(&tty->termios_rwsem);
540adc8d746SAlan Cox 	*kterm = tty->termios_locked;
5416a1c0680SPeter Hurley 	up_read(&tty->termios_rwsem);
54296fd7ce5SGreg Kroah-Hartman }
54396fd7ce5SGreg Kroah-Hartman 
get_termio(struct tty_struct * tty,struct termio __user * termio)54496fd7ce5SGreg Kroah-Hartman static int get_termio(struct tty_struct *tty, struct termio __user *termio)
54596fd7ce5SGreg Kroah-Hartman {
54696fd7ce5SGreg Kroah-Hartman 	struct ktermios kterm;
54796fd7ce5SGreg Kroah-Hartman 	copy_termios(tty, &kterm);
54896fd7ce5SGreg Kroah-Hartman 	if (kernel_termios_to_user_termio(termio, &kterm))
54996fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
55096fd7ce5SGreg Kroah-Hartman 	return 0;
55196fd7ce5SGreg Kroah-Hartman }
55296fd7ce5SGreg Kroah-Hartman 
55396fd7ce5SGreg Kroah-Hartman #ifdef TIOCGETP
55496fd7ce5SGreg Kroah-Hartman /*
55596fd7ce5SGreg Kroah-Hartman  * These are deprecated, but there is limited support..
55696fd7ce5SGreg Kroah-Hartman  *
55796fd7ce5SGreg Kroah-Hartman  * The "sg_flags" translation is a joke..
55896fd7ce5SGreg Kroah-Hartman  */
get_sgflags(struct tty_struct * tty)55996fd7ce5SGreg Kroah-Hartman static int get_sgflags(struct tty_struct *tty)
56096fd7ce5SGreg Kroah-Hartman {
56196fd7ce5SGreg Kroah-Hartman 	int flags = 0;
56296fd7ce5SGreg Kroah-Hartman 
5639db276f8SPeter Hurley 	if (!L_ICANON(tty)) {
5649db276f8SPeter Hurley 		if (L_ISIG(tty))
56596fd7ce5SGreg Kroah-Hartman 			flags |= 0x02;		/* cbreak */
56696fd7ce5SGreg Kroah-Hartman 		else
56796fd7ce5SGreg Kroah-Hartman 			flags |= 0x20;		/* raw */
56896fd7ce5SGreg Kroah-Hartman 	}
5699db276f8SPeter Hurley 	if (L_ECHO(tty))
57096fd7ce5SGreg Kroah-Hartman 		flags |= 0x08;			/* echo */
5719db276f8SPeter Hurley 	if (O_OPOST(tty))
5729db276f8SPeter Hurley 		if (O_ONLCR(tty))
57396fd7ce5SGreg Kroah-Hartman 			flags |= 0x10;		/* crmod */
57496fd7ce5SGreg Kroah-Hartman 	return flags;
57596fd7ce5SGreg Kroah-Hartman }
57696fd7ce5SGreg Kroah-Hartman 
get_sgttyb(struct tty_struct * tty,struct sgttyb __user * sgttyb)57796fd7ce5SGreg Kroah-Hartman static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
57896fd7ce5SGreg Kroah-Hartman {
57996fd7ce5SGreg Kroah-Hartman 	struct sgttyb tmp;
58096fd7ce5SGreg Kroah-Hartman 
5816a1c0680SPeter Hurley 	down_read(&tty->termios_rwsem);
582adc8d746SAlan Cox 	tmp.sg_ispeed = tty->termios.c_ispeed;
583adc8d746SAlan Cox 	tmp.sg_ospeed = tty->termios.c_ospeed;
584adc8d746SAlan Cox 	tmp.sg_erase = tty->termios.c_cc[VERASE];
585adc8d746SAlan Cox 	tmp.sg_kill = tty->termios.c_cc[VKILL];
58696fd7ce5SGreg Kroah-Hartman 	tmp.sg_flags = get_sgflags(tty);
5876a1c0680SPeter Hurley 	up_read(&tty->termios_rwsem);
58896fd7ce5SGreg Kroah-Hartman 
58996fd7ce5SGreg Kroah-Hartman 	return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
59096fd7ce5SGreg Kroah-Hartman }
59196fd7ce5SGreg Kroah-Hartman 
set_sgflags(struct ktermios * termios,int flags)59296fd7ce5SGreg Kroah-Hartman static void set_sgflags(struct ktermios *termios, int flags)
59396fd7ce5SGreg Kroah-Hartman {
5949833facfSAlan Cox 	termios->c_iflag = ICRNL | IXON;
5959833facfSAlan Cox 	termios->c_oflag = 0;
5969833facfSAlan Cox 	termios->c_lflag = ISIG | ICANON;
59796fd7ce5SGreg Kroah-Hartman 	if (flags & 0x02) {	/* cbreak */
5989833facfSAlan Cox 		termios->c_iflag = 0;
5999833facfSAlan Cox 		termios->c_lflag &= ~ICANON;
60096fd7ce5SGreg Kroah-Hartman 	}
60196fd7ce5SGreg Kroah-Hartman 	if (flags & 0x08) {		/* echo */
6029833facfSAlan Cox 		termios->c_lflag |= ECHO | ECHOE | ECHOK |
60396fd7ce5SGreg Kroah-Hartman 				    ECHOCTL | ECHOKE | IEXTEN;
60496fd7ce5SGreg Kroah-Hartman 	}
60596fd7ce5SGreg Kroah-Hartman 	if (flags & 0x10) {		/* crmod */
6069833facfSAlan Cox 		termios->c_oflag |= OPOST | ONLCR;
60796fd7ce5SGreg Kroah-Hartman 	}
60896fd7ce5SGreg Kroah-Hartman 	if (flags & 0x20) {	/* raw */
6099833facfSAlan Cox 		termios->c_iflag = 0;
6109833facfSAlan Cox 		termios->c_lflag &= ~(ISIG | ICANON);
61196fd7ce5SGreg Kroah-Hartman 	}
6129833facfSAlan Cox 	if (!(termios->c_lflag & ICANON)) {
6139833facfSAlan Cox 		termios->c_cc[VMIN] = 1;
6149833facfSAlan Cox 		termios->c_cc[VTIME] = 0;
61596fd7ce5SGreg Kroah-Hartman 	}
61696fd7ce5SGreg Kroah-Hartman }
61796fd7ce5SGreg Kroah-Hartman 
61896fd7ce5SGreg Kroah-Hartman /**
61996fd7ce5SGreg Kroah-Hartman  * set_sgttyb - set legacy terminal values
62096fd7ce5SGreg Kroah-Hartman  * @tty: tty structure
62196fd7ce5SGreg Kroah-Hartman  * @sgttyb: pointer to old style terminal structure
62296fd7ce5SGreg Kroah-Hartman  *
6233b1a696bSJiri Slaby (SUSE)  * Updates a terminal from the legacy BSD style terminal information structure.
62496fd7ce5SGreg Kroah-Hartman  *
6253b1a696bSJiri Slaby (SUSE)  * Locking: &tty_struct->termios_rwsem
6263b1a696bSJiri Slaby (SUSE)  *
6273b1a696bSJiri Slaby (SUSE)  * Returns: 0 on success, an error otherwise
62896fd7ce5SGreg Kroah-Hartman  */
set_sgttyb(struct tty_struct * tty,struct sgttyb __user * sgttyb)62996fd7ce5SGreg Kroah-Hartman static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
63096fd7ce5SGreg Kroah-Hartman {
63196fd7ce5SGreg Kroah-Hartman 	int retval;
63296fd7ce5SGreg Kroah-Hartman 	struct sgttyb tmp;
63396fd7ce5SGreg Kroah-Hartman 	struct ktermios termios;
63496fd7ce5SGreg Kroah-Hartman 
63596fd7ce5SGreg Kroah-Hartman 	retval = tty_check_change(tty);
63696fd7ce5SGreg Kroah-Hartman 	if (retval)
63796fd7ce5SGreg Kroah-Hartman 		return retval;
63896fd7ce5SGreg Kroah-Hartman 
63996fd7ce5SGreg Kroah-Hartman 	if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
64096fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
64196fd7ce5SGreg Kroah-Hartman 
6426a1c0680SPeter Hurley 	down_write(&tty->termios_rwsem);
643adc8d746SAlan Cox 	termios = tty->termios;
64496fd7ce5SGreg Kroah-Hartman 	termios.c_cc[VERASE] = tmp.sg_erase;
64596fd7ce5SGreg Kroah-Hartman 	termios.c_cc[VKILL] = tmp.sg_kill;
64696fd7ce5SGreg Kroah-Hartman 	set_sgflags(&termios, tmp.sg_flags);
64796fd7ce5SGreg Kroah-Hartman 	/* Try and encode into Bfoo format */
64896fd7ce5SGreg Kroah-Hartman 	tty_termios_encode_baud_rate(&termios, termios.c_ispeed,
64996fd7ce5SGreg Kroah-Hartman 						termios.c_ospeed);
6506a1c0680SPeter Hurley 	up_write(&tty->termios_rwsem);
6518d075b19SAlan Cox 	tty_set_termios(tty, &termios);
65296fd7ce5SGreg Kroah-Hartman 	return 0;
65396fd7ce5SGreg Kroah-Hartman }
65496fd7ce5SGreg Kroah-Hartman #endif
65596fd7ce5SGreg Kroah-Hartman 
65696fd7ce5SGreg Kroah-Hartman #ifdef TIOCGETC
get_tchars(struct tty_struct * tty,struct tchars __user * tchars)65796fd7ce5SGreg Kroah-Hartman static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars)
65896fd7ce5SGreg Kroah-Hartman {
65996fd7ce5SGreg Kroah-Hartman 	struct tchars tmp;
66096fd7ce5SGreg Kroah-Hartman 
6616a1c0680SPeter Hurley 	down_read(&tty->termios_rwsem);
662adc8d746SAlan Cox 	tmp.t_intrc = tty->termios.c_cc[VINTR];
663adc8d746SAlan Cox 	tmp.t_quitc = tty->termios.c_cc[VQUIT];
664adc8d746SAlan Cox 	tmp.t_startc = tty->termios.c_cc[VSTART];
665adc8d746SAlan Cox 	tmp.t_stopc = tty->termios.c_cc[VSTOP];
666adc8d746SAlan Cox 	tmp.t_eofc = tty->termios.c_cc[VEOF];
667adc8d746SAlan Cox 	tmp.t_brkc = tty->termios.c_cc[VEOL2];	/* what is brkc anyway? */
6686a1c0680SPeter Hurley 	up_read(&tty->termios_rwsem);
66996fd7ce5SGreg Kroah-Hartman 	return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
67096fd7ce5SGreg Kroah-Hartman }
67196fd7ce5SGreg Kroah-Hartman 
set_tchars(struct tty_struct * tty,struct tchars __user * tchars)67296fd7ce5SGreg Kroah-Hartman static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars)
67396fd7ce5SGreg Kroah-Hartman {
67496fd7ce5SGreg Kroah-Hartman 	struct tchars tmp;
67596fd7ce5SGreg Kroah-Hartman 
67696fd7ce5SGreg Kroah-Hartman 	if (copy_from_user(&tmp, tchars, sizeof(tmp)))
67796fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
6786a1c0680SPeter Hurley 	down_write(&tty->termios_rwsem);
679adc8d746SAlan Cox 	tty->termios.c_cc[VINTR] = tmp.t_intrc;
680adc8d746SAlan Cox 	tty->termios.c_cc[VQUIT] = tmp.t_quitc;
681adc8d746SAlan Cox 	tty->termios.c_cc[VSTART] = tmp.t_startc;
682adc8d746SAlan Cox 	tty->termios.c_cc[VSTOP] = tmp.t_stopc;
683adc8d746SAlan Cox 	tty->termios.c_cc[VEOF] = tmp.t_eofc;
684adc8d746SAlan Cox 	tty->termios.c_cc[VEOL2] = tmp.t_brkc;	/* what is brkc anyway? */
6856a1c0680SPeter Hurley 	up_write(&tty->termios_rwsem);
68696fd7ce5SGreg Kroah-Hartman 	return 0;
68796fd7ce5SGreg Kroah-Hartman }
68896fd7ce5SGreg Kroah-Hartman #endif
68996fd7ce5SGreg Kroah-Hartman 
69096fd7ce5SGreg Kroah-Hartman #ifdef TIOCGLTC
get_ltchars(struct tty_struct * tty,struct ltchars __user * ltchars)69196fd7ce5SGreg Kroah-Hartman static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
69296fd7ce5SGreg Kroah-Hartman {
69396fd7ce5SGreg Kroah-Hartman 	struct ltchars tmp;
69496fd7ce5SGreg Kroah-Hartman 
6956a1c0680SPeter Hurley 	down_read(&tty->termios_rwsem);
696adc8d746SAlan Cox 	tmp.t_suspc = tty->termios.c_cc[VSUSP];
69796fd7ce5SGreg Kroah-Hartman 	/* what is dsuspc anyway? */
698adc8d746SAlan Cox 	tmp.t_dsuspc = tty->termios.c_cc[VSUSP];
699adc8d746SAlan Cox 	tmp.t_rprntc = tty->termios.c_cc[VREPRINT];
70096fd7ce5SGreg Kroah-Hartman 	/* what is flushc anyway? */
701adc8d746SAlan Cox 	tmp.t_flushc = tty->termios.c_cc[VEOL2];
702adc8d746SAlan Cox 	tmp.t_werasc = tty->termios.c_cc[VWERASE];
703adc8d746SAlan Cox 	tmp.t_lnextc = tty->termios.c_cc[VLNEXT];
7046a1c0680SPeter Hurley 	up_read(&tty->termios_rwsem);
70596fd7ce5SGreg Kroah-Hartman 	return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0;
70696fd7ce5SGreg Kroah-Hartman }
70796fd7ce5SGreg Kroah-Hartman 
set_ltchars(struct tty_struct * tty,struct ltchars __user * ltchars)70896fd7ce5SGreg Kroah-Hartman static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
70996fd7ce5SGreg Kroah-Hartman {
71096fd7ce5SGreg Kroah-Hartman 	struct ltchars tmp;
71196fd7ce5SGreg Kroah-Hartman 
71296fd7ce5SGreg Kroah-Hartman 	if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
71396fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
71496fd7ce5SGreg Kroah-Hartman 
7156a1c0680SPeter Hurley 	down_write(&tty->termios_rwsem);
716adc8d746SAlan Cox 	tty->termios.c_cc[VSUSP] = tmp.t_suspc;
71796fd7ce5SGreg Kroah-Hartman 	/* what is dsuspc anyway? */
718adc8d746SAlan Cox 	tty->termios.c_cc[VEOL2] = tmp.t_dsuspc;
719adc8d746SAlan Cox 	tty->termios.c_cc[VREPRINT] = tmp.t_rprntc;
72096fd7ce5SGreg Kroah-Hartman 	/* what is flushc anyway? */
721adc8d746SAlan Cox 	tty->termios.c_cc[VEOL2] = tmp.t_flushc;
722adc8d746SAlan Cox 	tty->termios.c_cc[VWERASE] = tmp.t_werasc;
723adc8d746SAlan Cox 	tty->termios.c_cc[VLNEXT] = tmp.t_lnextc;
7246a1c0680SPeter Hurley 	up_write(&tty->termios_rwsem);
72596fd7ce5SGreg Kroah-Hartman 	return 0;
72696fd7ce5SGreg Kroah-Hartman }
72796fd7ce5SGreg Kroah-Hartman #endif
72896fd7ce5SGreg Kroah-Hartman 
72996fd7ce5SGreg Kroah-Hartman /**
73096fd7ce5SGreg Kroah-Hartman  * tty_change_softcar - carrier change ioctl helper
73196fd7ce5SGreg Kroah-Hartman  * @tty: tty to update
7323b1a696bSJiri Slaby (SUSE)  * @enable: enable/disable %CLOCAL
73396fd7ce5SGreg Kroah-Hartman  *
7343b1a696bSJiri Slaby (SUSE)  * Perform a change to the %CLOCAL state and call into the driver layer to make
7353b1a696bSJiri Slaby (SUSE)  * it visible.
7363b1a696bSJiri Slaby (SUSE)  *
7373b1a696bSJiri Slaby (SUSE)  * Locking: &tty_struct->termios_rwsem.
7383b1a696bSJiri Slaby (SUSE)  *
7393b1a696bSJiri Slaby (SUSE)  * Returns: 0 on success, an error otherwise
74096fd7ce5SGreg Kroah-Hartman  */
tty_change_softcar(struct tty_struct * tty,bool enable)74177b425e4SJiri Slaby (SUSE) static int tty_change_softcar(struct tty_struct *tty, bool enable)
74296fd7ce5SGreg Kroah-Hartman {
74396fd7ce5SGreg Kroah-Hartman 	int ret = 0;
74496fd7ce5SGreg Kroah-Hartman 	struct ktermios old;
74577b425e4SJiri Slaby (SUSE) 	tcflag_t bit = enable ? CLOCAL : 0;
74696fd7ce5SGreg Kroah-Hartman 
7476a1c0680SPeter Hurley 	down_write(&tty->termios_rwsem);
748adc8d746SAlan Cox 	old = tty->termios;
749adc8d746SAlan Cox 	tty->termios.c_cflag &= ~CLOCAL;
750adc8d746SAlan Cox 	tty->termios.c_cflag |= bit;
75196fd7ce5SGreg Kroah-Hartman 	if (tty->ops->set_termios)
75296fd7ce5SGreg Kroah-Hartman 		tty->ops->set_termios(tty, &old);
7539db276f8SPeter Hurley 	if (C_CLOCAL(tty) != bit)
75496fd7ce5SGreg Kroah-Hartman 		ret = -EINVAL;
7556a1c0680SPeter Hurley 	up_write(&tty->termios_rwsem);
75696fd7ce5SGreg Kroah-Hartman 	return ret;
75796fd7ce5SGreg Kroah-Hartman }
75896fd7ce5SGreg Kroah-Hartman 
75996fd7ce5SGreg Kroah-Hartman /**
76096fd7ce5SGreg Kroah-Hartman  * tty_mode_ioctl - mode related ioctls
76196fd7ce5SGreg Kroah-Hartman  * @tty: tty for the ioctl
76296fd7ce5SGreg Kroah-Hartman  * @cmd: command
76396fd7ce5SGreg Kroah-Hartman  * @arg: ioctl argument
76496fd7ce5SGreg Kroah-Hartman  *
7653b1a696bSJiri Slaby (SUSE)  * Perform non-line discipline specific mode control ioctls. This is designed
7663b1a696bSJiri Slaby (SUSE)  * to be called by line disciplines to ensure they provide consistent mode
7673b1a696bSJiri Slaby (SUSE)  * setting.
76896fd7ce5SGreg Kroah-Hartman  */
tty_mode_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)769dcc223e8SJiri Slaby int tty_mode_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
77096fd7ce5SGreg Kroah-Hartman {
77196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *real_tty;
77296fd7ce5SGreg Kroah-Hartman 	void __user *p = (void __user *)arg;
77396fd7ce5SGreg Kroah-Hartman 	int ret = 0;
77496fd7ce5SGreg Kroah-Hartman 	struct ktermios kterm;
77596fd7ce5SGreg Kroah-Hartman 
77696fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
77796fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_MASTER)
77896fd7ce5SGreg Kroah-Hartman 		real_tty = tty->link;
77996fd7ce5SGreg Kroah-Hartman 	else
78096fd7ce5SGreg Kroah-Hartman 		real_tty = tty;
78196fd7ce5SGreg Kroah-Hartman 
78296fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
78396fd7ce5SGreg Kroah-Hartman #ifdef TIOCGETP
78496fd7ce5SGreg Kroah-Hartman 	case TIOCGETP:
78596fd7ce5SGreg Kroah-Hartman 		return get_sgttyb(real_tty, (struct sgttyb __user *) arg);
78696fd7ce5SGreg Kroah-Hartman 	case TIOCSETP:
78796fd7ce5SGreg Kroah-Hartman 	case TIOCSETN:
78896fd7ce5SGreg Kroah-Hartman 		return set_sgttyb(real_tty, (struct sgttyb __user *) arg);
78996fd7ce5SGreg Kroah-Hartman #endif
79096fd7ce5SGreg Kroah-Hartman #ifdef TIOCGETC
79196fd7ce5SGreg Kroah-Hartman 	case TIOCGETC:
79296fd7ce5SGreg Kroah-Hartman 		return get_tchars(real_tty, p);
79396fd7ce5SGreg Kroah-Hartman 	case TIOCSETC:
79496fd7ce5SGreg Kroah-Hartman 		return set_tchars(real_tty, p);
79596fd7ce5SGreg Kroah-Hartman #endif
79696fd7ce5SGreg Kroah-Hartman #ifdef TIOCGLTC
79796fd7ce5SGreg Kroah-Hartman 	case TIOCGLTC:
79896fd7ce5SGreg Kroah-Hartman 		return get_ltchars(real_tty, p);
79996fd7ce5SGreg Kroah-Hartman 	case TIOCSLTC:
80096fd7ce5SGreg Kroah-Hartman 		return set_ltchars(real_tty, p);
80196fd7ce5SGreg Kroah-Hartman #endif
80296fd7ce5SGreg Kroah-Hartman 	case TCSETSF:
80396fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD);
80496fd7ce5SGreg Kroah-Hartman 	case TCSETSW:
80596fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD);
80696fd7ce5SGreg Kroah-Hartman 	case TCSETS:
80796fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, TERMIOS_OLD);
80896fd7ce5SGreg Kroah-Hartman #ifndef TCGETS2
80996fd7ce5SGreg Kroah-Hartman 	case TCGETS:
81096fd7ce5SGreg Kroah-Hartman 		copy_termios(real_tty, &kterm);
81196fd7ce5SGreg Kroah-Hartman 		if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
81296fd7ce5SGreg Kroah-Hartman 			ret = -EFAULT;
81396fd7ce5SGreg Kroah-Hartman 		return ret;
81496fd7ce5SGreg Kroah-Hartman #else
81596fd7ce5SGreg Kroah-Hartman 	case TCGETS:
81696fd7ce5SGreg Kroah-Hartman 		copy_termios(real_tty, &kterm);
81796fd7ce5SGreg Kroah-Hartman 		if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
81896fd7ce5SGreg Kroah-Hartman 			ret = -EFAULT;
81996fd7ce5SGreg Kroah-Hartman 		return ret;
82096fd7ce5SGreg Kroah-Hartman 	case TCGETS2:
82196fd7ce5SGreg Kroah-Hartman 		copy_termios(real_tty, &kterm);
82296fd7ce5SGreg Kroah-Hartman 		if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm))
82396fd7ce5SGreg Kroah-Hartman 			ret = -EFAULT;
82496fd7ce5SGreg Kroah-Hartman 		return ret;
82596fd7ce5SGreg Kroah-Hartman 	case TCSETSF2:
82696fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT);
82796fd7ce5SGreg Kroah-Hartman 	case TCSETSW2:
82896fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, TERMIOS_WAIT);
82996fd7ce5SGreg Kroah-Hartman 	case TCSETS2:
83096fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, 0);
83196fd7ce5SGreg Kroah-Hartman #endif
83296fd7ce5SGreg Kroah-Hartman 	case TCGETA:
83396fd7ce5SGreg Kroah-Hartman 		return get_termio(real_tty, p);
83496fd7ce5SGreg Kroah-Hartman 	case TCSETAF:
83596fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO);
83696fd7ce5SGreg Kroah-Hartman 	case TCSETAW:
83796fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_TERMIO);
83896fd7ce5SGreg Kroah-Hartman 	case TCSETA:
83996fd7ce5SGreg Kroah-Hartman 		return set_termios(real_tty, p, TERMIOS_TERMIO);
84096fd7ce5SGreg Kroah-Hartman #ifndef TCGETS2
84196fd7ce5SGreg Kroah-Hartman 	case TIOCGLCKTRMIOS:
84296fd7ce5SGreg Kroah-Hartman 		copy_termios_locked(real_tty, &kterm);
84396fd7ce5SGreg Kroah-Hartman 		if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
84496fd7ce5SGreg Kroah-Hartman 			ret = -EFAULT;
84596fd7ce5SGreg Kroah-Hartman 		return ret;
84696fd7ce5SGreg Kroah-Hartman 	case TIOCSLCKTRMIOS:
847*e0f25b89SAdrian Reber 		if (!checkpoint_restore_ns_capable(&init_user_ns))
84896fd7ce5SGreg Kroah-Hartman 			return -EPERM;
84996fd7ce5SGreg Kroah-Hartman 		copy_termios_locked(real_tty, &kterm);
85096fd7ce5SGreg Kroah-Hartman 		if (user_termios_to_kernel_termios(&kterm,
85196fd7ce5SGreg Kroah-Hartman 					       (struct termios __user *) arg))
85296fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
8536a1c0680SPeter Hurley 		down_write(&real_tty->termios_rwsem);
854adc8d746SAlan Cox 		real_tty->termios_locked = kterm;
8556a1c0680SPeter Hurley 		up_write(&real_tty->termios_rwsem);
85696fd7ce5SGreg Kroah-Hartman 		return 0;
85796fd7ce5SGreg Kroah-Hartman #else
85896fd7ce5SGreg Kroah-Hartman 	case TIOCGLCKTRMIOS:
85996fd7ce5SGreg Kroah-Hartman 		copy_termios_locked(real_tty, &kterm);
86096fd7ce5SGreg Kroah-Hartman 		if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
86196fd7ce5SGreg Kroah-Hartman 			ret = -EFAULT;
86296fd7ce5SGreg Kroah-Hartman 		return ret;
86396fd7ce5SGreg Kroah-Hartman 	case TIOCSLCKTRMIOS:
864*e0f25b89SAdrian Reber 		if (!checkpoint_restore_ns_capable(&init_user_ns))
86596fd7ce5SGreg Kroah-Hartman 			return -EPERM;
86696fd7ce5SGreg Kroah-Hartman 		copy_termios_locked(real_tty, &kterm);
86796fd7ce5SGreg Kroah-Hartman 		if (user_termios_to_kernel_termios_1(&kterm,
86896fd7ce5SGreg Kroah-Hartman 					       (struct termios __user *) arg))
86996fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
8706a1c0680SPeter Hurley 		down_write(&real_tty->termios_rwsem);
871adc8d746SAlan Cox 		real_tty->termios_locked = kterm;
8726a1c0680SPeter Hurley 		up_write(&real_tty->termios_rwsem);
87396fd7ce5SGreg Kroah-Hartman 		return ret;
87496fd7ce5SGreg Kroah-Hartman #endif
87596fd7ce5SGreg Kroah-Hartman #ifdef TCGETX
876e0efb316SJann Horn 	case TCGETX:
87796fd7ce5SGreg Kroah-Hartman 	case TCSETX:
87896fd7ce5SGreg Kroah-Hartman 	case TCSETXW:
87996fd7ce5SGreg Kroah-Hartman 	case TCSETXF:
8808871de06SJohan Hovold 		return -ENOTTY;
88196fd7ce5SGreg Kroah-Hartman #endif
88296fd7ce5SGreg Kroah-Hartman 	case TIOCGSOFTCAR:
88396fd7ce5SGreg Kroah-Hartman 		copy_termios(real_tty, &kterm);
88496fd7ce5SGreg Kroah-Hartman 		ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
88596fd7ce5SGreg Kroah-Hartman 						(int __user *)arg);
88696fd7ce5SGreg Kroah-Hartman 		return ret;
88796fd7ce5SGreg Kroah-Hartman 	case TIOCSSOFTCAR:
88896fd7ce5SGreg Kroah-Hartman 		if (get_user(arg, (unsigned int __user *) arg))
88996fd7ce5SGreg Kroah-Hartman 			return -EFAULT;
89096fd7ce5SGreg Kroah-Hartman 		return tty_change_softcar(real_tty, arg);
89196fd7ce5SGreg Kroah-Hartman 	default:
89296fd7ce5SGreg Kroah-Hartman 		return -ENOIOCTLCMD;
89396fd7ce5SGreg Kroah-Hartman 	}
89496fd7ce5SGreg Kroah-Hartman }
89596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_mode_ioctl);
89696fd7ce5SGreg Kroah-Hartman 
89796fd7ce5SGreg Kroah-Hartman 
898e7f3880cSPeter Hurley /* Caller guarantees ldisc reference is held */
__tty_perform_flush(struct tty_struct * tty,unsigned long arg)899e7f3880cSPeter Hurley static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg)
900e7f3880cSPeter Hurley {
901e7f3880cSPeter Hurley 	struct tty_ldisc *ld = tty->ldisc;
902e7f3880cSPeter Hurley 
90396fd7ce5SGreg Kroah-Hartman 	switch (arg) {
90496fd7ce5SGreg Kroah-Hartman 	case TCIFLUSH:
905a1bf9584SIlya Zykov 		if (ld && ld->ops->flush_buffer) {
90696fd7ce5SGreg Kroah-Hartman 			ld->ops->flush_buffer(tty);
907a1bf9584SIlya Zykov 			tty_unthrottle(tty);
908a1bf9584SIlya Zykov 		}
90996fd7ce5SGreg Kroah-Hartman 		break;
91096fd7ce5SGreg Kroah-Hartman 	case TCIOFLUSH:
911a1bf9584SIlya Zykov 		if (ld && ld->ops->flush_buffer) {
91296fd7ce5SGreg Kroah-Hartman 			ld->ops->flush_buffer(tty);
913a1bf9584SIlya Zykov 			tty_unthrottle(tty);
914a1bf9584SIlya Zykov 		}
915df561f66SGustavo A. R. Silva 		fallthrough;
91696fd7ce5SGreg Kroah-Hartman 	case TCOFLUSH:
91796fd7ce5SGreg Kroah-Hartman 		tty_driver_flush_buffer(tty);
91896fd7ce5SGreg Kroah-Hartman 		break;
91996fd7ce5SGreg Kroah-Hartman 	default:
92096fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
92196fd7ce5SGreg Kroah-Hartman 	}
92296fd7ce5SGreg Kroah-Hartman 	return 0;
92396fd7ce5SGreg Kroah-Hartman }
924e7f3880cSPeter Hurley 
tty_perform_flush(struct tty_struct * tty,unsigned long arg)925e7f3880cSPeter Hurley int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
926e7f3880cSPeter Hurley {
927e7f3880cSPeter Hurley 	struct tty_ldisc *ld;
928e7f3880cSPeter Hurley 	int retval = tty_check_change(tty);
929e7f3880cSPeter Hurley 	if (retval)
930e7f3880cSPeter Hurley 		return retval;
931e7f3880cSPeter Hurley 
932e7f3880cSPeter Hurley 	ld = tty_ldisc_ref_wait(tty);
933e7f3880cSPeter Hurley 	retval = __tty_perform_flush(tty, arg);
934e7f3880cSPeter Hurley 	if (ld)
935e7f3880cSPeter Hurley 		tty_ldisc_deref(ld);
936e7f3880cSPeter Hurley 	return retval;
937e7f3880cSPeter Hurley }
93896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_perform_flush);
93996fd7ce5SGreg Kroah-Hartman 
n_tty_ioctl_helper(struct tty_struct * tty,unsigned int cmd,unsigned long arg)9407c783601SJiri Slaby int n_tty_ioctl_helper(struct tty_struct *tty, unsigned int cmd,
9417c783601SJiri Slaby 		unsigned long arg)
94296fd7ce5SGreg Kroah-Hartman {
94396fd7ce5SGreg Kroah-Hartman 	int retval;
94496fd7ce5SGreg Kroah-Hartman 
94596fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
94696fd7ce5SGreg Kroah-Hartman 	case TCXONC:
94796fd7ce5SGreg Kroah-Hartman 		retval = tty_check_change(tty);
94896fd7ce5SGreg Kroah-Hartman 		if (retval)
94996fd7ce5SGreg Kroah-Hartman 			return retval;
95096fd7ce5SGreg Kroah-Hartman 		switch (arg) {
95196fd7ce5SGreg Kroah-Hartman 		case TCOOFF:
9526e94dbc7SJiri Slaby 			spin_lock_irq(&tty->flow.lock);
9536e94dbc7SJiri Slaby 			if (!tty->flow.tco_stopped) {
9546e94dbc7SJiri Slaby 				tty->flow.tco_stopped = true;
955c545b66cSPeter Hurley 				__stop_tty(tty);
95696fd7ce5SGreg Kroah-Hartman 			}
9576e94dbc7SJiri Slaby 			spin_unlock_irq(&tty->flow.lock);
95896fd7ce5SGreg Kroah-Hartman 			break;
95996fd7ce5SGreg Kroah-Hartman 		case TCOON:
9606e94dbc7SJiri Slaby 			spin_lock_irq(&tty->flow.lock);
9616e94dbc7SJiri Slaby 			if (tty->flow.tco_stopped) {
9626e94dbc7SJiri Slaby 				tty->flow.tco_stopped = false;
963c545b66cSPeter Hurley 				__start_tty(tty);
96496fd7ce5SGreg Kroah-Hartman 			}
9656e94dbc7SJiri Slaby 			spin_unlock_irq(&tty->flow.lock);
96696fd7ce5SGreg Kroah-Hartman 			break;
96796fd7ce5SGreg Kroah-Hartman 		case TCIOFF:
96896fd7ce5SGreg Kroah-Hartman 			if (STOP_CHAR(tty) != __DISABLED_CHAR)
969c274f6efSPeter Hurley 				retval = tty_send_xchar(tty, STOP_CHAR(tty));
97096fd7ce5SGreg Kroah-Hartman 			break;
97196fd7ce5SGreg Kroah-Hartman 		case TCION:
97296fd7ce5SGreg Kroah-Hartman 			if (START_CHAR(tty) != __DISABLED_CHAR)
973c274f6efSPeter Hurley 				retval = tty_send_xchar(tty, START_CHAR(tty));
97496fd7ce5SGreg Kroah-Hartman 			break;
97596fd7ce5SGreg Kroah-Hartman 		default:
97696fd7ce5SGreg Kroah-Hartman 			return -EINVAL;
97796fd7ce5SGreg Kroah-Hartman 		}
978c274f6efSPeter Hurley 		return retval;
97996fd7ce5SGreg Kroah-Hartman 	case TCFLSH:
9805cec7bf6SPeter Hurley 		retval = tty_check_change(tty);
9815cec7bf6SPeter Hurley 		if (retval)
9825cec7bf6SPeter Hurley 			return retval;
983e7f3880cSPeter Hurley 		return __tty_perform_flush(tty, arg);
98496fd7ce5SGreg Kroah-Hartman 	default:
98596fd7ce5SGreg Kroah-Hartman 		/* Try the mode commands */
986dcc223e8SJiri Slaby 		return tty_mode_ioctl(tty, cmd, arg);
98796fd7ce5SGreg Kroah-Hartman 	}
98896fd7ce5SGreg Kroah-Hartman }
98996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(n_tty_ioctl_helper);
990