summaryrefslogtreecommitdiff
path: root/drivers/tty/tty_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/tty_ioctl.c')
-rw-r--r--drivers/tty/tty_ioctl.c584
1 files changed, 313 insertions, 271 deletions
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 9245fffdbceb..90c70d8d14e3 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -7,6 +7,7 @@
* discipline handling modules (like SLIP).
*/
+#include <linux/bits.h>
#include <linux/types.h>
#include <linux/termios.h>
#include <linux/errno.h>
@@ -21,59 +22,48 @@
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/compat.h>
+#include <linux/termios_internal.h>
+#include "tty.h"
#include <asm/io.h>
#include <linux/uaccess.h>
-#undef TTY_DEBUG_WAIT_UNTIL_SENT
-
-#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
-# define tty_debug_wait_until_sent(tty, f, args...) tty_debug(tty, f, ##args)
-#else
-# define tty_debug_wait_until_sent(tty, f, args...) do {} while (0)
-#endif
-
#undef DEBUG
/*
* Internal flag options for termios setting behavior
*/
-#define TERMIOS_FLUSH 1
-#define TERMIOS_WAIT 2
-#define TERMIOS_TERMIO 4
-#define TERMIOS_OLD 8
-
+#define TERMIOS_FLUSH BIT(0)
+#define TERMIOS_WAIT BIT(1)
+#define TERMIOS_TERMIO BIT(2)
+#define TERMIOS_OLD BIT(3)
/**
- * tty_chars_in_buffer - characters pending
- * @tty: terminal
+ * tty_chars_in_buffer - characters pending
+ * @tty: terminal
*
- * Return the number of bytes of data in the device private
- * output queue. If no private method is supplied there is assumed
- * to be no queue on the device.
+ * Returns: the number of bytes of data in the device private output queue. If
+ * no private method is supplied there is assumed to be no queue on the device.
*/
-
-int tty_chars_in_buffer(struct tty_struct *tty)
+unsigned int tty_chars_in_buffer(struct tty_struct *tty)
{
if (tty->ops->chars_in_buffer)
return tty->ops->chars_in_buffer(tty);
- else
- return 0;
+ return 0;
}
EXPORT_SYMBOL(tty_chars_in_buffer);
/**
- * tty_write_room - write queue space
- * @tty: terminal
+ * tty_write_room - write queue space
+ * @tty: terminal
*
- * Return the number of bytes that can be queued to this device
- * at the present time. The result should be treated as a guarantee
- * and the driver cannot offer a value it later shrinks by more than
- * the number of bytes written. If no method is provided 2K is always
- * returned and data may be lost as there will be no flow control.
+ * Returns: the number of bytes that can be queued to this device at the present
+ * time. The result should be treated as a guarantee and the driver cannot
+ * offer a value it later shrinks by more than the number of bytes written. If
+ * no method is provided, 2K is always returned and data may be lost as there
+ * will be no flow control.
*/
-
-int tty_write_room(struct tty_struct *tty)
+unsigned int tty_write_room(struct tty_struct *tty)
{
if (tty->ops->write_room)
return tty->ops->write_room(tty);
@@ -82,12 +72,12 @@ int tty_write_room(struct tty_struct *tty)
EXPORT_SYMBOL(tty_write_room);
/**
- * tty_driver_flush_buffer - discard internal buffer
- * @tty: terminal
+ * tty_driver_flush_buffer - discard internal buffer
+ * @tty: terminal
*
- * Discard the internal output buffer for this device. If no method
- * is provided then either the buffer cannot be hardware flushed or
- * there is no buffer driver side.
+ * Discard the internal output buffer for this device. If no method is provided,
+ * then either the buffer cannot be hardware flushed or there is no buffer
+ * driver side.
*/
void tty_driver_flush_buffer(struct tty_struct *tty)
{
@@ -97,128 +87,97 @@ void tty_driver_flush_buffer(struct tty_struct *tty)
EXPORT_SYMBOL(tty_driver_flush_buffer);
/**
- * tty_throttle - flow control
- * @tty: terminal
- *
- * Indicate that a tty should stop transmitting data down the stack.
- * Takes the termios rwsem to protect against parallel throttle/unthrottle
- * and also to ensure the driver can consistently reference its own
- * termios data at this point when implementing software flow control.
- */
-
-void tty_throttle(struct tty_struct *tty)
-{
- down_write(&tty->termios_rwsem);
- /* check TTY_THROTTLED first so it indicates our state */
- if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) &&
- tty->ops->throttle)
- tty->ops->throttle(tty);
- tty->flow_change = 0;
- up_write(&tty->termios_rwsem);
-}
-EXPORT_SYMBOL(tty_throttle);
-
-/**
- * tty_unthrottle - flow control
- * @tty: terminal
+ * tty_unthrottle - flow control
+ * @tty: terminal
*
- * Indicate that a tty may continue transmitting data down the stack.
- * Takes the termios rwsem to protect against parallel throttle/unthrottle
- * and also to ensure the driver can consistently reference its own
- * termios data at this point when implementing software flow control.
+ * Indicate that a @tty may continue transmitting data down the stack. Takes
+ * the &tty_struct->termios_rwsem to protect against parallel
+ * throttle/unthrottle and also to ensure the driver can consistently reference
+ * its own termios data at this point when implementing software flow control.
*
- * Drivers should however remember that the stack can issue a throttle,
- * then change flow control method, then unthrottle.
+ * Drivers should however remember that the stack can issue a throttle, then
+ * change flow control method, then unthrottle.
*/
-
void tty_unthrottle(struct tty_struct *tty)
{
down_write(&tty->termios_rwsem);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
tty->ops->unthrottle)
tty->ops->unthrottle(tty);
- tty->flow_change = 0;
+ tty->flow_change = TTY_FLOW_NO_CHANGE;
up_write(&tty->termios_rwsem);
}
EXPORT_SYMBOL(tty_unthrottle);
/**
- * tty_throttle_safe - flow control
- * @tty: terminal
+ * tty_throttle_safe - flow control
+ * @tty: terminal
*
- * Similar to tty_throttle() but will only attempt throttle
- * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental
- * throttle due to race conditions when throttling is conditional
- * on factors evaluated prior to throttling.
+ * Indicate that a @tty should stop transmitting data down the stack.
+ * tty_throttle_safe() will only attempt throttle if @tty->flow_change is
+ * %TTY_THROTTLE_SAFE. Prevents an accidental throttle due to race conditions
+ * when throttling is conditional on factors evaluated prior to throttling.
*
- * Returns 0 if tty is throttled (or was already throttled)
+ * Returns: %true if @tty is throttled (or was already throttled)
*/
-
-int tty_throttle_safe(struct tty_struct *tty)
+bool tty_throttle_safe(struct tty_struct *tty)
{
- int ret = 0;
+ guard(mutex)(&tty->throttle_mutex);
- mutex_lock(&tty->throttle_mutex);
- if (!tty_throttled(tty)) {
- if (tty->flow_change != TTY_THROTTLE_SAFE)
- ret = 1;
- else {
- set_bit(TTY_THROTTLED, &tty->flags);
- if (tty->ops->throttle)
- tty->ops->throttle(tty);
- }
- }
- mutex_unlock(&tty->throttle_mutex);
+ if (tty_throttled(tty))
+ return true;
- return ret;
+ if (tty->flow_change != TTY_THROTTLE_SAFE)
+ return false;
+
+ set_bit(TTY_THROTTLED, &tty->flags);
+ if (tty->ops->throttle)
+ tty->ops->throttle(tty);
+
+ return true;
}
/**
- * tty_unthrottle_safe - flow control
- * @tty: terminal
+ * tty_unthrottle_safe - flow control
+ * @tty: terminal
*
- * Similar to tty_unthrottle() but will only attempt unthrottle
- * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental
- * unthrottle due to race conditions when unthrottling is conditional
- * on factors evaluated prior to unthrottling.
+ * Similar to tty_unthrottle() but will only attempt unthrottle if
+ * @tty->flow_change is %TTY_UNTHROTTLE_SAFE. Prevents an accidental unthrottle
+ * due to race conditions when unthrottling is conditional on factors evaluated
+ * prior to unthrottling.
*
- * Returns 0 if tty is unthrottled (or was already unthrottled)
+ * Returns: %true if @tty is unthrottled (or was already unthrottled)
*/
-
-int tty_unthrottle_safe(struct tty_struct *tty)
+bool tty_unthrottle_safe(struct tty_struct *tty)
{
- int ret = 0;
+ guard(mutex)(&tty->throttle_mutex);
- mutex_lock(&tty->throttle_mutex);
- if (tty_throttled(tty)) {
- if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
- ret = 1;
- else {
- clear_bit(TTY_THROTTLED, &tty->flags);
- if (tty->ops->unthrottle)
- tty->ops->unthrottle(tty);
- }
- }
- mutex_unlock(&tty->throttle_mutex);
+ if (!tty_throttled(tty))
+ return true;
- return ret;
+ if (tty->flow_change != TTY_UNTHROTTLE_SAFE)
+ return false;
+
+ clear_bit(TTY_THROTTLED, &tty->flags);
+ if (tty->ops->unthrottle)
+ tty->ops->unthrottle(tty);
+
+ return true;
}
/**
- * tty_wait_until_sent - wait for I/O to finish
- * @tty: tty we are waiting for
- * @timeout: how long we will wait
+ * tty_wait_until_sent - wait for I/O to finish
+ * @tty: tty we are waiting for
+ * @timeout: how long we will wait
*
- * Wait for characters pending in a tty driver to hit the wire, or
- * for a timeout to occur (eg due to flow control)
+ * Wait for characters pending in a tty driver to hit the wire, or for a
+ * timeout to occur (eg due to flow control).
*
- * Locking: none
+ * Locking: none
*/
void tty_wait_until_sent(struct tty_struct *tty, long timeout)
{
- tty_debug_wait_until_sent(tty, "wait until sent, timeout=%ld\n", timeout);
-
if (!timeout)
timeout = MAX_SCHEDULE_TIMEOUT;
@@ -240,7 +199,7 @@ EXPORT_SYMBOL(tty_wait_until_sent);
* Termios Helper Methods
*/
-static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old)
+static void unset_locked_termios(struct tty_struct *tty, const struct ktermios *old)
{
struct ktermios *termios = &tty->termios;
struct ktermios *locked = &tty->termios_locked;
@@ -260,17 +219,16 @@ static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old)
}
/**
- * tty_termios_copy_hw - copy hardware settings
- * @new: New termios
- * @old: Old termios
+ * tty_termios_copy_hw - copy hardware settings
+ * @new: new termios
+ * @old: old termios
*
- * Propagate the hardware specific terminal setting bits from
- * the old termios structure to the new one. This is used in cases
- * where the hardware does not support reconfiguration or as a helper
- * in some cases where only minimal reconfiguration is supported
+ * Propagate the hardware specific terminal setting bits from the @old termios
+ * structure to the @new one. This is used in cases where the hardware does not
+ * support reconfiguration or as a helper in some cases where only minimal
+ * reconfiguration is supported.
*/
-
-void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old)
+void tty_termios_copy_hw(struct ktermios *new, const struct ktermios *old)
{
/* The bits a dumb device handles in software. Smart devices need
to always provide a set_termios method */
@@ -282,35 +240,82 @@ void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old)
EXPORT_SYMBOL(tty_termios_copy_hw);
/**
- * tty_termios_hw_change - check for setting change
- * @a: termios
- * @b: termios to compare
+ * tty_termios_hw_change - check for setting change
+ * @a: termios
+ * @b: termios to compare
+ *
+ * Check if any of the bits that affect a dumb device have changed between the
+ * two termios structures, or a speed change is needed.
*
- * Check if any of the bits that affect a dumb device have changed
- * between the two termios structures, or a speed change is needed.
+ * Returns: %true if change is needed
*/
-
-int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
+bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
{
if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed)
- return 1;
+ return true;
if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL))
- return 1;
- return 0;
+ return true;
+ return false;
}
EXPORT_SYMBOL(tty_termios_hw_change);
/**
- * tty_set_termios - update termios values
- * @tty: tty to update
- * @new_termios: desired new value
+ * tty_get_char_size - get size of a character
+ * @cflag: termios cflag value
*
- * Perform updates to the termios values set on this terminal.
- * A master pty's termios should never be set.
+ * Returns: size (in bits) of a character depending on @cflag's %CSIZE setting
+ */
+unsigned char tty_get_char_size(unsigned int cflag)
+{
+ switch (cflag & CSIZE) {
+ case CS5:
+ return 5;
+ case CS6:
+ return 6;
+ case CS7:
+ return 7;
+ case CS8:
+ default:
+ return 8;
+ }
+}
+EXPORT_SYMBOL_GPL(tty_get_char_size);
+
+/**
+ * tty_get_frame_size - get size of a frame
+ * @cflag: termios cflag value
+ *
+ * Get the size (in bits) of a frame depending on @cflag's %CSIZE, %CSTOPB, and
+ * %PARENB setting. The result is a sum of character size, start and stop bits
+ * -- one bit each -- second stop bit (if set), and parity bit (if set).
*
- * Locking: termios_rwsem
+ * Returns: size (in bits) of a frame depending on @cflag's setting.
*/
+unsigned char tty_get_frame_size(unsigned int cflag)
+{
+ unsigned char bits = 2 + tty_get_char_size(cflag);
+ if (cflag & CSTOPB)
+ bits++;
+ if (cflag & PARENB)
+ bits++;
+ if (cflag & ADDRB)
+ bits++;
+
+ return bits;
+}
+EXPORT_SYMBOL_GPL(tty_get_frame_size);
+
+/**
+ * tty_set_termios - update termios values
+ * @tty: tty to update
+ * @new_termios: desired new value
+ *
+ * Perform updates to the termios values set on this @tty. A master pty's
+ * termios should never be set.
+ *
+ * Locking: &tty_struct->termios_rwsem
+ */
int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
{
struct ktermios old_termios;
@@ -329,6 +334,8 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
old_termios = tty->termios;
tty->termios = *new_termios;
unset_locked_termios(tty, &old_termios);
+ /* Reset any ADDRB changes, ADDRB is changed through ->rs485_config() */
+ tty->termios.c_cflag ^= (tty->termios.c_cflag ^ old_termios.c_cflag) & ADDRB;
if (tty->ops->set_termios)
tty->ops->set_termios(tty, &old_termios);
@@ -346,19 +353,94 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
}
EXPORT_SYMBOL_GPL(tty_set_termios);
+
+/*
+ * Translate a "termio" structure into a "termios". Ugh.
+ */
+__weak int user_termio_to_kernel_termios(struct ktermios *termios,
+ struct termio __user *termio)
+{
+ struct termio v;
+
+ if (copy_from_user(&v, termio, sizeof(struct termio)))
+ return -EFAULT;
+
+ termios->c_iflag = (0xffff0000 & termios->c_iflag) | v.c_iflag;
+ termios->c_oflag = (0xffff0000 & termios->c_oflag) | v.c_oflag;
+ termios->c_cflag = (0xffff0000 & termios->c_cflag) | v.c_cflag;
+ termios->c_lflag = (0xffff0000 & termios->c_lflag) | v.c_lflag;
+ termios->c_line = (0xffff0000 & termios->c_lflag) | v.c_line;
+ memcpy(termios->c_cc, v.c_cc, NCC);
+ return 0;
+}
+
+/*
+ * Translate a "termios" structure into a "termio". Ugh.
+ */
+__weak int kernel_termios_to_user_termio(struct termio __user *termio,
+ struct ktermios *termios)
+{
+ struct termio v;
+ memset(&v, 0, sizeof(struct termio));
+ v.c_iflag = termios->c_iflag;
+ v.c_oflag = termios->c_oflag;
+ v.c_cflag = termios->c_cflag;
+ v.c_lflag = termios->c_lflag;
+ v.c_line = termios->c_line;
+ memcpy(v.c_cc, termios->c_cc, NCC);
+ return copy_to_user(termio, &v, sizeof(struct termio));
+}
+
+#ifdef TCGETS2
+__weak int user_termios_to_kernel_termios(struct ktermios *k,
+ struct termios2 __user *u)
+{
+ return copy_from_user(k, u, sizeof(struct termios2));
+}
+__weak int kernel_termios_to_user_termios(struct termios2 __user *u,
+ struct ktermios *k)
+{
+ return copy_to_user(u, k, sizeof(struct termios2));
+}
+__weak int user_termios_to_kernel_termios_1(struct ktermios *k,
+ struct termios __user *u)
+{
+ return copy_from_user(k, u, sizeof(struct termios));
+}
+__weak int kernel_termios_to_user_termios_1(struct termios __user *u,
+ struct ktermios *k)
+{
+ return copy_to_user(u, k, sizeof(struct termios));
+}
+
+#else
+
+__weak int user_termios_to_kernel_termios(struct ktermios *k,
+ struct termios __user *u)
+{
+ return copy_from_user(k, u, sizeof(struct termios));
+}
+__weak int kernel_termios_to_user_termios(struct termios __user *u,
+ struct ktermios *k)
+{
+ return copy_to_user(u, k, sizeof(struct termios));
+}
+#endif /* TCGETS2 */
+
/**
- * set_termios - set termios values for a tty
- * @tty: terminal device
- * @arg: user data
- * @opt: option information
+ * set_termios - set termios values for a tty
+ * @tty: terminal device
+ * @arg: user data
+ * @opt: option information
*
- * Helper function to prepare termios data and run necessary other
- * functions before using tty_set_termios to do the actual changes.
+ * Helper function to prepare termios data and run necessary other functions
+ * before using tty_set_termios() to do the actual changes.
*
- * Locking:
- * Called functions take ldisc and termios_rwsem locks
+ * Locking: called functions take &tty_struct->ldisc_sem and
+ * &tty_struct->termios_rwsem locks
+ *
+ * Returns: 0 on success, an error otherwise
*/
-
static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
{
struct ktermios tmp_termios;
@@ -397,21 +479,42 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios);
tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios);
- ld = tty_ldisc_ref(tty);
+ if (opt & (TERMIOS_FLUSH|TERMIOS_WAIT)) {
+retry_write_wait:
+ retval = wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty));
+ if (retval < 0)
+ return retval;
- if (ld != NULL) {
- if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
- ld->ops->flush_buffer(tty);
- tty_ldisc_deref(ld);
- }
+ if (tty_write_lock(tty, false) < 0)
+ goto retry_write_wait;
- if (opt & TERMIOS_WAIT) {
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -ERESTARTSYS;
- }
+ /* Racing writer? */
+ if (tty_chars_in_buffer(tty)) {
+ tty_write_unlock(tty);
+ goto retry_write_wait;
+ }
+
+ ld = tty_ldisc_ref(tty);
+ if (ld != NULL) {
+ if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
+ ld->ops->flush_buffer(tty);
+ tty_ldisc_deref(ld);
+ }
+
+ if ((opt & TERMIOS_WAIT) && tty->ops->wait_until_sent) {
+ tty->ops->wait_until_sent(tty, 0);
+ if (signal_pending(current)) {
+ tty_write_unlock(tty);
+ return -ERESTARTSYS;
+ }
+ }
+
+ tty_set_termios(tty, &tmp_termios);
- tty_set_termios(tty, &tmp_termios);
+ tty_write_unlock(tty);
+ } else {
+ tty_set_termios(tty, &tmp_termios);
+ }
/* FIXME: Arguably if tmp_termios == tty->termios AND the
actual requested termios was not tmp_termios then we may
@@ -443,51 +546,6 @@ static int get_termio(struct tty_struct *tty, struct termio __user *termio)
return 0;
}
-
-#ifdef TCGETX
-
-/**
- * set_termiox - set termiox fields if possible
- * @tty: terminal
- * @arg: termiox structure from user
- * @opt: option flags for ioctl type
- *
- * Implement the device calling points for the SYS5 termiox ioctl
- * interface in Linux
- */
-
-static int set_termiox(struct tty_struct *tty, void __user *arg, int opt)
-{
- struct termiox tnew;
- struct tty_ldisc *ld;
-
- if (tty->termiox == NULL)
- return -EINVAL;
- if (copy_from_user(&tnew, arg, sizeof(struct termiox)))
- return -EFAULT;
-
- ld = tty_ldisc_ref(tty);
- if (ld != NULL) {
- if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
- ld->ops->flush_buffer(tty);
- tty_ldisc_deref(ld);
- }
- if (opt & TERMIOS_WAIT) {
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -ERESTARTSYS;
- }
-
- down_write(&tty->termios_rwsem);
- if (tty->ops->set_termiox)
- tty->ops->set_termiox(tty, &tnew);
- up_write(&tty->termios_rwsem);
- return 0;
-}
-
-#endif
-
-
#ifdef TIOCGETP
/*
* These are deprecated, but there is limited support..
@@ -554,16 +612,16 @@ static void set_sgflags(struct ktermios *termios, int flags)
}
/**
- * set_sgttyb - set legacy terminal values
- * @tty: tty structure
- * @sgttyb: pointer to old style terminal structure
+ * set_sgttyb - set legacy terminal values
+ * @tty: tty structure
+ * @sgttyb: pointer to old style terminal structure
*
- * Updates a terminal from the legacy BSD style terminal information
- * structure.
+ * Updates a terminal from the legacy BSD style terminal information structure.
*
- * Locking: termios_rwsem
+ * Locking: &tty_struct->termios_rwsem
+ *
+ * Returns: 0 on success, an error otherwise
*/
-
static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
{
int retval;
@@ -583,10 +641,8 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
termios.c_cc[VKILL] = tmp.sg_kill;
set_sgflags(&termios, tmp.sg_flags);
/* Try and encode into Bfoo format */
-#ifdef BOTHER
tty_termios_encode_baud_rate(&termios, termios.c_ispeed,
termios.c_ospeed);
-#endif
up_write(&tty->termios_rwsem);
tty_set_termios(tty, &termios);
return 0;
@@ -667,19 +723,22 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars)
#endif
/**
- * tty_change_softcar - carrier change ioctl helper
- * @tty: tty to update
- * @arg: enable/disable CLOCAL
+ * tty_change_softcar - carrier change ioctl helper
+ * @tty: tty to update
+ * @enable: enable/disable %CLOCAL
+ *
+ * Perform a change to the %CLOCAL state and call into the driver layer to make
+ * it visible.
*
- * Perform a change to the CLOCAL state and call into the driver
- * layer to make it visible. All done with the termios rwsem
+ * Locking: &tty_struct->termios_rwsem.
+ *
+ * Returns: 0 on success, an error otherwise
*/
-
-static int tty_change_softcar(struct tty_struct *tty, int arg)
+static int tty_change_softcar(struct tty_struct *tty, bool enable)
{
int ret = 0;
- int bit = arg ? CLOCAL : 0;
struct ktermios old;
+ tcflag_t bit = enable ? CLOCAL : 0;
down_write(&tty->termios_rwsem);
old = tty->termios;
@@ -694,27 +753,22 @@ static int tty_change_softcar(struct tty_struct *tty, int arg)
}
/**
- * tty_mode_ioctl - mode related ioctls
- * @tty: tty for the ioctl
- * @file: file pointer for the tty
- * @cmd: command
- * @arg: ioctl argument
+ * tty_mode_ioctl - mode related ioctls
+ * @tty: tty for the ioctl
+ * @cmd: command
+ * @arg: ioctl argument
*
- * Perform non line discipline specific mode control ioctls. This
- * is designed to be called by line disciplines to ensure they provide
- * consistent mode setting.
+ * Perform non-line discipline specific mode control ioctls. This is designed
+ * to be called by line disciplines to ensure they provide consistent mode
+ * setting.
*/
-
-int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+int tty_mode_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
struct tty_struct *real_tty;
void __user *p = (void __user *)arg;
int ret = 0;
struct ktermios kterm;
- BUG_ON(file == NULL);
-
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
@@ -786,7 +840,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
ret = -EFAULT;
return ret;
case TIOCSLCKTRMIOS:
- if (!capable(CAP_SYS_ADMIN))
+ if (!checkpoint_restore_ns_capable(&init_user_ns))
return -EPERM;
copy_termios_locked(real_tty, &kterm);
if (user_termios_to_kernel_termios(&kterm,
@@ -803,7 +857,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
ret = -EFAULT;
return ret;
case TIOCSLCKTRMIOS:
- if (!capable(CAP_SYS_ADMIN))
+ if (!checkpoint_restore_ns_capable(&init_user_ns))
return -EPERM;
copy_termios_locked(real_tty, &kterm);
if (user_termios_to_kernel_termios_1(&kterm,
@@ -815,24 +869,12 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
return ret;
#endif
#ifdef TCGETX
- case TCGETX: {
- struct termiox ktermx;
- if (real_tty->termiox == NULL)
- return -EINVAL;
- down_read(&real_tty->termios_rwsem);
- memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox));
- up_read(&real_tty->termios_rwsem);
- if (copy_to_user(p, &ktermx, sizeof(struct termiox)))
- ret = -EFAULT;
- return ret;
- }
+ case TCGETX:
case TCSETX:
- return set_termiox(real_tty, p, 0);
case TCSETXW:
- return set_termiox(real_tty, p, TERMIOS_WAIT);
case TCSETXF:
- return set_termiox(real_tty, p, TERMIOS_FLUSH);
-#endif
+ return -ENOTTY;
+#endif
case TIOCGSOFTCAR:
copy_termios(real_tty, &kterm);
ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
@@ -866,7 +908,7 @@ static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg)
ld->ops->flush_buffer(tty);
tty_unthrottle(tty);
}
- /* fall through */
+ fallthrough;
case TCOFLUSH:
tty_driver_flush_buffer(tty);
break;
@@ -891,8 +933,8 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
}
EXPORT_SYMBOL_GPL(tty_perform_flush);
-int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+int n_tty_ioctl_helper(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg)
{
int retval;
@@ -903,20 +945,20 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
return retval;
switch (arg) {
case TCOOFF:
- spin_lock_irq(&tty->flow_lock);
- if (!tty->flow_stopped) {
- tty->flow_stopped = 1;
+ spin_lock_irq(&tty->flow.lock);
+ if (!tty->flow.tco_stopped) {
+ tty->flow.tco_stopped = true;
__stop_tty(tty);
}
- spin_unlock_irq(&tty->flow_lock);
+ spin_unlock_irq(&tty->flow.lock);
break;
case TCOON:
- spin_lock_irq(&tty->flow_lock);
- if (tty->flow_stopped) {
- tty->flow_stopped = 0;
+ spin_lock_irq(&tty->flow.lock);
+ if (tty->flow.tco_stopped) {
+ tty->flow.tco_stopped = false;
__start_tty(tty);
}
- spin_unlock_irq(&tty->flow_lock);
+ spin_unlock_irq(&tty->flow.lock);
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
@@ -937,7 +979,7 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
return __tty_perform_flush(tty, arg);
default:
/* Try the mode commands */
- return tty_mode_ioctl(tty, file, cmd, arg);
+ return tty_mode_ioctl(tty, cmd, arg);
}
}
EXPORT_SYMBOL(n_tty_ioctl_helper);