V8/usr/sys/chncp/chdr11c.c

#include "../chunix/chsys.h"
#include "../chunix/chconf.h"
#include "../chaos/chaos.h"
/*
 * DR11-C Chaos driver.
 * This driver does framing and a block check for each packet.
 * The framing is similar to standard synchronous links, except that
 * the "byte" is 16 bits.  Basically a frame starts after 2 or more
 * DRSYNC words, then the next word being a count and the next word being the
 * complement of the count (both count and its complement must not be DRSYNC).
 * Then the number of words specified by the count are the actual data,
 * followed by an LRC-16.  Data words equal to DRSYNC or DRESC are preceded by
 * DRESC. The count does not include the inserted DRESC words, but the CRC
 * word does. The LRC word does not include the initial DRSYNC's or the count.
 * This framing scheme was thought up quickly and may well be overkill.
 * The LRC is a simple XOR of all data words.
 * Chaos N.C.P.'s that use this file must include <chaos/dr11c.h> in their
 * chconf.h files.
 * Keep in mind that the interrupt priority level of this driver maybe less
 * than either the clock or the LOCK macro supplied by the user.
 * Also remember that the block check is not to great.
 */

#define	xc_draddr	xc_info.xc_dr11c.dr_addr
#define	xc_drtptr	xc_info.xc_dr11c.dr_tptr
#define	xc_drrptr	xc_info.xc_dr11c.dr_rptr
#define	xc_drtcnt	xc_info.xc_dr11c.dr_tcnt
#define	xc_drrcnt	xc_info.xc_dr11c.dr_rcnt
#define	xc_drrstate	xc_info.xc_dr11c.dr_rstate
#define	xc_drtstate	xc_info.xc_dr11c.dr_tstate
#define	xc_drrcheck	xc_info.xc_dr11c.dr_rcheck
#define	xc_drtcheck	xc_info.xc_dr11c.dr_tcheck
#define	xc_drintrup	xc_info.xc_dr11c.dr_intrup

struct chxcvr dr11cxcvr[NCHDR];


/*
 * dr11-c initialization. Also does initialization to the static chxcvr
 * structure since initialized unions don't work anyway.
 */

int chdrstart(), chdrreset();
/*
 * Initialize all dr11c interfaces - called from low priority
 */
chdrinit()
{
	register struct chxcvr *xp;
	register short *hp;
#ifdef VMUNIX
	register int i = 0;
#else
	register char *dp = (char *)DR11CBASE;/* must be char * to use DR11CINC */
#endif

	hp = &dr11chosts[0];
	for (xp = &dr11cxcvr[0]; xp < &dr11cxcvr[NDR11C]; xp++) {
		xp->xc_draddr =
#ifdef VMUNIX
			(struct dr11c *) chdrdinfo[i++]->ui_addr;
#else
			(struct dr11c *) dp;
		dp += DR11CINC;
#endif
		/*DEBUG*/ if (xp->xc_draddr == 0) panic("draddr");
		xp->xc_start = chdrstart;
		xp->xc_reset = chdrreset;
		xp->xc_host = *hp++;
		LOCK;
		chdrreset(xp);
		UNLOCK;
	}
}
/*
 * Check for hung interfaces - if so flush the packet.
 * Assumed called at clock level.
 */
chdrxtime()
{
	register struct chxcvr *xp;

	for (xp = &dr11cxcvr[0]; xp < &dr11cxcvr[NDR11C]; xp++) {
		if (xp->xc_drintrup)
			continue;
		if (xp->xc_rpkt != NOPKT &&
		    Chclock - xp->xc_rtime > DR11CHUNG) {
			ch_free((char *)xp->xc_rpkt);
			xp->xc_rpkt = NOPKT;
			xp->xc_drrstate = DRIDLE;
			debug(DABNOR, printf("Rec pkt timeout\n"));
		}
		if (xp->xc_tpkt != NOPKT &&
		    Chclock - xp->xc_ttime > DR11CHUNG) {
			debug(DABNOR, printf("Trans pkt timeout\n"));
			xp->xc_drtstate = DRTDONE;
			chdrxint(xp - dr11cxcvr);
		}
	}
}

/*
 * Reset things from an unknown state (high priority)
 * Basically enable interrupts and tell him we're ready for input.
 * If he already has a word, take it right away.
 */
chdrreset(xp)
register struct chxcvr *xp;
{
	register struct dr11c *dp = xp->xc_draddr;

	xp->xc_drrstate = xp->xc_drtstate = DRIDLE;
	dp->dr_csr = 0;
	dp->dr_csr = DRIN|DROUT|DRIE|DROE;
}
/*
 * Start output on an idle line - called when nothing happening
 */
chdrstart(xp)
register struct chxcvr *xp;
{

	if (xp->xc_tpkt != NOPKT)
		panic("chdrstart: already busy");
	chdrxint(xp - dr11cxcvr);
}
/*
 * Receiver interrupt
 */
chdrrint(dev)
{
	register unsigned short data; /* register short is dubious - be careful! */
	register struct chxcvr *xp = &dr11cxcvr[dev];
	register struct dr11c *dp = xp->xc_draddr;

	if ((dp->dr_csr & DRIRDY) == 0) {
		debug(DABNOR, printf("extra dr11c rint\n"));
		return;
	}
	dp->dr_csr &= ~DRIN;
	data = dp->dr_ibuf;
	dp->dr_csr |= DRIN;		/* set his DRORDY, and cause his chdrxint */
	
	xp->xc_drintrup = 1;
	switch(xp->xc_drrstate) {
	case DRIDLE:
		if (data == DRSYNC)
			xp->xc_drrstate = DRSYN1;
		break;
	case DRSYN1:
		xp->xc_drrstate = data == DRSYNC ? DRSYN2 : DRIDLE;
		break;
	case DRSYN2:
		if (data != DRSYNC) {
			xp->xc_drrcnt = data;
			xp->xc_drrstate = DRCNT1;
		}
		break;
	case DRCNT1:
		xp->xc_drrstate = DRIDLE;
		data = ~data;
		debug(DTRANS,printf("Rec size=%d\n", data));
		if (data != xp->xc_drrcnt)
			debug(DABNOR, printf("Rec bad count\n"));
		else if ((data <<= 1) < sizeof(struct pkt_header) ||
			 data > CHMAXDATA + sizeof(struct pkt_header))
			debug(DABNOR, printf("Rec bad size: %d", data));
		else {
			LOCK;	/* in case of clock interrupt */
			if ((xp->xc_rpkt = (struct packet *)ch_alloc(
			    (int)data + sizeof(struct ncp_header) , 1))
				!= NOPKT) {
			xp->xc_drrstate = DRDATA;
			xp->xc_drrptr = (short *)&xp->xc_rpkt->pk_phead;
			xp->xc_drrcheck = 0;
			xp->xc_rtime = Chclock;
			} else
				debug(DABNOR, printf("Rec alloc fail: %d\n", data));
		}
		break;
	case DRDATA:
		if (data == DRESC) {
			xp->xc_drrstate = DRESC1;
			xp->xc_drrcheck ^= data;
			break;
		}
		/* Falls into... */
	case DRESC1:
		xp->xc_drrcheck ^= data;
		*xp->xc_drrptr++ = data;
		if (--xp->xc_drrcnt == 0)
			xp->xc_drrstate = DRCHECK;
		else
			xp->xc_drrstate = DRDATA;
		break;
	case DRCHECK:
		xp->xc_drrstate = DRIDLE;
		LOCK;	/* raise to possibly higher priority */
		if (data == xp->xc_drrcheck)
			rcvpkt(xp->xc_rpkt);
		else
			ch_free((char *)xp->xc_rpkt);
		xp->xc_rpkt = NOPKT;
	}
	xp->xc_drintrup = 0;
}
/*
 * Transmit interrupt
 */
chdrxint(dev)
{
	register struct chxcvr *xp = &dr11cxcvr[dev];
	register struct dr11c *dp = xp->xc_draddr;
	register struct packet *pkt;
	unsigned short data;

	if (xp->xc_tpkt != NOPKT && (dp->dr_csr & DRORDY) == 0) {
		debug(DABNOR, printf("Extra dr11c xint\n"));
		/*return;*/
	}
	dp->dr_csr &= ~DROUT;
	xp->xc_drintrup = 1;
	switch (xp->xc_drtstate) {
	case DRIDLE:
		xp->xc_tpkt = NOPKT;
	case DRTDONE:
		LOCK;
		if ((pkt = xcvrdone(xp)) == NOPKT)
			goto out;
		xp->xc_ttime = Chclock;
		data = sizeof(struct pkt_header) + pkt->pk_len;
		data = (data + (sizeof(short) - 1)) >> 1;
		xp->xc_drtcnt = data;
		xp->xc_drtptr = (short *)&pkt->pk_phead;
		debug(DTRANS,printf("Trans size=%d, %x\n",data, pkt));
		data = DRSYNC;
		xp->xc_drtstate = DRSYN1;
		break;
	case DRSYN1:
		data = DRSYNC;
		xp->xc_drtstate = DRSYN2;
		break;
	case DRSYN2:
		data = xp->xc_drtcnt;
		xp->xc_drtstate = DRCNT1;
		break;
	case DRCNT1:
		data = ~xp->xc_drtcnt;
		xp->xc_drtstate = DRDATA;
		xp->xc_drtcheck = 0;
		break;
	case DRDATA:
		if ((data = *xp->xc_drtptr) == DRESC || data == DRSYNC) {
			data = DRESC;
			xp->xc_drtcheck ^= data;
			xp->xc_drtstate = DRESC1;
			break;
		}
		/* Falls into... */
	case DRESC1:
		data = *xp->xc_drtptr++;
		xp->xc_drtcheck ^= data;
		if (--xp->xc_drtcnt == 0)
			xp->xc_drtstate = DRCHECK;
		else
			xp->xc_drtstate = DRDATA;
		break;
	case DRCHECK:
		data = xp->xc_drtcheck;
		xp->xc_drtstate = DRTDONE;
	}
	dp->dr_obuf = data;
	dp->dr_csr |= DROUT;		/* set his DRIRDY, cause his chdrrint */
out:
	xp->xc_drintrup = 0;
}