n_tty: fix EXTPROC vs ICANON interaction with TIOCINQ (aka FIONREAD)
[pandora-kernel.git] / drivers / tty / n_tty.c
index 39d6ab6..304f58e 100644 (file)
@@ -1298,8 +1298,7 @@ handle_newline:
                        tty->canon_data++;
                        spin_unlock_irqrestore(&tty->read_lock, flags);
                        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
-                       if (waitqueue_active(&tty->read_wait))
-                               wake_up_interruptible(&tty->read_wait);
+                       wake_up_interruptible(&tty->read_wait);
                        return;
                }
        }
@@ -1422,8 +1421,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
        if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
                L_EXTPROC(tty)) {
                kill_fasync(&tty->fasync, SIGIO, POLL_IN);
-               if (waitqueue_active(&tty->read_wait))
-                       wake_up_interruptible(&tty->read_wait);
+               wake_up_interruptible(&tty->read_wait);
        }
 
        /*
@@ -1461,7 +1459,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
        BUG_ON(!tty);
 
        if (old)
-               canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON;
+               canon_change = (old->c_lflag ^ tty->termios->c_lflag) & (ICANON | EXTPROC);
        if (canon_change) {
                memset(&tty->read_flags, 0, sizeof tty->read_flags);
                tty->canon_head = tty->read_tail;
@@ -1530,6 +1528,14 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
                        tty->real_raw = 0;
        }
        n_tty_set_room(tty);
+       /*
+        * Fix tty hang when I_IXON(tty) is cleared, but the tty
+        * been stopped by STOP_CHAR(tty) before it.
+        */
+       if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) {
+               start_tty(tty);
+       }
+
        /* The termios change make the tty ready for I/O */
        wake_up_interruptible(&tty->write_wait);
        wake_up_interruptible(&tty->read_wait);
@@ -1728,7 +1734,8 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
 
 do_it_again:
 
-       BUG_ON(!tty->read_buf);
+       if (WARN_ON(!tty->read_buf))
+               return -EAGAIN;
 
        c = job_control(tty, file);
        if (c < 0)
@@ -1988,7 +1995,9 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
                                tty->ops->flush_chars(tty);
                } else {
                        while (nr > 0) {
+                               mutex_lock(&tty->output_lock);
                                c = tty->ops->write(tty, b, nr);
+                               mutex_unlock(&tty->output_lock);
                                if (c < 0) {
                                        retval = c;
                                        goto break_out;
@@ -2087,7 +2096,7 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
        case TIOCINQ:
                /* FIXME: Locking */
                retval = tty->read_cnt;
-               if (L_ICANON(tty))
+               if (L_ICANON(tty) && !L_EXTPROC(tty))
                        retval = inq_canon(tty);
                return put_user(retval, (unsigned int __user *) arg);
        default: