X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/55116fbc752c3417ed4d4807d23f02fefe8db5af..c01ca9709f7728353e608a09b815a10dc93eaa73:/sys/kern/tty.c diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 002180bedc..0b2b715b01 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -32,7 +32,33 @@ * 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.28 1994/03/26 14:08:23 ache Exp $ + */ + +/*- + * TODO: + * o Fix or remove TS_WOPEN. It's only used for forcing a HUPCL + * when a port is closed without being fully opened (this is + * better decided by noticing if the close is a device close) + * and for distinguishing the first CD change from later ones + * for MDMBUF handling. TS_WOPEN is broken for multiple opens + * and for non-blocking opens. + * o Fix races in ttnread(). + * o Do ttymodem() and nullmodem() in the same order and with + * early returns instead of inconsistent elses. + * o Some or all of the t_out wakeups need to do a selwakeup() + * since ttselect() now blocks when ttwrite() would be blocked + * by lack of carrier. + * o Fix various flags races in sio. E.g., the CLOCAL locking is + * harmed by the wakeups for delta-CLOCAL, the carrier wait loop + * needs to start nearer the top of sioopen(), and the there may + * need to be a wakeup on t_raw in comhardclose() to kick other + * processes out of the wait loop. "take it from the top" code + * gives DTR glitch in usual case (!com->active). bidir open + * doesn't set TS_WOPEN and triggers ttymodem() warning. com-> + * active may help recover. bidir open doesn't set CLOCAL early + * enough. + * o Call suser() to log privileged CLOCAL changes. */ #include "param.h" @@ -47,10 +73,14 @@ #include "dkstat.h" #include "uio.h" #include "kernel.h" +#include "malloc.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 @@ -61,19 +91,28 @@ #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)) +#define INPUT_LEN(tp) (RB_LEN((tp)->t_can) + RB_LEN((tp)->t_raw)) #undef MAX_INPUT /* XXX wrong in */ #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 @@ -151,6 +190,7 @@ char partab[] = { #undef CR extern struct tty *constty; /* temporary virtual console */ +extern int nldisp; /* * Is 'c' a line delimiter ("break" character)? @@ -158,6 +198,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; { @@ -168,6 +209,7 @@ ttychars(tp) /* * Flush tty after output has drained. */ +int ttywflush(tp) struct tty *tp; { @@ -181,20 +223,41 @@ ttywflush(tp) /* * Wait for output to drain. */ +int ttywait(tp) register struct tty *tp; { int error = 0, s = spltty(); - 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) && + CAN_DO_IO(tp) && 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) && + CAN_DO_IO(tp)) { + 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); } @@ -208,26 +271,28 @@ 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); + 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) { - flushq(&tp->t_out); - wakeup((caddr_t)&tp->t_out); + flushq(tp->t_out); + wakeup((caddr_t)tp->t_out); if (tp->t_wsel) { selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); tp->t_wsel = 0; @@ -247,16 +312,16 @@ ttyflush(tp, rw) * 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); + 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, - ttyout, hz / 10); - if (out_cc == 0 && RB_LEN(&tp->t_out) != 0) { + 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); + flushq(tp->t_out); } } } @@ -268,14 +333,14 @@ ttyflush(tp, rw) * on all enabled input flow control bits and propagate the change to the * driver. */ -/* static void */ +static void ttyblock(tp) struct tty *tp; { if ((tp->t_state & TS_TBLOCK) == 0 && tp->t_cc[VSTOP] != _POSIX_VDISABLE - && putc(tp->t_cc[VSTOP], &tp->t_out) == 0) + && 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; @@ -288,20 +353,20 @@ 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. */ -/* static */ -int +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) + && 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; { @@ -310,6 +375,8 @@ ttstart(tp) (*tp->t_oproc)(tp); } +#ifdef notused +void ttrstrt(tp) /* XXX */ struct tty *tp; { @@ -321,6 +388,7 @@ ttrstrt(tp) /* XXX */ tp->t_state &= ~TS_TIMEOUT; ttstart(tp); } +#endif /* notused */ /* @@ -330,12 +398,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; /* @@ -395,9 +465,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); } @@ -450,7 +520,7 @@ ttioctl(tp, com, data, flag) break; case TIOCOUTQ: - *(int *)data = RB_LEN(&tp->t_out); + *(int *)data = RB_LEN(tp->t_out); break; case TIOCSTOP: @@ -505,25 +575,26 @@ ttioctl(tp, com, data, flag) ttyflush(tp, FREAD); } if ((t->c_cflag&CIGNORE) == 0) { + tcflag_t old_cflag; + /* * set device hardware */ if (tp->t_param && (error = (*tp->t_param)(tp, t))) { splx(s); return (error); - } else { - if ((tp->t_state&TS_CARR_ON) == 0 && - (tp->t_cflag&CLOCAL) && - (t->c_cflag&CLOCAL) == 0) { - tp->t_state &= ~TS_ISOPEN; - tp->t_state |= TS_WOPEN; - ttwakeup(tp); - } - tp->t_cflag = t->c_cflag; - tp->t_ispeed = t->c_ispeed; - tp->t_ospeed = t->c_ospeed; } + old_cflag = tp->t_cflag; + tp->t_cflag = t->c_cflag; + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; ttsetwater(tp); + if (tp->t_cflag & CLOCAL) + tp->t_state &= ~TS_ZOMBIE; + if ((tp->t_cflag ^ old_cflag) & CLOCAL) { + ttwakeup(tp); + wakeup((caddr_t)tp->t_out); + } } if (com != TIOCSETAF) { if ((t->c_lflag&ICANON) != (tp->t_lflag&ICANON)) @@ -532,8 +603,8 @@ ttioctl(tp, com, data, flag) ttwakeup(tp); } else { - catb(&tp->t_raw, &tp->t_can); - catb(&tp->t_can, &tp->t_raw); + catb(tp->t_raw, tp->t_can); + catb(tp->t_can, tp->t_raw); } } tp->t_iflag = t->c_iflag; @@ -580,7 +651,9 @@ ttioctl(tp, com, data, flag) case TIOCSPGRP: { register struct pgrp *pgrp = pgfind(*(int *)data); - if (!isctty(p, tp)) + if (!suser(p->p_ucred, &p->p_acflag)) + ; + else if (!isctty(p, tp)) return (ENOTTY); else if (pgrp == NULL || pgrp->pg_session != p->p_session) return (EPERM); @@ -608,9 +681,7 @@ ttioctl(tp, com, data, flag) case TIOCCONS: if (*(int *)data) { - if (constty && constty != tp && - (constty->t_state & (TS_CARR_ON|TS_ISOPEN)) == - (TS_CARR_ON|TS_ISOPEN)) + if (constty && constty != tp && CAN_DO_IO(constty)) return (EBUSY); #ifndef UCONSOLE if (error = suser(p->p_ucred, &p->p_acflag)) @@ -636,35 +707,38 @@ 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) - nread += RB_LEN(&tp->t_raw); + nread = RB_LEN(tp->t_can); + 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; struct proc *p; { - register struct tty *tp = &cdevsw[major(dev)].d_ttys[minor(dev)]; - int nread; + register struct tty *tp = cdevsw[major(dev)].d_ttys[minor(dev)]; int s = spltty(); struct proc *selp; switch (rw) { case FREAD: - nread = ttnread(tp); - if (nread > 0 || - ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0)) + if (ttnread(tp) > 0 || tp->t_state & TS_ZOMBIE) goto win; if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait) tp->t_state |= TS_RCOLL; @@ -673,7 +747,8 @@ ttselect(dev, rw, p) break; case FWRITE: - if (RB_LEN(&tp->t_out) <= tp->t_lowat) + if (RB_LEN(tp->t_out) <= tp->t_lowat && CAN_DO_IO(tp) + || tp->t_state & TS_ZOMBIE) goto win; if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait) tp->t_state |= TS_WCOLL; @@ -691,9 +766,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; @@ -701,9 +778,9 @@ ttyopen(dev, tp) tp->t_state &= ~TS_WOPEN; if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_state |= TS_ISOPEN; - initrb(&tp->t_raw); - initrb(&tp->t_can); - initrb(&tp->t_out); + initrb(tp->t_raw); + initrb(tp->t_can); + initrb(tp->t_out); bzero((caddr_t)&tp->t_winsize, sizeof(tp->t_winsize)); } return (0); @@ -712,6 +789,7 @@ ttyopen(dev, tp) /* * "close" a line discipline */ +void ttylclose(tp, flag) struct tty *tp; int flag; @@ -724,16 +802,16 @@ ttylclose(tp, flag) } /* - * Handle close() on a tty line: flush and set to initial state, + * Handle close() on a tty line: set to initial state, * bumping generation number so that pending read/write calls * can detect recycling of the tty. */ +int ttyclose(tp) register struct tty *tp; { if (constty == tp) constty = NULL; - ttyflush(tp, FREAD|FWRITE); tp->t_session = NULL; tp->t_pgrp = NULL; tp->t_state = 0; @@ -746,10 +824,16 @@ 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; { +#ifdef DIAGNOSTIC + if ((tp->t_state & (TS_ISOPEN | TS_WOPEN)) == 0) + printf("ttymodem: not open\n"); +#endif if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_lflag&MDMBUF)) { /* * MDMBUF: do flow control according to carrier flag @@ -767,10 +851,14 @@ ttymodem(tp, flag) */ tp->t_state &= ~TS_CARR_ON; if (tp->t_state&TS_ISOPEN && (tp->t_cflag&CLOCAL) == 0) { + tp->t_state |= TS_ZOMBIE; if (tp->t_session && tp->t_session->s_leader) psignal(tp->t_session->s_leader, SIGHUP); ttyflush(tp, FREAD|FWRITE); return (0); + } else { + wakeup((caddr_t)tp->t_raw); + wakeup((caddr_t)tp->t_out); } } else { /* @@ -778,6 +866,7 @@ ttymodem(tp, flag) */ tp->t_state |= TS_CARR_ON; ttwakeup(tp); + wakeup((caddr_t)tp->t_out); } return (1); } @@ -786,16 +875,22 @@ 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; { - + +#ifdef DIAGNOSTIC + if ((tp->t_state & (TS_ISOPEN | TS_WOPEN)) == 0) + printf("nullmodem: not open\n"); +#endif if (flag) tp->t_state |= TS_CARR_ON; else { tp->t_state &= ~TS_CARR_ON; - if ((tp->t_cflag&CLOCAL) == 0) { + if (tp->t_state&TS_ISOPEN && (tp->t_cflag&CLOCAL) == 0) { + tp->t_state |= TS_ZOMBIE; if (tp->t_session && tp->t_session->s_leader) psignal(tp->t_session->s_leader, SIGHUP); return (0); @@ -808,20 +903,20 @@ 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; tp->t_state |= TS_TYPEN; - hd = tp->t_raw.rb_hd; - tl = tp->t_raw.rb_tl; - flushq(&tp->t_raw); + hd = tp->t_raw->rb_hd; + tl = tp->t_raw->rb_tl; + flushq(tp->t_raw); while (hd != tl) { ttyinput(*hd, tp); - hd = RB_SUCC(&tp->t_raw, hd); + hd = RB_SUCC(tp->t_raw, hd); } tp->t_state &= ~TS_TYPEN; } @@ -829,13 +924,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; /* @@ -863,7 +959,7 @@ ttyinput(c, tp) 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)) + && ((lflag & ICANON) == 0 || RB_LEN(tp->t_can) != 0)) ttyblock(tp); /* * Handle exceptional conditions (break, parity, framing). @@ -885,9 +981,9 @@ ttyinput(c, tp) 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); + putc(0377|TTY_QUOTE, tp->t_raw); + putc(0|TTY_QUOTE, tp->t_raw); + putc(c|TTY_QUOTE, tp->t_raw); goto endcase; } else c = 0; @@ -1000,23 +1096,23 @@ parmrk: * erase (^H / ^?) */ if (CCEQ(cc[VERASE], c)) { - if (RB_LEN(&tp->t_raw)) - ttyrub(unputc(&tp->t_raw), tp); + if (RB_LEN(tp->t_raw)) + ttyrub(unputc(tp->t_raw), tp); goto endcase; } /* * kill (^U) */ if (CCEQ(cc[VKILL], c)) { - if (lflag&ECHOKE && RB_LEN(&tp->t_raw) == tp->t_rocount && + if (lflag&ECHOKE && RB_LEN(tp->t_raw) == tp->t_rocount && (lflag&ECHOPRT) == 0) { - while (RB_LEN(&tp->t_raw)) - ttyrub(unputc(&tp->t_raw), tp); + while (RB_LEN(tp->t_raw)) + ttyrub(unputc(tp->t_raw), tp); } else { ttyecho(c, tp); if (lflag&ECHOK || lflag&ECHOKE) ttyecho('\n', tp); - while (getc(&tp->t_raw) > 0) + while (getc(tp->t_raw) > 0) ; tp->t_rocount = 0; } @@ -1028,12 +1124,11 @@ parmrk: */ if (CCEQ(cc[VWERASE], c)) { int ctype; - int alt = lflag&ALTWERASE; /* * erase whitespace */ - while ((c = unputc(&tp->t_raw)) == ' ' || c == '\t') + while ((c = unputc(tp->t_raw)) == ' ' || c == '\t') ttyrub(c, tp); if (c == -1) goto endcase; @@ -1042,14 +1137,14 @@ parmrk: * next chars type (for ALTWERASE) */ ttyrub(c, tp); - c = unputc(&tp->t_raw); + c = unputc(tp->t_raw); if (c == -1) goto endcase; /* * Handle one-letter word cases. */ if (c == ' ' || c == '\t') { - putc(c, &tp->t_raw); + putc(c, tp->t_raw); goto endcase; } ctype = ISALPHA(c); @@ -1058,12 +1153,13 @@ parmrk: */ do { ttyrub(c, tp); - c = unputc(&tp->t_raw); + c = unputc(tp->t_raw); if (c == -1) goto endcase; } while (c != ' ' && c != '\t' && - (alt == 0 || ISALPHA(c) == ctype)); - (void) putc(c, &tp->t_raw); + ((lflag & ALTWERASE) == 0 + || ISALPHA(c) == ctype)); + (void) putc(c, tp->t_raw); goto endcase; } /* @@ -1089,7 +1185,7 @@ parmrk: if (INPUT_LEN(tp) >= MAX_INPUT) { input_overflow: if (iflag&IMAXBEL) { - if (RB_LEN(&tp->t_out) < tp->t_hiwat) + if (RB_LEN(tp->t_out) < tp->t_hiwat) (void) ttyoutput(CTRL('g'), tp); } else ttyflush(tp, FREAD); @@ -1099,7 +1195,7 @@ input_overflow: * Put data char in q for user and * wakeup on seeing a line delimiter. */ - if (putc(c, &tp->t_raw) >= 0) { + if (putc(c, tp->t_raw) >= 0) { if ((lflag&ICANON) == 0) { ttwakeup(tp); ttyecho(c, tp); @@ -1107,7 +1203,7 @@ input_overflow: } if (ttbreakc(c)) { tp->t_rocount = 0; - catb(&tp->t_raw, &tp->t_can); + catb(tp->t_raw, tp->t_can); ttwakeup(tp); } else if (tp->t_rocount++ == 0) tp->t_rocol = tp->t_col; @@ -1124,7 +1220,7 @@ input_overflow: /* * 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--; @@ -1151,17 +1247,18 @@ 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) return (-1); - if (putc(c, &tp->t_out)) + if (putc(c, tp->t_out)) return (c); tk_nout++; tp->t_outcc++; @@ -1186,17 +1283,17 @@ ttyoutput(c, tp) #ifdef was c -= b_to_q(" ", c, &tp->t_outq); #else - i = min (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, 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 = imin(c - i, RB_CONTIGPUT(tp->t_out)); /* off end and still have space? */ if (i) { - 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); } #endif tk_nout += c; @@ -1216,7 +1313,7 @@ ttyoutput(c, tp) */ if (c == '\n' && (tp->t_oflag&ONLCR) && ttyoutput('\r', tp) >= 0) return (c); - if ((tp->t_lflag&FLUSHO) == 0 && putc(c, &tp->t_out)) + if ((tp->t_lflag&FLUSHO) == 0 && putc(c, tp->t_out)) return (c); col = tp->t_col; @@ -1251,16 +1348,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; @@ -1268,14 +1369,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) @@ -1291,40 +1395,174 @@ loop: * If canonical, use the canonical queue, * else use the raw queue. */ - qp = lflag&ICANON ? &tp->t_can : &tp->t_raw; + 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; - carrier = (tp->t_state&TS_CARR_ON) || (tp->t_cflag&CLOCAL); - if (!carrier && tp->t_state&TS_ISOPEN) { +sleep: + if (tp->t_state & TS_ZOMBIE) { splx(s); - return (0); /* EOF */ + return (0); /* EOF */ } + /* + * 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 = CAN_DO_IO(tp); if (flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } - error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, + 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) */ @@ -1357,16 +1595,21 @@ loop: */ if (lflag&ICANON && ttbreakc(c)) break; + s = spltty(); first = 0; } + s = spltty(); + /* * 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); + splx(s); return (error); } @@ -1378,12 +1621,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(); @@ -1391,16 +1634,17 @@ ttycheckoutq(tp, wait) oldsig = curproc->p_sig; else oldsig = 0; - if (RB_LEN(&tp->t_out) > hiwat + 200) - while (RB_LEN(&tp->t_out) > hiwat) { + if (RB_LEN(tp->t_out) > hiwat + 200) + while (RB_LEN(tp->t_out) > hiwat) { ttstart(tp); if (wait == 0 || (curproc && curproc->p_sig != oldsig)) { 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); + tsleep((caddr_t)tp->t_out, PZERO - 1, "ttchout", 0); } splx(s); return (1); @@ -1409,11 +1653,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; @@ -1424,11 +1670,12 @@ ttwrite(tp, uio, flag) error = 0; loop: s = spltty(); - if ((tp->t_state&TS_CARR_ON) == 0 && (tp->t_cflag&CLOCAL) == 0) { - if (tp->t_state&TS_ISOPEN) { - splx(s); - return (EIO); - } else if (flag & IO_NDELAY) { + if (tp->t_state & TS_ZOMBIE) { + splx(s); + goto out; + } + if (!CAN_DO_IO(tp)) { + if (flag & IO_NDELAY) { splx(s); error = EWOULDBLOCK; goto out; @@ -1436,7 +1683,7 @@ loop: /* * sleep awaiting carrier */ - error = ttysleep(tp, (caddr_t)&tp->t_raw, + error = ttysleep(tp, (caddr_t)tp->t_raw, TTIPRI | PCATCH,ttopen, 0); splx(s); if (error) @@ -1470,14 +1717,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) { @@ -1516,9 +1776,13 @@ loop: 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; } } @@ -1536,38 +1800,50 @@ loop: ce -= i; #else i = ce; - ce = min (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); + 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); i -= ce; if (i > 0) { int ii; - ii = min (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); + 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)) + if (error = ttysleep(tp, (caddr_t)tp->t_out, + 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); } @@ -1588,7 +1864,7 @@ ovhiwat: * This can only occur if FLUSHO is set in t_lflag, * or if ttstart/oproc is synchronous (or very fast). */ - if (RB_LEN(&tp->t_out) <= hiwat) { + if (RB_LEN(tp->t_out) <= hiwat) { splx(s); goto loop; } @@ -1600,7 +1876,7 @@ ovhiwat: return (0); } tp->t_state |= TS_ASLEEP; - error = ttysleep(tp, (caddr_t)&tp->t_out, TTOPRI | PCATCH, ttyout, 0); + error = ttysleep(tp, (caddr_t)tp->t_out, TTOPRI | PCATCH, ttyout, 0); splx(s); if (error) goto out; @@ -1611,8 +1887,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,7 +1927,7 @@ ttyrub(c, tp) case TAB: { int c; - if (tp->t_rocount < RB_LEN(&tp->t_raw)) { + if (tp->t_rocount < RB_LEN(tp->t_raw)) { ttyretype(tp); return; } @@ -1659,9 +1936,9 @@ ttyrub(c, tp) tp->t_state |= TS_CNTTB; tp->t_lflag |= FLUSHO; tp->t_col = tp->t_rocol; - cp = tp->t_raw.rb_hd; - for (c = nextc(&cp, &tp->t_raw); c ; - c = nextc(&cp, &tp->t_raw)) + cp = tp->t_raw->rb_hd; + for (c = nextc(&cp, tp->t_raw); c ; + c = nextc(&cp, tp->t_raw)) ttyecho(c, tp); tp->t_lflag &= ~FLUSHO; tp->t_state &= ~TS_CNTTB; @@ -1699,6 +1976,7 @@ ttyrub(c, tp) * Crt back over cnt chars perhaps * erasing them. */ +static void ttyrubo(tp, cnt) register struct tty *tp; int cnt; @@ -1712,6 +1990,7 @@ ttyrubo(tp, cnt) * Reprint the rawq line. * We assume c_cc has already been checked. */ +static void ttyretype(tp) register struct tty *tp; { @@ -1723,36 +2002,33 @@ ttyretype(tp) (void) ttyoutput('\n', tp); s = spltty(); - cp = tp->t_can.rb_hd; - for (c = nextc(&cp, &tp->t_can); c ; c = nextc(&cp, &tp->t_can)) + cp = tp->t_can->rb_hd; + for (c = nextc(&cp, tp->t_can); c ; c = nextc(&cp, tp->t_can)) ttyecho(c, tp); - cp = tp->t_raw.rb_hd; - for (c = nextc(&cp, &tp->t_raw); c ; c = nextc(&cp, &tp->t_raw)) + cp = tp->t_raw->rb_hd; + for (c = nextc(&cp, tp->t_raw); c ; c = nextc(&cp, tp->t_raw)) ttyecho(c, tp); tp->t_state &= ~TS_ERASE; splx(s); - tp->t_rocount = RB_LEN(&tp->t_raw); + tp->t_rocount = RB_LEN(tp->t_raw); tp->t_rocol = 0; } /* * 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) { @@ -1770,6 +2046,7 @@ ttyecho(c, tp) /* * send string cp to tp */ +static void ttyoutstr(cp, tp) register char *cp; register struct tty *tp; @@ -1783,6 +2060,7 @@ ttyoutstr(cp, tp) /* * Wake up any readers on a tty. */ +void ttwakeup(tp) register struct tty *tp; { @@ -1794,14 +2072,16 @@ ttwakeup(tp) } if (tp->t_state & TS_ASYNC) pgsignal(tp->t_pgrp, SIGIO, 1); - wakeup((caddr_t)&tp->t_raw); + wakeup((caddr_t)tp->t_raw); } /* * 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; { @@ -1818,6 +2098,7 @@ ttspeedtab(speed, table) * from hi to low water. * */ +void ttsetwater(tp) struct tty *tp; { @@ -1835,19 +2116,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, tmp = 0; 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"); @@ -1856,14 +2138,38 @@ 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 @@ -1876,6 +2182,8 @@ ttyinfo(tp) if (curproc == pick) splx(tmp); + ttyprintf(tp, " cmd: %s %d [%s] ", comm, pid, wmesg); + /* Print user time. */ ttyprintf(tp, "%d.%02du ", utime.tv_sec, (utime.tv_usec + 5000) / 10000); @@ -1884,12 +2192,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 */ } @@ -1972,13 +2278,14 @@ proc_compare(p1, p2) /* * Output char to tty; console putchar style. */ +int tputchar(c, tp) int c; struct tty *tp; { register s = spltty(); - if ((tp->t_state & (TS_CARR_ON|TS_ISOPEN)) == (TS_CARR_ON|TS_ISOPEN)) { + if (CAN_DO_IO(tp)) { if (c == '\n') (void) ttyoutput('\r', tp); (void) ttyoutput(c, tp); @@ -1996,15 +2303,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); @@ -2012,3 +2320,57 @@ ttysleep(tp, chan, pri, wmesg, timo) return (ERESTART); return (0); } + + +/* + * Allocate a tty structure and its associated buffers. + */ +struct tty * +ttymalloc(itp) + struct tty *itp; +{ + struct tty *tp; + +#ifndef broken + /* + * Note that the itp input is not necessary when we can dealloc + * the struct tty. + */ + if(itp == NULL) { + MALLOC(tp, struct tty *, sizeof(struct tty), M_TTYS, M_WAITOK); + bzero(tp, sizeof *tp); + } else { + tp = itp; + } +#endif + if(tp->t_raw == NULL) { + MALLOC(tp->t_raw, struct ringb *, sizeof(struct ringb), M_TTYS, M_WAITOK); + bzero(tp->t_raw, sizeof *tp->t_raw); + } + if(tp->t_can == NULL) { + MALLOC(tp->t_can, struct ringb *, sizeof(struct ringb), M_TTYS, M_WAITOK); + bzero(tp->t_can, sizeof *tp->t_can); + } + if(tp->t_out == NULL) { + MALLOC(tp->t_out, struct ringb *, sizeof(struct ringb), M_TTYS, M_WAITOK); + bzero(tp->t_out, sizeof *tp->t_out); + } + return(tp); +} + +/* + * Free a tty structure and its buffers. + */ +void +ttyfree(tp) +struct tty *tp; +{ + FREE(tp->t_raw, M_TTYS); + FREE(tp->t_can, M_TTYS); + FREE(tp->t_out, M_TTYS); + tp->t_raw = tp->t_can = tp->t_out = NULL; +#ifdef broken /* session holds a ref to the tty; can't deallocate */ + /* also set tp to NULL when this isn't broken anymore */ + FREE(tp, M_TTYS); +#endif +}