]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/char/tty_ioctl.c
tty: Termios locking - sort out real_tty confusions and lock reads
[linux-2.6-omap-h63xx.git] / drivers / char / tty_ioctl.c
index ea9fc5d03b991d924761a12a74b029546629d4a3..30670851e51aa0a7d2fc058f9187b78efe2d9827 100644 (file)
@@ -579,6 +579,50 @@ 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 -EINTR;
+       }
+
+       mutex_lock(&tty->termios_mutex);
+       if (tty->ops->set_termiox)
+               tty->ops->set_termiox(tty, &tnew);
+       mutex_unlock(&tty->termios_mutex);
+       return 0;
+}
+
+#endif
+
 static unsigned long inq_canon(struct tty_struct *tty)
 {
        int nr, head, tail;
@@ -849,6 +893,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
 {
        struct tty_struct *real_tty;
        void __user *p = (void __user *)arg;
+       int ret = 0;
 
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->driver->subtype == PTY_TYPE_MASTER)
@@ -884,18 +929,24 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
                return set_termios(real_tty, p, TERMIOS_OLD);
 #ifndef TCGETS2
        case TCGETS:
+               mutex_lock(&real_tty->termios_mutex);
                if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios))
-                       return -EFAULT;
-               return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
 #else
        case TCGETS:
+               mutex_lock(&real_tty->termios_mutex);
                if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios))
-                       return -EFAULT;
-               return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
        case TCGETS2:
+               mutex_lock(&real_tty->termios_mutex);
                if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios))
-                       return -EFAULT;
-               return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
        case TCSETSF2:
                return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT);
        case TCSETSW2:
@@ -913,36 +964,63 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
                return set_termios(real_tty, p, TERMIOS_TERMIO);
 #ifndef TCGETS2
        case TIOCGLCKTRMIOS:
+               mutex_lock(&real_tty->termios_mutex);
                if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked))
-                       return -EFAULT;
-               return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
        case TIOCSLCKTRMIOS:
                if (!capable(CAP_SYS_ADMIN))
                        return -EPERM;
+               mutex_lock(&real_tty->termios_mutex);
                if (user_termios_to_kernel_termios(real_tty->termios_locked,
                                               (struct termios __user *) arg))
-                       return -EFAULT;
-               return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
 #else
        case TIOCGLCKTRMIOS:
+               mutex_lock(&real_tty->termios_mutex);
                if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked))
-                       return -EFAULT;
-               return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
        case TIOCSLCKTRMIOS:
                if (!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
+                       ret = -EPERM;
+               mutex_lock(&real_tty->termios_mutex);
                if (user_termios_to_kernel_termios_1(real_tty->termios_locked,
                                               (struct termios __user *) arg))
-                       return -EFAULT;
-                       return 0;
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
 #endif
+#ifdef TCGETX
+       case TCGETX:
+               if (real_tty->termiox == NULL)
+                       return -EINVAL;
+               mutex_lock(&real_tty->termios_mutex);
+               if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox)))
+                       ret = -EFAULT;
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
+       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         
        case TIOCGSOFTCAR:
-               return put_user(C_CLOCAL(tty) ? 1 : 0,
+               mutex_lock(&real_tty->termios_mutex);
+               ret = put_user(C_CLOCAL(real_tty) ? 1 : 0,
                                                (int __user *)arg);
+               mutex_unlock(&real_tty->termios_mutex);
+               return ret;
        case TIOCSSOFTCAR:
                if (get_user(arg, (unsigned int __user *) arg))
                        return -EFAULT;
-               return tty_change_softcar(tty, arg);
+               return tty_change_softcar(real_tty, arg);
        default:
                return -ENOIOCTLCMD;
        }