# /* * Pseudo-teletype Driver * (Actually two drivers, requiring two entries in 'cdevsw') */ #include "../h/param.h" #include "../h/conf.h" #include "../h/inode.h" #include "../h/user.h" #include "../h/tty.h" #define NPTY 16 /* Number of pseudo-teletypes */ /* * A pseudo-teletype is a special device which is not unlike a pipe. * It is used to communicate between two processes. However, it allows * one to simulate a teletype, including mode setting, interrupt, and * multiple end of files (all not possible on a pipe). There are two * really two drivers here. One is the device which looks like a TTY * and can be thought of as the slave device, and hence its routines are * are prefixed with 'pts' (PTY slave). The other driver can be * thought of as the controlling device, and its routines are prefixed * by 'ptc' (PTY controller). To type on the simulated keyboard of the * PTY, one does a 'write' to the controlling device. To get the * simulated printout from the PTY, one does a 'read' on the controlling * device. Normally, the controlling device is called 'ptyx' and the * slave device is called 'ttyx' (to make programs like 'who' happy). * * Modified by Dan Franklin: * Removed commented-out ptcwrite code; it will block RUBOUT * after TTHIWAT chars typed, which is undesirable. * 9/11/78 made ptcopen turn on virtual carrier. Otherwise the sequence * ptsopen - ptsopen - ptcclose - ptcopen leaves pty open with carrier off, * which causes slave reads to return EOF, which causes getty to go away * as soon as init forks it -- repeatedly! * Also removed wakeup on rawq.c_cf, since ptcwrite no longer sleeps on it * Added await and capacity stuff. * 9/27/78 ptsopen now sleeps on virtual carrier. ptsclose, ptsread, ptswrite, * ptcopen simplified accordingly. CHANGE: ptswrite now returns 0 on attempt * to write when controller side closed. This is consistent with handling * of "carrier" in other tty drivers, and means that random garbage from one * (server telnet) session will not get received by the next. Changed await * stuff to give controller side its own slot. * 1/4/78 BBN(dan) to fix stty bug: an stty on slave after * controller closed called ttstart when t_addr == 0. Changed to not * clear t_addr in ptcclose. Also changed ptsstart to check if controller * closed and flushtty if so -- this assures that after controller closed, * sleeping ttwrites will still complete. * 1/16/78 BBN(dan) to fix stty bug: stty on controller could lead to deadlock * waiting for output to drain if the process doing the stty is the one * that would drain the output. Fixed by imitating stty code in ttystty * without wflushtty call. Also changed ptccap to return half of what it * what it was before -- this allows for every char input being followed * by \377 in ttyinput. * 3/22/79 BBN(dan) feature: ctrller can now tell whether slave open * by examining speeds. Nonzero speed means open, zero speed means closed. * 4/9/79 BBN(dan): made use new await interface. * Not very reliable, but sufficient for some applications.. * 3/26/79 BBN(dan): Made empty() work on pty ctrller (ptcsgtty). * 4/25/79 BBN(dan): Deleted empty() line from ptcsgtty. * 5/4/79 BBN(dan): Made ptcopen set TS_SSTART bit (it already set t_addr). * Otherwise stty on ctrller with slave closed can crash system. * Also made ptsopen set t_addr (it already set TS_SSTART). * 5/13/79 converted to new tty driver. This included modifying ptcsgtty * to just turn off M_OWAIT to duplicate the effect formerly achieved by * by having a separate copy of the wflushtty code. * 6/22/79 Implemented Page-length processing. */ struct { struct tty _XXXX; int t_itpc; /* For await on controller side */ } pty[NPTY]; /* TTY headers for PTYs */ /* ----------------------------- P T S O P E N ---------------------- */ ptsopen(dev,flag) { register struct tty *tp; extern int pgrpct; extern ptsstart(); if (dev.d_minor >= NPTY) /* Verify minor device number */ { u.u_error = ENXIO; /* No such device */ return; } tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ tp->t_state =| TS_SSTART; /* Special start routine */ tp->t_addr = &ptsstart; if ((tp->t_state & TS_ISOPEN) == 0) /* Not already open? */ { /* Mark as open */ tp->t_state =| TS_ISOPEN; # ifdef NEWTTY ttyinit(tp); # endif # ifndef NEWTTY tp->t_flags = 0; tp->t_erase = CERASE; tp->t_kill = CKILL; # endif } ttyopen(dev, tp); while((tp->t_state & TS_CARR_ON) == 0) sleep(&tp->t_addr, TTIPRI); # ifdef NEWTTY tp->t_modes.t_ispeed = tp->t_modes.t_ospeed = B300; # endif # ifndef NEWTTY tp->t_speeds.lobyte = tp->t_speeds.hibyte = B300; # endif } /* ----------------------------- P T S C L O S E -------------------- */ ptsclose(dev) { register struct tty *tp; tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ if (tp->t_state & TS_CARR_ON) /* Other side open? */ wflushtty(tp); /* Yes, wait for it */ else flushtty(tp); /* No, blast it */ tp->t_state =& ~TS_ISOPEN; /* Mark as closed */ # ifdef NEWTTY tp->t_modes.t_ispeed = BHANGUP; /* So ctrller can find out */ tp->t_modes.t_ospeed = BHANGUP; # endif # ifndef NEWTTY tp->t_speeds.lobyte = tp->t_speeds.hibyte = BHANGUP; # endif } /* ----------------------------- P T S R E A D ---------------------- */ ptsread(dev) /* Read from PTY, i.e. from data written by controlling device */ { register struct tty *tp; tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ ttread(tp); if (tp-> t_itpc) awake (tp-> t_itpc, -1); /* kahn - wake controller */ } /* ----------------------------- P T S W R I T E -------------------- */ /* * Write on PTY, chars will be read from controlling device */ ptswrite(dev) { register struct tty *tp; tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ ttwrite(tp); } /* -------------------------- P T C O P E N -------------------- */ ptcopen(dev,flag) /* Open for PTY Controller */ { register struct tty *tp; extern ptsstart(); /* Routine to start write from slave */ if (dev.d_minor >= NPTY) /* Verify minor device number */ { u.u_error = ENXIO; /* No such device */ return; } tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ if (tp->t_state & TS_CARR_ON) /* Already open? */ { u.u_error = EIO; /* I/O error (Is there something better?) */ return; } tp->t_state =| TS_SSTART; tp->t_addr = &ptsstart; /* Set address of start routine */ tp->t_state =| TS_CARR_ON; /* Turn on virtual carrier */ tp->t_itpc = 0; /* No controller side awaiters yet */ if (tp->t_itp) awake(tp->t_itp, -1); /* Wake up slave's awaiters */ wakeup(&tp->t_addr); /* Wake up slave's openers */ } /* -------------------------- P T C C L O S E ------------------- */ ptcclose(dev) /* Close controlling part of PTY */ { register struct tty *tp; tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ if (tp->t_state & TS_ISOPEN) /* Is it slave open? */ signal(tp->t_pgrp,SIGHUP); /* Yes, send a HANGUP */ tp->t_state =& ~TS_CARR_ON; /* Virtual carrier is gone */ flushtty(tp); /* Clean things up */ } /* -------------------------- P T C R E A D ---------------------- */ ptcread(dev) /* Read from PTY's output buffer */ { register struct tty *tp; register int c; /* Unsigned char */ register int literal; tp = &pty[dev.d_minor]; # ifdef NEWTTY literal = tp->t_modes.t_oflags & TO_LITERAL; # endif /* Wait for something to arrive, or for XOFFHUNG condition to leave */ while (tp->t_outq.c_cc == 0 || tp->t_state & (TSO_XOFFHNG|TS_ENDPAGE)) sleep(&tp->t_outq.c_cf,TTIPRI); /* (Woken by ptsstart) */ /* Copy in what's there, or all that will fit */ while ((c = getc(&tp->t_outq)) != -1) { # ifdef NEWTTY if (literal == 0) if (c == CPAUSE) /* Page-len char? */ { if (tp->t_state & TS_ZOOM) /* In zoom mode, ignore */ continue; tp->t_state =| TS_ENDPAGE; /* Otherwise, set TS_ENDPAGE */ break; /* And be done for now */ } else # endif if (c >= 0200) /* If delay char, ignore. */ continue; if (passc(c) < 0) /* OK, pass it. If buffer fills, stop */ break; } /* Now, decide whether to wake up process waiting on input queue */ if (tp->t_outq.c_cc <= TTLOWAT) { if (tp->t_itp) awake(tp->t_itp, -1); /* Wake up slave side */ if (tp->t_state & TS_ASLEEP) { tp->t_state =& ~ TS_ASLEEP; wakeup(&tp->t_outq); } } } /* -------------------------- P T S S T A R T ------------------- */ ptsstart(tp) /* Called by 'ttstart' to output a character. */ /* Merely wakes up controlling half, which does actual work */ { if ((tp->t_state & TS_CARR_ON) == 0) /* Nobody there! */ flushtty(tp); /* To get rid of chars */ if (tp->t_itpc) awake(tp->t_itpc, -1); /* Wake up controller awaiters */ wakeup(&tp->t_outq.c_cf); } /* -------------------------- P T C W R I T E ------------------- */ ptcwrite(dev) /* Stuff characters into PTY's input buffer */ { register struct tty *tp; register int c; tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ /* For each character... */ while ((c = cpass()) >= 0) ttyinput(c,tp); } /* -------------------------- P T S S G T T Y ------------------- */ /* * Slave sgtty routine. Just call ttystty. */ ptssgtty(dev, v1, flag, v2) int dev; char *v1; int flag; char *v2; { register struct tty *tp; tp = &pty[dev.d_minor]; ttystty(tp, v1, flag, v2); } /* -------------------------- P T C S G T T Y ------------------- */ /* * Controller sgtty. Differs from slave only in that it does not * wait for pending output (it could wait a long time!). */ ptcsgtty(dev, v1, flag, v2) char *v1; int flag; char *v2; { register struct tty *tp; register char *v; tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */ # ifdef NEWTTY ttystty(tp, v1, flag & ~ M_OWAIT, v2); # endif # ifndef NEWTTY if (v1) /* gtty? */ ttystty(tp, v1); /* Normal */ else { /* Monkey ttystty without wflushtty */ v = u.u_arg; tp->t_speeds = *v++; tp->t_erase = v->lobyte; tp->t_kill = v->hibyte; tp->t_flags = v[1]; } # endif } /* =================== A W A I T & C A P A C I T Y ========== */ /* -------------------------- P T C C A P ----------------------- */ ptccap(ip, v) struct inode *ip; int *v; { register struct tty *tp; tp = &pty[ip->i_addr[0].d_minor]; *v = tp->t_outq.c_cc; /* Can read whatever's in output queue */ if (tp->t_state & (TSO_XOFFHNG|TS_ENDPAGE)) /* Unless it's hung */ *v = 0; v++; *v = TTYHOG - tp->t_rawq.c_cc; /* After this many, chars thrown away */ # ifdef NEWTTY *v =- 2; /* Allow some slop for T_EOFMARK */ # endif # ifndef NEWTTY *v =/ 2; /* Allow for every user char being followed by delimiter */ # endif } /* -------------------------- P T S C A P ---------------------- */ ptscap(ip, v) struct inode *ip; int *v; { register struct tty *tp; tp = &pty[ip->i_addr[0].d_minor]; ttycap(tp, v); } /* -------------------------- P T S A W T --------------------- */ ptsawt(type, ip, fd) char type; struct inode *ip; int fd; { extern char *ablei(); register struct tty *tp; tp = &pty[ip->i_addr[0].d_minor]; tp->t_itp = ablei(type, 0, ip, fd); } /* -------------------------- P T C A W T --------------------- */ ptcawt(type, ip, fd) char type; struct inode *ip; int fd; { extern char *ablei(); register struct tty *tp; tp = &pty[ip->i_addr[0].d_minor]; tp->t_itpc = ablei(type, 0, ip, fd); }