This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.1'.
[unix-history] / sys / kern / tty.c
index 002180b..f0cc16e 100644 (file)
@@ -32,7 +32,7 @@
  * SUCH DAMAGE.
  *
  *     from: @(#)tty.c 7.44 (Berkeley) 5/28/91
  * SUCH DAMAGE.
  *
  *     from: @(#)tty.c 7.44 (Berkeley) 5/28/91
- *     $Id: tty.c,v 1.7 1993/11/08 19:15:00 ache Exp $
+ *     $Id: tty.c,v 1.19 1994/02/13 17:21:31 ache Exp $
  */
 
 #include "param.h"
  */
 
 #include "param.h"
 #include "kernel.h"
 #include "vnode.h"
 #include "syslog.h"
 #include "kernel.h"
 #include "vnode.h"
 #include "syslog.h"
-
+#include "signalvar.h"
 #include "vm/vm.h"
 #include "vm/vm.h"
+#include "kinfo.h"
+#include "kinfo_proc.h"
+
 
 /*
  * Input control starts when we would not be able to fit the maximum
 
 /*
  * Input control starts when we would not be able to fit the maximum
 #define        MAX_INPUT       TTYHOG
 
 static int proc_compare __P((struct proc *p1, struct proc *p2));
 #define        MAX_INPUT       TTYHOG
 
 static int proc_compare __P((struct proc *p1, struct proc *p2));
+static void ttyblock __P((struct tty *tp));
+static void ttyecho __P((int c, struct tty *tp));
+static int ttyoutput __P((int c, register struct tty *tp));
+static void ttyoutstr __P((char *cp, struct tty *tp));
+static void ttypend __P((struct tty *tp));
+static void ttyretype __P((struct tty *tp));
+static void ttyrub __P((int c, struct tty *tp));
+static void ttyrubo __P((struct tty *tp, int cnt));
+static void ttyunblock __P((struct tty *tp));
 
 /* symbolic sleep message strings */
 
 /* symbolic sleep message strings */
-char ttyin[] = "ttyin";
-char ttyout[] = "ttyout";
-char ttopen[] = "ttyopn";
-char ttclos[] = "ttycls";
-char ttybg[] = "ttybg";
-char ttybuf[] = "ttybuf";
+const char ttyin[] = "ttyin";
+const char ttyout[] = "ttyout";
+const char ttopen[] = "ttyopn";
+const char ttclos[] = "ttycls";
+const char ttybg[] = "ttybg";
+const char ttybuf[] = "ttybuf";
 
 /*
  * Table giving parity for characters and indicating
 
 /*
  * Table giving parity for characters and indicating
@@ -151,6 +163,7 @@ char partab[] = {
 #undef CR
 
 extern struct tty *constty;            /* temporary virtual console */
 #undef CR
 
 extern struct tty *constty;            /* temporary virtual console */
+extern int nldisp;
 
 /*
  * Is 'c' a line delimiter ("break" character)?
 
 /*
  * Is 'c' a line delimiter ("break" character)?
@@ -158,6 +171,7 @@ extern struct tty *constty;         /* temporary virtual console */
 #define ttbreakc(c) ((c) == '\n' || ((c) == cc[VEOF] || \
        (c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)
 
 #define ttbreakc(c) ((c) == '\n' || ((c) == cc[VEOF] || \
        (c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)
 
+void
 ttychars(tp)
        struct tty *tp;
 {
 ttychars(tp)
        struct tty *tp;
 {
@@ -168,6 +182,7 @@ ttychars(tp)
 /*
  * Flush tty after output has drained.
  */
 /*
  * Flush tty after output has drained.
  */
+int
 ttywflush(tp)
        struct tty *tp;
 {
 ttywflush(tp)
        struct tty *tp;
 {
@@ -181,6 +196,7 @@ ttywflush(tp)
 /*
  * Wait for output to drain.
  */
 /*
  * Wait for output to drain.
  */
+int
 ttywait(tp)
        register struct tty *tp;
 {
 ttywait(tp)
        register struct tty *tp;
 {
@@ -189,12 +205,33 @@ ttywait(tp)
        while ((RB_LEN(&tp->t_out) || tp->t_state&TS_BUSY) &&
            (tp->t_state&TS_CARR_ON || tp->t_cflag&CLOCAL) && 
            tp->t_oproc) {
        while ((RB_LEN(&tp->t_out) || tp->t_state&TS_BUSY) &&
            (tp->t_state&TS_CARR_ON || tp->t_cflag&CLOCAL) && 
            tp->t_oproc) {
+               /*
+                * XXX temporary fix for deadlock.
+                *
+                * If two processes wait for output to drain from the same
+                * tty, and the amount of output to drain is > 0 and
+                * <= tp->t_lowat, then the processes will take turns
+                * uselessly waking each other up until the output drains,
+                * with cpl higher than spltty() throughout.
+                *
+                * The sleep address and TS_ASLEEP flag ought to be different
+                * for the different events (output done) and (output almost
+                * done).
+                */
+               tp->t_lowat = 0;
+
                (*tp->t_oproc)(tp);
                (*tp->t_oproc)(tp);
-               tp->t_state |= TS_ASLEEP;
-               if (error = ttysleep(tp, (caddr_t)&tp->t_out, 
-                   TTOPRI | PCATCH, ttyout, 0))
+               if ((RB_LEN(&tp->t_out) || tp->t_state&TS_BUSY) &&
+                   (tp->t_state&TS_CARR_ON || tp->t_cflag&CLOCAL)) {
+                       tp->t_state |= TS_ASLEEP;
+                       if (error = ttysleep(tp, (caddr_t)&tp->t_out,
+                           TTOPRI | PCATCH, "ttywai", 0))
+                               break;
+               } else
                        break;
        }
                        break;
        }
+       if (tp->t_lowat == 0)
+               ttsetwater(tp);
        splx(s);
        return (error);
 }
        splx(s);
        return (error);
 }
@@ -208,10 +245,12 @@ ttywait(tp)
  * Flush TTY read and/or write queues,
  * notifying anyone waiting.
  */
  * Flush TTY read and/or write queues,
  * notifying anyone waiting.
  */
+void
 ttyflush(tp, rw)
        register struct tty *tp;
 ttyflush(tp, rw)
        register struct tty *tp;
+       int rw;
 {
 {
-       register s;
+       register int s;
 
        s = spltty();
        if (rw & FWRITE)
 
        s = spltty();
        if (rw & FWRITE)
@@ -222,7 +261,7 @@ ttyflush(tp, rw)
                flushq(&tp->t_raw);
                tp->t_rocount = 0;
                tp->t_rocol = 0;
                flushq(&tp->t_raw);
                tp->t_rocount = 0;
                tp->t_rocol = 0;
-               tp->t_state &= ~(TS_LOCAL|TS_TBLOCK);   /* XXX - should be TS_RTSBLOCK */
+               tp->t_state &= ~TS_LOCAL;
                ttwakeup(tp);
        }
        if (rw & FWRITE) {
                ttwakeup(tp);
        }
        if (rw & FWRITE) {
@@ -253,7 +292,7 @@ ttyflush(tp, rw)
                        tp->t_state &= ~TS_TBLOCK;
                        if (t_state & TS_TBLOCK && RB_LEN(&tp->t_out) != 0)
                                ttysleep(tp, (caddr_t)&tp->t_out, TTIPRI,
                        tp->t_state &= ~TS_TBLOCK;
                        if (t_state & TS_TBLOCK && RB_LEN(&tp->t_out) != 0)
                                ttysleep(tp, (caddr_t)&tp->t_out, TTIPRI,
-                                        ttyout, hz / 10);
+                                        "ttyfls", hz / 10);
                        if (out_cc == 0 && RB_LEN(&tp->t_out) != 0) {
                                (*cdevsw[major(tp->t_dev)].d_stop)(tp, FWRITE);
                                flushq(&tp->t_out);
                        if (out_cc == 0 && RB_LEN(&tp->t_out) != 0) {
                                (*cdevsw[major(tp->t_dev)].d_stop)(tp, FWRITE);
                                flushq(&tp->t_out);
@@ -268,7 +307,7 @@ ttyflush(tp, rw)
  * on all enabled input flow control bits and propagate the change to the
  * driver.
  */
  * on all enabled input flow control bits and propagate the change to the
  * driver.
  */
-/* static void */
+static void
 ttyblock(tp)
        struct tty *tp;
 {
 ttyblock(tp)
        struct tty *tp;
 {
@@ -288,8 +327,7 @@ ttyblock(tp)
  * Handle input low water.  Send start character for the IXOFF case.  Turn
  * off our input flow control bits and propagate the change to the driver.
  */
  * Handle input low water.  Send start character for the IXOFF case.  Turn
  * off our input flow control bits and propagate the change to the driver.
  */
-/* static */
-int
+static void
 ttyunblock(tp)
        struct tty *tp;
 {
 ttyunblock(tp)
        struct tty *tp;
 {
@@ -302,6 +340,7 @@ ttyunblock(tp)
        ttstart(tp);
 }
 
        ttstart(tp);
 }
 
+void
 ttstart(tp)
        struct tty *tp;
 {
 ttstart(tp)
        struct tty *tp;
 {
@@ -310,6 +349,8 @@ ttstart(tp)
                (*tp->t_oproc)(tp);
 }
 
                (*tp->t_oproc)(tp);
 }
 
+#ifdef notused
+void
 ttrstrt(tp)                            /* XXX */
        struct tty *tp;
 {
 ttrstrt(tp)                            /* XXX */
        struct tty *tp;
 {
@@ -321,6 +362,7 @@ ttrstrt(tp)                         /* XXX */
        tp->t_state &= ~TS_TIMEOUT;
        ttstart(tp);
 }
        tp->t_state &= ~TS_TIMEOUT;
        ttstart(tp);
 }
+#endif /* notused */
 
 
 /*
 
 
 /*
@@ -330,12 +372,14 @@ ttrstrt(tp)                               /* XXX */
  * and/or reject any of these ioctl commands.
  */
 /*ARGSUSED*/
  * and/or reject any of these ioctl commands.
  */
 /*ARGSUSED*/
+int
 ttioctl(tp, com, data, flag)
        register struct tty *tp;
 ttioctl(tp, com, data, flag)
        register struct tty *tp;
+       int com;
        caddr_t data;
        caddr_t data;
+       int flag;
 {
        register struct proc *p = curproc;              /* XXX */
 {
        register struct proc *p = curproc;              /* XXX */
-       extern int nldisp;
        int s, error;
 
        /*
        int s, error;
 
        /*
@@ -395,9 +439,9 @@ ttioctl(tp, com, data, flag)
                if (t != tp->t_line) {
                        s = spltty();
                        (*linesw[tp->t_line].l_close)(tp, flag);
                if (t != tp->t_line) {
                        s = spltty();
                        (*linesw[tp->t_line].l_close)(tp, flag);
-                       error = (*linesw[t].l_open)(dev, tp);
+                       error = (*linesw[t].l_open)(dev, tp, 0);
                        if (error) {
                        if (error) {
-                               (void)(*linesw[tp->t_line].l_open)(dev, tp);
+                               (void)(*linesw[tp->t_line].l_open)(dev, tp, 0);
                                splx(s);
                                return (error);
                        }
                                splx(s);
                                return (error);
                        }
@@ -512,6 +556,22 @@ ttioctl(tp, com, data, flag)
                                splx(s);
                                return (error);
                        } else {
                                splx(s);
                                return (error);
                        } else {
+                               /*
+                                * XXX doubtful.  We mostly check both CLOCAL
+                                * and TS_CARR_ON before doing anything, and
+                                * changing TS_ISOPEN here just give another
+                                * flag to worry about, and is probably
+                                * inconsistent with not changing TS_ISOPEN
+                                * when carrier drops or CLOCAL rises.  OTOH
+                                * we should maintain a flag to keep track
+                                * of the combination of CLOCAL and TS_CARR_ON.
+                                * This could be just TS_CARR_ON (if we don't
+                                * need to 
+                                *
+                                * XXX ttselect() doesn't worry about
+                                * TS_ISOPEN, so it is inconsistent with
+                                * ttread() after TS_ISOPEN gets cleared here.
+                                */
                                if ((tp->t_state&TS_CARR_ON) == 0 &&
                                    (tp->t_cflag&CLOCAL) &&
                                    (t->c_cflag&CLOCAL) == 0) {
                                if ((tp->t_state&TS_CARR_ON) == 0 &&
                                    (tp->t_cflag&CLOCAL) &&
                                    (t->c_cflag&CLOCAL) == 0) {
@@ -636,19 +696,25 @@ ttioctl(tp, com, data, flag)
        return (0);
 }
 
        return (0);
 }
 
+int
 ttnread(tp)
        struct tty *tp;
 {
        int nread = 0;
 
 ttnread(tp)
        struct tty *tp;
 {
        int nread = 0;
 
+       /* XXX races. */
        if (tp->t_lflag & PENDIN)
                ttypend(tp);
        nread = RB_LEN(&tp->t_can);
        if (tp->t_lflag & PENDIN)
                ttypend(tp);
        nread = RB_LEN(&tp->t_can);
-       if ((tp->t_lflag & ICANON) == 0)
+       if ((tp->t_lflag & ICANON) == 0) {
                nread += RB_LEN(&tp->t_raw);
                nread += RB_LEN(&tp->t_raw);
+               if (nread < tp->t_cc[VMIN])
+                       nread = 0;
+       }
        return (nread);
 }
 
        return (nread);
 }
 
+int
 ttselect(dev, rw, p)
        dev_t dev;
        int rw;
 ttselect(dev, rw, p)
        dev_t dev;
        int rw;
@@ -691,9 +757,11 @@ win:
 /*
  * Initial open of tty, or (re)entry to standard tty line discipline.
  */
 /*
  * Initial open of tty, or (re)entry to standard tty line discipline.
  */
-ttyopen(dev, tp)
+int
+ttyopen(dev, tp, dummy)
        dev_t dev;
        register struct tty *tp;
        dev_t dev;
        register struct tty *tp;
+       int dummy;
 {
 
        tp->t_dev = dev;
 {
 
        tp->t_dev = dev;
@@ -712,6 +780,7 @@ ttyopen(dev, tp)
 /*
  * "close" a line discipline
  */
 /*
  * "close" a line discipline
  */
+void
 ttylclose(tp, flag)
        struct tty *tp;
        int flag;
 ttylclose(tp, flag)
        struct tty *tp;
        int flag;
@@ -728,6 +797,7 @@ ttylclose(tp, flag)
  * bumping generation number so that pending read/write calls
  * can detect recycling of the tty.
  */
  * bumping generation number so that pending read/write calls
  * can detect recycling of the tty.
  */
+int
 ttyclose(tp)
        register struct tty *tp;
 {
 ttyclose(tp)
        register struct tty *tp;
 {
@@ -746,8 +816,10 @@ ttyclose(tp)
  * Flag indicates new state of carrier.
  * Returns 0 if the line should be turned off, otherwise 1.
  */
  * Flag indicates new state of carrier.
  * Returns 0 if the line should be turned off, otherwise 1.
  */
+int
 ttymodem(tp, flag)
        register struct tty *tp;
 ttymodem(tp, flag)
        register struct tty *tp;
+       int flag;
 {
 
        if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_lflag&MDMBUF)) {
 {
 
        if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_lflag&MDMBUF)) {
@@ -786,6 +858,7 @@ ttymodem(tp, flag)
  * Default modem control routine (for other line disciplines).
  * Return argument flag, to turn off device on carrier drop.
  */
  * Default modem control routine (for other line disciplines).
  * Return argument flag, to turn off device on carrier drop.
  */
+int
 nullmodem(tp, flag)
        register struct tty *tp;
        int flag;
 nullmodem(tp, flag)
        register struct tty *tp;
        int flag;
@@ -808,10 +881,10 @@ nullmodem(tp, flag)
  * reinput pending characters after state switch
  * call at spltty().
  */
  * reinput pending characters after state switch
  * call at spltty().
  */
+static void
 ttypend(tp)
        register struct tty *tp;
 {
 ttypend(tp)
        register struct tty *tp;
 {
-       register c;
        char *hd, *tl;
 
        tp->t_lflag &= ~PENDIN;
        char *hd, *tl;
 
        tp->t_lflag &= ~PENDIN;
@@ -829,13 +902,14 @@ ttypend(tp)
 /*
  * Process input of a single character received on a tty.
  */
 /*
  * Process input of a single character received on a tty.
  */
+void
 ttyinput(c, tp)
 ttyinput(c, tp)
-       register c;
+       register int c;
        register struct tty *tp;
 {
        register struct tty *tp;
 {
-       register int iflag = tp->t_iflag;
-       register int lflag = tp->t_lflag;
-       register u_char *cc = tp->t_cc;
+       register tcflag_t iflag = tp->t_iflag;
+       register tcflag_t lflag = tp->t_lflag;
+       register cc_t *cc = tp->t_cc;
        int i, err;
 
        /*
        int i, err;
 
        /*
@@ -1028,7 +1102,6 @@ parmrk:
                 */
                if (CCEQ(cc[VWERASE], c)) {     
                        int ctype;
                 */
                if (CCEQ(cc[VWERASE], c)) {     
                        int ctype;
-                       int alt = lflag&ALTWERASE;
 
                        /* 
                         * erase whitespace 
 
                        /* 
                         * erase whitespace 
@@ -1062,7 +1135,8 @@ parmrk:
                                if (c == -1)
                                        goto endcase;
                        } while (c != ' ' && c != '\t' && 
                                if (c == -1)
                                        goto endcase;
                        } while (c != ' ' && c != '\t' && 
-                               (alt == 0 || ISALPHA(c) == ctype));
+                               ((lflag & ALTWERASE) == 0
+                                || ISALPHA(c) == ctype));
                        (void) putc(c, &tp->t_raw);
                        goto endcase;
                }
                        (void) putc(c, &tp->t_raw);
                        goto endcase;
                }
@@ -1124,7 +1198,7 @@ input_overflow:
                        /*
                         * Place the cursor over the '^' of the ^D.
                         */
                        /*
                         * Place the cursor over the '^' of the ^D.
                         */
-                       i = MIN(2, tp->t_col - i);
+                       i = imin(2, tp->t_col - i);
                        while (i > 0) {
                                (void) ttyoutput('\b', tp);
                                i--;
                        while (i > 0) {
                                (void) ttyoutput('\b', tp);
                                i--;
@@ -1151,12 +1225,13 @@ startoutput:
  * Returns < 0 if putc succeeds, otherwise returns char to resend.
  * Must be recursive.
  */
  * Returns < 0 if putc succeeds, otherwise returns char to resend.
  * Must be recursive.
  */
+static int
 ttyoutput(c, tp)
 ttyoutput(c, tp)
-       register c;
+       register int c;
        register struct tty *tp;
 {
        register int col;
        register struct tty *tp;
 {
        register int col;
-       register long oflag = tp->t_oflag;
+       register tcflag_t oflag = tp->t_oflag;
        
        if ((oflag&OPOST) == 0) {
                if (tp->t_lflag&FLUSHO) 
        
        if ((oflag&OPOST) == 0) {
                if (tp->t_lflag&FLUSHO) 
@@ -1186,11 +1261,11 @@ ttyoutput(c, tp)
 #ifdef was
                        c -= b_to_q("        ", c, &tp->t_outq);
 #else
 #ifdef was
                        c -= b_to_q("        ", c, &tp->t_outq);
 #else
-                       i = min (c, RB_CONTIGPUT(&tp->t_out));
+                       i = imin(c, RB_CONTIGPUT(&tp->t_out));
                        bcopy("        ", tp->t_out.rb_tl, i);
                        tp->t_out.rb_tl =
                                RB_ROLLOVER(&tp->t_out, tp->t_out.rb_tl+i);
                        bcopy("        ", tp->t_out.rb_tl, i);
                        tp->t_out.rb_tl =
                                RB_ROLLOVER(&tp->t_out, tp->t_out.rb_tl+i);
-                       i = min (c-i, RB_CONTIGPUT(&tp->t_out));
+                       i = imin(c - i, RB_CONTIGPUT(&tp->t_out));
 
                        /* off end and still have space? */
                        if (i) {
 
                        /* off end and still have space? */
                        if (i) {
@@ -1251,16 +1326,20 @@ ttyoutput(c, tp)
 /*
  * Process a read call on a tty device.
  */
 /*
  * Process a read call on a tty device.
  */
+int
 ttread(tp, uio, flag)
        register struct tty *tp;
        struct uio *uio;
 ttread(tp, uio, flag)
        register struct tty *tp;
        struct uio *uio;
+       int flag;
 {
        register struct ringb *qp;
        register int c;
 {
        register struct ringb *qp;
        register int c;
-       register long lflag;
-       register u_char *cc = tp->t_cc;
+       register tcflag_t lflag;
+       register cc_t *cc = tp->t_cc;
        register struct proc *p = curproc;
        register struct proc *p = curproc;
-       int s, first, error = 0;
+       int s, first, error = 0, rblen;
+       int has_stime = 0, last_cc = 0;
+       long slp = 0;           /* XXX this should be renamed `timo'. */
 
 loop:
        lflag = tp->t_lflag;
 
 loop:
        lflag = tp->t_lflag;
@@ -1268,14 +1347,17 @@ loop:
        /*
         * take pending input first 
         */
        /*
         * take pending input first 
         */
-       if (lflag&PENDIN)
+       if (lflag&PENDIN) {
                ttypend(tp);
                ttypend(tp);
-       splx(s);
+               splx(s);        /* reduce latency */
+               s = spltty();
+       }
 
        /*
         * Hang process if it's in the background.
         */
        if (isbackground(p, tp)) {
 
        /*
         * Hang process if it's in the background.
         */
        if (isbackground(p, tp)) {
+               splx(s);
                if ((p->p_sigignore & sigmask(SIGTTIN)) ||
                   (p->p_sigmask & sigmask(SIGTTIN)) ||
                    p->p_flag&SPPWAIT || p->p_pgrp->pg_jobc == 0)
                if ((p->p_sigignore & sigmask(SIGTTIN)) ||
                   (p->p_sigmask & sigmask(SIGTTIN)) ||
                    p->p_flag&SPPWAIT || p->p_pgrp->pg_jobc == 0)
@@ -1292,16 +1374,100 @@ loop:
         * else use the raw queue.
         */
        qp = lflag&ICANON ? &tp->t_can : &tp->t_raw;
         * else use the raw queue.
         */
        qp = lflag&ICANON ? &tp->t_can : &tp->t_raw;
+       rblen = RB_LEN(qp);
 
 
-       /*
-        * If there is no input, sleep on rawq
-        * awaiting hardware receipt and notification.
-        * If we have data, we don't need to check for carrier.
-        */
-       s = spltty();
-       if (RB_LEN(qp) <= 0) {
+       if ((lflag & ICANON) == 0) {
+               int m = cc[VMIN];
+               long t = cc[VTIME];
+               struct timeval stime, timecopy;
+               int x;
+
+               /*
+                * Check each of the four combinations.
+                * (m > 0 && t == 0) is the normal read case.
+                * It should be fairly efficient, so we check that and its
+                * companion case (m == 0 && t == 0) first.
+                * For the other two cases, we compute the target sleep time
+                * into slp.
+                */
+               if (t == 0) {
+                       if (rblen < m)
+                               goto sleep;
+                       if (rblen > 0)
+                               goto read;
+
+                       /* m, t and rblen are all 0.  0 is enough input. */
+                       splx(s);
+                       return (0);
+               }
+               t *= 100000;            /* time in us */
+#define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \
+                        ((t1).tv_usec - (t2).tv_usec))
+               if (m > 0) {
+                       if (rblen <= 0)
+                               goto sleep;
+                       if (rblen >= m)
+                               goto read;
+                       x = splclock();
+                       timecopy = time;
+                       splx(x);
+                       if (!has_stime) {
+                               /* first character, start timer */
+                               has_stime = 1;
+                               stime = timecopy;
+                               slp = t;
+                       } else if (rblen > last_cc) {
+                               /* got a character, restart timer */
+                               stime = timecopy;
+                               slp = t;
+                       } else {
+                               /* nothing, check expiration */
+                               slp = t - diff(timecopy, stime);
+                               if (slp <= 0)
+                                       goto read;
+                       }
+                       last_cc = rblen;
+               } else {        /* m == 0 */
+                       if (rblen > 0)
+                               goto read;
+                       x = splclock();
+                       timecopy = time;
+                       splx(x);
+                       if (!has_stime) {
+                               has_stime = 1;
+                               stime = timecopy;
+                               slp = t;
+                       } else {
+                               slp = t - diff(timecopy, stime);
+                               if (slp <= 0) {
+                                       /* Timed out, but 0 is enough input. */
+                                       splx(s);
+                                       return (0);
+                               }
+                       }
+               }
+#undef diff
+               /*
+                * Rounding down may make us wake up just short
+                * of the target, so we round up.
+                * The formula is ceiling(slp * hz/1000000).
+                * 32-bit arithmetic is enough for hz < 169.
+                * XXX see hzto() for how to avoid overflow if hz
+                * is large (divide by `tick' and/or arrange to
+                * use hzto() if hz is large).
+                */
+               slp = (long) (((u_long)slp * hz) + 999999) / 1000000;
+               goto sleep;
+       }
+       if (rblen <= 0) {
                int carrier;
 
                int carrier;
 
+sleep:
+               /*
+                * If there is no input, sleep on rawq
+                * awaiting hardware receipt and notification.
+                * If we have data, we don't need to check for carrier.
+                */
                carrier = (tp->t_state&TS_CARR_ON) || (tp->t_cflag&CLOCAL);
                if (!carrier && tp->t_state&TS_ISOPEN) {
                        splx(s);
                carrier = (tp->t_state&TS_CARR_ON) || (tp->t_cflag&CLOCAL);
                if (!carrier && tp->t_state&TS_ISOPEN) {
                        splx(s);
@@ -1311,20 +1477,70 @@ loop:
                        splx(s);
                        return (EWOULDBLOCK);
                }
                        splx(s);
                        return (EWOULDBLOCK);
                }
+               if (slp) {
+                       /*
+                        * Use plain wakeup() not ttwakeup().
+                        * XXX why not use the timeout built into tsleep?
+                        */
+                       timeout((timeout_func_t)wakeup, (caddr_t)qp, (int)slp);
+               }
                error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
                    carrier ? ttyin : ttopen, 0);
                error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH,
                    carrier ? ttyin : ttopen, 0);
+               if (slp) {
+                       slp = 0;
+                       untimeout((timeout_func_t)wakeup, (caddr_t)qp);
+               }
                splx(s);
                if (error)
                        return (error);
                splx(s);
                if (error)
                        return (error);
+               /*
+                * XXX what happens if ICANON, MIN or TIME changes or
+                * another process eats some input while we are asleep
+                * (not just here)?  It would be safest to detect changes
+                * and reset our state variables (has_stime and last_cc).
+                */
                goto loop;
        }
                goto loop;
        }
-       splx(s);
 
 
+read:
        /*
         * Input present, check for input mapping and processing.
         */
        first = 1;
        /*
         * Input present, check for input mapping and processing.
         */
        first = 1;
-       while ((c = getc(qp)) >= 0) {
+       if (lflag & (ICANON | ISIG))
+               goto slowcase;
+       for (;;) {
+               int rcc;
+
+               rcc = RB_CONTIGGET(qp);
+               if (rcc > uio->uio_resid)
+                       rcc = uio->uio_resid;
+               if (rcc <= 0) {
+                       if (first) {
+                               /* queue got flushed (can't happen) */
+                               splx(s);
+                               goto loop;
+                       }
+                       break;
+               }
+               error = uiomove(qp->rb_hd, rcc, uio);
+               if (error)
+                       break;
+               qp->rb_hd = RB_ROLLOVER(qp, qp->rb_hd + rcc);
+               splx(s);
+               s = spltty();
+               first = 0;
+       }
+       goto out;
+slowcase:
+       for (;;) {
+               c = getc(qp);
+               splx(s);
+               if (c < 0) {
+                       if (first)
+                               goto loop;
+                       break;
+               }
                /*
                 * delayed suspend (^Y)
                 */
                /*
                 * delayed suspend (^Y)
                 */
@@ -1357,16 +1573,21 @@ loop:
                 */
                if (lflag&ICANON && ttbreakc(c))
                        break;
                 */
                if (lflag&ICANON && ttbreakc(c))
                        break;
+               s = spltty();
                first = 0;
        }
                first = 0;
        }
+       s = spltty();
+
        /*
         * Look to unblock input now that (presumably)
         * the input queue has gone down.
         */
        /*
         * Look to unblock input now that (presumably)
         * the input queue has gone down.
         */
+out:
        if (tp->t_state & (TS_TBLOCK | TS_HW_IFLOW)
            && INPUT_LEN(tp) <= I_LOW_WATER)
                ttyunblock(tp);
 
        if (tp->t_state & (TS_TBLOCK | TS_HW_IFLOW)
            && INPUT_LEN(tp) <= I_LOW_WATER)
                ttyunblock(tp);
 
+       splx(s);
        return (error);
 }
 
        return (error);
 }
 
@@ -1378,12 +1599,12 @@ loop:
  * Sleeps here are not interruptible, but we return prematurely
  * if new signals come in.
  */
  * Sleeps here are not interruptible, but we return prematurely
  * if new signals come in.
  */
+int
 ttycheckoutq(tp, wait)
        register struct tty *tp;
        int wait;
 {
        int hiwat, s, oldsig;
 ttycheckoutq(tp, wait)
        register struct tty *tp;
        int wait;
 {
        int hiwat, s, oldsig;
-       extern int wakeup();
 
        hiwat = tp->t_hiwat;
        s = spltty();
 
        hiwat = tp->t_hiwat;
        s = spltty();
@@ -1398,7 +1619,8 @@ ttycheckoutq(tp, wait)
                                splx(s);
                                return (0);
                        }
                                splx(s);
                                return (0);
                        }
-                       timeout(wakeup, (caddr_t)&tp->t_out, hz);
+                       timeout((timeout_func_t)wakeup, (caddr_t)&tp->t_out,
+                               hz); /* XXX */
                        tp->t_state |= TS_ASLEEP;
                        tsleep((caddr_t)&tp->t_out, PZERO - 1, "ttchout", 0);
                }
                        tp->t_state |= TS_ASLEEP;
                        tsleep((caddr_t)&tp->t_out, PZERO - 1, "ttchout", 0);
                }
@@ -1409,11 +1631,13 @@ ttycheckoutq(tp, wait)
 /*
  * Process a write call on a tty device.
  */
 /*
  * Process a write call on a tty device.
  */
+int
 ttwrite(tp, uio, flag)
        register struct tty *tp;
        register struct uio *uio;
 ttwrite(tp, uio, flag)
        register struct tty *tp;
        register struct uio *uio;
+       int flag;
 {
 {
-       register char *cp;
+       register char *cp = NULL;
        register int cc = 0, ce;
        register struct proc *p = curproc;
        int i, hiwat, cnt, error, s;
        register int cc = 0, ce;
        register struct proc *p = curproc;
        int i, hiwat, cnt, error, s;
@@ -1470,14 +1694,27 @@ loop:
                        uio->uio_resid = 0;
                        return (0);
                }
                        uio->uio_resid = 0;
                        return (0);
                }
-               if (RB_LEN(&tp->t_out) > hiwat)
+               /*
+                * The output queue may be changed by the interrupt handler:
+                *      (1) certain inputs flush it
+                *      (2) sio hacks on it for output completion.
+                * The queue length always (?) shrinks so stale counts from
+                * RB_LEN() are not a problem.  However, incorrect counts
+                * caused by in-between pointers are a problem.  The locking
+                * to fix this is messy because of all the gotos.
+                */
+               s = spltty();
+               if (RB_LEN(&tp->t_out) > hiwat) {
+                       splx(s);
                        goto ovhiwat;
                        goto ovhiwat;
+               }
+               splx(s);
                /*
                 * Grab a hunk of data from the user,
                 * unless we have some leftover from last time.
                 */
                if (cc == 0) {
                /*
                 * Grab a hunk of data from the user,
                 * unless we have some leftover from last time.
                 */
                if (cc == 0) {
-                       cc = min(uio->uio_resid, OBUFSIZ);
+                       cc = imin(uio->uio_resid, OBUFSIZ);
                        cp = obuf;
                        error = uiomove(cp, cc, uio);
                        if (error) {
                        cp = obuf;
                        error = uiomove(cp, cc, uio);
                        if (error) {
@@ -1516,9 +1753,13 @@ loop:
                                            goto loop;
                                        }
                                        cp++, cc--;
                                            goto loop;
                                        }
                                        cp++, cc--;
+                                       s = spltty();
                                        if ((tp->t_lflag&FLUSHO) ||
                                        if ((tp->t_lflag&FLUSHO) ||
-                                           RB_LEN(&tp->t_out) > hiwat)
+                                           RB_LEN(&tp->t_out) > hiwat) {
+                                               splx(s);
                                                goto ovhiwat;
                                                goto ovhiwat;
+                                       }
+                                       splx(s);
                                        continue;
                                }
                        }
                                        continue;
                                }
                        }
@@ -1536,7 +1777,8 @@ loop:
                        ce -= i;
 #else
                        i = ce;
                        ce -= i;
 #else
                        i = ce;
-                       ce = min (ce, RB_CONTIGPUT(&tp->t_out));
+                       s = spltty();
+                       ce = imin(ce, RB_CONTIGPUT(&tp->t_out));
                        bcopy(cp, tp->t_out.rb_tl, ce);
                        tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
                                tp->t_out.rb_tl + ce);
                        bcopy(cp, tp->t_out.rb_tl, ce);
                        tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
                                tp->t_out.rb_tl + ce);
@@ -1544,30 +1786,41 @@ loop:
                        if (i > 0) {
                                int ii;
 
                        if (i > 0) {
                                int ii;
 
-                               ii = min (i, RB_CONTIGPUT(&tp->t_out));
+                               ii = imin(i, RB_CONTIGPUT(&tp->t_out));
                                bcopy(cp + ce, tp->t_out.rb_tl, ii);
                                tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
                                        tp->t_out.rb_tl + ii);
                                i -= ii;
                                ce += ii;
                        }
                                bcopy(cp + ce, tp->t_out.rb_tl, ii);
                                tp->t_out.rb_tl = RB_ROLLOVER(&tp->t_out,
                                        tp->t_out.rb_tl + ii);
                                i -= ii;
                                ce += ii;
                        }
+                       splx(s);
 #endif
                        tp->t_col += ce;
                        cp += ce, cc -= ce, tk_nout += ce;
                        tp->t_outcc += ce;
                        if (i > 0) {
                                ttstart(tp);
 #endif
                        tp->t_col += ce;
                        cp += ce, cc -= ce, tk_nout += ce;
                        tp->t_outcc += ce;
                        if (i > 0) {
                                ttstart(tp);
-                               if (RB_CONTIGPUT(&tp->t_out) > 0)
+                               s = spltty();
+                               if (RB_CONTIGPUT(&tp->t_out) > 0) {
+                                       splx(s);
                                        goto loop;      /* synchronous/fast */
                                        goto loop;      /* synchronous/fast */
+                               }
                                /* out of space, wait a bit */
                                tp->t_state |= TS_ASLEEP;
                                if (error = ttysleep(tp, (caddr_t)&tp->t_out,
                                /* out of space, wait a bit */
                                tp->t_state |= TS_ASLEEP;
                                if (error = ttysleep(tp, (caddr_t)&tp->t_out,
-                                           TTOPRI | PCATCH, ttybuf, 0))
+                                           TTOPRI | PCATCH, ttybuf, 0)) {
+                                       splx(s);
                                        break;
                                        break;
+                               }
+                               splx(s);
                                goto loop;
                        }
                                goto loop;
                        }
-                       if (tp->t_lflag&FLUSHO || RB_LEN(&tp->t_out) > hiwat)
+                       s = spltty();
+                       if (tp->t_lflag&FLUSHO || RB_LEN(&tp->t_out) > hiwat) {
+                               splx(s);
                                break;
                                break;
+                       }
+                       splx(s);
                }
                ttstart(tp);
        }
                }
                ttstart(tp);
        }
@@ -1611,8 +1864,9 @@ ovhiwat:
  * Rubout one character from the rawq of tp
  * as cleanly as possible.
  */
  * Rubout one character from the rawq of tp
  * as cleanly as possible.
  */
+static void
 ttyrub(c, tp)
 ttyrub(c, tp)
-       register c;
+       register int c;
        register struct tty *tp;
 {
        char *cp;
        register struct tty *tp;
 {
        char *cp;
@@ -1699,6 +1953,7 @@ ttyrub(c, tp)
  * Crt back over cnt chars perhaps
  * erasing them.
  */
  * Crt back over cnt chars perhaps
  * erasing them.
  */
+static void
 ttyrubo(tp, cnt)
        register struct tty *tp;
        int cnt;
 ttyrubo(tp, cnt)
        register struct tty *tp;
        int cnt;
@@ -1712,6 +1967,7 @@ ttyrubo(tp, cnt)
  * Reprint the rawq line.
  * We assume c_cc has already been checked.
  */
  * Reprint the rawq line.
  * We assume c_cc has already been checked.
  */
+static void
 ttyretype(tp)
        register struct tty *tp;
 {
 ttyretype(tp)
        register struct tty *tp;
 {
@@ -1739,20 +1995,17 @@ ttyretype(tp)
 /*
  * Echo a typed character to the terminal.
  */
 /*
  * Echo a typed character to the terminal.
  */
+static void
 ttyecho(c, tp)
 ttyecho(c, tp)
-       register c;
+       register int c;
        register struct tty *tp;
 {
        if ((tp->t_state & TS_CNTTB) == 0)
                tp->t_lflag &= ~FLUSHO;
        register struct tty *tp;
 {
        if ((tp->t_state & TS_CNTTB) == 0)
                tp->t_lflag &= ~FLUSHO;
-       if (tp->t_lflag & EXTPROC)
+       if (tp->t_lflag & EXTPROC
+           || (tp->t_lflag & ECHO) == 0
+               && (c != '\n' || (tp->t_lflag & ECHONL) == 0))
                return;
                return;
-       if ((tp->t_lflag & ECHO) == 0) {
-               if ((tp->t_lflag & ECHONL) == 0)
-                       return;
-               else if  (c != '\n')
-                       return;
-       }
        if (tp->t_lflag & ECHOCTL) {
                if ((c & TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' ||
                    c == 0177) {
        if (tp->t_lflag & ECHOCTL) {
                if ((c & TTY_CHARMASK) <= 037 && c != '\t' && c != '\n' ||
                    c == 0177) {
@@ -1770,6 +2023,7 @@ ttyecho(c, tp)
 /*
  * send string cp to tp
  */
 /*
  * send string cp to tp
  */
+static void
 ttyoutstr(cp, tp)
        register char *cp;
        register struct tty *tp;
 ttyoutstr(cp, tp)
        register char *cp;
        register struct tty *tp;
@@ -1783,6 +2037,7 @@ ttyoutstr(cp, tp)
 /*
  * Wake up any readers on a tty.
  */
 /*
  * Wake up any readers on a tty.
  */
+void
 ttwakeup(tp)
        register struct tty *tp;
 {
 ttwakeup(tp)
        register struct tty *tp;
 {
@@ -1801,7 +2056,9 @@ ttwakeup(tp)
  * Look up a code for a specified speed in a conversion table;
  * used by drivers to map software speed values to hardware parameters.
  */
  * Look up a code for a specified speed in a conversion table;
  * used by drivers to map software speed values to hardware parameters.
  */
+int
 ttspeedtab(speed, table)
 ttspeedtab(speed, table)
+       int speed;
        register struct speedtab *table;
 {
 
        register struct speedtab *table;
 {
 
@@ -1818,6 +2075,7 @@ ttspeedtab(speed, table)
  * from hi to low water.
  * 
  */
  * from hi to low water.
  * 
  */
+void
 ttsetwater(tp)
        struct tty *tp;
 {
 ttsetwater(tp)
        struct tty *tp;
 {
@@ -1835,19 +2093,20 @@ ttsetwater(tp)
 /*
  * Report on state of foreground process group.
  */
 /*
  * Report on state of foreground process group.
  */
+void
 ttyinfo(tp)
        register struct tty *tp;
 {
        register struct proc *p, *pick;
        struct timeval utime, stime;
 ttyinfo(tp)
        register struct tty *tp;
 {
        register struct proc *p, *pick;
        struct timeval utime, stime;
-       int tmp;
+       int loadtmp;
 
        if (ttycheckoutq(tp,0) == 0) 
                return;
 
        /* Print load average. */
 
        if (ttycheckoutq(tp,0) == 0) 
                return;
 
        /* Print load average. */
-       tmp = (averunnable[0] * 100 + FSCALE / 2) >> FSHIFT;
-       ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100);
+       loadtmp = (averunnable[0] * 100 + FSCALE / 2) >> FSHIFT;
+       ttyprintf(tp, "load: %d.%02d ", loadtmp / 100, loadtmp % 100);
 
        if (tp->t_session == NULL)
                ttyprintf(tp, "not a controlling terminal\n");
 
        if (tp->t_session == NULL)
                ttyprintf(tp, "not a controlling terminal\n");
@@ -1856,25 +2115,47 @@ ttyinfo(tp)
        else if ((p = tp->t_pgrp->pg_mem) == NULL)
                ttyprintf(tp, "empty foreground process group\n");
        else {
        else if ((p = tp->t_pgrp->pg_mem) == NULL)
                ttyprintf(tp, "empty foreground process group\n");
        else {
+               int kspace;
+               int s;
+               pid_t pid;
+               char wmesg[WMESGLEN+1];
+               char comm[MAXCOMLEN+1];
                /* Pick interesting process. */
                for (pick = NULL; p != NULL; p = p->p_pgrpnxt)
                        if (proc_compare(pick, p))
                                pick = p;
 
                /* Pick interesting process. */
                for (pick = NULL; p != NULL; p = p->p_pgrpnxt)
                        if (proc_compare(pick, p))
                                pick = p;
 
-               ttyprintf(tp, " cmd: %s %d [%s] ", pick->p_comm, pick->p_pid,
+               if( pick == NULL) {
+                       ttyprintf(tp, "process went away\n");
+                       goto finish;
+               }
+
+#define        pgtok(a)        (((a) * NBPG) / 1024)
+
+               if( (pick->p_flag & SLOAD) && (pick->p_vmspace)) 
+                       kspace = pgtok(pick->p_vmspace->vm_pmap.pm_stats.resident_count);
+               else
+                       kspace = 0;
+               strncpy(wmesg, ((pick->p_flag & SLOAD) == 0 ? "swapped":
                    pick->p_stat == SRUN ? "running" :
                    pick->p_stat == SRUN ? "running" :
-                   pick->p_wmesg ? pick->p_wmesg : "iowait");
+                   pick->p_wmesg ? pick->p_wmesg : "iowait"), WMESGLEN);
+               wmesg[WMESGLEN] = 0;
+
+               strncpy(comm, pick->p_comm ? pick->p_comm : "", MAXCOMLEN);
+               comm[MAXCOMLEN] = 0;
+
+               loadtmp = (pick->p_pctcpu * 10000 + FSCALE / 2) >> FSHIFT;
+
+               pid = pick->p_pid;
 
                /*
                 * Lock out clock if process is running; get user/system
                 * cpu time.
                 */
 
                /*
                 * Lock out clock if process is running; get user/system
                 * cpu time.
                 */
-               if (curproc == pick)
-                       tmp = splclock();
                utime = pick->p_utime;
                stime = pick->p_stime;
                utime = pick->p_utime;
                stime = pick->p_stime;
-               if (curproc == pick)
-                       splx(tmp);
+
+               ttyprintf(tp, " cmd: %s %d [%s] ", comm, pid, wmesg);
 
                /* Print user time. */
                ttyprintf(tp, "%d.%02du ",
 
                /* Print user time. */
                ttyprintf(tp, "%d.%02du ",
@@ -1884,12 +2165,10 @@ ttyinfo(tp)
                ttyprintf(tp, "%d.%02ds ",
                    stime.tv_sec, (stime.tv_usec + 5000) / 10000);
 
                ttyprintf(tp, "%d.%02ds ",
                    stime.tv_sec, (stime.tv_usec + 5000) / 10000);
 
-#define        pgtok(a)        (((a) * NBPG) / 1024)
                /* Print percentage cpu, resident set size. */
                /* Print percentage cpu, resident set size. */
-               tmp = pick->p_pctcpu * 10000 + FSCALE / 2 >> FSHIFT;
-               ttyprintf(tp, "%d%% %dk\n",
-                  tmp / 100, pgtok(pick->p_vmspace->vm_rssize));
+               ttyprintf(tp, "%d%% %dk\n", loadtmp / 100, kspace);
        }
        }
+finish:
        tp->t_rocount = 0;      /* so pending input will be retyped if BS */
 }
 
        tp->t_rocount = 0;      /* so pending input will be retyped if BS */
 }
 
@@ -1972,6 +2251,7 @@ proc_compare(p1, p2)
 /*
  * Output char to tty; console putchar style.
  */
 /*
  * Output char to tty; console putchar style.
  */
+int
 tputchar(c, tp)
        int c;
        struct tty *tp;
 tputchar(c, tp)
        int c;
        struct tty *tp;
@@ -1996,15 +2276,16 @@ tputchar(c, tp)
  * reported by tsleep.  If the tty is revoked, restarting a pending
  * call will redo validation done at the start of the call.
  */
  * reported by tsleep.  If the tty is revoked, restarting a pending
  * call will redo validation done at the start of the call.
  */
+int
 ttysleep(tp, chan, pri, wmesg, timo)
        struct tty *tp;
        caddr_t chan;
        int pri;
 ttysleep(tp, chan, pri, wmesg, timo)
        struct tty *tp;
        caddr_t chan;
        int pri;
-       char *wmesg;
+       const char *wmesg;
        int timo;
 {
        int error;
        int timo;
 {
        int error;
-       short gen = tp->t_gen;
+       int gen = tp->t_gen;
 
        if (error = tsleep(chan, pri, wmesg, timo))
                return (error);
 
        if (error = tsleep(chan, pri, wmesg, timo))
                return (error);