BBN-V6/dmr/xpty.c
#
/*
* 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/xty.h"
#define ptcawt xtcawt
#define ptccap xtccap
#define ptcclose xtcclose
#define ptcopen xtcopen
#define ptcread xtcread
#define ptcsgtty xtcsgtty
#define ptcwrite xtcwrite
#define ptsawt xtsawt
#define ptscap xtscap
#define ptsclose xtsclose
#define ptsopen xtsopen
#define ptsread xtsread
#define ptssgtty xtssgtty
#define ptsstart xtsstart
#define ptswrite xtswrite
#define pty xpty
#define NPTY 2 /* Number of pseudo-teletypes */
#define NODELAY 0377
/*
* 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).
*/
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;
register struct proc *pp;
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 */
if ((tp->t_state & TS_ISOPEN) == 0) /* Not already open? */
{
/* Mark as open and with special start routine */
tp->t_state =| TS_ISOPEN | TS_SSTART;
tp->t_addr = &ptsstart;
tp->t_modes.t_erase = CERASE;
tp->t_modes.t_kill = CKILL;
}
ttyopen(dev, tp);
while((tp->t_state & TS_CARR_ON) == 0)
sleep(&tp->t_addr, TTIPRI);
tp->t_modes.t_speeds = (07<<L_SPEED) | 07; /* Set to 300 baud */
}
/* ----------------------------- 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); /* Otherwise, just blast it */
tp->t_state =& ~TS_ISOPEN; /* Mark as closed */
tp->t_modes.t_speeds = 0; /* So ctrller can find out */
}
/* ----------------------------- 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);
}
/* ----------------------------- 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_addr =| 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 temp;
tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */
while (tp->t_outq.c_cc==0) /* Wait for something to arrive */
sleep(&tp->t_outq.c_cf,TTIPRI); /* (Woken by ptsstart) */
temp = tp->t_outq.c_cc; /* Remember it */
/* Copy in what's there, or all that will fit */
while (tp->t_outq.c_cc && passc(getc(&tp->t_outq))>=0);
/* Now, decide whether to wake up process waiting on input queue */
if (tp->t_outq.c_cc==0 || (temp>TTLOWAT && tp->t_outq.c_cc<=TTLOWAT))
{
if (tp->t_itp)
awake(tp->t_itp, -1); /* Wake up slave side */
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, v, flag)
int *v;
int flag;
{
register struct tty *tp;
tp = &pty[dev.d_minor];
ttystty(tp, v, flag);
}
/* -------------------------- P T C S G T T Y ------------------- */
/*
* Controller sgtty. Differs from slave only in that stty calls
* do not do a wflushtty.
*/
ptcsgtty(dev, v, flag)
int *v;
int flag;
{
register struct tty *tp;
tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */
ttystty(tp, v, flag);
}
/* =================== 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 */
*v = TTYHOG - tp->t_inq.c_cc; /* After this many, chars thrown away */
*v =/ 2; /* Allow for every user char being followed by T_DELIM */
}
/* -------------------------- 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);
}