BBN-Vax-TCP/dev/pty.c

Compare this file to the similar file:
Show the results in this format:

/*	pty.c	4.7	81/03/11	*/

/*
 * Pseudo-teletype Driver
 * (Actually two drivers, requiring two entries in 'cdevsw')
 */
#include "pty.h"

#if NPTY > 0

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/tty.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/conf.h"
#include "../h/buf.h"
#include "../h/file.h"

#define NPTY 16			/* Number of pseudo-teletypes */
#define BUFSIZ 100		/* Chunk size iomoved from user */
#define ALLDELAYS (NLDELAY|TBDELAY|XTABS|CRDELAY|VTDELAY)
/*
 * 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
 * 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 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 pt_tty[NPTY];		/* TTY headers for PTYs */

/*ARGSUSED*/
ptsopen(dev, flag)
dev_t dev;
{					/* Open for PTY Slave */
	register struct tty *tp;

	if(minor(dev) >= NPTY) {
		u.u_error = ENXIO;
		return;
	}
	tp = &pt_tty[minor(dev)];
	if((tp->t_state & ISOPEN) == 0) {
		ttychars(tp);		/* Set up default chars */
		tp->t_flags = 0;	/* No features (nor raw mode) */
	} else if(tp->t_state&XCLUDE && u.u_uid != 0) {
		u.u_error = EBUSY;
		return;
	}
	if(tp->t_oproc)			/* Ctrlr still around. */
		tp->t_state |= CARR_ON;
	while((tp->t_state & CARR_ON) == 0) {
		tp->t_state |= WOPEN;
		sleep((caddr_t)&tp->t_rawq, TTIPRI);
	}
	(*linesw[tp->t_line].l_open)(dev, tp);
}

ptsclose(dev)
dev_t dev;
{					/* Close slave part of PTY */
	register struct tty *tp;

	tp = &pt_tty[minor(dev)];
	(*linesw[tp->t_line].l_close)(tp);
}

ptsread(dev)
dev_t dev;
{	/* Read from PTY, i.e. from data written by controlling device */
	register struct tty    *tp;

	tp = &pt_tty[minor(dev)];
	if(tp->t_oproc) {
		(*linesw[tp->t_line].l_read)(tp);
				/* Wakeup other half if sleeping */
		wakeup((caddr_t)&tp->t_rawq.c_cf);
	}
}

ptswrite(dev)
dev_t dev;
{			/* Write on PTY, i.e. to be read from
			   controlling device */
	register struct tty *tp;

	tp = &pt_tty[minor(dev)];
			/* Wait for controlling device to be opened */
	if(tp->t_oproc)
		(*linesw[tp->t_line].l_write)(tp);
}

ptsstart(tp)
struct tty *tp;
{			/* Called by 'ttstart' to output a character.
			   Merely wakes up controlling half, which
			   does actual work */
	if(tp->t_state & TTSTOP)
		return;
	wakeup((caddr_t)&tp->t_outq.c_cf);
}

/*ARGSUSED*/
ptcopen(dev, flag)
dev_t dev;
{				/* Open for PTY Controller */
	register struct tty *tp;

	if(minor(dev) >= NPTY) {
		u.u_error = ENXIO;
		return;
	}
	tp = &pt_tty[minor(dev)];
	if(tp->t_oproc) {
		u.u_error = EIO;
		return;
	}
	tp->t_oproc = ptsstart;		/* Set address of start routine */
	tp->t_iproc = 0;
	if(tp->t_state & WOPEN)
		wakeup((caddr_t)&tp->t_rawq);
	tp->t_state |= CARR_ON;
}

ptcclose(dev)
dev_t dev;
{					/* Close controlling part of PTY */
	register struct tty *tp;

	tp = &pt_tty[minor(dev)];
	if(tp->t_state & ISOPEN)
		gsignal(tp->t_pgrp, SIGHUP);
	tp->t_state &= ~CARR_ON;	/* Virtual carrier is gone */
	flushtty(tp, FREAD|FWRITE);		     /* Clean things up */
	tp->t_oproc = 0;		/* Mark as closed */
}

ptcread(dev)
dev_t dev;
{					/* Read from PTY's output buffer */
	register struct tty *tp;

	tp = &pt_tty[minor(dev)];
	if((tp->t_state&(CARR_ON|ISOPEN)) == 0)
		return;
	while(tp->t_outq.c_cc == 0 ||	/* Wait for something to arrive */
	      (tp->t_state&TTSTOP))	/* (Woken by ptsstart) */
		sleep((caddr_t)&tp->t_outq.c_cf, TTIPRI);
	while(tp->t_outq.c_cc && passc(getc(&tp->t_outq)) >= 0);
	if(tp->t_outq.c_cc <= TTLOWAT(tp)  && (tp->t_state&ASLEEP)) {
		tp->t_state &= ~ASLEEP;
		if(tp->t_chan)
			mcstart(tp->t_chan, (caddr_t)&tp->t_outq);
		else
			wakeup((caddr_t)&tp->t_outq);
	}
}

ptcwrite(dev)
dev_t dev;
{			/* Stuff characters into PTY's input buffer */
	register struct tty *tp;
	register char *cp, *ce;
	register int cc;
	char locbuf[BUFSIZ];

	tp = &pt_tty[minor(dev)];
	if((tp->t_state&(CARR_ON|ISOPEN)) == 0)
		return;
	while(u.u_count) {
		cc = MIN(u.u_count, BUFSIZ);
		cp = locbuf;
		iomove(cp, (unsigned)cc, B_WRITE);
		if(u.u_error)
			break;
		ce = cp + cc;
		while(cp < ce) {
			while(tp->t_delct && tp->t_rawq.c_cc >= TTYHOG - 2) {
				wakeup((caddr_t)&tp->t_rawq);
				/* Better than just flushing it! */
				/* Wait for something to be read */
				sleep((caddr_t)&tp->t_rawq.c_cf, TTOPRI);
			}
			(*linesw[tp->t_line].l_rint)(*cp++, tp);
		}
	}
}

/* Note: Both slave and controlling device have the same routine for */
/* 'ioctl' (but note check for controller - 4/12/78:mob)*/
/*ARGSUSED*/
ptyioctl(dev, cmd, addr, flag)
caddr_t addr;
dev_t dev;
{					/* Read and write status bits */
	register struct tty *tp;
	register int tbd;
#ifdef BLAND
	register int nld;
#endif

	tp = &pt_tty[minor(dev)];
		/* if controller stty then must flush to prevent a hang */
	if(cdevsw[major(dev)].d_open == ptcopen && cmd == TIOCSETP)
		while(getc(&tp->t_outq) >= 0);
	if(ttioctl(tp, cmd, addr, dev)) {
		if(cmd == TIOCSETP || cmd == TIOCSETN) {
#ifdef BLAND
			nld = tp->t_flags & NLDELAY;
#endif
			tbd = tp->t_flags & TBDELAY;
			tp->t_flags &= ~ALLDELAYS;
			if(tbd == TBDELAY)	/* Wants tab expansion */
				tp->t_flags |= tbd;
#ifdef BLAND
			if(nld == NLDELAY)	/* Allow ANN ARBOR mode. */
				tp->t_flags |= nld;
#endif
		}
	} else
		u.u_error = ENOTTY;
}
#endif