V9/sys/dev.old/ec.c

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

#include "ec.h"


#include "../h/param.h"
#include "../h/systm.h"
#include "../h/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/conf.h"
#include "../h/ttyio.h"
#include "../h/enio.h"
#include "../h/ttyld.h"
#include "../h/stream.h"
#include "../h/ethernet.h"
#include "sparam.h"

extern	char ECmem[];			/* .set in locore.s */
#define	ECADDR	((caddr_t)(ECmem))	/* Virt addr of EC buffer mem */

/* NOTE: The ECRHBF/ECRLBF/ECTBF definitions must match the address
	and size of EC memory as defined in locore.s in the kernel.
	If locore maps in full 32K, then use all buffers, otherwise,
	change accordingly.
*/

#define	ENETDOWN	ENODEV
#define	ECRHBF	11
#define	ECRLBF	1
#define	ECTBF	0

/* Following ethernet address only has meaning if UE prom is missing */

#define ECPRI   (PZERO+8)
#define	ECBFSZ	2048		/* Size of each EC buffer */

struct	ecdevice	{
	short	ecrcr;		/* Transmit Control Register */
	short	ecxcr;		/* Receive Control Register */
};

/* Bits in ecxcr: */
#define	EC_WBN		0x0010		/* Write Buffer Number */
#define	EC_CLRDN	0x0020		/* Clear Done */
#define	EC_IE		0x0040		/* Interrupt Enable */
#define	EC_DONE		0x0080		/* Xmt/Rcv complete */
#define	EC_CLR		0x0100		/* Clear EC */
#define	EC_CLRJAM	0x2000		/* Clear Jam */
#define	EC_JIE		0x4000		/* Jam Interrupt Enable */
#define	EC_JAM		0x8000		/* Jam */

/* Bits WBN, CLRDN, IE, and DONE are also used for ecrcr */
#define	EC_BITS		"\20\20JAM\17JIE\16CLRJAM\11CLR\10DONE\7IE\6CLRDN\5WBN"

/* Bits in ecrcr: */
#define	EC_RANDATA	0xA00	/* Alternating 1/0 in 4 data bits */
#define	EC_ADATAMSK	0xF00	/* Mask for 4 data bits */
#define	EC_OWN_BDCST	0x600	/* Recog addrs for me + broadcast*/
#define	EC_ARALL	0	/* Pass all addresses */
#define	EC_AWCLK	0x1000	/* Clock data into address recog ram */
#define	EC_AROM		0x2000	/* Select rom vs default ram */
#define	EC_ASTEP	0x4000	/* Step rom/ram address one bit */


/*
 * Useful combinations
 */
#define	EC_READ		(EC_OWN_BDCST|EC_IE|EC_WBN)
#define	EC_WRITE	(EC_JIE|EC_IE|EC_WBN)
#define	EC_CLEAR	(EC_JIE|EC_IE|EC_CLRJAM)

#define	ECXBUSY	010
#define	ECDEBUG	0200

/* corresponds to minor device numbers (# of servers) */
#define	CHANS_PER_UNIT	8

struct	ec {			/* per controller */
	char	myethadr[6];	/* My Ethernet address */
	char	attached;	/* Non zero when ec has been attached */
	char	active;
	short	collisions;		/* Jam counter */
	int	state;		/* Device state */
	int	bits;
	int	ierrors, oerrors;
	int	ipackets, opackets;
} ec[NEC] = {	{0x02, 0x60, 0x8c, 0x00, 0x4, 0x62},	};

#define	NECCHAN	(CHANS_PER_UNIT * NEC)
struct	ecchan {
	int	unit;
	int	packets;
	struct	queue *ecq;
	int	type;
} ecchan[NECCHAN];

int	nodev(), ecopen(), ecclose(), ecput(), ecsrv();
static struct qinit ecrinit = { nodev, NULL, ecopen, ecclose, 0, 0 };
static struct qinit ecwinit = { ecput, NULL, ecopen, ecclose, 1514, 0 };
	/* 1514 bytes is minimum highwater mark to send 1514 byte packets */
struct streamtab ecsinfo = { &ecrinit, &ecwinit };

/* JUNK FOR AUTOCONFIGURE */
struct uba_device *ecinfo[1];
int ecprobe(), ecattach();
u_short ecstd[] = { 0 };
struct uba_driver ecdriver =
	{ ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo };
extern bsize[];

ecprobe(reg)
caddr_t reg;
{
	register int br, cvec;		/* value-result */
	br = 0x16;			/* BR Level 16 = UNIBUS 6 */
	cvec = 0440;			/* Int vector */
	return 1;
}

/* ARGSUSED */
ecattach(ui)
struct uba_device *ui;
{
	register struct ecdevice *ecaddr = (struct ecdevice *)ui->ui_addr;
	register struct ec *ecu = &ec[ui->ui_unit];
	unsigned short s0, s1;
	register i, j;
	register char *cp;
	char bdethadr[6];		/* copy of bd rom; -1=>chip missing */


	if(ecu->attached)			/* Do this only once */
		return;
	ecaddr->ecxcr = EC_CLR;		/* Reset the EC */

/* Fetch out the ROM ethernet address into tmp ethadr */

	ecaddr->ecrcr = EC_AROM;		/* Select the ROM */
	cp = bdethadr;
	for(i = 0; i < 6; i++) {
		*cp = 0;
		for(j = 0; j <= 4; j += 4) {
			s1 = ecaddr->ecrcr;
			*cp |= ((s1 >> 8) & 017) << j;
			for(s1 = 0; s1 < 4; s1++) {
				ecaddr->ecrcr = EC_ASTEP+EC_AROM;
				ecaddr->ecrcr = EC_AROM;
			}
		}
		cp++;
	}
	for(i = 0, cp = bdethadr; i < 6; i++)
		if(*cp++ != -1) {	/* Rom is good if not -1 */
			for(i = 0; i < 6; i++)
				ecu->myethadr[i] = bdethadr[i];
		}
/* use the RAM for addr recog */
	laddrec(ui);

	for(i = ECRHBF; i >= ECRLBF; i--)
		ecaddr->ecrcr = EC_READ + i;
	ecsrand((long)ecu->myethadr[5]);
	ecu->attached = 1;
	ecu->bits = EC_READ;
}

laddrec(ui)
struct uba_device *ui;
{
	register struct ecdevice *ecaddr = (struct ecdevice *)ui->ui_addr;
	register struct ec *ecu = &ec[ui->ui_unit];
	register	i, j, s0, s1;
	register char	*cp;

/* Have to load myethadr into hardware ram! */
	ecaddr->ecxcr = EC_CLR;	/* Reset bit ptr */
	for(i = 0, cp = ecu->myethadr; i < 6; i++, cp++)
		for(j = 0; j <= 4; j += 4) {
			s1 = ((*cp >> j) & 017) << 8;
			ecaddr->ecrcr = s1;
			ecaddr->ecrcr = s1 | EC_AWCLK;
			ecaddr->ecrcr = s1;
			for(s0 = 0; s0 < 4; s0++) {
				ecaddr->ecrcr = EC_ASTEP;
				ecaddr->ecrcr = 0;
			}
		}
}


/* ARGSUSED */
ecopen(q, dev)
register struct queue *q;
register dev_t dev;
{
	register struct ecchan *ecd;
	register struct ec *ecu;
	register unit;

	dev = minor(dev);
	unit = dev / CHANS_PER_UNIT;

	if (dev >= NECCHAN)
		return(0);
	if (unit >= NEC)
		return(0);

	ecu = &ec[unit];
	if(!ecu->attached)
		return(0);
	ecd = &ecchan[dev];
	if (ecd->ecq)
		return(0);

	ecd->unit = unit;
	ecd->ecq = q;
	q->ptr = (caddr_t)ecd;
	q->flag |= QBIGB | QDELIM;
	WR(q)->ptr = (caddr_t)ecd;
	WR(q)->flag |= QBIGB|QDELIM;

	if (ecu->state & ECDEBUG)
		printf("ECo%d, ", minor(dev));
	return(1);
}
ecclose(q)
register struct queue *q;
{
	register struct ecchan *ecd;
	register struct ec *ecu;
	register unit;

	ecd = (struct ecchan *)q->ptr;
	ecu = &ec[ecd->unit];
	if (ecu->state & ECDEBUG)
		printf("ECc\n");
	flushq(WR(q), 1);
	ecd->ecq = NULL;
}

ecput(q, bp)
register struct queue *q;
register struct block *bp;
{
	register struct ecchan *ecd;
	register struct ec *ecu;
	register unit, type, count, ps;

	ecd = (struct ecchan *)q->ptr;
	unit = ecd->unit;
	ecu = &ec[unit];

	switch(bp->type) {
	
		case M_IOCTL:
			ecioctl(q, bp);
			return;

		case M_DATA:
			putq(q, bp);
			return;
		
		case M_DELIM:
			break;

		default:
			freeb(bp);
			return;
	}

	putq(q, bp);
	ps = spl6();
	ecd->packets++;
	if (ecu->active == 0)
		ecstart(unit);
	splx(ps);
}
ecstart(unit)
{
	register u_char	*tsp, *fsp;
	register struct ec *ecu;
	register struct ecchan *ecd;
	register struct ecdevice *ecaddr = (struct ecdevice *)ecinfo[unit]->ui_addr;
	register struct queue *q, *bq;
	register struct block *bp, *head, *nbp, **bnext;
	register len, count = 0, i;

	ecu = &ec[unit];
	if (ecu->active) {
		printf(" start but active %d\n", unit);
		ecu->active = 0;
	}
	ecd = &ecchan[unit * CHANS_PER_UNIT];
	for(i = 0; i < CHANS_PER_UNIT; i++, ecd++)
		if (ecd->ecq && ecd->packets > 0)
			break;
	if (i >= CHANS_PER_UNIT)
		return;

	ecd->packets--;
	q = WR(ecd->ecq);

	/* The packet must be justified to the end of the buffer.
	 * Therefore, we have to count the bytes before copying.
	 */
	bnext = &head;
	while (*bnext = bp = getq(q)) {
		if (bp->type == M_DELIM) {
			bp->next = 0;
			break;
		}
		count += bp->wptr - bp->rptr;
		bnext = &bp->next;
	}
	bp = head;

	/* if there is no ethernet header in packet, throw it out */
	if (count < sizeof(struct etherpup)) {
		while(bp) {
			nbp = bp->next;
			freeb(bp);
			bp = nbp;
		}
		return;
	}
	bcopy(ecu->myethadr, ((struct etherpup*)bp->rptr)->shost, 6);
	ecu->active = 1;

	/* test for small packets */
	if (count < 46 + sizeof(struct etherpup))
		count = 46 + sizeof(struct etherpup);

	/* test for too large packets */
	else if (count > 1500 + sizeof(struct etherpup))
		count = 1500 + sizeof(struct etherpup);

	/* Fill buffer - packet is justified to end of buffer. */
	tsp = (u_char *)(ECmem+(ECBFSZ*ECTBF));
	*(short *)tsp = (ECBFSZ) - count;	/* Stuff offset into buffer */
	tsp += ((ECBFSZ) - count);		/* Adjust ptr into buffer */

	while (count > 0 && bp != 0) {
		len = bp->wptr - bp->rptr;
		fsp = bp->rptr;
		count -= len;
		while (len-- > 0)		/* Could this use scopy()? */
			*tsp++ = *fsp++;
		nbp = bp->next;
		freeb(bp);
		bp = nbp;
	}

	/* if no buffers left but count > 0, (i.e. padded packet), zero fill */
	while (count-- > 0)
		*tsp++ = 0;

	/* flush remaining buffers, if any (too large packet: count > 1500) */
	while (bp) {
		nbp = bp->next;
		freeb(bp);
		bp = nbp;
	}

	if (ecu->state & ECDEBUG) {
		register i;
		printf("\ncount=%d, packet = ", count);
		tsp = (u_char *)(ECmem+(ECBFSZ*ECTBF)) + (ECBFSZ - count);
		for(i = 0; i < count; i++)
			printf("%02x ", (unsigned int)*tsp++);
		printf("\n");
	}
	ecu->state |= ECXBUSY;
	ecu->collisions = 0;
	ecaddr->ecxcr = EC_WRITE + ECTBF;
}

ecioctl(q, bp)
register struct queue *q;
register struct block *bp;
{
#ifndef EIOCSETP
#define	EIOCSETP	(('e'<<8)|3)	/* set ec_state (state of driver) */
#define	EIOCGETP	(('e'<<8)|4)	/* get ec_state (driver state) */
#define	EIOCAREC	(('e'<<8)|5)	/* set address recognition */
#endif
	register struct ecdevice *ecaddr;
	register struct ecchan *ecd;
	register struct ec *ecu;
	register u_char	*msg;
	register	unit, i, ps, cmd;

	ecd = (struct ecchan *)q->ptr;
	unit = ecd->unit;
	ecu = &ec[unit];
	ecaddr = (struct ecdevice *)ecinfo[unit]->ui_addr;
	cmd = ((union stmsg *)(bp->rptr))->ioc1.com;
	msg = (u_char *)&((union stmsg *)(bp->rptr))->ioc1.sb;

	bp->type = M_IOCACK;
	if (ecu->state & ECDEBUG)
		printf("ecioctl: cmd = %x\n");
	switch(cmd) {
		case ENIOADDR:          /* get my Ethernet address */
			bcopy(ecu->myethadr, (int *)msg, 6);
			break;

/*
 * SETP, SETD, SSIG, AREC should be super user ioctl calls.
 * Since it is impossible to know who the initiator of an ioctl call is,
 * it would be desirable to have a special minor device number for super user
 * privileges
 */
		case EIOCSETP:
		case EIOCGETP:
			if (cmd == EIOCGETP) {
				*(int *)msg = ecu->state;
			} else {
				ecu->state = *(int *)msg;
			}
			if (ecu->state & ECDEBUG)
				printf("state = %o\n", ecu->state);
			break;

		case ENIOTYPE:
			ecd->type = *(int *)msg;
			break;
			
		case EIOCAREC:
			ecu->bits = *(int *)msg;
			ecu->bits |= EC_IE|EC_WBN;
			if (ecu->state & ECDEBUG)
				printf("set addr rec, bits = %b\n", ecu->bits, EC_BITS);
			ps = spl6();
			ecaddr->ecxcr = EC_CLR;		/* Reset the UE */
			for(i = ECRHBF; i >= ECRLBF; i--)
				ecaddr->ecrcr = ecu->bits + i;
			splx(ps);
			break;

		default:
			bp->type = M_IOCNAK;
			break;
	}
	qreply(q, bp);
}


/* Come here only after hardware has decided packet is for us--
   either exactly, or multi-cast */
ecrint(unit)
{
	register struct ecchan *ecd;
	register struct ec *ecu = &ec[unit];
	register struct ecdevice *ecaddr = (struct ecdevice *)ecinfo[unit]->ui_addr;
	register struct queue *q;
	register struct block *bp;
	register short buf;			/* Extract done rcv buffer */
	register short type;
	register short *fsp;
	register len, min, i;
	register u_char *p;

	while (ecaddr->ecrcr & EC_DONE) {
		buf = ecaddr->ecrcr & 0xF;		/* Extract buffer # */
		if (buf < ECRLBF || buf > ECRHBF)
			panic("ec%d: rcvr interrupt (bad buf=%d)\n", unit, buf);

		/* fsp points to the buffer in VAX virt memory */

		fsp = (short *)(ECmem+(ECBFSZ*buf));
		if (*fsp < 0) {		/* negative offset => FCS err */
			ecu->ierrors++;
			goto out;
		}
		if (((*fsp) & 0x3fff) == 0) {  /* 0 offset => TOO LONG pkt */
			ecu->ierrors++;
			goto out;
		}
/* *fsp contains offset to end of packet - ie (length + beginning offset);
   packet starts at offset 528 into the buffer */

		if (*fsp < 592 || *fsp > 2046) {  /* illegal count */
			ecu->ierrors++;
			goto out;
		}
		len = *fsp - (528+4);	/* +4 is the FCS */
		fsp = fsp + (528/2);		/* Offset of beg of packet */
		type = *(fsp + 6);
		p = (u_char *)fsp;

		ecd = &ecchan[unit * CHANS_PER_UNIT];
		for (i = 0; i < CHANS_PER_UNIT; i++, ecd++)
			if (ecd->ecq && ecd->type == type)
				break;
		if (i >= CHANS_PER_UNIT)
			goto out;
		q = ecd->ecq;
		if (q->next->flag & QFULL) {
			if(ecu->state & ECDEBUG)
				printf(" q full\n");
			ecu->ierrors++;
			goto out;
		}
		while (len > 0) {
			bp = allocb(len);
			if (bp == 0) {
				printf("ecrint no buffers\n");
				goto out;
			}
			min = MIN((bp->lim - bp->base), len);
			len -= min;
			while (min-- > 0)
				*bp->wptr++ = *p++;
			(*q->next->qinfo->putp)(q->next, bp);
		}
		if (putctl(q->next, M_DELIM))
			ecu->ipackets++;
		else 
			printf("ecrint no DELIM bp\n");
	out:
		ecaddr->ecrcr = ecu->bits | EC_CLRDN | buf;
	}
}

ecjint(unit)
{
	register struct ecdevice *ecaddr = (struct ecdevice *)ecinfo[unit]->ui_addr;
	register struct ec *ecu = &ec[unit];
	register i;

	if(++ecu->collisions > 8) {
		printf("ec gak\n");
		ecu->oerrors++;
		/* Hmmm, can't xmit in 8 tries */
		ecaddr->ecxcr = EC_CLR;		/* Reset the UE */
						/* And re-read all rcv buffers */
		for(i = ECRHBF; i >= ECRLBF; i--)
			ecaddr->ecrcr = ecu->bits + i;
		ecu->state &= ~ECXBUSY;
		ecu->active = 0;
	} else {
		ecu->collisions++;
		backoff(ecu->collisions);
		ecaddr->ecxcr = EC_CLEAR; /* RESET */
	}
}


/* backoff:  this routine implements an approximation of the binary
 *	exponential backoff algorithm.  When called with an argument n,
 *	the retry number, this routine will delay (in a busy loop)
 *	a random amount of time between 0 and 2**n slot times.  A slot
 *	time is defined as 51.2 microseconds.  This routine assumes it
 *	takes about 300 microseconds to be called after the jam actually
 *	occurs, and that the random number generator takes about 50
 *	microseconds.
 */
backoff(n)
register int n;
{
	long ecrand();
	register int time;

	if (n < 4)
	    return;
	if (n > 10)
	    n = 10;

 /* compute number of slot times to delay */
	time = ((int)ecrand() & ((1 << n) - 1)) - 7;
	if (time <= 0)
	    return;

	while(time--)
	    ecidle();
}

static	long	randx = 1;

ecsrand(x)
unsigned x;
{
	randx = x;
}

ecrand()
{
	return(((randx = randx*1103515245 + 12345)>>16) & 077777);
}


ecidle()
{
	register i = 40;

	while(--i) ;
}

/*
 * ecxint gets called when a transmit completes normally.
 */
ecxint(unit)
{
	register struct ecdevice *ecaddr = (struct ecdevice *)ecinfo[unit]->ui_addr;
	register struct ec *ecu;
	register struct ecchan *ecd;
	register struct block *bp;
	register buf = ecaddr->ecxcr & 0xF;

	if((ecaddr->ecxcr & EC_DONE) == 0 || buf != ECTBF)
		printf("ec%d: xmit interrupt-bad bufno, xcsr=%b\n", unit, ecaddr->ecxcr, EC_BITS);

	ecu = &ec[unit];
	if(ecu->active == 0) {
		printf("ec%d: stray xmit interrupt, xcsr=%b\n", unit, ecaddr->ecxcr, EC_BITS);
		return;
	}

	ecaddr->ecxcr = EC_CLRDN;
	ecu->state &= ~ECXBUSY;
	ecu->active = 0;
	ecu->opackets++;
	ecstart(unit);
}
scopy(f, t, c)
register u_char	*f, *t;
register	c;
{
	while(c-- > 0)
		*t++ = *f++;
}