merge in vnode changes
[unix-history] / usr / src / sys / kern / tty_pty.c
index da46101..4a85929 100644 (file)
@@ -1,4 +1,10 @@
-/*     tty_pty.c       6.11    85/06/07        */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ *     @(#)tty_pty.c   7.5 (Berkeley) %G%
+ */
 
 /*
  * Pseudo-teletype Driver
 
 /*
  * Pseudo-teletype Driver
@@ -18,6 +24,7 @@
 #include "proc.h"
 #include "uio.h"
 #include "kernel.h"
 #include "proc.h"
 #include "uio.h"
 #include "kernel.h"
+#include "tsleep.h"
 
 #if NPTY == 1
 #undef NPTY
 
 #if NPTY == 1
 #undef NPTY
 struct tty pt_tty[NPTY];
 struct pt_ioctl {
        int     pt_flags;
 struct tty pt_tty[NPTY];
 struct pt_ioctl {
        int     pt_flags;
-       int     pt_gensym;
        struct  proc *pt_selr, *pt_selw;
        u_char  pt_send;
        u_char  pt_ucntl;
        struct  proc *pt_selr, *pt_selw;
        u_char  pt_send;
        u_char  pt_ucntl;
+       struct  clist pt_ioc;
 } pt_ioctl[NPTY];
 int    npty = NPTY;            /* for pstat -t */
 
 } pt_ioctl[NPTY];
 int    npty = NPTY;            /* for pstat -t */
 
-#define        PF_RCOLL        0x01
-#define        PF_WCOLL        0x02
-#define        PF_NBIO         0x04
-#define        PF_PKT          0x08            /* packet mode */
-#define        PF_STOPPED      0x10            /* user told stopped */
-#define        PF_REMOTE       0x20            /* remote and flow controlled input */
-#define        PF_NOSTOP       0x40
-#define PF_UCNTL       0x80            /* user control mode */
+int ptydebug = 0;
+
+#define        PF_RCOLL        0x0001
+#define        PF_WCOLL        0x0002
+#define        PF_NBIO         0x0004
+#define        PF_PKT          0x0008          /* packet mode */
+#define        PF_STOPPED      0x0010          /* user told stopped */
+#define        PF_REMOTE       0x0020          /* remote and flow controlled input */
+#define        PF_NOSTOP       0x0040
+#define        PF_UCNTL        0x0080          /* user control mode */
+#define        PF_TIOC         0x0100          /* transparent control mode */
+#define        PF_LIOC         0x0200          /* transparent control locked */
+#define        PF_WIOC         0x0400          /* waiting for PF_LIOC to clear */
+#define        PF_BLOCK        0x0800          /* block writes to slave */
+#define        PF_OWAIT        0x1000          /* waiting for PF_BLOCK to clear */
 
 /*ARGSUSED*/
 ptsopen(dev, flag)
 
 /*ARGSUSED*/
 ptsopen(dev, flag)
@@ -64,17 +78,23 @@ ptsopen(dev, flag)
        tp = &pt_tty[minor(dev)];
        if ((tp->t_state & TS_ISOPEN) == 0) {
                ttychars(tp);           /* Set up default chars */
        tp = &pt_tty[minor(dev)];
        if ((tp->t_state & TS_ISOPEN) == 0) {
                ttychars(tp);           /* Set up default chars */
-               tp->t_ispeed = tp->t_ospeed = EXTB;
-               tp->t_flags = 0;        /* No features (nor raw mode) */
+               tp->t_iflag = TTYDEF_IFLAG;
+               tp->t_oflag = TTYDEF_OFLAG;
+               tp->t_lflag = TTYDEF_LFLAG;
+               tp->t_cflag = TTYDEF_CFLAG;
+               tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+               ttsetwater(tp);         /* would be done in xxparam() */
        } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
                return (EBUSY);
        if (tp->t_oproc)                        /* Ctrlr still around. */
                tp->t_state |= TS_CARR_ON;
        while ((tp->t_state & TS_CARR_ON) == 0) {
                tp->t_state |= TS_WOPEN;
        } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
                return (EBUSY);
        if (tp->t_oproc)                        /* Ctrlr still around. */
                tp->t_state |= TS_CARR_ON;
        while ((tp->t_state & TS_CARR_ON) == 0) {
                tp->t_state |= TS_WOPEN;
+               if (flag&FNDELAY)
+                       break;
                sleep((caddr_t)&tp->t_rawq, TTIPRI);
        }
                sleep((caddr_t)&tp->t_rawq, TTIPRI);
        }
-       error = (*linesw[tp->t_line].l_open)(dev, tp);
+       error = (*linesw[tp->t_line].l_open)(dev, tp, flag);
        ptcwakeup(tp, FREAD|FWRITE);
        return (error);
 }
        ptcwakeup(tp, FREAD|FWRITE);
        return (error);
 }
@@ -88,9 +108,10 @@ ptsclose(dev)
        (*linesw[tp->t_line].l_close)(tp);
        ttyclose(tp);
        ptcwakeup(tp, FREAD|FWRITE);
        (*linesw[tp->t_line].l_close)(tp);
        ttyclose(tp);
        ptcwakeup(tp, FREAD|FWRITE);
+       return (0);
 }
 
 }
 
-ptsread(dev, uio)
+ptsread(dev, uio, flag)
        dev_t dev;
        struct uio *uio;
 {
        dev_t dev;
        struct uio *uio;
 {
@@ -100,19 +121,18 @@ ptsread(dev, uio)
 
 again:
        if (pti->pt_flags & PF_REMOTE) {
 
 again:
        if (pti->pt_flags & PF_REMOTE) {
-               while (tp == u.u_ttyp && u.u_procp->p_pgrp != tp->t_pgrp) {
+               while (tp == u.u_ttyp && 
+                      u.u_procp->p_pgrp->pg_id != tp->t_pgid){
                        if ((u.u_procp->p_sigignore & sigmask(SIGTTIN)) ||
                            (u.u_procp->p_sigmask & sigmask(SIGTTIN)) ||
                        if ((u.u_procp->p_sigignore & sigmask(SIGTTIN)) ||
                            (u.u_procp->p_sigmask & sigmask(SIGTTIN)) ||
-       /*
-                           (u.u_procp->p_flag&SDETACH) ||
-       */
+                           !u.u_procp->p_pgrp->pg_jobc ||
                            u.u_procp->p_flag&SVFORK)
                                return (EIO);
                            u.u_procp->p_flag&SVFORK)
                                return (EIO);
-                       gsignal(u.u_procp->p_pgrp, SIGTTIN);
+                       pgsignal(u.u_procp->p_pgrp, SIGTTIN);
                        sleep((caddr_t)&lbolt, TTIPRI);
                }
                if (tp->t_canq.c_cc == 0) {
                        sleep((caddr_t)&lbolt, TTIPRI);
                }
                if (tp->t_canq.c_cc == 0) {
-                       if (tp->t_state & TS_NBIO)
+                       if (flag & FNDELAY)
                                return (EWOULDBLOCK);
                        sleep((caddr_t)&tp->t_canq, TTIPRI);
                        goto again;
                                return (EWOULDBLOCK);
                        sleep((caddr_t)&tp->t_canq, TTIPRI);
                        goto again;
@@ -128,7 +148,7 @@ again:
                        return (error);
        } else
                if (tp->t_oproc)
                        return (error);
        } else
                if (tp->t_oproc)
-                       error = (*linesw[tp->t_line].l_read)(tp, uio);
+                       error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
        ptcwakeup(tp, FWRITE);
        return (error);
 }
        ptcwakeup(tp, FWRITE);
        return (error);
 }
@@ -138,16 +158,22 @@ again:
  * Wakeups of controlling tty will happen
  * indirectly, when tty driver calls ptsstart.
  */
  * Wakeups of controlling tty will happen
  * indirectly, when tty driver calls ptsstart.
  */
-ptswrite(dev, uio)
+ptswrite(dev, uio, flag)
        dev_t dev;
        struct uio *uio;
 {
        dev_t dev;
        struct uio *uio;
 {
-       register struct tty *tp;
+       register struct tty *tp = &pt_tty[minor(dev)];
+       register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
 
 
-       tp = &pt_tty[minor(dev)];
        if (tp->t_oproc == 0)
                return (EIO);
        if (tp->t_oproc == 0)
                return (EIO);
-       return ((*linesw[tp->t_line].l_write)(tp, uio));
+
+       while (pti->pt_flags & PF_BLOCK) {
+               pti->pt_flags |= PF_OWAIT;
+               sleep((caddr_t)pti + 1, TTOPRI);
+       }
+
+       return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
 }
 
 /*
 }
 
 /*
@@ -187,6 +213,7 @@ ptcwakeup(tp, flag)
                        pti->pt_selw = 0;
                        pti->pt_flags &= ~PF_WCOLL;
                }
                        pti->pt_selw = 0;
                        pti->pt_flags &= ~PF_WCOLL;
                }
+if (ptydebug) printf("WAKEUP c_cf %d\n", u.u_procp->p_pid);
                wakeup((caddr_t)&tp->t_rawq.c_cf);
        }
 }
                wakeup((caddr_t)&tp->t_rawq.c_cf);
        }
 }
@@ -205,9 +232,7 @@ ptcopen(dev, flag)
        if (tp->t_oproc)
                return (EIO);
        tp->t_oproc = ptsstart;
        if (tp->t_oproc)
                return (EIO);
        tp->t_oproc = ptsstart;
-       if (tp->t_state & TS_WOPEN)
-               wakeup((caddr_t)&tp->t_rawq);
-       tp->t_state |= TS_CARR_ON;
+       (void)(*linesw[tp->t_line].l_modem)(tp, 1);
        pti = &pt_ioctl[minor(dev)];
        pti->pt_flags = 0;
        pti->pt_send = 0;
        pti = &pt_ioctl[minor(dev)];
        pti->pt_flags = 0;
        pti->pt_send = 0;
@@ -221,14 +246,13 @@ ptcclose(dev)
        register struct tty *tp;
 
        tp = &pt_tty[minor(dev)];
        register struct tty *tp;
 
        tp = &pt_tty[minor(dev)];
-       if (tp->t_state & TS_ISOPEN)
-               gsignal(tp->t_pgrp, SIGHUP);
-       tp->t_state &= ~TS_CARR_ON;     /* virtual carrier gone */
-       ttyflush(tp, FREAD|FWRITE);
+       (void)(*linesw[tp->t_line].l_modem)(tp, 0);
+       tp->t_state &= ~TS_CARR_ON;
        tp->t_oproc = 0;                /* mark closed */
        tp->t_oproc = 0;                /* mark closed */
+       return (0);
 }
 
 }
 
-ptcread(dev, uio)
+ptcread(dev, uio, flag)
        dev_t dev;
        struct uio *uio;
 {
        dev_t dev;
        struct uio *uio;
 {
@@ -246,29 +270,43 @@ ptcread(dev, uio)
        for (;;) {
                if (tp->t_state&TS_ISOPEN) {
                        if (pti->pt_flags&PF_PKT && pti->pt_send) {
        for (;;) {
                if (tp->t_state&TS_ISOPEN) {
                        if (pti->pt_flags&PF_PKT && pti->pt_send) {
-                               error = ureadc(pti->pt_send, uio);
+                               error = ureadc((int)pti->pt_send, uio);
                                if (error)
                                        return (error);
                                pti->pt_send = 0;
                                return (0);
                        }
                        if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) {
                                if (error)
                                        return (error);
                                pti->pt_send = 0;
                                return (0);
                        }
                        if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) {
-                               error = ureadc(pti->pt_ucntl, uio);
+                               error = ureadc((int)pti->pt_ucntl, uio);
                                if (error)
                                        return (error);
                                pti->pt_ucntl = 0;
                                return (0);
                        }
                                if (error)
                                        return (error);
                                pti->pt_ucntl = 0;
                                return (0);
                        }
+                       if (pti->pt_flags&PF_TIOC && pti->pt_ioc.c_cc) {
+                               if (uio->uio_resid < pti->pt_ioc.c_cc + 1)
+                                       return (E2BIG);
+                               error = ureadc(TIOCPKT_TIOC, uio);
+                               while (error == 0 && pti->pt_ioc.c_cc > 0) {
+                                       cc = q_to_b(&pti->pt_ioc, buf,
+                                           MIN(pti->pt_ioc.c_cc, BUFSIZ));
+                                       if (cc <= 0)    /* impossible? */
+                                               break;
+                                       error = uiomove(buf, cc, UIO_READ, uio);
+                               }
+                               return (error);
+                       }
                        if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
                                break;
                }
                if ((tp->t_state&TS_CARR_ON) == 0)
                        if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
                                break;
                }
                if ((tp->t_state&TS_CARR_ON) == 0)
-                       return (EIO);
-               if (pti->pt_flags&PF_NBIO)
+                       return (0);     /* EOF */
+               if (flag&FNDELAY)
                        return (EWOULDBLOCK);
                        return (EWOULDBLOCK);
+if (ptydebug) printf("SLEEP(1) c_cf %d\n", u.u_procp->p_pid);
                sleep((caddr_t)&tp->t_outq.c_cf, TTIPRI);
        }
                sleep((caddr_t)&tp->t_outq.c_cf, TTIPRI);
        }
-       if (pti->pt_flags & (PF_PKT|PF_UCNTL))
+       if (pti->pt_flags & (PF_PKT|PF_UCNTL|PF_TIOC))
                error = ureadc(0, uio);
        while (uio->uio_resid > 0 && error == 0) {
                cc = q_to_b(&tp->t_outq, buf, MIN(uio->uio_resid, BUFSIZ));
                error = ureadc(0, uio);
        while (uio->uio_resid > 0 && error == 0) {
                cc = q_to_b(&tp->t_outq, buf, MIN(uio->uio_resid, BUFSIZ));
@@ -276,20 +314,25 @@ ptcread(dev, uio)
                        break;
                error = uiomove(buf, cc, UIO_READ, uio);
        }
                        break;
                error = uiomove(buf, cc, UIO_READ, uio);
        }
-       if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
-               if (tp->t_state&TS_ASLEEP) {
-                       tp->t_state &= ~TS_ASLEEP;
-                       wakeup((caddr_t)&tp->t_outq);
-               }
-               if (tp->t_wsel) {
-                       selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
-                       tp->t_wsel = 0;
-                       tp->t_state &= ~TS_WCOLL;
-               }
-       }
+       if (tp->t_outq.c_cc <= TTLOWAT(tp) && !(pti->pt_flags & PF_BLOCK))
+               ptswake(tp);
        return (error);
 }
 
        return (error);
 }
 
+ptswake(tp)
+       register struct tty *tp;
+{
+       if (tp->t_state&TS_ASLEEP) {
+               tp->t_state &= ~TS_ASLEEP;
+               wakeup((caddr_t)&tp->t_outq);
+       }
+       if (tp->t_wsel) {
+               selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
+               tp->t_wsel = 0;
+               tp->t_state &= ~TS_WCOLL;
+       }
+}
+
 ptsstop(tp, flush)
        register struct tty *tp;
        int flush;
 ptsstop(tp, flush)
        register struct tty *tp;
        int flush;
@@ -310,7 +353,7 @@ ptsstop(tp, flush)
                flag |= FWRITE;
        if (flush & FWRITE)
                flag |= FREAD;
                flag |= FWRITE;
        if (flush & FWRITE)
                flag |= FREAD;
-       ptcwakeup(tp, flush);
+       ptcwakeup(tp, flag);
 }
 
 ptcselect(dev, rw)
 }
 
 ptcselect(dev, rw)
@@ -324,40 +367,57 @@ ptcselect(dev, rw)
 
        if ((tp->t_state&TS_CARR_ON) == 0)
                return (1);
 
        if ((tp->t_state&TS_CARR_ON) == 0)
                return (1);
-       s = spl5();
        switch (rw) {
 
        case FREAD:
        switch (rw) {
 
        case FREAD:
+               /*
+                * Need to block timeouts (ttrstart).
+                */
+               s = spltty();
                if ((tp->t_state&TS_ISOPEN) &&
                if ((tp->t_state&TS_ISOPEN) &&
-                   (pti->pt_flags&PF_PKT && pti->pt_send ||
-                    pti->pt_flags&PF_UCNTL && pti->pt_ucntl ||
-                    tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)) {
+                    tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) {
                        splx(s);
                        return (1);
                }
                        splx(s);
                        return (1);
                }
+               splx(s);
+               /* FALLTHROUGH */
+
+       case 0:                                 /* exceptional */
+               if ((tp->t_state&TS_ISOPEN) &&
+                   (pti->pt_flags&PF_PKT && pti->pt_send ||
+                    pti->pt_flags&PF_TIOC && pti->pt_ioc.c_cc ||
+                    pti->pt_flags&PF_UCNTL && pti->pt_ucntl))
+                       return (1);
                if ((p = pti->pt_selr) && p->p_wchan == (caddr_t)&selwait)
                        pti->pt_flags |= PF_RCOLL;
                else
                        pti->pt_selr = u.u_procp;
                break;
 
                if ((p = pti->pt_selr) && p->p_wchan == (caddr_t)&selwait)
                        pti->pt_flags |= PF_RCOLL;
                else
                        pti->pt_selr = u.u_procp;
                break;
 
+
        case FWRITE:
        case FWRITE:
-               if ((tp->t_state&TS_ISOPEN) &&
-                   ((pti->pt_flags&PF_REMOTE) == 0 || tp->t_canq.c_cc == 0)) {
-                       splx(s);
-                       return (1);
+               if (tp->t_state&TS_ISOPEN) {
+                       if (pti->pt_flags & PF_REMOTE) {
+                           if (tp->t_canq.c_cc == 0)
+                               return (1);
+                       } else {
+                           if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2)
+                                   return (1);
+                           if (tp->t_canq.c_cc == 0 && (tp->t_iflag&ICANON))
+                                   return (1);
+                       }
                }
                if ((p = pti->pt_selw) && p->p_wchan == (caddr_t)&selwait)
                        pti->pt_flags |= PF_WCOLL;
                else
                        pti->pt_selw = u.u_procp;
                break;
                }
                if ((p = pti->pt_selw) && p->p_wchan == (caddr_t)&selwait)
                        pti->pt_flags |= PF_WCOLL;
                else
                        pti->pt_selw = u.u_procp;
                break;
+
        }
        }
-       splx(s);
        return (0);
 }
 
        return (0);
 }
 
-ptcwrite(dev, uio)
+ptcwrite(dev, uio, flag)
        dev_t dev;
        register struct uio *uio;
 {
        dev_t dev;
        register struct uio *uio;
 {
@@ -373,26 +433,31 @@ ptcwrite(dev, uio)
 again:
        if ((tp->t_state&TS_ISOPEN) == 0)
                goto block;
 again:
        if ((tp->t_state&TS_ISOPEN) == 0)
                goto block;
-       if (cnt == 0 && pti->pt_flags & PF_REMOTE) {
-               if (uio->uio_iovcnt <= 0)
-                       return (0);
+       if (pti->pt_flags & PF_REMOTE) {
                if (tp->t_canq.c_cc)
                        goto block;
                if (tp->t_canq.c_cc)
                        goto block;
-               iov = uio->uio_iov;
-               if (cc == 0 && iov->iov_len) {
-                       cc = MIN(iov->iov_len, BUFSIZ);
-                       cp = locbuf;
-                       error = uiomove(cp, cc, UIO_WRITE, uio);
-                       if (error)
-                               return (error);
-                       /* check again for safety */
-                       if ((tp->t_state&TS_ISOPEN) == 0)
-                               return (EIO);
-                       if (tp->t_canq.c_cc)
-                               goto block;
+               while (uio->uio_iovcnt > 0 && tp->t_canq.c_cc < TTYHOG - 1) {
+                       iov = uio->uio_iov;
+                       if (iov->iov_len == 0) {
+                               uio->uio_iovcnt--;      
+                               uio->uio_iov++;
+                               continue;
+                       }
+                       if (cc == 0) {
+                               cc = MIN(iov->iov_len, BUFSIZ);
+                               cc = MIN(cc, TTYHOG - 1 - tp->t_canq.c_cc);
+                               cp = locbuf;
+                               error = uiomove(cp, cc, UIO_WRITE, uio);
+                               if (error)
+                                       return (error);
+                               /* check again for safety */
+                               if ((tp->t_state&TS_ISOPEN) == 0)
+                                       return (EIO);
+                       }
+                       if (cc)
+                               (void) b_to_q(cp, cc, &tp->t_canq);
+                       cc = 0;
                }
                }
-               if (cc)
-                       (void) b_to_q(cp, cc, &tp->t_canq);
                (void) putc(0, &tp->t_canq);
                ttwakeup(tp);
                wakeup((caddr_t)&tp->t_canq);
                (void) putc(0, &tp->t_canq);
                ttwakeup(tp);
                wakeup((caddr_t)&tp->t_canq);
@@ -415,29 +480,36 @@ again:
                        if ((tp->t_state&TS_ISOPEN) == 0)
                                return (EIO);
                }
                        if ((tp->t_state&TS_ISOPEN) == 0)
                                return (EIO);
                }
-               while (--cc >= 0) {
-                       (*linesw[tp->t_line].l_rint)(*cp++, tp);
+               while (cc > 0) {
+                       if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
+                          (tp->t_canq.c_cc > 0 || !(tp->t_iflag&ICANON))) {
+                               wakeup((caddr_t)&tp->t_rawq);
+                               goto block;
+                       }
+                       (*linesw[tp->t_line].l_rint)(*cp++&0377, tp);
                        cnt++;
                        cnt++;
+                       cc--;
                }
                cc = 0;
        }
        return (0);
 block:
        /*
                }
                cc = 0;
        }
        return (0);
 block:
        /*
-        * Come here to wait for slave to open or for space
-        * in outq.
+        * Come here to wait for slave to open, for space
+        * in outq, or space in rawq.
         */
        if ((tp->t_state&TS_CARR_ON) == 0)
                return (EIO);
         */
        if ((tp->t_state&TS_CARR_ON) == 0)
                return (EIO);
-       if (pti->pt_flags & PF_NBIO) {
-               if (cnt == 0)
-                       return (EWOULDBLOCK);
+       if ((pti->pt_flags & PF_NBIO) || (flag & FNDELAY)) {
                iov->iov_base -= cc;
                iov->iov_len += cc;
                uio->uio_resid += cc;
                uio->uio_offset -= cc;
                iov->iov_base -= cc;
                iov->iov_len += cc;
                uio->uio_resid += cc;
                uio->uio_offset -= cc;
+               if (cnt == 0)
+                       return (EWOULDBLOCK);
                return (0);
        }
                return (0);
        }
+if (ptydebug) printf("SLEEP(2) c_cf %d\n", u.u_procp->p_pid);
        sleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI);
        goto again;
 }
        sleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI);
        goto again;
 }
@@ -449,10 +521,22 @@ ptyioctl(dev, cmd, data, flag)
 {
        register struct tty *tp = &pt_tty[minor(dev)];
        register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
 {
        register struct tty *tp = &pt_tty[minor(dev)];
        register struct pt_ioctl *pti = &pt_ioctl[minor(dev)];
+       register u_char *cc = tp->t_cc;
        int stop, error;
        int stop, error;
+       extern ttyinput();
 
 
-       /* IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG ??? */
-       if (cdevsw[major(dev)].d_open == ptcopen)
+       /*
+        * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
+        * ttywflush(tp) will hang if there are characters in the outq.
+        */
+       if (cdevsw[major(dev)].d_open == ptcopen) {
+               if ((cmd & 0xffff) == (TIOCIOANS(0) & 0xffff)) {
+                       if (!(pti->pt_flags & PF_LIOC) || pti->pt_ioc.c_cc)
+                               return (EINVAL);
+                       (void) b_to_q(data, IOCPARM_LEN(cmd), &pti->pt_ioc);
+                       wakeup((caddr_t)&pti->pt_ioc);
+                       return (0);
+               }
                switch (cmd) {
 
                case TIOCPKT:
                switch (cmd) {
 
                case TIOCPKT:
@@ -473,6 +557,30 @@ ptyioctl(dev, cmd, data, flag)
                                pti->pt_flags &= ~PF_UCNTL;
                        return (0);
 
                                pti->pt_flags &= ~PF_UCNTL;
                        return (0);
 
+               case TIOCTIOC:
+                       if (*(int *)data) {
+                               if (pti->pt_flags & PF_UCNTL)
+                                       return (EINVAL);
+                               pti->pt_flags |= PF_TIOC;
+                       } else {
+                               pti->pt_flags &= ~(PF_TIOC|PF_LIOC|PF_WIOC);
+                               while (pti->pt_ioc.c_cc)
+                                       (void) getc(&pti->pt_ioc);
+                               wakeup((caddr_t)&pti->pt_ioc);
+                       }
+                       return (0);
+
+               case TIOCBLK:
+                       if (*(int *)data)
+                               pti->pt_flags |= PF_BLOCK;
+                       else {
+                               if (pti->pt_flags & PF_OWAIT)
+                                       wakeup((caddr_t)pti + 1);
+                               pti->pt_flags &= ~(PF_BLOCK|PF_OWAIT);
+                               ptswake(tp);
+                       }
+                       return (0);
+
                case TIOCREMOTE:
                        if (*(int *)data)
                                pti->pt_flags |= PF_REMOTE;
                case TIOCREMOTE:
                        if (*(int *)data)
                                pti->pt_flags |= PF_REMOTE;
@@ -488,15 +596,113 @@ ptyioctl(dev, cmd, data, flag)
                                pti->pt_flags &= ~PF_NBIO;
                        return (0);
 
                                pti->pt_flags &= ~PF_NBIO;
                        return (0);
 
-               case TIOCSETP:
+               case FIONREAD:
+                       *(int *)data = tp->t_outq.c_cc;
+                       return (0);
+
+               case TIOCSETP:          
+               case TIOCSETN:
+               case TIOCSETD:
+               case TIOCSETA:
+               case TIOCSETAW:
+               case TIOCSETAF:
+               case TIOCSETAS:
+               case TIOCSETAWS:
+               case TIOCSETAFS:
                        while (getc(&tp->t_outq) >= 0)
                                ;
                        break;
                }
                        while (getc(&tp->t_outq) >= 0)
                                ;
                        break;
                }
-       error = ttioctl(tp, cmd, data, flag);
+       } else if (pti->pt_flags & PF_TIOC) {
+               while (pti->pt_flags & PF_LIOC) {
+                       pti->pt_flags |= PF_WIOC;
+                       switch (tsleep((caddr_t)&pti->pt_flags,TTIPRI-1,5*hz)) {
+                       case TS_OK:
+                               continue;
+                       case TS_SIG:
+                       case TS_TIME:
+                               return (EBUSY);
+                       }
+               }
+               pti->pt_flags |= PF_LIOC | PF_BLOCK;
+               while (pti->pt_ioc.c_cc)
+                       (void) getc(&pti->pt_ioc);
+               (void) b_to_q(&cmd, sizeof cmd, &pti->pt_ioc);
+               if (cmd & IOC_IN)
+                       (void) b_to_q(data, IOCPARM_LEN(cmd), &pti->pt_ioc);
+               ptcwakeup(tp, FREAD);
+               switch (tsleep((caddr_t)&pti->pt_ioc, TTIPRI-1, 5*hz)) {
+               case TS_SIG:
+               case TS_TIME:
+                       while (pti->pt_ioc.c_cc)
+                               (void) getc(&pti->pt_ioc);
+                       if (pti->pt_flags & PF_WIOC)
+                               wakeup((caddr_t)&pti->pt_flags);
+                       if (pti->pt_flags & PF_OWAIT)
+                               wakeup((caddr_t)pti + 1);
+                       pti->pt_flags &= ~(PF_LIOC|PF_WIOC|PF_BLOCK|PF_OWAIT);
+                       ptswake(tp);
+                       return (EBUSY);
+               case TS_OK:
+                       break;
+               }
+               if (pti->pt_ioc.c_cc == 0) {
+                       if (pti->pt_flags & PF_WIOC)
+                               wakeup((caddr_t)&pti->pt_flags);
+                       if (pti->pt_flags & PF_OWAIT)
+                               wakeup((caddr_t)pti + 1);
+                       pti->pt_flags &= ~(PF_LIOC|PF_WIOC|PF_BLOCK|PF_OWAIT);
+                       ptswake(tp);
+                       goto doioctl;
+               }
+               if (q_to_b(&pti->pt_ioc, &error, sizeof error) != sizeof error)
+                       error = EINVAL;
+               if (error == 0 && cmd & IOC_OUT) {
+                       if (IOCPARM_LEN(cmd) != pti->pt_ioc.c_cc)
+                               error = EINVAL;
+                       else
+                               (void) q_to_b(&pti->pt_ioc, data,
+                                   pti->pt_ioc.c_cc);
+               }
+               while (pti->pt_ioc.c_cc)
+                       (void) getc(&pti->pt_ioc);
+               if (pti->pt_flags & PF_WIOC)
+                       wakeup((caddr_t)&pti->pt_flags);
+               if (pti->pt_flags & PF_OWAIT)
+                       wakeup((caddr_t)pti + 1);
+               pti->pt_flags &= ~(PF_LIOC|PF_WIOC|PF_BLOCK|PF_OWAIT);
+               ptswake(tp);
+               return (error);
+       }
+
+ doioctl:
+       error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
+       if (error < 0)
+                error = ttioctl(tp, cmd, data, flag);
+       /*
+        * Since we use the tty queues internally,
+        * pty's can't be switched to disciplines which overwrite
+        * the queues.  We can't tell anything about the discipline
+        * from here...
+        *
+        * Nb: this is not really good enough, the line disc open routine
+        * may have done anything at all, no guarantees that close
+        * will fix it.  This also has the effect of losing the
+        * previous discipline, which an error on a TIOCSETD shouldn't
+        * do...  Sometime it should be done via an explicit check
+        * for TIOCSETD, then check to see what linesw[new_number].l_rint
+        * really is.
+        */
+       if (linesw[tp->t_line].l_rint != ttyinput) {
+               (*linesw[tp->t_line].l_close)(tp);
+               tp->t_line = 0;
+               (void)(*linesw[tp->t_line].l_open)(dev, tp, flag);
+               error = ENOTTY;
+       }
+
        if (error < 0) {
                if (pti->pt_flags & PF_UCNTL &&
        if (error < 0) {
                if (pti->pt_flags & PF_UCNTL &&
-                   (cmd & ~0xff) == _IO(u,0)) {
+                   (cmd & ~0xff) == UIOCCMD(0)) {
                        if (cmd & 0xff) {
                                pti->pt_ucntl = (u_char)cmd;
                                ptcwakeup(tp, FREAD);
                        if (cmd & 0xff) {
                                pti->pt_ucntl = (u_char)cmd;
                                ptcwakeup(tp, FREAD);
@@ -505,11 +711,11 @@ ptyioctl(dev, cmd, data, flag)
                }
                error = ENOTTY;
        }
                }
                error = ENOTTY;
        }
-       stop = (tp->t_flags & RAW) == 0 &&
-           tp->t_stopc == CTRL(s) && tp->t_startc == CTRL(q);
+       stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) 
+               && CCEQ(cc[VSTART], CTRL('q'));
        if (pti->pt_flags & PF_NOSTOP) {
                if (stop) {
        if (pti->pt_flags & PF_NOSTOP) {
                if (stop) {
-                       pti->pt_send &= TIOCPKT_NOSTOP;
+                       pti->pt_send &= ~TIOCPKT_NOSTOP;
                        pti->pt_send |= TIOCPKT_DOSTOP;
                        pti->pt_flags &= ~PF_NOSTOP;
                        ptcwakeup(tp, FREAD);
                        pti->pt_send |= TIOCPKT_DOSTOP;
                        pti->pt_flags &= ~PF_NOSTOP;
                        ptcwakeup(tp, FREAD);