SRI-NOSC/dmr/pty.c
#/*
* Pseudo-teletype Driver
* (Actually two drivers, requiring two entries in 'cdevsw')
*/
#include "param.h"
#include "conf.h"
#include "user.h"
#include "tty.h"
#ifndef NPTY
#define NPTY 8 /* Number of pseudo-teletypes */
#endif
#define NODELAY (HUPCL | XTABS | LCASE | ECHO | CRMOD | RAW | ODDP | EVENP)
/*
* 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;
int 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 */
/* The slave signals that it is open by setting the ISOPEN or WOPEN
* bit in the t_state word. The controller signals that it is open
* by setting the CARR_ON bit in the t_state word.
*/
if ((tp->t_state&(ISOPEN|WOPEN))==0) /* closed? */
{
tp->t_addr = &ptsstart; /* special start routine */
tp->t_flags = 0;/* No special features (nor raw mode) */
tp->t_erase = CERASE;
tp->t_kill = CKILL;
}
for(;;) {
tp->t_state =| WOPEN|SSTART; /* Mark as opening */
if(tp->t_state&CARR_ON) break; /* has ptc openned yet? */
sleep(&tp->t_addr,TTIPRI+3); /* arbitrary prio */
}
ttyopen(dev,tp);
}
ptsclose(dev) /* Close slave part of PTY */
{
register struct tty *tp;
tp = &pty[dev.d_minor]; /* Setup pointer to PTY header */
/* wakeup(&tp->t_rawq); /* ???? */
tp->t_state =& ~(ISOPEN|WOPEN); /* Mark as closed */
wakeup(&tp->t_addr); /* Wakeup anybody trying to open */
ptsstart(tp); /* Let controller return EOF */
wflushtty(tp); /* Wait for it */
}
ptsread(dev) /* Read from PTY, i.e. from data written by controlling device */
{
ttread(&pty[dev.d_minor]);
}
ptswrite(dev) /* Write on PTY, i.e. to be read from controlling device */
{
ttwrite(&pty[dev.d_minor]);
}
/*
* The PTY Controller simulates a line driver -- it sets CARR_ON when there
* is somebody there to do something with it, and drops the carrier when he
* goes away. BUSY is set as long as the controlling side is open.
*/
ptcopen(dev,flag) /* Open for PTY Controller */
{
register struct tty *tp;
if (dev.d_minor >= NPTY || /* Verify minor device number */
(tp = &pty[dev.d_minor])->t_state&BUSY /* Already open? */
|| (tp->t_state&(ISOPEN|WOPEN)) == 0) { /* Disabled? */
u.u_error = ENXIO; /* No such device */
return;
}
tp->t_state =| (CARR_ON|BUSY); /* Mark as open */
wakeup(&tp->t_addr); /* Wakeup slave's open sleep */
}
ptcclose(dev) /* Close controlling part of PTY */
{
register struct tty *tp;
tp = &pty[dev.d_minor]; /* Point to PTY struct */
tp->t_state =& ~(CARR_ON|BUSY); /* Mark as closed */
if (tp->t_state&ISOPEN) /* Is slave open? */
signal(tp->t_pgrp,SIGHUP); /* Yes, send a HANGUP */
flushtty(tp); /* Clean things up */
}
ptcread(dev) /* Read from PTY's output buffer */
{
register struct tty *tp;
int ptsstart(), nullsys();
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|CARR_ON))==(ISOPEN|CARR_ON)) {
tp->t_addr = &ptsstart;
sleep(&tp->t_outq.c_cf,TTIPRI+2); /* (Woken by ptsstart) */
/*different prio for debuggin*/
tp->t_addr = &nullsys;
}
/* Copy in what's there, or all that will fit */
while (tp->t_outq.c_cc && passc(getc(&tp->t_outq))>=0);
/* Potentially wake up process waiting on output queue */
if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) {
tp->t_state =& ~ASLEEP;
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))
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;
tp = &pty[dev.d_minor];
if(ttystty(tp, v))
return; /* gtty */
/* Delay loses on pty's */
tp->t_flags =& NODELAY; /* was stty */
if(tp->t_speeds.lobyte == 0) {
tp->t_flags =| HUPCL;
tp->t_flags =& ~CARR_ON;
signal(tp->t_pgrp, SIGHUP);
ptsstart();
}
}