File:  [MW Coherent from dump] / coherent / b / kernel / io.386 / pty.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:37 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * File:	pty.c
 *
 * Purpose:	pseudoterminal device driver
 *
 *	Master devices are named /dev/pty[pqrs][0-f].
 *	Corresponding slaves are /dev/tty[pqrs][0-f].
 *
 *	Minor numbers are 0..127 assigned in increasing order,
 *	plus 128 for master device.
 *
 *	Output written to master appears as input to slave and
 *	vice versa.  Data path is:
 *
 *		app	master		slave	line	app
 *		using	device	shunt	device	disc.	using
 *		master	driver		driver	module	slave
 *
 *      slave read does ttread() which is fed by ttin()
 *	master write does ttin()
 *
 *      slave write does ttwrite() which is fed by ttout()
 *	master read does ttout()
 *
 * $Log: pty.c,v $
 * Revision 1.1.1.1  2019/05/29 04:56:37  root
 * coherent
 *
 * Revision 1.4  93/04/14  10:12:10  root
 * r75
 * 
 * Revision 1.3  92/07/16  16:35:29  hal
 * Kernel #58
 * 
 * Revision 1.2  92/03/18  07:45:33  hal
 * master device polling added
 * 
 * Revision 1.1  92/03/16  12:57:31  hal
 * Initial revision
 * 
 */

/*
 * -----------------------------------------------------------------
 * Includes.
 */
#include <sys/coherent.h>
#include <sys/stat.h>
#include <sys/proc.h>
#include <sys/tty.h>		/* indirectly includes sgtty.h */
#include <sys/con.h>
#include <sys/devices.h>
#include <errno.h>
#include <poll.h>
#include <sys/sched.h>		/* CVTTOUT, IVTTOUT, SVTTOUT */

/*
 * -----------------------------------------------------------------
 * Definitions.
 *	Constants.
 *	Macros with argument lists.
 *	Typedefs.
 *	Enums.
 */

#define channel(dev)	(dev & 0x7F)
#define master(dev)	(dev & 0x80)
#ifdef _I386
#define	EEBUSY	EBUSY
#else
#define	EEBUSY	EDBUSY
#endif

/*
 * Explanation of p_mopen values:
 * 0 - master is closed
 * 1 - master open, waiting for slave to open
 * 2 - master and slave both open
 * 3 - master open, slave has closed
 */

typedef struct pty {
	TTY p_tp;
	event_t p_iev;
	event_t p_oev;
	char p_mopen;
	char p_asleep;	/* master is asleep in read or write awaiting slave */
	char p_ttwr;	/* slave is suspended in mid ttwrite() */
} PTY;

/*
 * -----------------------------------------------------------------
 * Functions.
 *	Import Functions.
 *	Export Functions.
 *	Local Functions.
 */
int nulldev();
void pollwake();

/*
 * Configuration functions (local functions).
 */
static void ptyclose();
static void ptyioctl();
static void ptyload();
static void ptyopen();
static void ptyread();
static void ptyunload();
static void ptywrite();
static void ptystart();
static int ptypoll();

/*
 * Support functions (local functions).
 */
static void ptycycle();

/*
 * -----------------------------------------------------------------
 * Global Data.
 *	Import Variables.
 *	Export Variables.
 *	Local Variables.
 */
int NUPTY = 8;

/*
 * Configuration table (export data).
 */
CON ptycon ={
	DFCHR|DFPOL,			/* Flags */
	PTY_MAJOR,			/* Major index */
	ptyopen,			/* Open */
	ptyclose,			/* Close */
	nulldev,			/* Block */
	ptyread,			/* Read */
	ptywrite,			/* Write */
	ptyioctl,			/* Ioctl */
	nulldev,			/* Powerfail */
	nulldev,			/* Timeout */
	ptyload,			/* Load */
	ptyunload,			/* Unload */
	ptypoll				/* Poll */
};

static PTY * p;

/*
 * -----------------------------------------------------------------
 * Code.
 */

/*
 * ptyload()
 */
static void
ptyload()
{
	int i;
	TTY * tp;

	if ((p = (PTY *)kalloc(NUPTY*sizeof(PTY))) == 0) {
		printf("ptyload: can't allocate %s pty's\n", NUPTY);
		return;
	}
	kclear(p, NUPTY*sizeof(PTY));
	for (i = 0; i < NUPTY; i++) {
		tp = &p[i].p_tp;
		tp->t_cs_sel  = cs_sel();
		tp->t_start   = ptystart;
		tp->t_param   = nulldev;
		tp->t_ddp     = (char *)i;
	}
}

/*
 * ptyunload()
 */
static void
ptyunload()
{
	if (p)
		kfree(p);
	printf("pty driver unloaded\n");
}

/*
 * ptyopen()
 */
static void
ptyopen(dev, mode)
dev_t dev;
int mode;
{
	int chan = channel(dev);
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;

	if (chan >= NUPTY) {
		u.u_error = ENXIO;
		goto open_done;
	}

	if (master(dev)){
		if (pp->p_mopen) {
			u.u_error = EEBUSY;
			goto open_done;
		}
		if (tp->t_open)
			pp->p_mopen = 2;
		else
			pp->p_mopen = 1;
		wakeup((char *)(&tp->t_open));
		ptycycle(chan);
	} else {
		tp->t_flags |= T_HOPEN | T_STOP;
		for (;;) {	/* wait for carrier */
			if (pp->p_mopen)
				break;
#ifdef _I386
			x_sleep((char *)(&tp->t_open), pritty, slpriSigCatch,
			  "ptycd");
#else
			v_sleep((char *)(&tp->t_open), CVTTOUT, IVTTOUT,
			  SVTTOUT, "ptycd");
#endif
			/* PTY driver is waiting for carrier.  */
			if (SELF->p_ssig && nondsig()) {  /* signal? */
				u.u_error = EINTR;
				tp->t_flags &= ~(T_HOPEN | T_STOP);
				goto open_done;
			}
		}
		tp->t_flags |= T_CARR;
		tp->t_flags &= ~(T_HOPEN | T_STOP);
		ttopen(tp);
		tp->t_open++;
		ttsetgrp(tp, dev, mode);
		if (pp->p_mopen == 1 || pp->p_mopen == 3)
			pp->p_mopen = 2;
	}
open_done:;
}

/*
 * ptyclose()
 */
static void
ptyclose(dev, mode)
dev_t dev;
int mode;
{
	int chan = channel(dev);
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;

	if (chan >= NUPTY) {
		u.u_error = ENXIO;
		return;
	}

	if (master(dev)){
		if (pp->p_mopen) {
			tp->t_flags &= ~T_CARR;
			tthup(tp);
			pp->p_mopen = 0;
		}
	} else {
		if (--tp->t_open == 0) {
			ttclose(tp);
			if (pp->p_mopen == 2)
				pp->p_mopen = 3;
			wakeup(&pp->p_mopen);
		}
	}
}

/*
 * ptyread()
 */
static void
ptyread(dev, iop)
dev_t dev;
register IO * iop;
{
	int chan = channel(dev);
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;
	int c;

	if (master(dev)){
		int char_read = 0;

		while (iop->io_ioc) {
			c = ttout(tp);
			if (c == -1) { /* nothing to fetch */
				if (char_read) {
					ttstart(tp);
					goto read_done;
				}
				if (iop->io_flag & IONDLY) {
					u.u_error = EAGAIN;
					goto read_done;
				}
				if (pp->p_mopen == 3) {
					u.u_error = EIO;
					goto read_done;
				}
				ttstart(tp);
				pp->p_asleep = 1;
#ifdef _I386
				x_sleep(&pp->p_mopen, pritty, slpriSigCatch,
				  "ptyread");
#else
				v_sleep(&pp->p_mopen, CVTTOUT, IVTTOUT,
				  SVTTOUT, "ptyread");
#endif
				/* The PTY driver is waiting for a read.  */
				if (SELF->p_ssig && nondsig()) {
					u.u_error = EINTR;
					goto read_done;
				}
			} else {
				ioputc(c, iop);
				char_read = 1;
			}
		}
read_done:;
	} else {
#if 0
		if (pp->p_asleep) {
			pp->p_asleep = 0;
			wakeup(&pp->p_mopen);
		}
		pollwake(&pp->p_oev);
		ttread(tp, iop);
#else
		pp->p_asleep = 0;	/* ttread0 will awaken the sleeper */
		ttread0(tp,iop,wakeup,&pp->p_mopen,pollwake,&pp->p_oev);
#endif
	}
}

/*
 * ptywrite()
 */
static void
ptywrite(dev, iop)
dev_t dev;
register IO * iop;
{
	int chan = channel(dev);
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;
	int c;

	if (master(dev)){
		while (iop->io_ioc) {
			if (!ttinp(tp)) {
				if (iop->io_flag & IONDLY) {
					u.u_error = EAGAIN;
					goto write_done;
				}
				if (pp->p_mopen == 3) {
					u.u_error = EIO;
					goto write_done;
				}
				pp->p_asleep = 1;
#ifdef _I386
				x_sleep(&pp->p_mopen, pritty, slpriSigCatch,
				  "ptywrite");
#else
				v_sleep(&pp->p_mopen, CVTTOUT, IVTTOUT,
				  SVTTOUT, "ptywrite");
#endif
				/* The PTY driver is waiting for a write.  */
				if (SELF->p_ssig && nondsig()) {  /* signal? */
					u.u_error = EINTR;
					goto write_done;
				}
			}
			c = iogetc(iop);
			ttin(tp, c);
		}
		wakeup(&pp->p_mopen);
	} else {
		pp->p_ttwr = 1;
		ttwrite0(tp,iop,wakeup,&pp->p_mopen,pollwake,&pp->p_iev);
		pp->p_ttwr = 0;
	}
write_done:;
}

/*
 * ptyioctl()
 */
static void
ptyioctl(dev, com, vec)
dev_t	dev;
int	com;
struct sgttyb *vec;
{
	int chan = channel(dev);
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;

	if (master(dev)){
		u.u_error = EINVAL;
	} else {
		ttioctl(tp, com, vec);
	}
}

/*
 * ptystart()
 */
static void
ptystart(tp)
TTY * tp;
{
	int chan = (int)tp->t_ddp;
	PTY * pp = p + chan;

	if (chan >= 0 && chan < NUPTY) {
		if (pp->p_ttwr)
			wakeup(&pp->p_mopen);
	}
}

/*
 * ptypoll()
 */
static int
ptypoll(dev, ev, msec)
dev_t dev;
int ev;
int msec;
{
	int chan = channel(dev);
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;
	int ret;

	if (master(dev)) {
		/*
		 * Priority polls not supported.
		 */
		ev &= (POLLIN | POLLOUT);

		/*
		 * Input poll with no data present.
		 */
		if ((ev & POLLIN) && (ttoutp(tp) == 0)) {

			/*
			 * Blocking input poll.
			 */
			if (msec != 0) {
				pollopen(&pp->p_iev);
			}

			/*
			 * Second look to avoid interrupt race.
			 */
			if (ttoutp(tp) == 0)
				ev &= ~POLLIN;
		}

		/*
		 * Output poll with no space.
		 */
		if ((ev & POLLOUT) && (ttinp(tp) == 0)) {

			/*
			 * Blocking output poll.
			 */
			if (msec != 0) {
				pollopen(&pp->p_oev);
			}

			/*
			 * Second look to avoid interrupt race.
			 */
			if (ttinp(tp) == 0)
				ev &= ~POLLOUT;
		}

		ret = ev;
	} else
		ret = ttpoll(tp, ev, msec);
	return ret;
}

/*
 * ptycycle()
 *
 * Do a wakeup of any sleeping pty's at regular intervals.
 */
static void
ptycycle(chan)
int chan;
{
	PTY * pp = p + chan;
	TTY * tp = &pp->p_tp;

	/*
	 * Do wakeups.
	 */
	if (pp->p_asleep || pp->p_ttwr) {
		wakeup(&pp->p_mopen);
		pollwake(&pp->p_oev);
	}

	/*
	 * Schedule next cycle.
	 */
	if (pp->p_mopen)
		timeout(&tp->t_rawtim, HZ/10, ptycycle, chan);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.