aboutsummaryrefslogtreecommitdiffstats
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.c262
1 files changed, 151 insertions, 111 deletions
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 9245fffdbceb..ce511557b98b 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -21,6 +21,8 @@
#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>
@@ -53,12 +55,11 @@
* 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);
@@ -73,7 +74,7 @@ EXPORT_SYMBOL(tty_chars_in_buffer);
* 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);
@@ -97,28 +98,6 @@ 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
*
@@ -146,10 +125,11 @@ EXPORT_SYMBOL(tty_unthrottle);
* 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)
*/
@@ -240,7 +220,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;
@@ -270,7 +250,7 @@ static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old)
* 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 */
@@ -301,6 +281,53 @@ int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
EXPORT_SYMBOL(tty_termios_hw_change);
/**
+ * tty_get_char_size - get size of a character
+ * @cflag: termios cflag value
+ *
+ * Get the 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).
+ */
+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
@@ -329,6 +356,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,6 +375,80 @@ 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
@@ -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..
@@ -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;
@@ -696,7 +752,6 @@ 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
*
@@ -705,16 +760,13 @@ static int tty_change_softcar(struct tty_struct *tty, int arg)
* 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;
@@ -815,24 +867,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 +906,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 +931,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 +943,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 +977,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);