xref: /plan9/sys/src/9/mtx/devether.c (revision d95be1c011c2cbadfb86cb2f56a061c3646d53ba)
19a747e4fSDavid du Colombier #include "u.h"
29a747e4fSDavid du Colombier #include "../port/lib.h"
39a747e4fSDavid du Colombier #include "mem.h"
49a747e4fSDavid du Colombier #include "dat.h"
59a747e4fSDavid du Colombier #include "fns.h"
69a747e4fSDavid du Colombier #include "io.h"
79a747e4fSDavid du Colombier #include "ureg.h"
89a747e4fSDavid du Colombier #include "../port/error.h"
99a747e4fSDavid du Colombier #include "../port/netif.h"
109a747e4fSDavid du Colombier 
119a747e4fSDavid du Colombier #include "etherif.h"
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier static Ether *etherxx[MaxEther];
149a747e4fSDavid du Colombier 
159a747e4fSDavid du Colombier Chan*
etherattach(char * spec)169a747e4fSDavid du Colombier etherattach(char* spec)
179a747e4fSDavid du Colombier {
189a747e4fSDavid du Colombier 	ulong ctlrno;
199a747e4fSDavid du Colombier 	char *p;
209a747e4fSDavid du Colombier 	Chan *chan;
219a747e4fSDavid du Colombier 
229a747e4fSDavid du Colombier 	ctlrno = 0;
239a747e4fSDavid du Colombier 	if(spec && *spec){
249a747e4fSDavid du Colombier 		ctlrno = strtoul(spec, &p, 0);
259a747e4fSDavid du Colombier 		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
269a747e4fSDavid du Colombier 			error(Ebadarg);
279a747e4fSDavid du Colombier 	}
289a747e4fSDavid du Colombier 	if(etherxx[ctlrno] == 0)
299a747e4fSDavid du Colombier 		error(Enodev);
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	chan = devattach('l', spec);
329a747e4fSDavid du Colombier 	chan->dev = ctlrno;
339a747e4fSDavid du Colombier 	if(etherxx[ctlrno]->attach)
349a747e4fSDavid du Colombier 		etherxx[ctlrno]->attach(etherxx[ctlrno]);
359a747e4fSDavid du Colombier 	return chan;
369a747e4fSDavid du Colombier }
379a747e4fSDavid du Colombier 
389a747e4fSDavid du Colombier static Walkqid*
etherwalk(Chan * chan,Chan * nchan,char ** name,int nname)399a747e4fSDavid du Colombier etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
409a747e4fSDavid du Colombier {
419a747e4fSDavid du Colombier 	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
429a747e4fSDavid du Colombier }
439a747e4fSDavid du Colombier 
449a747e4fSDavid du Colombier static int
etherstat(Chan * chan,uchar * dp,int n)459a747e4fSDavid du Colombier etherstat(Chan* chan, uchar* dp, int n)
469a747e4fSDavid du Colombier {
479a747e4fSDavid du Colombier 	return netifstat(etherxx[chan->dev], chan, dp, n);
489a747e4fSDavid du Colombier }
499a747e4fSDavid du Colombier 
509a747e4fSDavid du Colombier static Chan*
etheropen(Chan * chan,int omode)519a747e4fSDavid du Colombier etheropen(Chan* chan, int omode)
529a747e4fSDavid du Colombier {
539a747e4fSDavid du Colombier 	return netifopen(etherxx[chan->dev], chan, omode);
549a747e4fSDavid du Colombier }
559a747e4fSDavid du Colombier 
569a747e4fSDavid du Colombier static void
ethercreate(Chan *,char *,int,ulong)579a747e4fSDavid du Colombier ethercreate(Chan*, char*, int, ulong)
589a747e4fSDavid du Colombier {
599a747e4fSDavid du Colombier }
609a747e4fSDavid du Colombier 
619a747e4fSDavid du Colombier static void
etherclose(Chan * chan)629a747e4fSDavid du Colombier etherclose(Chan* chan)
639a747e4fSDavid du Colombier {
649a747e4fSDavid du Colombier 	netifclose(etherxx[chan->dev], chan);
659a747e4fSDavid du Colombier }
669a747e4fSDavid du Colombier 
679a747e4fSDavid du Colombier static long
etherread(Chan * chan,void * buf,long n,vlong off)689a747e4fSDavid du Colombier etherread(Chan* chan, void* buf, long n, vlong off)
699a747e4fSDavid du Colombier {
709a747e4fSDavid du Colombier 	Ether *ether;
719a747e4fSDavid du Colombier 	ulong offset = off;
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier 	ether = etherxx[chan->dev];
749a747e4fSDavid du Colombier 	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
759a747e4fSDavid du Colombier 		/*
769a747e4fSDavid du Colombier 		 * With some controllers it is necessary to reach
779a747e4fSDavid du Colombier 		 * into the chip to extract statistics.
789a747e4fSDavid du Colombier 		 */
799a747e4fSDavid du Colombier 		if(NETTYPE(chan->qid.path) == Nifstatqid)
809a747e4fSDavid du Colombier 			return ether->ifstat(ether, buf, n, offset);
819a747e4fSDavid du Colombier 		else if(NETTYPE(chan->qid.path) == Nstatqid)
829a747e4fSDavid du Colombier 			ether->ifstat(ether, buf, 0, offset);
839a747e4fSDavid du Colombier 	}
849a747e4fSDavid du Colombier 
859a747e4fSDavid du Colombier 	return netifread(ether, chan, buf, n, offset);
869a747e4fSDavid du Colombier }
879a747e4fSDavid du Colombier 
889a747e4fSDavid du Colombier static Block*
etherbread(Chan * chan,long n,ulong offset)899a747e4fSDavid du Colombier etherbread(Chan* chan, long n, ulong offset)
909a747e4fSDavid du Colombier {
919a747e4fSDavid du Colombier 	return netifbread(etherxx[chan->dev], chan, n, offset);
929a747e4fSDavid du Colombier }
939a747e4fSDavid du Colombier 
949a747e4fSDavid du Colombier static int
etherwstat(Chan * chan,uchar * dp,int n)959a747e4fSDavid du Colombier etherwstat(Chan* chan, uchar* dp, int n)
969a747e4fSDavid du Colombier {
979a747e4fSDavid du Colombier 	return netifwstat(etherxx[chan->dev], chan, dp, n);
989a747e4fSDavid du Colombier }
999a747e4fSDavid du Colombier 
1009a747e4fSDavid du Colombier static void
etherrtrace(Netfile * f,Etherpkt * pkt,int len)1019a747e4fSDavid du Colombier etherrtrace(Netfile* f, Etherpkt* pkt, int len)
1029a747e4fSDavid du Colombier {
1039a747e4fSDavid du Colombier 	int i, n;
1049a747e4fSDavid du Colombier 	Block *bp;
1059a747e4fSDavid du Colombier 
1069a747e4fSDavid du Colombier 	if(qwindow(f->in) <= 0)
1079a747e4fSDavid du Colombier 		return;
1089a747e4fSDavid du Colombier 	if(len > 58)
1099a747e4fSDavid du Colombier 		n = 58;
1109a747e4fSDavid du Colombier 	else
1119a747e4fSDavid du Colombier 		n = len;
1129a747e4fSDavid du Colombier 	bp = iallocb(64);
1139a747e4fSDavid du Colombier 	if(bp == nil)
1149a747e4fSDavid du Colombier 		return;
1159a747e4fSDavid du Colombier 	memmove(bp->wp, pkt->d, n);
1169a747e4fSDavid du Colombier 	i = TK2MS(MACHP(0)->ticks);
1179a747e4fSDavid du Colombier 	bp->wp[58] = len>>8;
1189a747e4fSDavid du Colombier 	bp->wp[59] = len;
1199a747e4fSDavid du Colombier 	bp->wp[60] = i>>24;
1209a747e4fSDavid du Colombier 	bp->wp[61] = i>>16;
1219a747e4fSDavid du Colombier 	bp->wp[62] = i>>8;
1229a747e4fSDavid du Colombier 	bp->wp[63] = i;
1239a747e4fSDavid du Colombier 	bp->wp += 64;
1249a747e4fSDavid du Colombier 	qpass(f->in, bp);
1259a747e4fSDavid du Colombier }
1269a747e4fSDavid du Colombier 
1279a747e4fSDavid du Colombier Block*
etheriq(Ether * ether,Block * bp,int fromwire)1289a747e4fSDavid du Colombier etheriq(Ether* ether, Block* bp, int fromwire)
1299a747e4fSDavid du Colombier {
1309a747e4fSDavid du Colombier 	Etherpkt *pkt;
1319a747e4fSDavid du Colombier 	ushort type;
1329a747e4fSDavid du Colombier 	int len, multi, tome, fromme;
1339a747e4fSDavid du Colombier 	Netfile **ep, *f, **fp, *fx;
1349a747e4fSDavid du Colombier 	Block *xbp;
1359a747e4fSDavid du Colombier 
1369a747e4fSDavid du Colombier 	ether->inpackets++;
1379a747e4fSDavid du Colombier 
1389a747e4fSDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
1399a747e4fSDavid du Colombier 	len = BLEN(bp);
1409a747e4fSDavid du Colombier 	type = (pkt->type[0]<<8)|pkt->type[1];
1419a747e4fSDavid du Colombier 	fx = 0;
1429a747e4fSDavid du Colombier 	ep = &ether->f[Ntypes];
1439a747e4fSDavid du Colombier 
1449a747e4fSDavid du Colombier 	multi = pkt->d[0] & 1;
145*d95be1c0SDavid du Colombier 	/* check for valid multicast addresses */
1469a747e4fSDavid du Colombier 	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) && ether->prom == 0){
1479a747e4fSDavid du Colombier 		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
1489a747e4fSDavid du Colombier 			if(fromwire){
1499a747e4fSDavid du Colombier 				freeb(bp);
1509a747e4fSDavid du Colombier 				bp = 0;
1519a747e4fSDavid du Colombier 			}
1529a747e4fSDavid du Colombier 			return bp;
1539a747e4fSDavid du Colombier 		}
1549a747e4fSDavid du Colombier 	}
1559a747e4fSDavid du Colombier 
1569a747e4fSDavid du Colombier 	/* is it for me? */
1579a747e4fSDavid du Colombier 	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
1589a747e4fSDavid du Colombier 	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
1599a747e4fSDavid du Colombier 
1609a747e4fSDavid du Colombier 	/*
1619a747e4fSDavid du Colombier 	 * Multiplex the packet to all the connections which want it.
1629a747e4fSDavid du Colombier 	 * If the packet is not to be used subsequently (fromwire != 0),
1639a747e4fSDavid du Colombier 	 * attempt to simply pass it into one of the connections, thereby
1649a747e4fSDavid du Colombier 	 * saving a copy of the data (usual case hopefully).
1659a747e4fSDavid du Colombier 	 */
1669a747e4fSDavid du Colombier 	for(fp = ether->f; fp < ep; fp++){
1679a747e4fSDavid du Colombier 		if(f = *fp)
1689a747e4fSDavid du Colombier 		if(f->type == type || f->type < 0)
1699a747e4fSDavid du Colombier 		if(tome || multi || f->prom){
1709a747e4fSDavid du Colombier 			/* Don't want to hear bridged packets */
1719a747e4fSDavid du Colombier 			if(f->bridge && !fromwire && !fromme)
1729a747e4fSDavid du Colombier 				continue;
1739a747e4fSDavid du Colombier 			if(!f->headersonly){
1749a747e4fSDavid du Colombier 				if(fromwire && fx == 0)
1759a747e4fSDavid du Colombier 					fx = f;
1769a747e4fSDavid du Colombier 				else if(xbp = iallocb(len)){
1779a747e4fSDavid du Colombier 					memmove(xbp->wp, pkt, len);
1789a747e4fSDavid du Colombier 					xbp->wp += len;
1799a747e4fSDavid du Colombier 					qpass(f->in, xbp);
1809a747e4fSDavid du Colombier 				}
1819a747e4fSDavid du Colombier 				else
1829a747e4fSDavid du Colombier 					ether->soverflows++;
1839a747e4fSDavid du Colombier 			}
1849a747e4fSDavid du Colombier 			else
1859a747e4fSDavid du Colombier 				etherrtrace(f, pkt, len);
1869a747e4fSDavid du Colombier 		}
1879a747e4fSDavid du Colombier 	}
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier 	if(fx){
1909a747e4fSDavid du Colombier 		if(qpass(fx->in, bp) < 0)
1919a747e4fSDavid du Colombier 			ether->soverflows++;
1929a747e4fSDavid du Colombier 		return 0;
1939a747e4fSDavid du Colombier 	}
1949a747e4fSDavid du Colombier 	if(fromwire){
1959a747e4fSDavid du Colombier 		freeb(bp);
1969a747e4fSDavid du Colombier 		return 0;
1979a747e4fSDavid du Colombier 	}
1989a747e4fSDavid du Colombier 
1999a747e4fSDavid du Colombier 	return bp;
2009a747e4fSDavid du Colombier }
2019a747e4fSDavid du Colombier 
2029a747e4fSDavid du Colombier static int
etheroq(Ether * ether,Block * bp)2039a747e4fSDavid du Colombier etheroq(Ether* ether, Block* bp)
2049a747e4fSDavid du Colombier {
2059a747e4fSDavid du Colombier 	int len, loopback, s;
2069a747e4fSDavid du Colombier 	Etherpkt *pkt;
2079a747e4fSDavid du Colombier 
2089a747e4fSDavid du Colombier 	ether->outpackets++;
2099a747e4fSDavid du Colombier 
2109a747e4fSDavid du Colombier 	/*
2119a747e4fSDavid du Colombier 	 * Check if the packet has to be placed back onto the input queue,
2129a747e4fSDavid du Colombier 	 * i.e. if it's a loopback or broadcast packet or the interface is
2139a747e4fSDavid du Colombier 	 * in promiscuous mode.
2149a747e4fSDavid du Colombier 	 * If it's a loopback packet indicate to etheriq that the data isn't
2159a747e4fSDavid du Colombier 	 * needed and return, etheriq will pass-on or free the block.
2169a747e4fSDavid du Colombier 	 * To enable bridging to work, only packets that were originated
2179a747e4fSDavid du Colombier 	 * by this interface are fed back.
2189a747e4fSDavid du Colombier 	 */
2199a747e4fSDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
2209a747e4fSDavid du Colombier 	len = BLEN(bp);
2219a747e4fSDavid du Colombier 	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
2229a747e4fSDavid du Colombier 	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
2239a747e4fSDavid du Colombier 		s = splhi();
2249a747e4fSDavid du Colombier 		etheriq(ether, bp, 0);
2259a747e4fSDavid du Colombier 		splx(s);
2269a747e4fSDavid du Colombier 	}
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier 	if(!loopback){
2299a747e4fSDavid du Colombier 		qbwrite(ether->oq, bp);
2309a747e4fSDavid du Colombier 		ether->transmit(ether);
2319a747e4fSDavid du Colombier 	} else
2329a747e4fSDavid du Colombier 		freeb(bp);
2339a747e4fSDavid du Colombier 
2349a747e4fSDavid du Colombier 	return len;
2359a747e4fSDavid du Colombier }
2369a747e4fSDavid du Colombier 
2379a747e4fSDavid du Colombier static long
etherwrite(Chan * chan,void * buf,long n,vlong)2389a747e4fSDavid du Colombier etherwrite(Chan* chan, void* buf, long n, vlong)
2399a747e4fSDavid du Colombier {
2409a747e4fSDavid du Colombier 	Ether *ether;
2419a747e4fSDavid du Colombier 	Block *bp;
2429a747e4fSDavid du Colombier 	int nn;
2439a747e4fSDavid du Colombier 
2449a747e4fSDavid du Colombier 	ether = etherxx[chan->dev];
2459a747e4fSDavid du Colombier 	if(NETTYPE(chan->qid.path) != Ndataqid) {
2469a747e4fSDavid du Colombier 		nn = netifwrite(ether, chan, buf, n);
2479a747e4fSDavid du Colombier 		if(nn >= 0)
2489a747e4fSDavid du Colombier 			return nn;
2499a747e4fSDavid du Colombier 
2503ff48bf5SDavid du Colombier 		if(n == sizeof("nonblocking")-1 && strncmp((char*)buf, "nonblocking", n) == 0){
2513ff48bf5SDavid du Colombier 			qnoblock(ether->oq, 1);
25259c21d95SDavid du Colombier 			return n;
2533ff48bf5SDavid du Colombier 		}
2543ff48bf5SDavid du Colombier 
2559a747e4fSDavid du Colombier 		if(ether->ctl!=nil)
2569a747e4fSDavid du Colombier 			return ether->ctl(ether,buf,n);
2579a747e4fSDavid du Colombier 
2589a747e4fSDavid du Colombier 		error(Ebadctl);
2599a747e4fSDavid du Colombier 	}
2609a747e4fSDavid du Colombier 
2619a747e4fSDavid du Colombier 	if(n > ether->maxmtu)
2629a747e4fSDavid du Colombier 		error(Etoobig);
2639a747e4fSDavid du Colombier 	if(n < ether->minmtu)
2649a747e4fSDavid du Colombier 		error(Etoosmall);
2659a747e4fSDavid du Colombier 
2669a747e4fSDavid du Colombier 	bp = allocb(n);
2679a747e4fSDavid du Colombier 	if(waserror()){
2689a747e4fSDavid du Colombier 		freeb(bp);
2699a747e4fSDavid du Colombier 		nexterror();
2709a747e4fSDavid du Colombier 	}
2719a747e4fSDavid du Colombier 	memmove(bp->rp, buf, n);
2729a747e4fSDavid du Colombier 	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
2739a747e4fSDavid du Colombier 	poperror();
2749a747e4fSDavid du Colombier 	bp->wp += n;
2759a747e4fSDavid du Colombier 
2769a747e4fSDavid du Colombier 	return etheroq(ether, bp);
2779a747e4fSDavid du Colombier }
2789a747e4fSDavid du Colombier 
2799a747e4fSDavid du Colombier static long
etherbwrite(Chan * chan,Block * bp,ulong)2809a747e4fSDavid du Colombier etherbwrite(Chan* chan, Block* bp, ulong)
2819a747e4fSDavid du Colombier {
2829a747e4fSDavid du Colombier 	Ether *ether;
2839a747e4fSDavid du Colombier 	long n;
2849a747e4fSDavid du Colombier 
2859a747e4fSDavid du Colombier 	n = BLEN(bp);
2869a747e4fSDavid du Colombier 	if(NETTYPE(chan->qid.path) != Ndataqid){
2879a747e4fSDavid du Colombier 		if(waserror()) {
2889a747e4fSDavid du Colombier 			freeb(bp);
2899a747e4fSDavid du Colombier 			nexterror();
2909a747e4fSDavid du Colombier 		}
2919a747e4fSDavid du Colombier 		n = etherwrite(chan, bp->rp, n, 0);
2929a747e4fSDavid du Colombier 		poperror();
2939a747e4fSDavid du Colombier 		freeb(bp);
2949a747e4fSDavid du Colombier 		return n;
2959a747e4fSDavid du Colombier 	}
2969a747e4fSDavid du Colombier 	ether = etherxx[chan->dev];
2979a747e4fSDavid du Colombier 
2989a747e4fSDavid du Colombier 	if(n > ether->maxmtu){
2999a747e4fSDavid du Colombier 		freeb(bp);
3009a747e4fSDavid du Colombier 		error(Etoobig);
3019a747e4fSDavid du Colombier 	}
3029a747e4fSDavid du Colombier 	if(n < ether->minmtu){
3039a747e4fSDavid du Colombier 		freeb(bp);
3049a747e4fSDavid du Colombier 		error(Etoosmall);
3059a747e4fSDavid du Colombier 	}
3069a747e4fSDavid du Colombier 
3079a747e4fSDavid du Colombier 	return etheroq(ether, bp);
3089a747e4fSDavid du Colombier }
3099a747e4fSDavid du Colombier 
3109a747e4fSDavid du Colombier static struct {
3119a747e4fSDavid du Colombier 	char*	type;
3129a747e4fSDavid du Colombier 	int	(*reset)(Ether*);
3139a747e4fSDavid du Colombier } cards[MaxEther+1];
3149a747e4fSDavid du Colombier 
3159a747e4fSDavid du Colombier void
addethercard(char * t,int (* r)(Ether *))3169a747e4fSDavid du Colombier addethercard(char* t, int (*r)(Ether*))
3179a747e4fSDavid du Colombier {
3189a747e4fSDavid du Colombier 	static int ncard;
3199a747e4fSDavid du Colombier 
3209a747e4fSDavid du Colombier 	if(ncard == MaxEther)
3219a747e4fSDavid du Colombier 		panic("too many ether cards");
3229a747e4fSDavid du Colombier 	cards[ncard].type = t;
3239a747e4fSDavid du Colombier 	cards[ncard].reset = r;
3249a747e4fSDavid du Colombier 	ncard++;
3259a747e4fSDavid du Colombier }
3269a747e4fSDavid du Colombier 
3279a747e4fSDavid du Colombier int
parseether(uchar * to,char * from)3289a747e4fSDavid du Colombier parseether(uchar *to, char *from)
3299a747e4fSDavid du Colombier {
3309a747e4fSDavid du Colombier 	char nip[4];
3319a747e4fSDavid du Colombier 	char *p;
3329a747e4fSDavid du Colombier 	int i;
3339a747e4fSDavid du Colombier 
3349a747e4fSDavid du Colombier 	p = from;
3359a747e4fSDavid du Colombier 	for(i = 0; i < 6; i++){
3369a747e4fSDavid du Colombier 		if(*p == 0)
3379a747e4fSDavid du Colombier 			return -1;
3389a747e4fSDavid du Colombier 		nip[0] = *p++;
3399a747e4fSDavid du Colombier 		if(*p == 0)
3409a747e4fSDavid du Colombier 			return -1;
3419a747e4fSDavid du Colombier 		nip[1] = *p++;
3429a747e4fSDavid du Colombier 		nip[2] = 0;
3439a747e4fSDavid du Colombier 		to[i] = strtoul(nip, 0, 16);
3449a747e4fSDavid du Colombier 		if(*p == ':')
3459a747e4fSDavid du Colombier 			p++;
3469a747e4fSDavid du Colombier 	}
3479a747e4fSDavid du Colombier 	return 0;
3489a747e4fSDavid du Colombier }
3499a747e4fSDavid du Colombier 
3509a747e4fSDavid du Colombier static void
etherreset(void)3519a747e4fSDavid du Colombier etherreset(void)
3529a747e4fSDavid du Colombier {
3539a747e4fSDavid du Colombier 	Ether *ether;
3549a747e4fSDavid du Colombier 	int i, n, ctlrno;
3559a747e4fSDavid du Colombier 	char name[32], buf[128];
3569a747e4fSDavid du Colombier 
3579a747e4fSDavid du Colombier 	for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
3589a747e4fSDavid du Colombier 		if(ether == 0)
3599a747e4fSDavid du Colombier 			ether = malloc(sizeof(Ether));
3609a747e4fSDavid du Colombier 		memset(ether, 0, sizeof(Ether));
3619a747e4fSDavid du Colombier 		ether->ctlrno = ctlrno;
3629a747e4fSDavid du Colombier 		ether->tbdf = BUSUNKNOWN;
3639a747e4fSDavid du Colombier 		ether->mbps = 10;
3649a747e4fSDavid du Colombier 		ether->minmtu = ETHERMINTU;
3659a747e4fSDavid du Colombier 		ether->maxmtu = ETHERMAXTU;
3669a747e4fSDavid du Colombier 		if(isaconfig("ether", ctlrno, ether) == 0)
3679a747e4fSDavid du Colombier 			continue;
3689a747e4fSDavid du Colombier 		for(n = 0; cards[n].type; n++){
3699a747e4fSDavid du Colombier 			if(cistrcmp(cards[n].type, ether->type))
3709a747e4fSDavid du Colombier 				continue;
3719a747e4fSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
3729a747e4fSDavid du Colombier 				if(strncmp(ether->opt[i], "ea=", 3))
3739a747e4fSDavid du Colombier 					continue;
3749a747e4fSDavid du Colombier 				if(parseether(ether->ea, &ether->opt[i][3]) == -1)
3759a747e4fSDavid du Colombier 					memset(ether->ea, 0, Eaddrlen);
3769a747e4fSDavid du Colombier 			}
3779a747e4fSDavid du Colombier 			if(cards[n].reset(ether))
3789a747e4fSDavid du Colombier 				break;
3799a747e4fSDavid du Colombier 
3809a747e4fSDavid du Colombier 			/*
3819a747e4fSDavid du Colombier 			 * IRQ2 doesn't really exist, it's used to gang the interrupt
3829a747e4fSDavid du Colombier 			 * controllers together. A device set to IRQ2 will appear on
3839a747e4fSDavid du Colombier 			 * the second interrupt controller as IRQ9.
3849a747e4fSDavid du Colombier 			 */
3859a747e4fSDavid du Colombier 			if(ether->irq == 2 && BUSTYPE(ether->tbdf) != BusPCI)
3869a747e4fSDavid du Colombier 				ether->irq = 9;
3879a747e4fSDavid du Colombier 			snprint(name, sizeof(name), "ether%d", ctlrno);
3889a747e4fSDavid du Colombier 
3899a747e4fSDavid du Colombier 			/*
390fb7f0c93SDavid du Colombier 			 * If ether->irq is <0, it is a hack to indicate no interrupt
3919a747e4fSDavid du Colombier 			 * used by ethersink.
3929a747e4fSDavid du Colombier 			 */
393fb7f0c93SDavid du Colombier 			if(ether->irq >= 0)
3949a747e4fSDavid du Colombier 				intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
3959a747e4fSDavid du Colombier 
396fb7f0c93SDavid du Colombier 			i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d",
3979a747e4fSDavid du Colombier 				ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
3989a747e4fSDavid du Colombier 			if(ether->mem)
3999a747e4fSDavid du Colombier 				i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
4009a747e4fSDavid du Colombier 			if(ether->size)
4019a747e4fSDavid du Colombier 				i += sprint(buf+i, " size 0x%luX", ether->size);
4026063170eSDavid du Colombier 			i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
4039a747e4fSDavid du Colombier 				ether->ea[0], ether->ea[1], ether->ea[2],
4049a747e4fSDavid du Colombier 				ether->ea[3], ether->ea[4], ether->ea[5]);
4059a747e4fSDavid du Colombier 			sprint(buf+i, "\n");
4069a747e4fSDavid du Colombier 			print(buf);
4079a747e4fSDavid du Colombier 
4089a747e4fSDavid du Colombier 			if(ether->mbps >= 100){
4099a747e4fSDavid du Colombier 				netifinit(ether, name, Ntypes, 256*1024);
4109a747e4fSDavid du Colombier 				if(ether->oq == 0)
4113ff48bf5SDavid du Colombier 					ether->oq = qopen(256*1024, Qmsg, 0, 0);
4129a747e4fSDavid du Colombier 			}
4139a747e4fSDavid du Colombier 			else{
4149a747e4fSDavid du Colombier 				netifinit(ether, name, Ntypes, 65*1024);
4159a747e4fSDavid du Colombier 				if(ether->oq == 0)
4163ff48bf5SDavid du Colombier 					ether->oq = qopen(65*1024, Qmsg, 0, 0);
4179a747e4fSDavid du Colombier 			}
4189a747e4fSDavid du Colombier 			if(ether->oq == 0)
4199a747e4fSDavid du Colombier 				panic("etherreset %s", name);
4209a747e4fSDavid du Colombier 			ether->alen = Eaddrlen;
4219a747e4fSDavid du Colombier 			memmove(ether->addr, ether->ea, Eaddrlen);
4229a747e4fSDavid du Colombier 			memset(ether->bcast, 0xFF, Eaddrlen);
4239a747e4fSDavid du Colombier 
4249a747e4fSDavid du Colombier 			etherxx[ctlrno] = ether;
4259a747e4fSDavid du Colombier 			ether = 0;
4269a747e4fSDavid du Colombier 			break;
4279a747e4fSDavid du Colombier 		}
4289a747e4fSDavid du Colombier 	}
4299a747e4fSDavid du Colombier 	if(ether)
4309a747e4fSDavid du Colombier 		free(ether);
4319a747e4fSDavid du Colombier }
4329a747e4fSDavid du Colombier 
4339a747e4fSDavid du Colombier #define POLY 0xedb88320
4349a747e4fSDavid du Colombier 
4359a747e4fSDavid du Colombier /* really slow 32 bit crc for ethers */
4369a747e4fSDavid du Colombier ulong
ethercrc(uchar * p,int len)4379a747e4fSDavid du Colombier ethercrc(uchar *p, int len)
4389a747e4fSDavid du Colombier {
4399a747e4fSDavid du Colombier 	int i, j;
4409a747e4fSDavid du Colombier 	ulong crc, b;
4419a747e4fSDavid du Colombier 
4429a747e4fSDavid du Colombier 	crc = 0xffffffff;
4439a747e4fSDavid du Colombier 	for(i = 0; i < len; i++){
4449a747e4fSDavid du Colombier 		b = *p++;
4459a747e4fSDavid du Colombier 		for(j = 0; j < 8; j++){
4469a747e4fSDavid du Colombier 			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
4479a747e4fSDavid du Colombier 			b >>= 1;
4489a747e4fSDavid du Colombier 		}
4499a747e4fSDavid du Colombier 	}
4509a747e4fSDavid du Colombier 	return crc;
4519a747e4fSDavid du Colombier }
4529a747e4fSDavid du Colombier 
4539a747e4fSDavid du Colombier Dev etherdevtab = {
4549a747e4fSDavid du Colombier 	'l',
4559a747e4fSDavid du Colombier 	"ether",
4569a747e4fSDavid du Colombier 
4579a747e4fSDavid du Colombier 	etherreset,
4589a747e4fSDavid du Colombier 	devinit,
4599a747e4fSDavid du Colombier 	devshutdown,
4609a747e4fSDavid du Colombier 	etherattach,
4619a747e4fSDavid du Colombier 	etherwalk,
4629a747e4fSDavid du Colombier 	etherstat,
4639a747e4fSDavid du Colombier 	etheropen,
4649a747e4fSDavid du Colombier 	ethercreate,
4659a747e4fSDavid du Colombier 	etherclose,
4669a747e4fSDavid du Colombier 	etherread,
4679a747e4fSDavid du Colombier 	etherbread,
4689a747e4fSDavid du Colombier 	etherwrite,
4699a747e4fSDavid du Colombier 	etherbwrite,
4709a747e4fSDavid du Colombier 	devremove,
4719a747e4fSDavid du Colombier 	etherwstat,
4729a747e4fSDavid du Colombier };
473