Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
[firefly-linux-kernel-4.4.55.git] / drivers / tty / n_tty.c
index 51cbdd22c18cec55b98de2a4e23b099f5a7a66cc..c9a9ddd1d0bc2aa5091d7e1678920df2d9292429 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
 
 
 /* number of characters left in xmit buffer before select has we have room */
@@ -92,7 +93,7 @@ struct n_tty_data {
        size_t canon_head;
        size_t echo_head;
        size_t echo_commit;
-       DECLARE_BITMAP(process_char_map, 256);
+       DECLARE_BITMAP(char_map, 256);
 
        /* private to n_tty_receive_overrun (single-threaded) */
        unsigned long overrun_time;
@@ -242,7 +243,7 @@ static void n_tty_write_wakeup(struct tty_struct *tty)
                kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
 }
 
-static inline void n_tty_check_throttle(struct tty_struct *tty)
+static void n_tty_check_throttle(struct tty_struct *tty)
 {
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY)
                return;
@@ -263,7 +264,7 @@ static inline void n_tty_check_throttle(struct tty_struct *tty)
        __tty_set_flow_change(tty, 0);
 }
 
-static inline void n_tty_check_unthrottle(struct tty_struct *tty)
+static void n_tty_check_unthrottle(struct tty_struct *tty)
 {
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) {
@@ -315,12 +316,9 @@ static inline void n_tty_check_unthrottle(struct tty_struct *tty)
  *     not active.
  */
 
-static void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
+static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
 {
-       if (read_cnt(ldata) < N_TTY_BUF_SIZE) {
-               *read_buf_addr(ldata, ldata->read_head) = c;
-               ldata->read_head++;
-       }
+       *read_buf_addr(ldata, ldata->read_head++) = c;
 }
 
 /**
@@ -1112,7 +1110,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
  *     Locking: ctrl_lock
  */
 
-static inline void isig(int sig, struct tty_struct *tty)
+static void isig(int sig, struct tty_struct *tty)
 {
        struct pid *tty_pgrp = tty_get_pgrp(tty);
        if (tty_pgrp) {
@@ -1135,7 +1133,7 @@ static inline void isig(int sig, struct tty_struct *tty)
  *     Note: may get exclusive termios_rwsem if flushing input buffer
  */
 
-static inline void n_tty_receive_break(struct tty_struct *tty)
+static void n_tty_receive_break(struct tty_struct *tty)
 {
        struct n_tty_data *ldata = tty->disc_data;
 
@@ -1173,7 +1171,7 @@ static inline void n_tty_receive_break(struct tty_struct *tty)
  *     private.
  */
 
-static inline void n_tty_receive_overrun(struct tty_struct *tty)
+static void n_tty_receive_overrun(struct tty_struct *tty)
 {
        struct n_tty_data *ldata = tty->disc_data;
        char buf[64];
@@ -1201,8 +1199,7 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty)
  *             caller holds non-exclusive termios_rwsem
  *             publishes read_head via put_tty_queue()
  */
-static inline void n_tty_receive_parity_error(struct tty_struct *tty,
-                                             unsigned char c)
+static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c)
 {
        struct n_tty_data *ldata = tty->disc_data;
 
@@ -1219,6 +1216,26 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
        wake_up_interruptible(&tty->read_wait);
 }
 
+static void
+n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
+{
+       if (!L_NOFLSH(tty)) {
+               /* flushing needs exclusive termios_rwsem */
+               up_read(&tty->termios_rwsem);
+               n_tty_flush_buffer(tty);
+               tty_driver_flush_buffer(tty);
+               down_read(&tty->termios_rwsem);
+       }
+       if (I_IXON(tty))
+               start_tty(tty);
+       if (L_ECHO(tty)) {
+               echo_char(c, tty);
+               commit_echoes(tty);
+       }
+       isig(signal, tty);
+       return;
+}
+
 /**
  *     n_tty_receive_char      -       perform processing
  *     @tty: terminal device
@@ -1232,119 +1249,49 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
  *             caller holds non-exclusive termios_rwsem
  *             publishes canon_head if canonical mode is active
  *             otherwise, publishes read_head via put_tty_queue()
+ *
+ *     Returns 1 if LNEXT was received, else returns 0
  */
 
-static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+static int
+n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
 {
        struct n_tty_data *ldata = tty->disc_data;
        int parmrk;
 
-       if (ldata->raw) {
-               put_tty_queue(c, ldata);
-               return;
-       }
-
-       if (I_ISTRIP(tty))
-               c &= 0x7f;
-       if (I_IUCLC(tty) && L_IEXTEN(tty))
-               c = tolower(c);
-
-       if (L_EXTPROC(tty)) {
-               put_tty_queue(c, ldata);
-               return;
-       }
-
-       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
-           I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
-           c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
-               start_tty(tty);
-               process_echoes(tty);
-       }
-
-       if (tty->closing) {
-               if (I_IXON(tty)) {
-                       if (c == START_CHAR(tty)) {
-                               start_tty(tty);
-                               process_echoes(tty);
-                       } else if (c == STOP_CHAR(tty))
-                               stop_tty(tty);
-               }
-               return;
-       }
-
-       /*
-        * If the previous character was LNEXT, or we know that this
-        * character is not one of the characters that we'll have to
-        * handle specially, do shortcut processing to speed things
-        * up.
-        */
-       if (!test_bit(c, ldata->process_char_map) || ldata->lnext) {
-               ldata->lnext = 0;
-               parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-               if (read_cnt(ldata) >= (N_TTY_BUF_SIZE - parmrk - 1)) {
-                       /* beep if no space */
-                       if (L_ECHO(tty))
-                               process_output('\a', tty);
-                       return;
-               }
-               if (L_ECHO(tty)) {
-                       finish_erasing(ldata);
-                       /* Record the column of first canon char. */
-                       if (ldata->canon_head == ldata->read_head)
-                               echo_set_canon_col(ldata);
-                       echo_char(c, tty);
-                       commit_echoes(tty);
-               }
-               if (parmrk)
-                       put_tty_queue(c, ldata);
-               put_tty_queue(c, ldata);
-               return;
-       }
-
        if (I_IXON(tty)) {
                if (c == START_CHAR(tty)) {
                        start_tty(tty);
                        commit_echoes(tty);
-                       return;
+                       return 0;
                }
                if (c == STOP_CHAR(tty)) {
                        stop_tty(tty);
-                       return;
+                       return 0;
                }
        }
 
        if (L_ISIG(tty)) {
-               int signal;
-               signal = SIGINT;
-               if (c == INTR_CHAR(tty))
-                       goto send_signal;
-               signal = SIGQUIT;
-               if (c == QUIT_CHAR(tty))
-                       goto send_signal;
-               signal = SIGTSTP;
-               if (c == SUSP_CHAR(tty)) {
-send_signal:
-                       if (!L_NOFLSH(tty)) {
-                               /* flushing needs exclusive termios_rwsem */
-                               up_read(&tty->termios_rwsem);
-                               n_tty_flush_buffer(tty);
-                               tty_driver_flush_buffer(tty);
-                               down_read(&tty->termios_rwsem);
-                       }
-                       if (I_IXON(tty))
-                               start_tty(tty);
-                       if (L_ECHO(tty)) {
-                               echo_char(c, tty);
-                               commit_echoes(tty);
-                       }
-                       isig(signal, tty);
-                       return;
+               if (c == INTR_CHAR(tty)) {
+                       n_tty_receive_signal_char(tty, SIGINT, c);
+                       return 0;
+               } else if (c == QUIT_CHAR(tty)) {
+                       n_tty_receive_signal_char(tty, SIGQUIT, c);
+                       return 0;
+               } else if (c == SUSP_CHAR(tty)) {
+                       n_tty_receive_signal_char(tty, SIGTSTP, c);
+                       return 0;
                }
        }
 
+       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+               start_tty(tty);
+               process_echoes(tty);
+       }
+
        if (c == '\r') {
                if (I_IGNCR(tty))
-                       return;
+                       return 0;
                if (I_ICRNL(tty))
                        c = '\n';
        } else if (c == '\n' && I_INLCR(tty))
@@ -1355,7 +1302,7 @@ send_signal:
                    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
                        eraser(c, tty);
                        commit_echoes(tty);
-                       return;
+                       return 0;
                }
                if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
                        ldata->lnext = 1;
@@ -1367,10 +1314,9 @@ send_signal:
                                        commit_echoes(tty);
                                }
                        }
-                       return;
+                       return 1;
                }
-               if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
-                   L_IEXTEN(tty)) {
+               if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) {
                        size_t tail = ldata->canon_head;
 
                        finish_erasing(ldata);
@@ -1381,14 +1327,9 @@ send_signal:
                                tail++;
                        }
                        commit_echoes(tty);
-                       return;
+                       return 0;
                }
                if (c == '\n') {
-                       if (read_cnt(ldata) >= N_TTY_BUF_SIZE) {
-                               if (L_ECHO(tty))
-                                       process_output('\a', tty);
-                               return;
-                       }
                        if (L_ECHO(tty) || L_ECHONL(tty)) {
                                echo_char_raw('\n', ldata);
                                commit_echoes(tty);
@@ -1396,8 +1337,6 @@ send_signal:
                        goto handle_newline;
                }
                if (c == EOF_CHAR(tty)) {
-                       if (read_cnt(ldata) >= N_TTY_BUF_SIZE)
-                               return;
                        c = __DISABLED_CHAR;
                        goto handle_newline;
                }
@@ -1405,11 +1344,6 @@ send_signal:
                    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
                        parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
                                 ? 1 : 0;
-                       if (read_cnt(ldata) >= (N_TTY_BUF_SIZE - parmrk)) {
-                               if (L_ECHO(tty))
-                                       process_output('\a', tty);
-                               return;
-                       }
                        /*
                         * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
                         */
@@ -1434,17 +1368,11 @@ handle_newline:
                        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
                        if (waitqueue_active(&tty->read_wait))
                                wake_up_interruptible(&tty->read_wait);
-                       return;
+                       return 0;
                }
        }
 
        parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
-       if (read_cnt(ldata) >= (N_TTY_BUF_SIZE - parmrk - 1)) {
-               /* beep if no space */
-               if (L_ECHO(tty))
-                       process_output('\a', tty);
-               return;
-       }
        if (L_ECHO(tty)) {
                finish_erasing(ldata);
                if (c == '\n')
@@ -1462,6 +1390,116 @@ handle_newline:
                put_tty_queue(c, ldata);
 
        put_tty_queue(c, ldata);
+       return 0;
+}
+
+static inline void
+n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       int parmrk;
+
+       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+               start_tty(tty);
+               process_echoes(tty);
+       }
+       if (L_ECHO(tty)) {
+               finish_erasing(ldata);
+               /* Record the column of first canon char. */
+               if (ldata->canon_head == ldata->read_head)
+                       echo_set_canon_col(ldata);
+               echo_char(c, tty);
+               commit_echoes(tty);
+       }
+       parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
+       if (parmrk)
+               put_tty_queue(c, ldata);
+       put_tty_queue(c, ldata);
+}
+
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+{
+       n_tty_receive_char_inline(tty, c);
+}
+
+static inline void
+n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+
+       if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) {
+               start_tty(tty);
+               process_echoes(tty);
+       }
+       if (L_ECHO(tty)) {
+               finish_erasing(ldata);
+               /* Record the column of first canon char. */
+               if (ldata->canon_head == ldata->read_head)
+                       echo_set_canon_col(ldata);
+               echo_char(c, tty);
+               commit_echoes(tty);
+       }
+       put_tty_queue(c, ldata);
+}
+
+static inline void
+n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c)
+{
+       if (I_ISTRIP(tty))
+               c &= 0x7f;
+       if (I_IUCLC(tty) && L_IEXTEN(tty))
+               c = tolower(c);
+
+       if (I_IXON(tty)) {
+               if (c == STOP_CHAR(tty))
+                       stop_tty(tty);
+               else if (c == START_CHAR(tty) ||
+                        (tty->stopped && !tty->flow_stopped && I_IXANY(tty) &&
+                         c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) &&
+                         c != SUSP_CHAR(tty))) {
+                       start_tty(tty);
+                       process_echoes(tty);
+               }
+       }
+}
+
+static void
+n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag)
+{
+       char buf[64];
+
+       switch (flag) {
+       case TTY_BREAK:
+               n_tty_receive_break(tty);
+               break;
+       case TTY_PARITY:
+       case TTY_FRAME:
+               n_tty_receive_parity_error(tty, c);
+               break;
+       case TTY_OVERRUN:
+               n_tty_receive_overrun(tty);
+               break;
+       default:
+               printk(KERN_ERR "%s: unknown flag %d\n",
+                      tty_name(tty, buf), flag);
+               break;
+       }
+}
+
+static void
+n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+
+       ldata->lnext = 0;
+       if (likely(flag == TTY_NORMAL)) {
+               if (I_ISTRIP(tty))
+                       c &= 0x7f;
+               if (I_IUCLC(tty) && L_IEXTEN(tty))
+                       c = tolower(c);
+               n_tty_receive_char(tty, c);
+       } else
+               n_tty_receive_char_flagged(tty, c, flag);
 }
 
 /**
@@ -1481,56 +1519,148 @@ handle_newline:
  *             publishes read_head and canon_head
  */
 
-static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
-                         char *fp, int count)
+static void
+n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp,
+                          char *fp, int count)
 {
        struct n_tty_data *ldata = tty->disc_data;
-       char    flags = TTY_NORMAL;
-       char    buf[64];
+       size_t n, head;
+
+       head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
+       n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
+       n = min_t(size_t, count, n);
+       memcpy(read_buf_addr(ldata, head), cp, n);
+       ldata->read_head += n;
+       cp += n;
+       count -= n;
+
+       head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
+       n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
+       n = min_t(size_t, count, n);
+       memcpy(read_buf_addr(ldata, head), cp, n);
+       ldata->read_head += n;
+}
 
-       if (ldata->real_raw) {
-               size_t n, head;
+static void
+n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp,
+                     char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       char flag = TTY_NORMAL;
 
-               head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
-               n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
-               n = min_t(size_t, count, n);
-               memcpy(read_buf_addr(ldata, head), cp, n);
-               ldata->read_head += n;
-               cp += n;
-               count -= n;
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL))
+                       put_tty_queue(*cp++, ldata);
+               else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
 
-               head = ldata->read_head & (N_TTY_BUF_SIZE - 1);
-               n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head);
-               n = min_t(size_t, count, n);
-               memcpy(read_buf_addr(ldata, head), cp, n);
-               ldata->read_head += n;
-       } else {
-               int i;
+static void
+n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp,
+                         char *fp, int count)
+{
+       char flag = TTY_NORMAL;
 
-               for (i = count; i; i--, cp++) {
-                       if (fp)
-                               flags = *fp++;
-                       switch (flags) {
-                       case TTY_NORMAL:
-                               n_tty_receive_char(tty, *cp);
-                               break;
-                       case TTY_BREAK:
-                               n_tty_receive_break(tty);
-                               break;
-                       case TTY_PARITY:
-                       case TTY_FRAME:
-                               n_tty_receive_parity_error(tty, *cp);
-                               break;
-                       case TTY_OVERRUN:
-                               n_tty_receive_overrun(tty);
-                               break;
-                       default:
-                               printk(KERN_ERR "%s: unknown flag %d\n",
-                                      tty_name(tty, buf), flags);
-                               break;
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL))
+                       n_tty_receive_char_closing(tty, *cp++);
+               else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
+static void
+n_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp,
+                         char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       char flag = TTY_NORMAL;
+
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL)) {
+                       unsigned char c = *cp++;
+
+                       if (I_ISTRIP(tty))
+                               c &= 0x7f;
+                       if (I_IUCLC(tty) && L_IEXTEN(tty))
+                               c = tolower(c);
+                       if (L_EXTPROC(tty)) {
+                               put_tty_queue(c, ldata);
+                               continue;
                        }
+                       if (!test_bit(c, ldata->char_map))
+                               n_tty_receive_char_inline(tty, c);
+                       else if (n_tty_receive_char_special(tty, c) && count) {
+                               if (fp)
+                                       flag = *fp++;
+                               n_tty_receive_char_lnext(tty, *cp++, flag);
+                               count--;
+                       }
+               } else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
+static void
+n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp,
+                      char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       char flag = TTY_NORMAL;
+
+       while (count--) {
+               if (fp)
+                       flag = *fp++;
+               if (likely(flag == TTY_NORMAL)) {
+                       unsigned char c = *cp++;
+
+                       if (!test_bit(c, ldata->char_map))
+                               n_tty_receive_char_fast(tty, c);
+                       else if (n_tty_receive_char_special(tty, c) && count) {
+                               if (fp)
+                                       flag = *fp++;
+                               n_tty_receive_char_lnext(tty, *cp++, flag);
+                               count--;
+                       }
+               } else
+                       n_tty_receive_char_flagged(tty, *cp++, flag);
+       }
+}
+
+static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
+                         char *fp, int count)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+       bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
+
+       if (ldata->real_raw)
+               n_tty_receive_buf_real_raw(tty, cp, fp, count);
+       else if (ldata->raw || (L_EXTPROC(tty) && !preops))
+               n_tty_receive_buf_raw(tty, cp, fp, count);
+       else if (tty->closing && !L_EXTPROC(tty))
+               n_tty_receive_buf_closing(tty, cp, fp, count);
+       else {
+               if (ldata->lnext) {
+                       char flag = TTY_NORMAL;
+
+                       if (fp)
+                               flag = *fp++;
+                       n_tty_receive_char_lnext(tty, *cp++, flag);
+                       count--;
                }
 
+               if (!preops && !I_PARMRK(tty))
+                       n_tty_receive_buf_fast(tty, cp, fp, count);
+               else
+                       n_tty_receive_buf_standard(tty, cp, fp, count);
+
                flush_echoes(tty);
                if (tty->ops->flush_chars)
                        tty->ops->flush_chars(tty);
@@ -1542,15 +1672,29 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
                if (waitqueue_active(&tty->read_wait))
                        wake_up_interruptible(&tty->read_wait);
        }
-
-       n_tty_check_throttle(tty);
 }
 
 static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
                              char *fp, int count)
 {
+       int room, n;
+
        down_read(&tty->termios_rwsem);
-       __receive_buf(tty, cp, fp, count);
+
+       while (1) {
+               room = receive_room(tty);
+               n = min(count, room);
+               if (!n)
+                       break;
+               __receive_buf(tty, cp, fp, n);
+               cp += n;
+               if (fp)
+                       fp += n;
+               count -= n;
+       }
+
+       tty->receive_room = room;
+       n_tty_check_throttle(tty);
        up_read(&tty->termios_rwsem);
 }
 
@@ -1558,20 +1702,31 @@ static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
                              char *fp, int count)
 {
        struct n_tty_data *ldata = tty->disc_data;
-       int room;
+       int room, n, rcvd = 0;
 
        down_read(&tty->termios_rwsem);
 
-       tty->receive_room = room = receive_room(tty);
-       if (!room)
-               ldata->no_room = 1;
-       count = min(count, room);
-       if (count)
-               __receive_buf(tty, cp, fp, count);
+       while (1) {
+               room = receive_room(tty);
+               n = min(count, room);
+               if (!n) {
+                       if (!room)
+                               ldata->no_room = 1;
+                       break;
+               }
+               __receive_buf(tty, cp, fp, n);
+               cp += n;
+               if (fp)
+                       fp += n;
+               count -= n;
+               rcvd += n;
+       }
 
+       tty->receive_room = room;
+       n_tty_check_throttle(tty);
        up_read(&tty->termios_rwsem);
 
-       return count;
+       return rcvd;
 }
 
 int is_ignored(int sig)
@@ -1618,41 +1773,38 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
            I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
            I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
            I_PARMRK(tty)) {
-               bitmap_zero(ldata->process_char_map, 256);
+               bitmap_zero(ldata->char_map, 256);
 
                if (I_IGNCR(tty) || I_ICRNL(tty))
-                       set_bit('\r', ldata->process_char_map);
+                       set_bit('\r', ldata->char_map);
                if (I_INLCR(tty))
-                       set_bit('\n', ldata->process_char_map);
+                       set_bit('\n', ldata->char_map);
 
                if (L_ICANON(tty)) {
-                       set_bit(ERASE_CHAR(tty), ldata->process_char_map);
-                       set_bit(KILL_CHAR(tty), ldata->process_char_map);
-                       set_bit(EOF_CHAR(tty), ldata->process_char_map);
-                       set_bit('\n', ldata->process_char_map);
-                       set_bit(EOL_CHAR(tty), ldata->process_char_map);
+                       set_bit(ERASE_CHAR(tty), ldata->char_map);
+                       set_bit(KILL_CHAR(tty), ldata->char_map);
+                       set_bit(EOF_CHAR(tty), ldata->char_map);
+                       set_bit('\n', ldata->char_map);
+                       set_bit(EOL_CHAR(tty), ldata->char_map);
                        if (L_IEXTEN(tty)) {
-                               set_bit(WERASE_CHAR(tty),
-                                       ldata->process_char_map);
-                               set_bit(LNEXT_CHAR(tty),
-                                       ldata->process_char_map);
-                               set_bit(EOL2_CHAR(tty),
-                                       ldata->process_char_map);
+                               set_bit(WERASE_CHAR(tty), ldata->char_map);
+                               set_bit(LNEXT_CHAR(tty), ldata->char_map);
+                               set_bit(EOL2_CHAR(tty), ldata->char_map);
                                if (L_ECHO(tty))
                                        set_bit(REPRINT_CHAR(tty),
-                                               ldata->process_char_map);
+                                               ldata->char_map);
                        }
                }
                if (I_IXON(tty)) {
-                       set_bit(START_CHAR(tty), ldata->process_char_map);
-                       set_bit(STOP_CHAR(tty), ldata->process_char_map);
+                       set_bit(START_CHAR(tty), ldata->char_map);
+                       set_bit(STOP_CHAR(tty), ldata->char_map);
                }
                if (L_ISIG(tty)) {
-                       set_bit(INTR_CHAR(tty), ldata->process_char_map);
-                       set_bit(QUIT_CHAR(tty), ldata->process_char_map);
-                       set_bit(SUSP_CHAR(tty), ldata->process_char_map);
+                       set_bit(INTR_CHAR(tty), ldata->char_map);
+                       set_bit(QUIT_CHAR(tty), ldata->char_map);
+                       set_bit(SUSP_CHAR(tty), ldata->char_map);
                }
-               clear_bit(__DISABLED_CHAR, ldata->process_char_map);
+               clear_bit(__DISABLED_CHAR, ldata->char_map);
                ldata->raw = 0;
                ldata->real_raw = 0;
        } else {
@@ -1970,6 +2122,17 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
        if (c < 0)
                return c;
 
+       /*
+        *      Internal serialization of reads.
+        */
+       if (file->f_flags & O_NONBLOCK) {
+               if (!mutex_trylock(&ldata->atomic_read_lock))
+                       return -EAGAIN;
+       } else {
+               if (mutex_lock_interruptible(&ldata->atomic_read_lock))
+                       return -ERESTARTSYS;
+       }
+
        down_read(&tty->termios_rwsem);
 
        minimum = time = 0;
@@ -1989,20 +2152,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
                }
        }
 
-       /*
-        *      Internal serialization of reads.
-        */
-       if (file->f_flags & O_NONBLOCK) {
-               if (!mutex_trylock(&ldata->atomic_read_lock)) {
-                       up_read(&tty->termios_rwsem);
-                       return -EAGAIN;
-               }
-       } else {
-               if (mutex_lock_interruptible(&ldata->atomic_read_lock)) {
-                       up_read(&tty->termios_rwsem);
-                       return -ERESTARTSYS;
-               }
-       }
        packet = tty->packet;
 
        add_wait_queue(&tty->read_wait, &wait);