SRI-NOSC/dmr/misc/pty.c.ill
#
#define PGRPS x
/*
* Pseudo-teletype Driver
* (Actually two drivers, requiring two entries in 'cdevsw')
*
* install with cc -Oc pty.c [-D EXTA] [-D PGRPS]
* ar r lib pty.o
*
* EXTA adds code so that the pty can be used as a batch device, ala
* TOPS-10 (This code is experimental and doesn't work).
* PGRPS if defined indicates that process groups are to be used.
* Otherwise the older non-process group signalling system will be
* used.
*
* This particular source is slightly more elegant than the current
* version of pty.c . Unfortunately this version is also 8 bytes
* bigger. For certain installations 8 bytes is a lot... So being
* basically chicken hearted, this version is classed as EXPERIMENTAL.
*/
#include "/usr/sys/h/param.h"
#include "/usr/sys/h/conf.h"
#include "/usr/sys/h/user.h"
#include "/usr/sys/h/tty.h"
#include "/usr/sys/h/proc.h"
#define NPTY 8 /* Number of pseudo-teletypes */
#define NODELAY (HUPCL | XTABS | LCASE | ECHO | CRMOD | RAW | ODDP | EVENP)
#define SSPEED 11 /* DLM Special 2400 */
/*#define EXTA ((14<<8)|14)*/
/*
* 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).
*/
struct tty pty[NPTY]; /* TTY headers for PTYs */
ptsopen(dev,flag) /* Open for PTY Slave */
{
register struct tty *tp;
#ifndef PGRPS
register struct proc *pp;
#endif PGRPS
extern int nullsys();
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 */
/* The slave signals that it is open by setting the ISOPEN
* bit in the t_state byte. The controller signals that it is
* is open by setting the CARR_ON bit in the t_state byte.
* The absense of these conditions signals that the respective
* side has closed. This signalling is necessary because these
* closes can occur asynchronously.
*/
if ((tp->t_state&(ISOPEN|WOPEN))==0) /* closed? */
{
tp->t_state =| WOPEN|SSTART;
/* Mark as open and with */
/* special start routine */
tp->t_flags = 0;/* No special features (nor raw mode) */
tp->t_erase = CERASE;
tp->t_kill = CKILL;
tp->t_speeds = (SSPEED | (SSPEED << 8)); /* DLM Special */
}
#ifdef PGRPS
ttyopen(dev,tp);
#endif PGRPS
#ifndef PGRPS
pp = u.u_procp;
if (pp->p_ttyp == 0)
{
pp->p_ttyp = tp; /* for /dev/tty etc */
tp->t_dev = dev;
}
tp->t_state =| ISOPEN;
#endif PGRPS
for(;;)
{
wakeup(&tp->t_state); /* Wakeup controller's open sleep */
if(tp->t_state&CARR_ON) /* has ptc openned yet? */
break; /* this should not happen the first
* time around the loop
*/
sleep(&tp->t_addr,TTIPRI+3); /* arbitrary prio */
}
}
ptsclose(dev) /* Close slave part of PTY */
{
register struct tty *tp;
extern int ptsstart();
tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */
tp->t_state =& ~WOPEN; /* Mark intermediate state */
if (tp->t_state&CARR_ON) /* Other side open? */
{
ptsstart(tp);
wakeup(&tp->t_rawq);
wflushtty(tp); /* Yes, wait for it */
}else{
flushtty(tp); /* Otherwise, just blast it */
}
tp->t_state =& ~ISOPEN; /* Mark as closed */
}
ptsread(dev) /* Read from PTY, i.e. from data written by controlling device */
{
register struct tty *tp;
tp = &pty[dev.d_minor]; /* point to PTY struct */
# ifdef EXTA
if(tp->t_speeds==EXTA)
ptttread(tp);
else
# endif EXTA
ttread(tp);
}
ptswrite(dev) /* Write on PTY, i.e. to be read from controlling device */
{
register struct tty *tp;
extern int ptsstart();
tp = &pty[dev.d_minor]; /* point to PTY struct */
ttwrite(tp);
}
ptcopen(dev,flag) /* Open for PTY Controller */
{
register struct tty *tp;
extern int 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&CARR_ON) /* Already open? */
{
u.u_error = EIO; /* I/O error
(Is there something better? */
return;
}
tp->t_addr = &ptsstart; /* Name of start routine */
tp->t_state =| CARR_ON;
for(;;)
{
wakeup(&tp->t_addr); /* Wakeup slave's open sleep */
if(tp->t_state&ISOPEN)
break; /* this should not happen the first
* time around the loop
*/
sleep(&tp->t_state,TTIPRI+4); /* arbitrary prio */
}
}
ptcclose(dev) /* Close controlling part of PTY */
{
register struct tty *tp;
extern int nullsys();
tp = &pty[dev.d_minor]; /* Point to PTY struct */
tp->t_state =& ~CARR_ON; /* Mark as closed */
if (tp->t_state&(ISOPEN|WOPEN)) /* Is it slave open? */
#ifdef PGRPS /* process group system */
signal(tp->t_pgrp,SIGHUP); /* Yes, send a HANGUP */
#endif
#ifndef PGRPS /* for old version six */
signal (tp, SIGHUP);
#endif
flushtty(tp); /* Clean things up */
}
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 */
wakeup(&tp->t_rawq);
/* Wait for something to arrive */
while (tp->t_outq.c_cc==0 && (tp->t_state&ISOPEN))
{
sleep(&tp->t_outq.c_cf,TTIPRI+2); /* (Woken by ptsstart */
/*different prio for debuggin*/
}
temp = tp->t_outq.c_cc; /* Remember so we don't check every time */
/* Copy in what's there, or all that will fix */
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))
wakeup(&tp->t_outq);
}
ptsstart(tp) /* Called by 'ttstart' to output a character. Merely wakes up */
{ /* controlling half, which does actual work */
wakeup(&tp->t_outq.c_cf);
}
ptcwrite(dev) /* Stuff characters into PTY's input buffer */
{
register struct tty *tp;
register char c;
tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */
/* For each character test if the other side is still open */
/* do this first in order to give the writer a better idea of how */
/* many chars he sent */
while ((c = cpass()) >= 0 && (tp->t_state&ISOPEN))
{
# ifdef EXTA
if(tp->t_speeds==EXTA) /* Extern A */
{
ptsstart(tp);
while(tp->t_delct>0 && (tp->t_state&ISOPEN))
{
sleep(&tp->t_rawq,TTIPRI+1); /* different
* prio so
* we can see
* it with ps
*/
}
}
# endif EXTA
ttyinput(c,tp);
}
}
/* Note: Both slave and controlling device have the same routine for */
/* 'sgtty' */
ptysgtty(dev,v) int *v; /* Read and write status bits */
{
register struct tty *tp;
register int *vv;
tp = &pty[dev.d_minor];
# ifdef EXTA
if(tp->t_speeds==EXTA && v == 0)
{
vv = &u.u_arg[1];
tp->t_erase = vv->lobyte;
tp->t_kill = vv->hibyte;
tp->t_flags = vv[1];
}
else
# endif EXTA
if(ttystty(tp, v))
return; /* gtty */
else
/* Delay loses on pty's */
tp->t_flags =& NODELAY; /* was stty */
}
/* This is the ttread for pty's if we are going to use pty's for
* batch processing. The only addition is that echoing is done at
* the time that the char is read up into user space. This increase
* in synchrony between the input and output is needed so that chars
* from user processes don't fall all over the echo of input chars.
* Echo is assumed
* to be off and speed of EXTA is used to indicate the special
* function.)
*/
#ifdef EXTA
ptttread(atp)
struct tty *atp;
{
register struct tty *tp;
register cnt;
register int c;
tp = atp;
cnt = 0;
if ((tp->t_state&CARR_ON)==0)
return( 0 );
if (tp->t_canq.c_cc || canon(tp)) {
while (tp->t_canq.c_cc && passc(c=getc(&tp->t_canq))>=0) {
if(tp->t_speeds==EXTA)
ttyoutput(c,tp);
cnt++;
}
}
return( cnt );
}
#endif EXTA