SRI-NOSC/dmr/pty.c

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

#/*
 * 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();
	}
}