239 lines · 4.7 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h"#include "msaturn.h" #include "etherif.h" enum{	Etcr = Saturn + 0x0c00,	Etsr = Saturn + 0x0c02,	Ercr = Saturn + 0x0c04,	Ersr = Saturn + 0x0c06,	Eisr = Saturn + 0x0d04,	Eimr = Saturn + 0x0d06,	Emacaddr0 = Saturn + 0x0e02,	Miicr = Saturn + 0x0f02,	Miiwdr = Saturn + 0x0f04,	Miirdr = Saturn + 0x0f06, 	Ethermem = 0xf2c00000,	Etherfsize = 0x2000,	Nrx = 14,	Ntx = 2,		// Nrx + Ntx must be 16 	Ersr_rxfpmask = 0xf,	Ersr_rxevent = RBIT(0, ushort),	Etcr_txfpmask = 0xf,	Ercr_rxenab = RBIT(0, ushort),	Ercr_auienab = RBIT(2, ushort),	Etcr_txstart = RBIT(1, ushort),	Etcr_retries = 0xf<<8,	Ei_txecall = RBIT(0, ushort),	Ei_txretry = RBIT(2, ushort),	Ei_txdefer = RBIT(3, ushort),	Ei_txcrs = RBIT(4, ushort),	Ei_txdone = RBIT(5, ushort),	Ei_rxcrcerr = RBIT(8, ushort),	Ei_rxdrib = RBIT(9, ushort),	Ei_rxdone = RBIT(10, ushort),	Ei_rxshort = RBIT(11, ushort),	Ei_rxlong = RBIT(12, ushort), 	Miicr_regshift = 6,	Miicr_read = RBIT(10, ushort),	Miicr_preambledis = RBIT(12, ushort),	Miicr_accack = RBIT(14, ushort),	Miicr_accbsy = RBIT(15, ushort),}; typedef struct {	Lock;	int		txbusy;	int		txempty;	int		txfull;	int		ntx;			/* number of entries in transmit ring */	int		rxlast; 	int		active;	ulong	interrupts;	/* statistics */	ulong	overflows;} Ctlr; uchar etheraddr[6] = { 0x90, 0x85, 0x82, 0x32, 0x83, 0x00};static ushort*etcr=(ushort*)Etcr;static ushort*etsr=(ushort*)Etsr;static ushort*ercr=(ushort*)Ercr;static ushort*ersr=(ushort*)Ersr;static ushort*eimr=(ushort*)Eimr;static ushort*eisr=(ushort*)Eisr;static ushort*miicr=(ushort*)Miicr;static ushort*miirdr=(ushort*)Miirdr; static voidtxfill(Ether*ether, Ctlr*ctlr){	int len;	Block *b;	ushort*dst; 	while(ctlr->ntx<Ntx){		if((b=qget(ether->oq)) == nil)			break; 		len = BLEN(b);		dst = (ushort*)(Ethermem+(ctlr->txempty+Nrx)*Etherfsize);		*dst = len;		memmove(&dst[1], b->rp, len);		ctlr->ntx++;		ctlr->txempty++;		if(ctlr->txempty==Ntx)			ctlr->txempty = 0;		freeb(b);	}} static voidtxrestart(Ctlr*ctlr){	if(ctlr->ntx==0 || ctlr->txbusy)		return;	ctlr->txbusy = 1;	*etcr = Etcr_txstart|Etcr_retries|(ctlr->txfull+Nrx);} static void interrupt(Ureg*, void*); static voidtransmit(Ether*ether){	Ctlr *ctlr; 	ctlr = ether->ctlr;	ilock(ctlr);	txfill(ether, ctlr);	txrestart(ctlr);	iunlock(ctlr); } static voidinterrupt(Ureg*, void*arg){	Ctlr*ctlr;	Ether*ether = arg;	Etherpkt*pkt;	ushort ie;	int rx, len;	Block *b; 	ctlr = ether->ctlr;	if(!ctlr->active)		return;	/* not ours */	ctlr->interrupts++; 	ilock(ctlr);	ie = *eisr;	*eisr = ie;	intack(); 	if(ie==0)		iprint("interrupt: no interrupt source?\n"); 	if(ie&Ei_txdone){		if((*etcr&Etcr_txstart)==0){			if(ctlr->txbusy){				ctlr->txbusy = 0;				ctlr->ntx--;				ctlr->txfull++;				if(ctlr->txfull==Ntx)					ctlr->txfull = 0;			}			txrestart(ctlr);			txfill(ether, ctlr);			txrestart(ctlr);		}		else			iprint("interrupt: bogus tx interrupt\n");		ie &= ~Ei_txdone;	} 	if(ie&Ei_rxdone){		rx=*ersr&Ersr_rxfpmask;		while(ctlr->rxlast!=rx){ 			ctlr->rxlast++;			if(ctlr->rxlast >= Nrx)				ctlr->rxlast = 0; 			pkt = (Etherpkt*)(Ethermem+ctlr->rxlast*Etherfsize);			len = *(ushort*)pkt;			if((b = iallocb(len+sizeof(ushort))) != nil){				memmove(b->wp, pkt, len+sizeof(ushort));				b->rp += sizeof(ushort);				b->wp = b->rp + len;				etheriq(ether, b, 1);			}else				ether->soverflows++;			rx=*ersr&Ersr_rxfpmask;		}		ie &= ~Ei_rxdone;	} 	if(ie&Ei_txretry){		iprint("ethersaturn: txretry!\n");		ie &= ~Ei_txretry;		ctlr->txbusy = 0;		txrestart(ctlr);	} 	ie &= ~Ei_txcrs;	if(ie)		iprint("interrupt: unhandled interrupts %.4uX\n", ie);	iunlock(ctlr);} static intreset(Ether* ether){	Ctlr*ctlr; 	*ercr = 0;	ctlr = malloc(sizeof(*ctlr));	memset(ctlr, 0, sizeof(*ctlr));	ctlr->active = 1; 	ether->ctlr = ctlr;	ether->transmit = transmit;	ether->interrupt = interrupt;	ether->irq = Vecether;	ether->arg = ether;	memmove(ether->ea, (ushort*)Emacaddr0, Eaddrlen); 	*ercr = Ercr_rxenab|Ercr_auienab|(Nrx-1);	*eimr = Ei_rxdone|Ei_txretry|Ei_txdone;		iprint("reset: ercr %.4uX\n", *ercr);	return 0;} voidethersaturnlink(void){	addethercard("saturn", reset);}