aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/tty.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/tty.c')
-rw-r--r--sys/kern/tty.c604
1 files changed, 467 insertions, 137 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index faf8b4d4a1a4..519b910ccfdb 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -32,7 +32,7 @@
* SUCH DAMAGE.
*
* from: @(#)tty.c 7.44 (Berkeley) 5/28/91
- * $Id: tty.c,v 1.5 1993/10/16 15:24:54 rgrimes Exp $
+ * $Id: tty.c,v 1.19.2.1 1994/03/24 08:19:44 rgrimes Exp $
*/
#include "param.h"
@@ -49,18 +49,43 @@
#include "kernel.h"
#include "vnode.h"
#include "syslog.h"
-
+#include "signalvar.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
+ * contents of the ping-pong buffers and finishes when we would be able
+ * to fit that much plus 1/8 more.
+ */
+#define I_HIGH_WATER (TTYHOG - 2 * 256) /* XXX */
+#define I_LOW_WATER ((TTYHOG - 2 * 256) * 7 / 8) /* XXX */
+
+/* XXX RB_LEN() is too slow. */
+#define INPUT_LEN(tp) (RB_LEN(&(tp)->t_can) + RB_LEN(&(tp)->t_raw))
+#undef MAX_INPUT /* XXX wrong in <sys/syslimits.h> */
+#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 */
-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
@@ -138,6 +163,7 @@ char partab[] = {
#undef CR
extern struct tty *constty; /* temporary virtual console */
+extern int nldisp;
/*
* Is 'c' a line delimiter ("break" character)?
@@ -145,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)
+void
ttychars(tp)
struct tty *tp;
{
@@ -155,6 +182,7 @@ ttychars(tp)
/*
* Flush tty after output has drained.
*/
+int
ttywflush(tp)
struct tty *tp;
{
@@ -168,6 +196,7 @@ ttywflush(tp)
/*
* Wait for output to drain.
*/
+int
ttywait(tp)
register struct tty *tp;
{
@@ -176,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) {
+ /*
+ * 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_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;
}
+ if (tp->t_lowat == 0)
+ ttsetwater(tp);
splx(s);
return (error);
}
@@ -195,23 +245,26 @@ ttywait(tp)
* Flush TTY read and/or write queues,
* notifying anyone waiting.
*/
+void
ttyflush(tp, rw)
register struct tty *tp;
+ int rw;
{
- register s;
+ register int s;
s = spltty();
+ if (rw & FWRITE)
+ tp->t_state &= ~TS_TTSTOP;
+ (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw);
if (rw & FREAD) {
flushq(&tp->t_can);
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) {
- tp->t_state &= ~TS_TTSTOP;
- (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw);
flushq(&tp->t_out);
wakeup((caddr_t)&tp->t_out);
if (tp->t_wsel) {
@@ -220,38 +273,74 @@ ttyflush(tp, rw)
tp->t_state &= ~TS_WCOLL;
}
}
+ if (rw & FREAD) {
+ if (tp->t_state & (TS_TBLOCK | TS_HW_IFLOW)
+ && INPUT_LEN(tp) < I_LOW_WATER) {
+ int out_cc;
+ int t_state;
+
+ /*
+ * We delayed turning off input flow control in case
+ * we have to send a start character and the output
+ * queue was previously full. Sending a start char
+ * is still tricky because we don't want to add a
+ * new obstruction to draining the output queue.
+ */
+ out_cc = RB_LEN(&tp->t_out);
+ t_state = tp->t_state;
+ ttyunblock(tp);
+ tp->t_state &= ~TS_TBLOCK;
+ if (t_state & TS_TBLOCK && RB_LEN(&tp->t_out) != 0)
+ ttysleep(tp, (caddr_t)&tp->t_out, TTIPRI,
+ "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);
+ }
+ }
+ }
splx(s);
}
/*
- * Send stop character on input overflow.
+ * Handle input high water. Send stop character for the IXOFF case. Turn
+ * on all enabled input flow control bits and propagate the change to the
+ * driver.
*/
+static void
ttyblock(tp)
- register struct tty *tp;
+ struct tty *tp;
{
- register x;
- int rawcc, cancc;
- rawcc = RB_LEN(&tp->t_raw);
- cancc = RB_LEN(&tp->t_can);
- x = rawcc + cancc;
- if (rawcc > TTYHOG) {
- ttyflush(tp, FREAD|FWRITE);
- }
- /*
- * Block further input iff:
- * Current input > threshold AND input is available to user program
- */
- if (x >= TTYHOG/2 && (tp->t_state & TS_TBLOCK) == 0 &&
- ((tp->t_lflag&ICANON) == 0) || (cancc > 0)) {
- if (tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
- putc(tp->t_cc[VSTOP], &tp->t_out);
- }
- tp->t_state |= TS_TBLOCK; /* XXX - should be TS_RTSBLOCK? */
- ttstart(tp);
- }
+ if ((tp->t_state & TS_TBLOCK) == 0
+ && tp->t_cc[VSTOP] != _POSIX_VDISABLE
+ && putc(tp->t_cc[VSTOP], &tp->t_out) == 0)
+ tp->t_state |= TS_TBLOCK;
+ if (tp->t_cflag & CDTR_IFLOW)
+ tp->t_state |= TS_DTR_IFLOW;
+ if (tp->t_cflag & CRTS_IFLOW)
+ tp->t_state |= TS_RTS_IFLOW;
+ ttstart(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.
+ */
+static void
+ttyunblock(tp)
+ struct tty *tp;
+{
+
+ if (tp->t_state & TS_TBLOCK
+ && tp->t_cc[VSTART] != _POSIX_VDISABLE
+ && putc(tp->t_cc[VSTART], &tp->t_out) == 0)
+ tp->t_state &= ~TS_TBLOCK;
+ tp->t_state &= ~TS_HW_IFLOW;
+ ttstart(tp);
+}
+
+void
ttstart(tp)
struct tty *tp;
{
@@ -260,6 +349,8 @@ ttstart(tp)
(*tp->t_oproc)(tp);
}
+#ifdef notused
+void
ttrstrt(tp) /* XXX */
struct tty *tp;
{
@@ -271,6 +362,7 @@ ttrstrt(tp) /* XXX */
tp->t_state &= ~TS_TIMEOUT;
ttstart(tp);
}
+#endif /* notused */
/*
@@ -280,12 +372,14 @@ ttrstrt(tp) /* XXX */
* and/or reject any of these ioctl commands.
*/
/*ARGSUSED*/
+int
ttioctl(tp, com, data, flag)
register struct tty *tp;
+ int com;
caddr_t data;
+ int flag;
{
register struct proc *p = curproc; /* XXX */
- extern int nldisp;
int s, error;
/*
@@ -345,9 +439,9 @@ ttioctl(tp, com, data, 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) {
- (void)(*linesw[tp->t_line].l_open)(dev, tp);
+ (void)(*linesw[tp->t_line].l_open)(dev, tp, 0);
splx(s);
return (error);
}
@@ -462,6 +556,22 @@ ttioctl(tp, com, data, flag)
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) {
@@ -586,19 +696,25 @@ ttioctl(tp, com, data, flag)
return (0);
}
+int
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 & ICANON) == 0)
+ if ((tp->t_lflag & ICANON) == 0) {
nread += RB_LEN(&tp->t_raw);
+ if (nread < tp->t_cc[VMIN])
+ nread = 0;
+ }
return (nread);
}
+int
ttselect(dev, rw, p)
dev_t dev;
int rw;
@@ -641,9 +757,11 @@ win:
/*
* 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;
+ int dummy;
{
tp->t_dev = dev;
@@ -662,6 +780,7 @@ ttyopen(dev, tp)
/*
* "close" a line discipline
*/
+void
ttylclose(tp, flag)
struct tty *tp;
int flag;
@@ -678,6 +797,7 @@ ttylclose(tp, flag)
* bumping generation number so that pending read/write calls
* can detect recycling of the tty.
*/
+int
ttyclose(tp)
register struct tty *tp;
{
@@ -686,10 +806,6 @@ ttyclose(tp)
ttyflush(tp, FREAD|FWRITE);
tp->t_session = NULL;
tp->t_pgrp = NULL;
-/*
- * XXX - do we need to send cc[VSTART] or do a ttstart() here in some cases?
- * (TS_TBLOCK and TS_RTSBLOCK are being cleared.)
- */
tp->t_state = 0;
tp->t_gen++;
return (0);
@@ -700,8 +816,10 @@ ttyclose(tp)
* 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;
+ int flag;
{
if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_lflag&MDMBUF)) {
@@ -709,10 +827,10 @@ ttymodem(tp, flag)
* MDMBUF: do flow control according to carrier flag
*/
if (flag) {
- tp->t_state &= ~TS_TTSTOP;
+ tp->t_state &= ~TS_CAR_OFLOW;
ttstart(tp);
- } else if ((tp->t_state&TS_TTSTOP) == 0) {
- tp->t_state |= TS_TTSTOP;
+ } else if ((tp->t_state&TS_CAR_OFLOW) == 0) {
+ tp->t_state |= TS_CAR_OFLOW;
(*cdevsw[major(tp->t_dev)].d_stop)(tp, 0);
}
} else if (flag == 0) {
@@ -740,6 +858,7 @@ ttymodem(tp, flag)
* 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;
@@ -762,10 +881,10 @@ nullmodem(tp, flag)
* reinput pending characters after state switch
* call at spltty().
*/
+static void
ttypend(tp)
register struct tty *tp;
{
- register c;
char *hd, *tl;
tp->t_lflag &= ~PENDIN;
@@ -783,13 +902,14 @@ ttypend(tp)
/*
* Process input of a single character received on a tty.
*/
+void
ttyinput(c, tp)
- register c;
+ register int c;
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;
/*
@@ -809,6 +929,17 @@ ttyinput(c, tp)
tp->t_rawcc++;
}
/*
+ * If input flow control is enabled and not yet invoked, check the
+ * high water mark. Block further input iff:
+ * current input > threshold AND input is available to user program.
+ * The 3 is slop for PARMRK.
+ */
+ if ((iflag & IXOFF && (tp->t_state & TS_TBLOCK) == 0
+ || tp->t_cflag & TS_HW_IFLOW && (tp->t_state & TS_HW_IFLOW) == 0)
+ && INPUT_LEN(tp) > I_HIGH_WATER - 3
+ && ((lflag & ICANON) == 0 || RB_LEN(&tp->t_can) != 0))
+ ttyblock(tp);
+ /*
* Handle exceptional conditions (break, parity, framing).
*/
if (err = (c&TTY_ERRORMASK)) {
@@ -826,6 +957,8 @@ ttyinput(c, tp)
goto endcase;
else if (iflag&PARMRK) {
parmrk:
+ if (INPUT_LEN(tp) > MAX_INPUT - 3)
+ goto input_overflow;
putc(0377|TTY_QUOTE, &tp->t_raw);
putc(0|TTY_QUOTE, &tp->t_raw);
putc(c|TTY_QUOTE, &tp->t_raw);
@@ -834,11 +967,6 @@ parmrk:
c = 0;
}
}
- /*
- * In tandem mode, check high water mark.
- */
- if (iflag&IXOFF)
- ttyblock(tp);
if ((tp->t_state&TS_TYPEN) == 0 && (iflag&ISTRIP))
c &= ~0x80;
if ((tp->t_lflag&EXTPROC) == 0) {
@@ -878,7 +1006,7 @@ parmrk:
else {
ttyflush(tp, FWRITE);
ttyecho(c, tp);
- if (RB_LEN(&tp->t_raw) + RB_LEN(&tp->t_can))
+ if (INPUT_LEN(tp) != 0)
ttyretype(tp);
tp->t_lflag |= FLUSHO;
}
@@ -974,7 +1102,6 @@ parmrk:
*/
if (CCEQ(cc[VWERASE], c)) {
int ctype;
- int alt = lflag&ALTWERASE;
/*
* erase whitespace
@@ -991,6 +1118,13 @@ parmrk:
c = unputc(&tp->t_raw);
if (c == -1)
goto endcase;
+ /*
+ * Handle one-letter word cases.
+ */
+ if (c == ' ' || c == '\t') {
+ putc(c, &tp->t_raw);
+ goto endcase;
+ }
ctype = ISALPHA(c);
/*
* erase rest of word
@@ -1001,7 +1135,8 @@ parmrk:
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;
}
@@ -1025,12 +1160,13 @@ parmrk:
/*
* Check for input buffer overflow
*/
- if (RB_LEN(&tp->t_raw)+RB_LEN(&tp->t_can) >= TTYHOG) {
+ if (INPUT_LEN(tp) >= MAX_INPUT) {
+input_overflow:
if (iflag&IMAXBEL) {
if (RB_LEN(&tp->t_out) < tp->t_hiwat)
(void) ttyoutput(CTRL('g'), tp);
} else
- ttyflush(tp, FREAD | FWRITE);
+ ttyflush(tp, FREAD);
goto endcase;
}
/*
@@ -1062,7 +1198,7 @@ parmrk:
/*
* 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--;
@@ -1089,12 +1225,13 @@ startoutput:
* Returns < 0 if putc succeeds, otherwise returns char to resend.
* Must be recursive.
*/
+static int
ttyoutput(c, tp)
- register c;
+ register int c;
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)
@@ -1124,11 +1261,11 @@ ttyoutput(c, tp)
#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);
- 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) {
@@ -1189,16 +1326,20 @@ ttyoutput(c, tp)
/*
* Process a read call on a tty device.
*/
+int
ttread(tp, uio, flag)
register struct tty *tp;
struct uio *uio;
+ int flag;
{
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;
- 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;
@@ -1206,14 +1347,17 @@ loop:
/*
* take pending input first
*/
- if (lflag&PENDIN)
+ if (lflag&PENDIN) {
ttypend(tp);
- splx(s);
+ splx(s); /* reduce latency */
+ s = spltty();
+ }
/*
* 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)
@@ -1230,16 +1374,100 @@ loop:
* 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;
+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);
@@ -1249,20 +1477,70 @@ loop:
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);
+ if (slp) {
+ slp = 0;
+ untimeout((timeout_func_t)wakeup, (caddr_t)qp);
+ }
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;
}
- splx(s);
+read:
/*
* 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)
*/
@@ -1295,28 +1573,21 @@ loop:
*/
if (lflag&ICANON && ttbreakc(c))
break;
+ s = spltty();
first = 0;
}
+ s = spltty();
+
/*
- * Look to unblock output now that (presumably)
+ * Look to unblock input now that (presumably)
* the input queue has gone down.
*/
-#if 0
- if (tp->t_state&TS_TBLOCK && RB_LEN(&tp->t_raw) < TTYHOG/5) {
- if (cc[VSTART] != _POSIX_VDISABLE &&
- putc(cc[VSTART], &tp->t_out) == 0) {
- tp->t_state &= ~TS_TBLOCK;
- ttstart(tp);
- }
- }
-#else
-#define TS_RTSBLOCK TS_TBLOCK /* XXX */
-#define RB_I_LOW_WATER ((RBSZ - 2 * 256) * 7 / 8) /* XXX */
- if (tp->t_state&TS_RTSBLOCK && RB_LEN(&tp->t_raw) <= RB_I_LOW_WATER) {
- tp->t_state &= ~TS_RTSBLOCK;
- ttstart(tp);
- }
-#endif
+out:
+ if (tp->t_state & (TS_TBLOCK | TS_HW_IFLOW)
+ && INPUT_LEN(tp) <= I_LOW_WATER)
+ ttyunblock(tp);
+
+ splx(s);
return (error);
}
@@ -1328,12 +1599,12 @@ loop:
* 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;
- extern int wakeup();
hiwat = tp->t_hiwat;
s = spltty();
@@ -1348,9 +1619,10 @@ ttycheckoutq(tp, wait)
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;
- sleep((caddr_t)&tp->t_out, PZERO - 1);
+ tsleep((caddr_t)&tp->t_out, PZERO - 1, "ttchout", 0);
}
splx(s);
return (1);
@@ -1359,11 +1631,13 @@ ttycheckoutq(tp, wait)
/*
* Process a write call on a tty device.
*/
+int
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;
@@ -1420,14 +1694,27 @@ loop:
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;
+ }
+ splx(s);
/*
* 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) {
@@ -1459,7 +1746,6 @@ loop:
if (ttyoutput(*cp, tp) >= 0) {
/* no c-lists, wait a bit */
ttstart(tp);
-printf("\nttysleep - no c-lists\n"); /* XXX */
if (error = ttysleep(tp,
(caddr_t)&lbolt,
TTOPRI | PCATCH, ttybuf, 0))
@@ -1467,9 +1753,13 @@ printf("\nttysleep - no c-lists\n"); /* XXX */
goto loop;
}
cp++, cc--;
+ s = spltty();
if ((tp->t_lflag&FLUSHO) ||
- RB_LEN(&tp->t_out) > hiwat)
+ RB_LEN(&tp->t_out) > hiwat) {
+ splx(s);
goto ovhiwat;
+ }
+ splx(s);
continue;
}
}
@@ -1487,7 +1777,8 @@ printf("\nttysleep - no c-lists\n"); /* XXX */
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);
@@ -1495,30 +1786,41 @@ printf("\nttysleep - no c-lists\n"); /* XXX */
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;
}
+ splx(s);
#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 */
+ }
/* 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;
+ }
+ splx(s);
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;
+ }
+ splx(s);
}
ttstart(tp);
}
@@ -1562,8 +1864,9 @@ ovhiwat:
* Rubout one character from the rawq of tp
* as cleanly as possible.
*/
+static void
ttyrub(c, tp)
- register c;
+ register int c;
register struct tty *tp;
{
char *cp;
@@ -1650,6 +1953,7 @@ ttyrub(c, tp)
* Crt back over cnt chars perhaps
* erasing them.
*/
+static void
ttyrubo(tp, cnt)
register struct tty *tp;
int cnt;
@@ -1663,6 +1967,7 @@ ttyrubo(tp, cnt)
* Reprint the rawq line.
* We assume c_cc has already been checked.
*/
+static void
ttyretype(tp)
register struct tty *tp;
{
@@ -1690,20 +1995,17 @@ ttyretype(tp)
/*
* Echo a typed character to the terminal.
*/
+static void
ttyecho(c, tp)
- register c;
+ register int c;
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;
- 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) {
@@ -1721,6 +2023,7 @@ ttyecho(c, tp)
/*
* send string cp to tp
*/
+static void
ttyoutstr(cp, tp)
register char *cp;
register struct tty *tp;
@@ -1734,6 +2037,7 @@ ttyoutstr(cp, tp)
/*
* Wake up any readers on a tty.
*/
+void
ttwakeup(tp)
register struct tty *tp;
{
@@ -1752,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.
*/
+int
ttspeedtab(speed, table)
+ int speed;
register struct speedtab *table;
{
@@ -1769,6 +2075,7 @@ ttspeedtab(speed, table)
* from hi to low water.
*
*/
+void
ttsetwater(tp)
struct tty *tp;
{
@@ -1786,19 +2093,20 @@ ttsetwater(tp)
/*
* Report on state of foreground process group.
*/
+void
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. */
- 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");
@@ -1807,25 +2115,47 @@ ttyinfo(tp)
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;
- 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_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.
*/
- if (curproc == pick)
- tmp = splclock();
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 ",
@@ -1835,12 +2165,10 @@ ttyinfo(tp)
ttyprintf(tp, "%d.%02ds ",
stime.tv_sec, (stime.tv_usec + 5000) / 10000);
-#define pgtok(a) (((a) * NBPG) / 1024)
/* 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 */
}
@@ -1923,6 +2251,7 @@ proc_compare(p1, p2)
/*
* Output char to tty; console putchar style.
*/
+int
tputchar(c, tp)
int c;
struct tty *tp;
@@ -1947,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.
*/
+int
ttysleep(tp, chan, pri, wmesg, timo)
struct tty *tp;
caddr_t chan;
int pri;
- char *wmesg;
+ const char *wmesg;
int timo;
{
int error;
- short gen = tp->t_gen;
+ int gen = tp->t_gen;
if (error = tsleep(chan, pri, wmesg, timo))
return (error);