h03402
s 00692/00000/00000
d D 1.3 82/09/13 14:21:03 ross 1 0
e
u
U
t
T
I 1
/*
 * Univac 'U100 Protocol' line discipline
 */

#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/tty.h"

#define	NO	0
#define	YES	1
#define NUV	4		/* number of pseudo-stations */
#define UVHOG	256		/* maximum queued output per user */
#define UVIHWM	512		/* max queued input per user */
#define UVILWM	32		/* min queued input per user */
#define UOBUFS	256		/* size of host output buffer */

/*
 * Information about a single pseudo-terminal station
 */
struct station {
	char	trid, tsid, tdid;	/* communication address */
	struct clist inq;	/* input to user from  host */
	struct clist outq;	/* output from user to host */
	int	msgcount;	/* count of lines in outq */
	int state;		/* internal state */
};

/*
 * Information about pseudo-multiplexor line
 */
struct univac {
	struct tty	*line;	/* tty structure of line device */
	int	nactive;	/* number of active stations */
	char	rid, sid, did;	/* poll address */
	char	*text;		/* start of text part of ibuf */
	int	tlen;		/* length of text part of ibuf */
	char	obuf[UOBUFS];	/* output buffer */
	int	olen;		/* length of message in output buffer */
	char	qbuf[32];	/* separate output buffer for DENQ response */
				/* this buffer was overflowing-->expanded */
};

/*
 * Host message types
 */
#define M_NULL		(-1)
#define M_TEXT		0
#define M_POLL		1
#define M_APOLL		2
#define M_STAT		3
#define M_ASTAT		4
#define M_DNAK		5

/*
 * Station response types
 */
#define R_NULL		(-1)
#define	R_TEXT		0
#define	R_TRAFFIC	1
#define	R_ACK		2
#define R_EOT		3
#define	R_DENQ		4
#define	R_AGAIN		5
#define R_WABT		6
#define R_THRU		7

/*
 * ASCII characters
 */
#define	NUL	'\000'
#define	SOH	'\001'
#define	STX	'\002'
#define	ETX	'\003'
#define	ENQ	'\005'
#define	BEL	'\007'
#define	DLE	'\020'
#define DC1	'\021'
#define	DC3	'\023'
#define	NAK	'\025'
#define	SYN	'\026'
#define	EOM	'\031'
#define DEL	'\177'

/*
 * General polling addresses
 */
#define GSID	'P'		/* general site id */
#define GDID	'p'		/* general device id */

struct univac	uv;
struct station	station[NUV];
int		tracing = 0;

char	Rid	= '\074';	/* common remote id for all stations */
char	Sid[]	= {		/* site id's: */
	'\121',		/*	CSCI01 */
	'\122',		/*	CSCI02 */
	'\123',		/*	CSCI03 */
	'\124',		/*	CSCI04 */
};
char	Did[]	= {		/* device id's for input to host */
	'x',
	'x',
	'x',
	'x',
};

#define	LOCK	spl4()
#define UNLOCK	spl0()
#define	UVIPRI	28
#define	UVOPRI	29

/********************************************************
 *							*
 *	Station pseudo-device interface			*
 *							*
 ********************************************************/

/*
 * Station pseudo-device open routine:
 *	set up polling addresses
 *	open real line device if necessary
 */
uvopen(dev)
{
	register struct station	*st;
	register struct tty *tp;
	int uvon();

	if (minor(dev) >= NUV || (tp = uv.line) == NULL
	    || linesw[tp->t_line].l_open != uvon) {
		u.u_error = ENXIO;
		return;
	}
	st = &station[minor(dev)];
	if (st->trid) {
		u.u_error = EBUSY;
		return;
	}
	if (uv.nactive == 0) {
		if (tp->t_state & ISOPEN)
			u.u_error = EBUSY;
		else
			(*cdevsw[major(tp->t_dev)].d_open)(tp->t_dev, 1);
		if (u.u_error)
			return;
	}
	uv.nactive++;
	st->trid = Rid;
	st->tsid = Sid[minor(dev)];
	st->tdid = Did[minor(dev)];
}

/*
 * Station pseudo-device close routine:
 *	set address to null so won't respond to any poll
 *	close real line device if necessary
 */
uvclose(dev)
{
	register struct station	*st = &station[minor(dev)];
	register struct tty *tp;

	st->trid = 0;
	while (getc(&st->inq) >= 0)
		;
	while (getc(&st->outq) >= 0)
		;
	st->state = 0;
	if (--uv.nactive == 0) {
		tp = uv.line;
		(*cdevsw[major(tp->t_dev)].d_close)(tp->t_dev);
	}
}

/*
 * Station pseudo-device read routine:
 */
uvread(dev)
{
	register struct station *st = &station[minor(dev)];

	LOCK;
	while (st->inq.c_cc == 0) {
		if ((uv.line->t_state&CARR_ON) == 0)
			return(0);
		sleep((caddr_t)&st->inq, UVIPRI);
	}
	UNLOCK;
	while (st->inq.c_cc && passc(getc(&st->inq)) >= 0)
		;
}

/*
 * Station pseudo-device write routine:
 */
uvwrite(dev)
{
	register struct station *st = &station[minor(dev)];
	register int	c;

	LOCK;
	while (st->msgcount) {
		if ((uv.line->t_state&CARR_ON) == 0)
			return;
		sleep((caddr_t)&st->outq, UVOPRI);
	}
	UNLOCK;
	while (st->outq.c_cc < UVHOG && (c = cpass()) >= 0)
		putc(c, &st->outq);
	st->msgcount++;
}

/********************************************************
 *							*
 *		Line discipline interface		*
 *							*
 ********************************************************/

/*
 * Line discipline open routine:
 *	start U100 protocol on line
 */
uvon(dev, tp)
register struct tty	*tp;
{
	if (uv.nactive != 0) {
		u.u_error = EBUSY;
		return;
	}
	uv.line = tp;
	if (tp->t_iproc)
		(*tp->t_iproc)(tp);
}

/*
 * Line discipline close routine:
 *	do nothing:  once protocol is turned on, it stays even after the
 *		line is closed
 */
uvoff(tp)
struct tty	*tp;
{
}

/*
 * Line discipline block input routine:
 */
uvrend(tp, pb, pe)
register struct tty	*tp;
register char		*pb, *pe;
{
	register char	*npb;
	register int	msgtype;
	extern int	trmask;

	*pe = ETX;	/* sentinel */
	while (pb < pe) {	/* Loop for maybe >1 msg in buffer */
		if ((npb = uvparity(pb)) == 0)
			msgtype = M_NULL;
		else
			msgtype = uvpoll(pb);
		if (trmask & 0x20) {
			putchar("NTPASQD"[msgtype+1]);
			if (msgtype == M_NULL) {
				putchar('[');
				while (pb < pe)
					printf(" %x", *pb++);
				printf(" ]");
			}
		}
		if (msgtype == M_NULL)
			break;
		if (uvmux(tp, msgtype))
			return;
		pb = npb;
	}
	if (tp->t_iproc)
		(*tp->t_iproc)(tp);
}

/*
 * Line discipline start-output routine:
 */
uvstart(tp)
register struct tty	*tp;
{
	if (tp->t_iproc)
		(*tp->t_iproc)(tp);
}

/********************************************************
 *							*
 *		Multiplexor emulator			*
 *							*
 ********************************************************/

/*
 * Response priorities
 */
char prio[] = {
	2,	/* R_TEXT */
	2,	/* R_TRAFFIC */
	3,	/* R_ACK */
	4,	/* R_EOT */
	1,	/* R_DENQ */
	1,	/* R_AGAIN */
	3,	/* R_WABT */
	2	/* R_THRU */
};

/*
 * Simulate action of terminal multiplexor
 *	- pass host message to all addressed stations
 *	- select responding station according to priority of response
 *	- combine ACK or WABT from one station with traffic from another
 */
uvmux(atp, msgtype)
register int	msgtype;
{
	register struct station	*tp;
	register int	resp;
	register int	s_prio = 10;
	register int	s_resp;
	register int	s_ack = R_NULL;
	register struct station	*s_tp;
	static struct station	*r_tp = &station[0];

	tp = r_tp;
	do {	/* examine stations in round-robin */
		if (uv.rid == tp->trid
		    && (uv.sid == GSID || uv.sid == tp->tsid)) {
			resp = uvstation(tp, msgtype, s_prio > 2);
			if (resp == R_ACK || resp == R_WABT)
				s_ack = resp;
			if (resp != R_NULL && prio[resp] < s_prio) {
				s_tp = tp;
				s_resp = resp;
				s_prio = prio[resp];
			}
		}
		if (++tp >= &station[NUV])
			tp = &station[0];
	} while (tp != r_tp);
	if (s_prio != 10) {
		uvrespond(atp, s_tp, s_resp, s_ack);
		r_tp = s_tp;
		return(1);
	}
	return(0);
}

/********************************************************
 *							*
 *		Station emulator			*
 *							*
 ********************************************************/

/*
 * States for Finite-State Automaton emulator
 */
#define	IDLE	0
#define	TWACK	1	/* awaiting acknowledgment for text */
#define	WACK	2	/* awaiting acknowledgment for non-text */
#define	OACK	3	/* owing acknowledgment */
#define WABT	4	/* awaiting acknowledgment for wait-a-bit */
#define WAIT	5	/* between wait-a-bit and thru */

/*
 * Decision table to map (state, host message type) -> action
 *	- actions determine response message type & next state
 */
char action[6][6] = {
	/*	M_TEXT	M_POLL	M_APOLL	M_STAT	M_ASTAT	M_DNAK */
/* IDLE */	2,	1,	1,	6,	6,	0,
/* TWACK */	0,	8,	7,	8,	6,	9,
/* WACK */	0,	4,	1,	4,	6,	5,
/* OACK */	0,	3,	3,	3,	3,	0,
/* WABT */	0,	11,	10,	11,	10,	12,
/* WAIT */	0,	10,	10,	10,	10,	0,
};

uvstation(tp, msgtype, selected)
register struct station	*tp;
int	msgtype;
int	selected;
{
	register int	resp;
	register int	state = tp->state;

	switch (action[state][msgtype]) {

		case 1:		/* traffic poll */
			if (tp->msgcount && selected) {
				resp = R_TEXT;
				state = TWACK;
			} else {
				resp = R_EOT;
				state = IDLE;
			}
			break;

		case 2:		/* save host output text for user */
			uvgetline(tp);
			resp = R_NULL;
			state = OACK;
			break;

		case 3:		/* acknowledge host output text */
			if (tp->inq.c_cc >= UVIHWM) {
				resp = R_WABT;
				state = WABT;
			} else {
				resp = R_ACK;
				state = WACK;
			}
			break;

		case 4:
		case 8:
		case 11:	/* ask for missing acknowledgment */
			resp = R_DENQ;
			break;

		case 5:
		case 9:
		case 12:	/* retransmit last response */
			resp = R_AGAIN;
			break;

		case 6:		/* respond to status poll */
			if (tp->msgcount && selected) {
				resp = R_TRAFFIC;
				state = WACK;
			} else {
				resp = R_EOT;
				state = IDLE;
			}
			break;

		case 7:		/* traffic poll after ack for text */
			resp = R_EOT;
			state = IDLE;
			break;

		case 10:	/* output suspended */
			if (tp->inq.c_cc <= UVILWM) {
				resp = R_THRU;
				state = WACK;
			} else if (uv.sid == GSID) {
				resp = R_EOT;
				state = WAIT;
			} else {
				resp = R_WABT;
				state = WABT;
			}
			break;

		case 0:		/* no response */
		default:
			resp = R_NULL;
	}
	tp->state = state;
	return(resp);
}

/*
 * Get line from host output message to station input queue
 */
uvgetline(tp)
register struct station	*tp;
{
	b_to_q(uv.text, uv.tlen, &tp->inq);
	wakeup((caddr_t)&tp->inq);
}

/*
 * Put line from station output queue to host input buffer
 *	- returns ptr to next character after transferred text in buffer
 */
char *
uvputline(tp, s)
register char	*s;
register struct station	*tp;
{
	s += q_to_b(&tp->outq, s, UOBUFS-16);
	tp->msgcount = 0;
	wakeup((caddr_t)&tp->outq);
	return(s);
}

/********************************************************
 *							*
 *		Poll message analysis			*
 *							*
 ********************************************************/

/*
 * Check parity of line pointed to by p
 */
uvparity(p)
char *p;
{
	register char	*from, *to;
	register int	c, parity;

	from = to = p;
	while ((c = *from) != SOH) {
		if (c == ETX)
			return(0);
		from++;
	}
	*to = c;
	parity = 0;
	do {
		c = *++from;
		if (c != NUL && c != SYN) {
			*++to = c;
			parity ^= c;
		}
	} while (c != ETX);
	c = *++from;
	*++to = c;
	*++to = '\0';
	return(c==parity? ++from : 0);
}

/*
 *	- 'ad hoc' parser
 *	- examines host input line in p[]
 *		(must end in ETX)
 *	- returns type of poll
 *	- saves polled address in uv.rid / uv.sid / uv.did
 *	- if text message, saves start & length of text in uv.text / uv.tlen
 *	- BCC (longitudinal parity) is ignored
 */
uvpoll(p)
register char *p;
{
	register char	c;
	register int	msgtype = M_NULL;

#define First	(c = *p)
#define This	(c)
#define Next	(c = *++p)
#define Here	(p)

	if (First != SOH)
		return(M_NULL);
	if (Next == ETX)
		return(M_NULL);
	uv.rid = This;
	if (Next == ETX)
		return(M_NULL);
	uv.sid = This;
	if (Next == ETX)
		return(M_NULL);
	uv.did = This;

	switch Next {
		case ENQ:
			msgtype = M_STAT;
			Next;
			break;
		case DLE:
			if (Next == NAK) {
				msgtype = M_DNAK;
				Next;
				break;
			}
			if (This == '1') {
				if (Next == ENQ) {
					msgtype = M_ASTAT;
					Next;
				} else
					msgtype = M_APOLL;
				break;
			}
			return(M_NULL);
		case BEL:
		case DC3:
			if (Next != STX)
				return(M_NULL);
			/* fall through to STX case */
		case STX:
			msgtype = M_TEXT;
			uv.text = Here + 1;
			while (Next != ETX)
				if (This == STX)
					uv.text = Here + 1;
			uv.tlen = Here - uv.text;
			break;
		default:
			msgtype = M_POLL;
	}
	if (This != ETX)
		msgtype = M_NULL;
	return(msgtype);
}

/********************************************************
 *							*
 *		Response message synthesis		*
 *							*
 ********************************************************/

/*
 * Response messages
 */
char *respmsg[] = {
	"\002",			/* text:	STX */
	"\0200",		/* traffic:	DLE 0 */
	"\0201",		/* ack:		DLE 1 */
	"",			/* eot */
	"\020\005",		/* denq:	DLE ENQ */
	"",			/* again: */
	"\020?",		/* wabt:	DLE ? */
	"\020;"			/* thru:	DLE ; */
};
char	eotmsg[] =	"\004\004\003\003";	/* eot: EOT EOT ETX BCC */


/*
 * Build response and send to host
 */
uvrespond(atp, tp, msgtype, acktype)
struct tty *atp;
register struct station	*tp;
register int	msgtype;
{
	register char	*from, *to, c;
	register char	*buf;
	register int	len;
	extern int	trmask;

	if (msgtype == R_EOT) {		/* no traffic */
		buf = eotmsg;
		len = sizeof(eotmsg);
	} else if (msgtype == R_AGAIN) {
		buf = uv.obuf;
		len = uv.olen;
	} else {
		to = buf = (msgtype == R_DENQ? uv.qbuf : uv.obuf);
		/*
		 * Start message with SOH / address
		 */
		*to++ = SOH;
		*to++ = tp->trid;
		*to++ = tp->tsid;
		*to++ = tp->tdid;
		/*
		 * Copy in ACK/WABT passed by multiplexor
		 */
		if (acktype!=R_NULL && msgtype!=R_ACK && msgtype!=R_WABT) {
			for (from = respmsg[acktype]; c = *from; from++)
				*to++ = c;
		}
		/*
		 * Copy in supervisory message
		 */
		for (from = respmsg[msgtype]; c = *from; from++)
			*to++ = c;
		/*
		 * Copy in text
		 */
		if (msgtype == R_TEXT)
			to = uvputline(tp, to);
		/*
		 * Add ETX and computed BCC (longitudinal parity)
		 */
		*to++ = ETX;
		c = 0;
		for (from = &buf[1]; from < to; from++)
			c ^= *from;
		*to++ = c;
		*to = '\0';	/* for trace */

		len = to - buf;
		if (msgtype != R_DENQ)
			uv.olen = len;
	}
	if (trmask & 0x20)
		putchar("ntraedgwh"[msgtype+1]);

	/*
	 * Transmit response to host
	 */
	b_to_q(buf, len, &atp->t_outq);
	(*atp->t_oproc)(atp);
}
E 1
