cpqarray: switch to seq_file
[safe/jmp/linux-2.6] / drivers / char / tty_ioctl.c
index 14cc19c..8e67d5c 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/module.h>
 #include <linux/bitops.h>
 #include <linux/mutex.h>
-#include <linux/smp_lock.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -97,14 +96,19 @@ EXPORT_SYMBOL(tty_driver_flush_buffer);
  *     @tty: terminal
  *
  *     Indicate that a tty should stop transmitting data down the stack.
+ *     Takes the termios mutex 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)
 {
+       mutex_lock(&tty->termios_mutex);
        /* 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);
+       mutex_unlock(&tty->termios_mutex);
 }
 EXPORT_SYMBOL(tty_throttle);
 
@@ -113,13 +117,21 @@ EXPORT_SYMBOL(tty_throttle);
  *     @tty: terminal
  *
  *     Indicate that a tty may continue transmitting data down the stack.
+ *     Takes the termios mutex 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.
  */
 
 void tty_unthrottle(struct tty_struct *tty)
 {
+       mutex_lock(&tty->termios_mutex);
        if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
            tty->ops->unthrottle)
                tty->ops->unthrottle(tty);
+       mutex_unlock(&tty->termios_mutex);
 }
 EXPORT_SYMBOL(tty_unthrottle);
 
@@ -381,9 +393,7 @@ void tty_termios_encode_baud_rate(struct ktermios *termios,
                termios->c_cflag |= (BOTHER << IBSHIFT);
 #else
        if (ifound == -1 || ofound == -1) {
-               static int warned;
-               if (!warned++)
-                       printk(KERN_WARNING "tty: Unable to return correct "
+               printk_once(KERN_WARNING "tty: Unable to return correct "
                          "speed data as your architecture needs updating.\n");
        }
 #endif
@@ -489,7 +499,6 @@ EXPORT_SYMBOL(tty_termios_hw_change);
 
 static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
 {
-       int canon_change;
        struct ktermios old_termios;
        struct tty_ldisc *ld;
        unsigned long flags;
@@ -505,18 +514,6 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
        old_termios = *tty->termios;
        *tty->termios = *new_termios;
        unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
-       canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
-       if (canon_change) {
-               memset(&tty->read_flags, 0, sizeof tty->read_flags);
-               tty->canon_head = tty->read_tail;
-               tty->canon_data = 0;
-               tty->erasing = 0;
-       }
-
-       /* This bit should be in the ldisc code */
-       if (canon_change && !L_ICANON(tty) && tty->read_cnt)
-               /* Get characters left over from canonical mode. */
-               wake_up_interruptible(&tty->read_wait);
 
        /* See if packet mode change of state. */
        if (tty->link && tty->link->packet) {
@@ -626,9 +623,25 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
        return 0;
 }
 
+static void copy_termios(struct tty_struct *tty, struct ktermios *kterm)
+{
+       mutex_lock(&tty->termios_mutex);
+       memcpy(kterm, tty->termios, sizeof(struct ktermios));
+       mutex_unlock(&tty->termios_mutex);
+}
+
+static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm)
+{
+       mutex_lock(&tty->termios_mutex);
+       memcpy(kterm, tty->termios_locked, sizeof(struct ktermios));
+       mutex_unlock(&tty->termios_mutex);
+}
+
 static int get_termio(struct tty_struct *tty, struct termio __user *termio)
 {
-       if (kernel_termios_to_user_termio(termio, tty->termios))
+       struct ktermios kterm;
+       copy_termios(tty, &kterm);
+       if (kernel_termios_to_user_termio(termio, &kterm))
                return -EFAULT;
        return 0;
 }
@@ -677,24 +690,6 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt)
 
 #endif
 
-static unsigned long inq_canon(struct tty_struct *tty)
-{
-       int nr, head, tail;
-
-       if (!tty->canon_data || !tty->read_buf)
-               return 0;
-       head = tty->canon_head;
-       tail = tty->read_tail;
-       nr = (head - tail) & (N_TTY_BUF_SIZE-1);
-       /* Skip EOF-chars.. */
-       while (head != tail) {
-               if (test_bit(tail, tty->read_flags) &&
-                   tty->read_buf[tail] == __DISABLED_CHAR)
-                       nr--;
-               tail = (tail+1) & (N_TTY_BUF_SIZE-1);
-       }
-       return nr;
-}
 
 #ifdef TIOCGETP
 /*
@@ -948,6 +943,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;
+       struct ktermios kterm;
 
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->driver->subtype == PTY_TYPE_MASTER)
@@ -983,23 +979,20 @@ 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))
+               copy_termios(real_tty, &kterm);
+               if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
                        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))
+               copy_termios(real_tty, &kterm);
+               if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
                        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))
+               copy_termios(real_tty, &kterm);
+               if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm))
                        ret = -EFAULT;
-               mutex_unlock(&real_tty->termios_mutex);
                return ret;
        case TCSETSF2:
                return set_termios(real_tty, p,  TERMIOS_FLUSH | TERMIOS_WAIT);
@@ -1018,46 +1011,51 @@ 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))
+               copy_termios_locked(real_tty, &kterm);
+               if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm))
                        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,
+               copy_termios_locked(real_tty, &kterm);
+               if (user_termios_to_kernel_termios(&kterm,
                                               (struct termios __user *) arg))
-                       ret = -EFAULT;
+                       return -EFAULT;
+               mutex_lock(&real_tty->termios_mutex);
+               memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios));
                mutex_unlock(&real_tty->termios_mutex);
-               return ret;
+               return 0;
 #else
        case TIOCGLCKTRMIOS:
-               mutex_lock(&real_tty->termios_mutex);
-               if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked))
+               copy_termios_locked(real_tty, &kterm);
+               if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm))
                        ret = -EFAULT;
-               mutex_unlock(&real_tty->termios_mutex);
                return ret;
        case TIOCSLCKTRMIOS:
                if (!capable(CAP_SYS_ADMIN))
-                       ret = -EPERM;
-               mutex_lock(&real_tty->termios_mutex);
-               if (user_termios_to_kernel_termios_1(real_tty->termios_locked,
+                       return -EPERM;
+               copy_termios_locked(real_tty, &kterm);
+               if (user_termios_to_kernel_termios_1(&kterm,
                                               (struct termios __user *) arg))
-                       ret = -EFAULT;
+                       return -EFAULT;
+               mutex_lock(&real_tty->termios_mutex);
+               memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios));
                mutex_unlock(&real_tty->termios_mutex);
                return ret;
 #endif
 #ifdef TCGETX
-       case TCGETX:
+       case TCGETX: {
+               struct termiox ktermx;
                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;
+               memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox));
                mutex_unlock(&real_tty->termios_mutex);
+               if (copy_to_user(p, &ktermx, sizeof(struct termiox)))
+                       ret = -EFAULT;
                return ret;
+       }
        case TCSETX:
                return set_termiox(real_tty, p, 0);
        case TCSETXW:
@@ -1066,10 +1064,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
                return set_termiox(real_tty, p, TERMIOS_FLUSH);
 #endif         
        case TIOCGSOFTCAR:
-               mutex_lock(&real_tty->termios_mutex);
-               ret = put_user(C_CLOCAL(real_tty) ? 1 : 0,
+               copy_termios(real_tty, &kterm);
+               ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0,
                                                (int __user *)arg);
-               mutex_unlock(&real_tty->termios_mutex);
                return ret;
        case TIOCSSOFTCAR:
                if (get_user(arg, (unsigned int __user *) arg))
@@ -1088,7 +1085,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
        if (retval)
                return retval;
 
-       ld = tty_ldisc_ref(tty);
+       ld = tty_ldisc_ref_wait(tty);
        switch (arg) {
        case TCIFLUSH:
                if (ld && ld->ops->flush_buffer)
@@ -1110,7 +1107,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg)
 }
 EXPORT_SYMBOL_GPL(tty_perform_flush);
 
-int n_tty_ioctl(struct tty_struct *tty, struct file *file,
+int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
                       unsigned int cmd, unsigned long arg)
 {
        unsigned long flags;
@@ -1148,13 +1145,6 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
                return 0;
        case TCFLSH:
                return tty_perform_flush(tty, arg);
-       case TIOCOUTQ:
-               return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
-       case TIOCINQ:
-               retval = tty->read_cnt;
-               if (L_ICANON(tty))
-                       retval = inq_canon(tty);
-               return put_user(retval, (unsigned int __user *) arg);
        case TIOCPKT:
        {
                int pktmode;
@@ -1180,4 +1170,4 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
                return tty_mode_ioctl(tty, file, cmd, arg);
        }
 }
-EXPORT_SYMBOL(n_tty_ioctl);
+EXPORT_SYMBOL(n_tty_ioctl_helper);