drm/radeon: 9800 SE has only one quadpipe
[safe/jmp/linux-2.6] / drivers / char / n_tty.c
index a9bc576..bdae832 100644 (file)
@@ -47,8 +47,9 @@
 #include <linux/bitops.h>
 #include <linux/audit.h>
 #include <linux/file.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
 
-#include <asm/uaccess.h>
 #include <asm/system.h>
 
 /* number of characters left in xmit buffer before select has we have room */
 #define ECHO_OP_SET_CANON_COL 0x81
 #define ECHO_OP_ERASE_TAB 0x82
 
-static inline unsigned char *alloc_buf(void)
-{
-       gfp_t prio = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
-
-       if (PAGE_SIZE != N_TTY_BUF_SIZE)
-               return kmalloc(N_TTY_BUF_SIZE, prio);
-       else
-               return (unsigned char *)__get_free_page(prio);
-}
-
-static inline void free_buf(unsigned char *buf)
-{
-       if (PAGE_SIZE != N_TTY_BUF_SIZE)
-               kfree(buf);
-       else
-               free_page((unsigned long) buf);
-}
-
 static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
                               unsigned char __user *ptr)
 {
@@ -290,7 +273,8 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty)
  *
  *     This is a helper function that handles one output character
  *     (including special characters like TAB, CR, LF, etc.),
- *     putting the results in the tty driver's write buffer.
+ *     doing OPOST processing and putting the results in the
+ *     tty driver's write buffer.
  *
  *     Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY
  *     and NLDLY.  They simply aren't relevant in the world today.
@@ -309,7 +293,7 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
 
        if (!space)
                return -1;
-       
+
        switch (c) {
        case '\n':
                if (O_ONLRET(tty))
@@ -318,8 +302,7 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
                        if (space < 2)
                                return -1;
                        tty->canon_column = tty->column = 0;
-                       tty_put_char(tty, '\r');
-                       tty_put_char(tty, c);
+                       tty->ops->write(tty, "\r\n", 2);
                        return 2;
                }
                tty->canon_column = tty->column;
@@ -351,10 +334,12 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
                        tty->column--;
                break;
        default:
-               if (O_OLCUC(tty))
-                       c = toupper(c);
-               if (!iscntrl(c) && !is_continuation(c, tty))
-                       tty->column++;
+               if (!iscntrl(c)) {
+                       if (O_OLCUC(tty))
+                               c = toupper(c);
+                       if (!is_continuation(c, tty))
+                               tty->column++;
+               }
                break;
        }
 
@@ -367,8 +352,9 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
  *     @c: character (or partial unicode symbol)
  *     @tty: terminal device
  *
- *     Perform OPOST processing.  Returns -1 when the output device is
- *     full and the character must be retried.
+ *     Output one character with OPOST processing.
+ *     Returns -1 when the output device is full and the character
+ *     must be retried.
  *
  *     Locking: output_lock to protect column state and space left
  *              (also, this is called from n_tty_write under the
@@ -394,8 +380,11 @@ static int process_output(unsigned char c, struct tty_struct *tty)
 /**
  *     process_output_block            -       block post processor
  *     @tty: terminal device
- *     @inbuf: user buffer
- *     @nr: number of bytes
+ *     @buf: character buffer
+ *     @nr: number of bytes to output
+ *
+ *     Output a block of characters with OPOST processing.
+ *     Returns the number of characters output.
  *
  *     This path is used to speed up block console writes, among other
  *     things when processing blocks of output data. It handles only
@@ -417,8 +406,7 @@ static ssize_t process_output_block(struct tty_struct *tty,
        mutex_lock(&tty->output_lock);
 
        space = tty_write_room(tty);
-       if (!space)
-       {
+       if (!space) {
                mutex_unlock(&tty->output_lock);
                return 0;
        }
@@ -426,7 +414,9 @@ static ssize_t process_output_block(struct tty_struct *tty,
                nr = space;
 
        for (i = 0, cp = buf; i < nr; i++, cp++) {
-               switch (*cp) {
+               unsigned char c = *cp;
+
+               switch (c) {
                case '\n':
                        if (O_ONLRET(tty))
                                tty->column = 0;
@@ -448,10 +438,12 @@ static ssize_t process_output_block(struct tty_struct *tty,
                                tty->column--;
                        break;
                default:
-                       if (O_OLCUC(tty))
-                               goto break_out;
-                       if (!iscntrl(*cp))
-                               tty->column++;
+                       if (!iscntrl(c)) {
+                               if (O_OLCUC(tty))
+                                       goto break_out;
+                               if (!is_continuation(c, tty))
+                                       tty->column++;
+                       }
                        break;
                }
        }
@@ -521,7 +513,7 @@ static void process_echoes(struct tty_struct *tty)
                        if (opp == buf_end)
                                opp -= N_TTY_BUF_SIZE;
                        op = *opp;
-                       
+
                        switch (op) {
                                unsigned int num_chars, num_bs;
 
@@ -585,33 +577,23 @@ static void process_echoes(struct tty_struct *tty)
                                break;
 
                        default:
-                               if (iscntrl(op)) {
-                                       if (L_ECHOCTL(tty)) {
-                                               /*
-                                                * Ensure there is enough space
-                                                * for the whole ctrl pair.
-                                                */
-                                               if (space < 2) {
-                                                       no_space_left = 1;
-                                                       break;
-                                               }
-                                               tty_put_char(tty, '^');
-                                               tty_put_char(tty, op ^ 0100);
-                                               tty->column += 2;
-                                               space -= 2;
-                                       } else {
-                                               if (!space) {
-                                                       no_space_left = 1;
-                                                       break;
-                                               }
-                                               tty_put_char(tty, op);
-                                               space--;
-                                       }
-                               }
                                /*
-                                * If above falls through, this was an
-                                * undefined op.
+                                * If the op is not a special byte code,
+                                * it is a ctrl char tagged to be echoed
+                                * as "^X" (where X is the letter
+                                * representing the control char).
+                                * Note that we must ensure there is
+                                * enough space for the whole ctrl pair.
+                                *
                                 */
+                               if (space < 2) {
+                                       no_space_left = 1;
+                                       break;
+                               }
+                               tty_put_char(tty, '^');
+                               tty_put_char(tty, op ^ 0100);
+                               tty->column += 2;
+                               space -= 2;
                                cp += 2;
                                nr -= 2;
                        }
@@ -619,11 +601,18 @@ static void process_echoes(struct tty_struct *tty)
                        if (no_space_left)
                                break;
                } else {
-                       int retval;
-
-                       if ((retval = do_output_char(c, tty, space)) < 0)
-                               break;
-                       space -= retval;
+                       if (O_OPOST(tty) &&
+                           !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
+                               int retval = do_output_char(c, tty, space);
+                               if (retval < 0)
+                                       break;
+                               space -= retval;
+                       } else {
+                               if (!space)
+                                       break;
+                               tty_put_char(tty, c);
+                               space -= 1;
+                       }
                        cp += 1;
                        nr -= 1;
                }
@@ -675,8 +664,7 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty)
                 * Since the buffer start position needs to be advanced,
                 * be sure to step by a whole operation byte group.
                 */
-               if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START)
-               {
+               if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) {
                        if (tty->echo_buf[(tty->echo_pos + 1) &
                                          (N_TTY_BUF_SIZE - 1)] ==
                                                ECHO_OP_ERASE_TAB) {
@@ -771,7 +759,7 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab,
        /* Set the high bit as a flag if num_chars is after a previous tab */
        if (after_tab)
                num_chars |= 0x80;
-       
+
        add_echo_byte(num_chars, tty);
 
        mutex_unlock(&tty->echo_lock);
@@ -812,8 +800,8 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty)
  *     Echo user input back onto the screen. This must be called only when
  *     L_ECHO(tty) is true. Called from the driver receive_buf path.
  *
- *     This variant tags control characters to be possibly echoed as
- *     as "^X" (where X is the letter representing the control char).
+ *     This variant tags control characters to be echoed as "^X"
+ *     (where X is the letter representing the control char).
  *
  *     Locking: echo_lock to protect the echo buffer
  */
@@ -826,7 +814,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty)
                add_echo_byte(ECHO_OP_START, tty);
                add_echo_byte(ECHO_OP_START, tty);
        } else {
-               if (iscntrl(c) && c != '\t')
+               if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
                        add_echo_byte(ECHO_OP_START, tty);
                add_echo_byte(c, tty);
        }
@@ -867,7 +855,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
 
        /* FIXME: locking needed ? */
        if (tty->read_head == tty->canon_head) {
-               /* echo_char_raw('\a', tty); */ /* what do you think? */
+               /* process_output('\a', tty); */ /* what do you think? */
                return;
        }
        if (c == ERASE_CHAR(tty))
@@ -959,8 +947,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
                                        if (c == '\t') {
                                                after_tab = 1;
                                                break;
-                                       }
-                                       else if (iscntrl(c)) {
+                                       } else if (iscntrl(c)) {
                                                if (L_ECHOCTL(tty))
                                                        num_chars += 2;
                                        } else if (!is_continuation(c, tty)) {
@@ -1103,6 +1090,7 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty,
 static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
 {
        unsigned long flags;
+       int parmrk;
 
        if (tty->raw) {
                put_tty_queue(c, tty);
@@ -1112,7 +1100,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
        if (I_ISTRIP(tty))
                c &= 0x7f;
        if (I_IUCLC(tty) && L_IEXTEN(tty))
-               c=tolower(c);
+               c = tolower(c);
 
        if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
            I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
@@ -1126,8 +1114,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
                        if (c == START_CHAR(tty)) {
                                start_tty(tty);
                                process_echoes(tty);
-                       }
-                       else if (c == STOP_CHAR(tty))
+                       } else if (c == STOP_CHAR(tty))
                                stop_tty(tty);
                }
                return;
@@ -1141,21 +1128,22 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
         */
        if (!test_bit(c, tty->process_char_map) || tty->lnext) {
                tty->lnext = 0;
+               parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
+               if (tty->read_cnt >= (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(tty);
-                       if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
-                               /* beep if no space */
-                               echo_char_raw('\a', tty);
-                               process_echoes(tty);
-                               return;
-                       }
                        /* Record the column of first canon char. */
                        if (tty->canon_head == tty->read_head)
                                echo_set_canon_col(tty);
                        echo_char(c, tty);
                        process_echoes(tty);
                }
-               if (I_PARMRK(tty) && c == (unsigned char) '\377')
+               if (parmrk)
                        put_tty_queue(c, tty);
                put_tty_queue(c, tty);
                return;
@@ -1247,15 +1235,20 @@ send_signal:
                        return;
                }
                if (c == '\n') {
+                       if (tty->read_cnt >= N_TTY_BUF_SIZE) {
+                               if (L_ECHO(tty))
+                                       process_output('\a', tty);
+                               return;
+                       }
                        if (L_ECHO(tty) || L_ECHONL(tty)) {
-                               if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
-                                       echo_char_raw('\a', tty);
                                echo_char_raw('\n', tty);
                                process_echoes(tty);
                        }
                        goto handle_newline;
                }
                if (c == EOF_CHAR(tty)) {
+                       if (tty->read_cnt >= N_TTY_BUF_SIZE)
+                               return;
                        if (tty->canon_head != tty->read_head)
                                set_bit(TTY_PUSH, &tty->flags);
                        c = __DISABLED_CHAR;
@@ -1263,12 +1256,17 @@ send_signal:
                }
                if ((c == EOL_CHAR(tty)) ||
                    (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
+                       parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty))
+                                ? 1 : 0;
+                       if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) {
+                               if (L_ECHO(tty))
+                                       process_output('\a', tty);
+                               return;
+                       }
                        /*
                         * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
                         */
                        if (L_ECHO(tty)) {
-                               if (tty->read_cnt >= N_TTY_BUF_SIZE-1)
-                                       echo_char_raw('\a', tty);
                                /* Record the column of first canon char. */
                                if (tty->canon_head == tty->read_head)
                                        echo_set_canon_col(tty);
@@ -1279,7 +1277,7 @@ send_signal:
                         * XXX does PARMRK doubling happen for
                         * EOL_CHAR and EOL2_CHAR?
                         */
-                       if (I_PARMRK(tty) && c == (unsigned char) '\377')
+                       if (parmrk)
                                put_tty_queue(c, tty);
 
 handle_newline:
@@ -1296,14 +1294,15 @@ handle_newline:
                }
        }
 
+       parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0;
+       if (tty->read_cnt >= (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(tty);
-               if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
-                       /* beep if no space */
-                       echo_char_raw('\a', tty);
-                       process_echoes(tty);
-                       return;
-               }
                if (c == '\n')
                        echo_char_raw('\n', tty);
                else {
@@ -1315,7 +1314,7 @@ handle_newline:
                process_echoes(tty);
        }
 
-       if (I_PARMRK(tty) && c == (unsigned char) '\377')
+       if (parmrk)
                put_tty_queue(c, tty);
 
        put_tty_queue(c, tty);
@@ -1333,13 +1332,8 @@ handle_newline:
 
 static void n_tty_write_wakeup(struct tty_struct *tty)
 {
-       /* Write out any echoed characters that are still pending */
-       process_echoes(tty);
-       
-       if (tty->fasync) {
-               set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+       if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
                kill_fasync(&tty->fasync, SIGIO, POLL_OUT);
-       }
 }
 
 /**
@@ -1544,11 +1538,11 @@ static void n_tty_close(struct tty_struct *tty)
 {
        n_tty_flush_buffer(tty);
        if (tty->read_buf) {
-               free_buf(tty->read_buf);
+               kfree(tty->read_buf);
                tty->read_buf = NULL;
        }
        if (tty->echo_buf) {
-               free_buf(tty->echo_buf);
+               kfree(tty->echo_buf);
                tty->echo_buf = NULL;
        }
 }
@@ -1570,17 +1564,16 @@ static int n_tty_open(struct tty_struct *tty)
 
        /* These are ugly. Currently a malloc failure here can panic */
        if (!tty->read_buf) {
-               tty->read_buf = alloc_buf();
+               tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
                if (!tty->read_buf)
                        return -ENOMEM;
        }
        if (!tty->echo_buf) {
-               tty->echo_buf = alloc_buf();
+               tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
+
                if (!tty->echo_buf)
                        return -ENOMEM;
        }
-       memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
-       memset(tty->echo_buf, 0, N_TTY_BUF_SIZE);
        reset_buffer_flags(tty);
        tty->column = 0;
        n_tty_set_termios(tty, NULL);
@@ -1591,6 +1584,7 @@ static int n_tty_open(struct tty_struct *tty)
 
 static inline int input_available_p(struct tty_struct *tty, int amt)
 {
+       tty_flush_to_ldisc(tty);
        if (tty->icanon) {
                if (tty->canon_data)
                        return 1;
@@ -1942,7 +1936,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
 
        /* Write out any echoed characters that are still pending */
        process_echoes(tty);
-       
+
        add_wait_queue(&tty->write_wait, &wait);
        while (1) {
                set_current_state(TASK_INTERRUPTIBLE);
@@ -1998,6 +1992,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
 break_out:
        __set_current_state(TASK_RUNNING);
        remove_wait_queue(&tty->write_wait, &wait);
+       if (b - buf != nr && tty->fasync)
+               set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
        return (b - buf) ? b - buf : retval;
 }
 
@@ -2096,3 +2092,19 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = {
        .receive_buf     = n_tty_receive_buf,
        .write_wakeup    = n_tty_write_wakeup
 };
+
+/**
+ *     n_tty_inherit_ops       -       inherit N_TTY methods
+ *     @ops: struct tty_ldisc_ops where to save N_TTY methods
+ *
+ *     Used by a generic struct tty_ldisc_ops to easily inherit N_TTY
+ *     methods.
+ */
+
+void n_tty_inherit_ops(struct tty_ldisc_ops *ops)
+{
+       *ops = tty_ldisc_N_TTY;
+       ops->owner = NULL;
+       ops->refcount = ops->flags = 0;
+}
+EXPORT_SYMBOL_GPL(n_tty_inherit_ops);