xref: /plan9-contrib/sys/src/9k/386/devether.c (revision 252486b26b9cc60b522573a4310b4c79b038aad2)
19ef1f84bSDavid du Colombier #include "u.h"
29ef1f84bSDavid du Colombier #include "../port/lib.h"
39ef1f84bSDavid du Colombier #include "mem.h"
49ef1f84bSDavid du Colombier #include "dat.h"
59ef1f84bSDavid du Colombier #include "fns.h"
69ef1f84bSDavid du Colombier #include "../port/error.h"
79ef1f84bSDavid du Colombier 
89ef1f84bSDavid du Colombier #include "../port/netif.h"
99ef1f84bSDavid du Colombier 
109ef1f84bSDavid du Colombier #include "etherif.h"
119ef1f84bSDavid du Colombier 
129ef1f84bSDavid du Colombier static Ether *etherxx[MaxEther];
139ef1f84bSDavid du Colombier 
149ef1f84bSDavid du Colombier Chan*
etherattach(char * spec)159ef1f84bSDavid du Colombier etherattach(char* spec)
169ef1f84bSDavid du Colombier {
179ef1f84bSDavid du Colombier 	int ctlrno;
189ef1f84bSDavid du Colombier 	char *p;
199ef1f84bSDavid du Colombier 	Chan *chan;
209ef1f84bSDavid du Colombier 
219ef1f84bSDavid du Colombier 	ctlrno = 0;
229ef1f84bSDavid du Colombier 	if(spec && *spec){
239ef1f84bSDavid du Colombier 		ctlrno = strtoul(spec, &p, 0);
249ef1f84bSDavid du Colombier 		if((ctlrno == 0 && p == spec) || *p != 0)
259ef1f84bSDavid du Colombier 			error(Ebadarg);
269ef1f84bSDavid du Colombier 		if(ctlrno < 0 || ctlrno >= MaxEther)
279ef1f84bSDavid du Colombier 			error(Ebadarg);
289ef1f84bSDavid du Colombier 	}
299ef1f84bSDavid du Colombier 	if(etherxx[ctlrno] == 0)
309ef1f84bSDavid du Colombier 		error(Enodev);
319ef1f84bSDavid du Colombier 
329ef1f84bSDavid du Colombier 	chan = devattach('l', spec);
339ef1f84bSDavid du Colombier 	if(waserror()){
349ef1f84bSDavid du Colombier 		chanfree(chan);
359ef1f84bSDavid du Colombier 		nexterror();
369ef1f84bSDavid du Colombier 	}
379ef1f84bSDavid du Colombier 	chan->devno = ctlrno;
389ef1f84bSDavid du Colombier 	if(etherxx[ctlrno]->attach)
399ef1f84bSDavid du Colombier 		etherxx[ctlrno]->attach(etherxx[ctlrno]);
409ef1f84bSDavid du Colombier 	poperror();
419ef1f84bSDavid du Colombier 	return chan;
429ef1f84bSDavid du Colombier }
439ef1f84bSDavid du Colombier 
449ef1f84bSDavid du Colombier static Walkqid*
etherwalk(Chan * chan,Chan * nchan,char ** name,int nname)459ef1f84bSDavid du Colombier etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
469ef1f84bSDavid du Colombier {
479ef1f84bSDavid du Colombier 	return netifwalk(etherxx[chan->devno], chan, nchan, name, nname);
489ef1f84bSDavid du Colombier }
499ef1f84bSDavid du Colombier 
509ef1f84bSDavid du Colombier static long
etherstat(Chan * chan,uchar * dp,long n)519ef1f84bSDavid du Colombier etherstat(Chan* chan, uchar* dp, long n)
529ef1f84bSDavid du Colombier {
539ef1f84bSDavid du Colombier 	return netifstat(etherxx[chan->devno], chan, dp, n);
549ef1f84bSDavid du Colombier }
559ef1f84bSDavid du Colombier 
569ef1f84bSDavid du Colombier static Chan*
etheropen(Chan * chan,int omode)579ef1f84bSDavid du Colombier etheropen(Chan* chan, int omode)
589ef1f84bSDavid du Colombier {
599ef1f84bSDavid du Colombier 	return netifopen(etherxx[chan->devno], chan, omode);
609ef1f84bSDavid du Colombier }
619ef1f84bSDavid du Colombier 
629ef1f84bSDavid du Colombier static void
ethercreate(Chan *,char *,int,int)639ef1f84bSDavid du Colombier ethercreate(Chan*, char*, int, int)
649ef1f84bSDavid du Colombier {
659ef1f84bSDavid du Colombier }
669ef1f84bSDavid du Colombier 
679ef1f84bSDavid du Colombier static void
etherclose(Chan * chan)689ef1f84bSDavid du Colombier etherclose(Chan* chan)
699ef1f84bSDavid du Colombier {
709ef1f84bSDavid du Colombier 	netifclose(etherxx[chan->devno], chan);
719ef1f84bSDavid du Colombier }
729ef1f84bSDavid du Colombier 
739ef1f84bSDavid du Colombier static long
etherread(Chan * chan,void * buf,long n,vlong off)749ef1f84bSDavid du Colombier etherread(Chan* chan, void* buf, long n, vlong off)
759ef1f84bSDavid du Colombier {
769ef1f84bSDavid du Colombier 	Ether *ether;
779ef1f84bSDavid du Colombier 	ulong offset = off;
789ef1f84bSDavid du Colombier 
799ef1f84bSDavid du Colombier 	ether = etherxx[chan->devno];
809ef1f84bSDavid du Colombier 	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
819ef1f84bSDavid du Colombier 		/*
829ef1f84bSDavid du Colombier 		 * With some controllers it is necessary to reach
839ef1f84bSDavid du Colombier 		 * into the chip to extract statistics.
849ef1f84bSDavid du Colombier 		 */
859ef1f84bSDavid du Colombier 		if(NETTYPE(chan->qid.path) == Nifstatqid)
869ef1f84bSDavid du Colombier 			return ether->ifstat(ether, buf, n, offset);
879ef1f84bSDavid du Colombier 		else if(NETTYPE(chan->qid.path) == Nstatqid)
889ef1f84bSDavid du Colombier 			ether->ifstat(ether, buf, 0, offset);
899ef1f84bSDavid du Colombier 	}
909ef1f84bSDavid du Colombier 
919ef1f84bSDavid du Colombier 	return netifread(ether, chan, buf, n, offset);
929ef1f84bSDavid du Colombier }
939ef1f84bSDavid du Colombier 
949ef1f84bSDavid du Colombier static Block*
etherbread(Chan * chan,long n,vlong offset)959ef1f84bSDavid du Colombier etherbread(Chan* chan, long n, vlong offset)
969ef1f84bSDavid du Colombier {
979ef1f84bSDavid du Colombier 	return netifbread(etherxx[chan->devno], chan, n, offset);
989ef1f84bSDavid du Colombier }
999ef1f84bSDavid du Colombier 
1009ef1f84bSDavid du Colombier static long
etherwstat(Chan * chan,uchar * dp,long n)1019ef1f84bSDavid du Colombier etherwstat(Chan* chan, uchar* dp, long n)
1029ef1f84bSDavid du Colombier {
1039ef1f84bSDavid du Colombier 	return netifwstat(etherxx[chan->devno], chan, dp, n);
1049ef1f84bSDavid du Colombier }
1059ef1f84bSDavid du Colombier 
1069ef1f84bSDavid du Colombier static void
etherrtrace(Netfile * f,Etherpkt * pkt,int len)1079ef1f84bSDavid du Colombier etherrtrace(Netfile* f, Etherpkt* pkt, int len)
1089ef1f84bSDavid du Colombier {
1099ef1f84bSDavid du Colombier 	int i, n;
1109ef1f84bSDavid du Colombier 	Block *bp;
1119ef1f84bSDavid du Colombier 
1129ef1f84bSDavid du Colombier 	if(qwindow(f->iq) <= 0)
1139ef1f84bSDavid du Colombier 		return;
1149ef1f84bSDavid du Colombier 	if(len > 58)
1159ef1f84bSDavid du Colombier 		n = 58;
1169ef1f84bSDavid du Colombier 	else
1179ef1f84bSDavid du Colombier 		n = len;
1189ef1f84bSDavid du Colombier 	bp = iallocb(64);
1199ef1f84bSDavid du Colombier 	if(bp == nil)
1209ef1f84bSDavid du Colombier 		return;
1219ef1f84bSDavid du Colombier 	memmove(bp->wp, pkt->d, n);
1229ef1f84bSDavid du Colombier 	i = TK2MS(sys->ticks);
1239ef1f84bSDavid du Colombier 	bp->wp[58] = len>>8;
1249ef1f84bSDavid du Colombier 	bp->wp[59] = len;
1259ef1f84bSDavid du Colombier 	bp->wp[60] = i>>24;
1269ef1f84bSDavid du Colombier 	bp->wp[61] = i>>16;
1279ef1f84bSDavid du Colombier 	bp->wp[62] = i>>8;
1289ef1f84bSDavid du Colombier 	bp->wp[63] = i;
1299ef1f84bSDavid du Colombier 	bp->wp += 64;
1309ef1f84bSDavid du Colombier 	qpass(f->iq, bp);
1319ef1f84bSDavid du Colombier }
1329ef1f84bSDavid du Colombier 
1339ef1f84bSDavid du Colombier Block*
etheriq(Ether * ether,Block * bp,int fromwire)1349ef1f84bSDavid du Colombier etheriq(Ether* ether, Block* bp, int fromwire)
1359ef1f84bSDavid du Colombier {
1369ef1f84bSDavid du Colombier 	Etherpkt *pkt;
1379ef1f84bSDavid du Colombier 	ushort type;
1389ef1f84bSDavid du Colombier 	int len, multi, tome, fromme;
1399ef1f84bSDavid du Colombier 	Netfile **ep, *f, **fp, *fx;
1409ef1f84bSDavid du Colombier 	Block *xbp;
1419ef1f84bSDavid du Colombier 
1429ef1f84bSDavid du Colombier 	ether->inpackets++;
1439ef1f84bSDavid du Colombier 
1449ef1f84bSDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
1459ef1f84bSDavid du Colombier 	len = BLEN(bp);
1469ef1f84bSDavid du Colombier 	type = (pkt->type[0]<<8)|pkt->type[1];
1479ef1f84bSDavid du Colombier 	fx = 0;
1489ef1f84bSDavid du Colombier 	ep = &ether->f[Ntypes];
1499ef1f84bSDavid du Colombier 
1509ef1f84bSDavid du Colombier 	multi = pkt->d[0] & 1;
151277b6efdSDavid du Colombier 	/* check for valid multicast addresses */
1529ef1f84bSDavid du Colombier 	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
1539ef1f84bSDavid du Colombier 		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
1549ef1f84bSDavid du Colombier 			if(fromwire){
1559ef1f84bSDavid du Colombier 				freeb(bp);
1569ef1f84bSDavid du Colombier 				bp = 0;
1579ef1f84bSDavid du Colombier 			}
1589ef1f84bSDavid du Colombier 			return bp;
1599ef1f84bSDavid du Colombier 		}
1609ef1f84bSDavid du Colombier 	}
1619ef1f84bSDavid du Colombier 
1629ef1f84bSDavid du Colombier 	/* is it for me? */
1639ef1f84bSDavid du Colombier 	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
1649ef1f84bSDavid du Colombier 	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
1659ef1f84bSDavid du Colombier 
1669ef1f84bSDavid du Colombier 	/*
1679ef1f84bSDavid du Colombier 	 * Multiplex the packet to all the connections which want it.
1689ef1f84bSDavid du Colombier 	 * If the packet is not to be used subsequently (fromwire != 0),
1699ef1f84bSDavid du Colombier 	 * attempt to simply pass it into one of the connections, thereby
1709ef1f84bSDavid du Colombier 	 * saving a copy of the data (usual case hopefully).
1719ef1f84bSDavid du Colombier 	 */
1729ef1f84bSDavid du Colombier 	for(fp = ether->f; fp < ep; fp++){
1739ef1f84bSDavid du Colombier 		if(f = *fp)
1749ef1f84bSDavid du Colombier 		if(f->type == type || f->type < 0)
1759ef1f84bSDavid du Colombier 		if(tome || multi || f->prom){
1769ef1f84bSDavid du Colombier 			/* Don't want to hear bridged packets */
1779ef1f84bSDavid du Colombier 			if(f->bridge && !fromwire && !fromme)
1789ef1f84bSDavid du Colombier 				continue;
1799ef1f84bSDavid du Colombier 			if(!f->headersonly){
1809ef1f84bSDavid du Colombier 				if(fromwire && fx == 0)
1819ef1f84bSDavid du Colombier 					fx = f;
1829ef1f84bSDavid du Colombier 				else if(xbp = iallocb(len)){
1839ef1f84bSDavid du Colombier 					memmove(xbp->wp, pkt, len);
1849ef1f84bSDavid du Colombier 					xbp->wp += len;
1859ef1f84bSDavid du Colombier 					if(qpass(f->iq, xbp) < 0)
1869ef1f84bSDavid du Colombier 						ether->soverflows++;
1879ef1f84bSDavid du Colombier 				}
1889ef1f84bSDavid du Colombier 				else
1899ef1f84bSDavid du Colombier 					ether->soverflows++;
1909ef1f84bSDavid du Colombier 			}
1919ef1f84bSDavid du Colombier 			else
1929ef1f84bSDavid du Colombier 				etherrtrace(f, pkt, len);
1939ef1f84bSDavid du Colombier 		}
1949ef1f84bSDavid du Colombier 	}
1959ef1f84bSDavid du Colombier 
1969ef1f84bSDavid du Colombier 	if(fx){
1979ef1f84bSDavid du Colombier 		if(qpass(fx->iq, bp) < 0)
1989ef1f84bSDavid du Colombier 			ether->soverflows++;
1999ef1f84bSDavid du Colombier 		return 0;
2009ef1f84bSDavid du Colombier 	}
2019ef1f84bSDavid du Colombier 	if(fromwire){
2029ef1f84bSDavid du Colombier 		freeb(bp);
2039ef1f84bSDavid du Colombier 		return 0;
2049ef1f84bSDavid du Colombier 	}
2059ef1f84bSDavid du Colombier 
2069ef1f84bSDavid du Colombier 	return bp;
2079ef1f84bSDavid du Colombier }
2089ef1f84bSDavid du Colombier 
2099ef1f84bSDavid du Colombier static int
etheroq(Ether * ether,Block * bp)2109ef1f84bSDavid du Colombier etheroq(Ether* ether, Block* bp)
2119ef1f84bSDavid du Colombier {
2129ef1f84bSDavid du Colombier 	int len, loopback, s;
2139ef1f84bSDavid du Colombier 	Etherpkt *pkt;
2149ef1f84bSDavid du Colombier 
2159ef1f84bSDavid du Colombier 	ether->outpackets++;
2169ef1f84bSDavid du Colombier 
2179ef1f84bSDavid du Colombier 	/*
2189ef1f84bSDavid du Colombier 	 * Check if the packet has to be placed back onto the input queue,
2199ef1f84bSDavid du Colombier 	 * i.e. if it's a loopback or broadcast packet or the interface is
2209ef1f84bSDavid du Colombier 	 * in promiscuous mode.
2219ef1f84bSDavid du Colombier 	 * If it's a loopback packet indicate to etheriq that the data isn't
2229ef1f84bSDavid du Colombier 	 * needed and return, etheriq will pass-on or free the block.
2239ef1f84bSDavid du Colombier 	 * To enable bridging to work, only packets that were originated
2249ef1f84bSDavid du Colombier 	 * by this interface are fed back.
2259ef1f84bSDavid du Colombier 	 */
2269ef1f84bSDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
2279ef1f84bSDavid du Colombier 	len = BLEN(bp);
2289ef1f84bSDavid du Colombier 	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
2299ef1f84bSDavid du Colombier 	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
2309ef1f84bSDavid du Colombier 		s = splhi();
2319ef1f84bSDavid du Colombier 		etheriq(ether, bp, 0);
2329ef1f84bSDavid du Colombier 		splx(s);
2339ef1f84bSDavid du Colombier 	}
2349ef1f84bSDavid du Colombier 
2359ef1f84bSDavid du Colombier 	if(!loopback){
236277b6efdSDavid du Colombier 		if(qfull(ether->oq))
237277b6efdSDavid du Colombier 			print("etheroq: WARNING: ether->oq full!\n");
2389ef1f84bSDavid du Colombier 		qbwrite(ether->oq, bp);
2399ef1f84bSDavid du Colombier 		if(ether->transmit != nil)
2409ef1f84bSDavid du Colombier 			ether->transmit(ether);
2419ef1f84bSDavid du Colombier 	} else
2429ef1f84bSDavid du Colombier 		freeb(bp);
2439ef1f84bSDavid du Colombier 
2449ef1f84bSDavid du Colombier 	return len;
2459ef1f84bSDavid du Colombier }
2469ef1f84bSDavid du Colombier 
2479ef1f84bSDavid du Colombier static long
etherwrite(Chan * chan,void * buf,long n,vlong)2489ef1f84bSDavid du Colombier etherwrite(Chan* chan, void* buf, long n, vlong)
2499ef1f84bSDavid du Colombier {
2509ef1f84bSDavid du Colombier 	Ether *ether;
2519ef1f84bSDavid du Colombier 	Block *bp;
2529ef1f84bSDavid du Colombier 	int nn, onoff;
2539ef1f84bSDavid du Colombier 	Cmdbuf *cb;
2549ef1f84bSDavid du Colombier 
2559ef1f84bSDavid du Colombier 	ether = etherxx[chan->devno];
2569ef1f84bSDavid du Colombier 	if(NETTYPE(chan->qid.path) != Ndataqid) {
2579ef1f84bSDavid du Colombier 		nn = netifwrite(ether, chan, buf, n);
2589ef1f84bSDavid du Colombier 		if(nn >= 0)
2599ef1f84bSDavid du Colombier 			return nn;
2609ef1f84bSDavid du Colombier 		cb = parsecmd(buf, n);
2619ef1f84bSDavid du Colombier 		if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){
2629ef1f84bSDavid du Colombier 			if(cb->nf <= 1)
2639ef1f84bSDavid du Colombier 				onoff = 1;
2649ef1f84bSDavid du Colombier 			else
2659ef1f84bSDavid du Colombier 				onoff = atoi(cb->f[1]);
2669ef1f84bSDavid du Colombier 			qnoblock(ether->oq, onoff);
2679ef1f84bSDavid du Colombier 			free(cb);
2689ef1f84bSDavid du Colombier 			return n;
2699ef1f84bSDavid du Colombier 		}
2709ef1f84bSDavid du Colombier 		free(cb);
2719ef1f84bSDavid du Colombier 		if(ether->ctl != nil)
2729ef1f84bSDavid du Colombier 			return ether->ctl(ether, buf, n);
2739ef1f84bSDavid du Colombier 
2749ef1f84bSDavid du Colombier 		error(Ebadctl);
2759ef1f84bSDavid du Colombier 	}
2769ef1f84bSDavid du Colombier 
277277b6efdSDavid du Colombier 	if(n > ether->mtu)
2789ef1f84bSDavid du Colombier 		error(Etoobig);
2799ef1f84bSDavid du Colombier 	if(n < ether->minmtu)
2809ef1f84bSDavid du Colombier 		error(Etoosmall);
2819ef1f84bSDavid du Colombier 
2829ef1f84bSDavid du Colombier 	bp = allocb(n);
2839ef1f84bSDavid du Colombier 	if(waserror()){
2849ef1f84bSDavid du Colombier 		freeb(bp);
2859ef1f84bSDavid du Colombier 		nexterror();
2869ef1f84bSDavid du Colombier 	}
2879ef1f84bSDavid du Colombier 	memmove(bp->rp, buf, n);
2889ef1f84bSDavid du Colombier 	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
2899ef1f84bSDavid du Colombier 	poperror();
2909ef1f84bSDavid du Colombier 	bp->wp += n;
2919ef1f84bSDavid du Colombier 
2929ef1f84bSDavid du Colombier 	return etheroq(ether, bp);
2939ef1f84bSDavid du Colombier }
2949ef1f84bSDavid du Colombier 
2959ef1f84bSDavid du Colombier static long
etherbwrite(Chan * chan,Block * bp,vlong)2969ef1f84bSDavid du Colombier etherbwrite(Chan* chan, Block* bp, vlong)
2979ef1f84bSDavid du Colombier {
2989ef1f84bSDavid du Colombier 	Ether *ether;
2999ef1f84bSDavid du Colombier 	long n;
3009ef1f84bSDavid du Colombier 
3019ef1f84bSDavid du Colombier 	n = BLEN(bp);
3029ef1f84bSDavid du Colombier 	if(NETTYPE(chan->qid.path) != Ndataqid){
3039ef1f84bSDavid du Colombier 		if(waserror()) {
3049ef1f84bSDavid du Colombier 			freeb(bp);
3059ef1f84bSDavid du Colombier 			nexterror();
3069ef1f84bSDavid du Colombier 		}
3079ef1f84bSDavid du Colombier 		n = etherwrite(chan, bp->rp, n, 0);
3089ef1f84bSDavid du Colombier 		poperror();
3099ef1f84bSDavid du Colombier 		freeb(bp);
3109ef1f84bSDavid du Colombier 		return n;
3119ef1f84bSDavid du Colombier 	}
3129ef1f84bSDavid du Colombier 	ether = etherxx[chan->devno];
3139ef1f84bSDavid du Colombier 
314277b6efdSDavid du Colombier 	if(n > ether->mtu){
3159ef1f84bSDavid du Colombier 		freeb(bp);
3169ef1f84bSDavid du Colombier 		error(Etoobig);
3179ef1f84bSDavid du Colombier 	}
3189ef1f84bSDavid du Colombier 	if(n < ether->minmtu){
3199ef1f84bSDavid du Colombier 		freeb(bp);
3209ef1f84bSDavid du Colombier 		error(Etoosmall);
3219ef1f84bSDavid du Colombier 	}
3229ef1f84bSDavid du Colombier 
3239ef1f84bSDavid du Colombier 	return etheroq(ether, bp);
3249ef1f84bSDavid du Colombier }
3259ef1f84bSDavid du Colombier 
3269ef1f84bSDavid du Colombier static struct {
3279ef1f84bSDavid du Colombier 	char*	type;
3289ef1f84bSDavid du Colombier 	int	(*reset)(Ether*);
3299ef1f84bSDavid du Colombier } cards[MaxEther+1];
3309ef1f84bSDavid du Colombier 
3319ef1f84bSDavid du Colombier void
addethercard(char * t,int (* r)(Ether *))3329ef1f84bSDavid du Colombier addethercard(char* t, int (*r)(Ether*))
3339ef1f84bSDavid du Colombier {
3349ef1f84bSDavid du Colombier 	static int ncard;
3359ef1f84bSDavid du Colombier 
3369ef1f84bSDavid du Colombier 	if(ncard == MaxEther)
3379ef1f84bSDavid du Colombier 		panic("too many ether cards");
3389ef1f84bSDavid du Colombier 	cards[ncard].type = t;
3399ef1f84bSDavid du Colombier 	cards[ncard].reset = r;
3409ef1f84bSDavid du Colombier 	ncard++;
3419ef1f84bSDavid du Colombier }
3429ef1f84bSDavid du Colombier 
3439ef1f84bSDavid du Colombier int
parseether(uchar * to,char * from)3449ef1f84bSDavid du Colombier parseether(uchar *to, char *from)
3459ef1f84bSDavid du Colombier {
3469ef1f84bSDavid du Colombier 	char nip[4];
3479ef1f84bSDavid du Colombier 	char *p;
3489ef1f84bSDavid du Colombier 	int i;
3499ef1f84bSDavid du Colombier 
3509ef1f84bSDavid du Colombier 	p = from;
3519ef1f84bSDavid du Colombier 	for(i = 0; i < Eaddrlen; i++){
3529ef1f84bSDavid du Colombier 		if(*p == 0)
3539ef1f84bSDavid du Colombier 			return -1;
3549ef1f84bSDavid du Colombier 		nip[0] = *p++;
3559ef1f84bSDavid du Colombier 		if(*p == 0)
3569ef1f84bSDavid du Colombier 			return -1;
3579ef1f84bSDavid du Colombier 		nip[1] = *p++;
3589ef1f84bSDavid du Colombier 		nip[2] = 0;
3599ef1f84bSDavid du Colombier 		to[i] = strtoul(nip, 0, 16);
3609ef1f84bSDavid du Colombier 		if(*p == ':')
3619ef1f84bSDavid du Colombier 			p++;
3629ef1f84bSDavid du Colombier 	}
3639ef1f84bSDavid du Colombier 	return 0;
3649ef1f84bSDavid du Colombier }
3659ef1f84bSDavid du Colombier 
3669ef1f84bSDavid du Colombier static Ether*
etherprobe(int cardno,int ctlrno)3679ef1f84bSDavid du Colombier etherprobe(int cardno, int ctlrno)
3689ef1f84bSDavid du Colombier {
3699ef1f84bSDavid du Colombier 	int i;
3709ef1f84bSDavid du Colombier 	Ether *ether;
3719ef1f84bSDavid du Colombier 	char buf[128], name[32];
3729ef1f84bSDavid du Colombier 
3739ef1f84bSDavid du Colombier 	ether = malloc(sizeof(Ether));
374277b6efdSDavid du Colombier 	if(ether == nil)
375277b6efdSDavid du Colombier 		error(Enomem);
3769ef1f84bSDavid du Colombier 	memset(ether, 0, sizeof(Ether));
3779ef1f84bSDavid du Colombier 	ether->ctlrno = ctlrno;
3789ef1f84bSDavid du Colombier 	ether->tbdf = -1;
3799ef1f84bSDavid du Colombier 	ether->mbps = 10;
3809ef1f84bSDavid du Colombier 	ether->minmtu = ETHERMINTU;
3819ef1f84bSDavid du Colombier 	ether->maxmtu = ETHERMAXTU;
382277b6efdSDavid du Colombier 	ether->mtu = ETHERMAXTU;
3839ef1f84bSDavid du Colombier 
3849ef1f84bSDavid du Colombier 	if(cardno < 0){
3859ef1f84bSDavid du Colombier 		if(isaconfig("ether", ctlrno, ether) == 0){
3869ef1f84bSDavid du Colombier 			free(ether);
3879ef1f84bSDavid du Colombier 			return nil;
3889ef1f84bSDavid du Colombier 		}
3899ef1f84bSDavid du Colombier 		for(cardno = 0; cards[cardno].type; cardno++){
3909ef1f84bSDavid du Colombier 			if(cistrcmp(cards[cardno].type, ether->type))
3919ef1f84bSDavid du Colombier 				continue;
3929ef1f84bSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
3939ef1f84bSDavid du Colombier 				if(strncmp(ether->opt[i], "ea=", 3))
3949ef1f84bSDavid du Colombier 					continue;
3959ef1f84bSDavid du Colombier 				if(parseether(ether->ea, &ether->opt[i][3]))
3969ef1f84bSDavid du Colombier 					memset(ether->ea, 0, Eaddrlen);
3979ef1f84bSDavid du Colombier 			}
3989ef1f84bSDavid du Colombier 			break;
3999ef1f84bSDavid du Colombier 		}
4009ef1f84bSDavid du Colombier 	}
4019ef1f84bSDavid du Colombier 
4029ef1f84bSDavid du Colombier 	if(cardno >= MaxEther || cards[cardno].type == nil){
4039ef1f84bSDavid du Colombier 		free(ether);
4049ef1f84bSDavid du Colombier 		return nil;
4059ef1f84bSDavid du Colombier 	}
4069ef1f84bSDavid du Colombier 	if(cards[cardno].reset(ether) < 0){
4079ef1f84bSDavid du Colombier 		free(ether);
4089ef1f84bSDavid du Colombier 		return nil;
4099ef1f84bSDavid du Colombier 	}
4109ef1f84bSDavid du Colombier 
4119ef1f84bSDavid du Colombier 	/*
4129ef1f84bSDavid du Colombier 	 * IRQ2 doesn't really exist, it's used to gang the interrupt
4139ef1f84bSDavid du Colombier 	 * controllers together. A device set to IRQ2 will appear on
4149ef1f84bSDavid du Colombier 	 * the second interrupt controller as IRQ9.
4159ef1f84bSDavid du Colombier 	 */
4169ef1f84bSDavid du Colombier 	if(ether->irq == 2)
4179ef1f84bSDavid du Colombier 		ether->irq = 9;
4189ef1f84bSDavid du Colombier 	snprint(name, sizeof(name), "ether%d", ctlrno);
4199ef1f84bSDavid du Colombier 
4209ef1f84bSDavid du Colombier 	/*
4219ef1f84bSDavid du Colombier 	 * If ether->irq is <0, it is a hack to indicate no interrupt
4229ef1f84bSDavid du Colombier 	 * used by ethersink.
4239ef1f84bSDavid du Colombier 	 * Or perhaps the driver has some other way to configure
424ca60fcb8SDavid du Colombier 	 * interrupts for itself, e.g. HyperTransport MSI.
4259ef1f84bSDavid du Colombier 	 */
4269ef1f84bSDavid du Colombier 	if(ether->irq >= 0)
427*252486b2SDavid du Colombier 		ether->vector = intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
4289ef1f84bSDavid du Colombier 
4299ef1f84bSDavid du Colombier 	i = sprint(buf, "#l%d: %s: ", ctlrno, cards[cardno].type);
4309ef1f84bSDavid du Colombier 	if(ether->mbps >= 1000)
4319ef1f84bSDavid du Colombier 		i += sprint(buf+i, "%dGbps", ether->mbps/1000);
4329ef1f84bSDavid du Colombier 	else
4339ef1f84bSDavid du Colombier 		i += sprint(buf+i, "%dMbps", ether->mbps);
4349ef1f84bSDavid du Colombier 	i += sprint(buf+i, " port %#p irq %d", ether->port, ether->irq);
4359ef1f84bSDavid du Colombier 	if(ether->mem)
4369ef1f84bSDavid du Colombier 		i += sprint(buf+i, " addr %#p", ether->mem);
4379ef1f84bSDavid du Colombier 	if(ether->size)
4389ef1f84bSDavid du Colombier 		i += sprint(buf+i, " size %ld", ether->size);
4399ef1f84bSDavid du Colombier 	i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
4409ef1f84bSDavid du Colombier 		ether->ea[0], ether->ea[1], ether->ea[2],
4419ef1f84bSDavid du Colombier 		ether->ea[3], ether->ea[4], ether->ea[5]);
4429ef1f84bSDavid du Colombier 	sprint(buf+i, "\n");
4439ef1f84bSDavid du Colombier 	print(buf);
4449ef1f84bSDavid du Colombier 
4459ef1f84bSDavid du Colombier 	if(ether->mbps >= 1000){
4469ef1f84bSDavid du Colombier 		netifinit(ether, name, Ntypes, 512*1024);
4479ef1f84bSDavid du Colombier 		if(ether->oq == 0)
4489ef1f84bSDavid du Colombier 			ether->oq = qopen(512*1024, Qmsg, 0, 0);
4499ef1f84bSDavid du Colombier 	}else if(ether->mbps >= 100){
4509ef1f84bSDavid du Colombier 		netifinit(ether, name, Ntypes, 256*1024);
4519ef1f84bSDavid du Colombier 		if(ether->oq == 0)
4529ef1f84bSDavid du Colombier 			ether->oq = qopen(256*1024, Qmsg, 0, 0);
453277b6efdSDavid du Colombier 	}else{
4549ef1f84bSDavid du Colombier 		netifinit(ether, name, Ntypes, 128*1024);
4559ef1f84bSDavid du Colombier 		if(ether->oq == 0)
4569ef1f84bSDavid du Colombier 			ether->oq = qopen(128*1024, Qmsg, 0, 0);
4579ef1f84bSDavid du Colombier 	}
4589ef1f84bSDavid du Colombier 	if(ether->oq == 0)
4599ef1f84bSDavid du Colombier 		panic("etherreset %s", name);
4609ef1f84bSDavid du Colombier 	ether->alen = Eaddrlen;
4619ef1f84bSDavid du Colombier 	memmove(ether->addr, ether->ea, Eaddrlen);
4629ef1f84bSDavid du Colombier 	memset(ether->bcast, 0xFF, Eaddrlen);
4639ef1f84bSDavid du Colombier 
4649ef1f84bSDavid du Colombier 	return ether;
4659ef1f84bSDavid du Colombier }
4669ef1f84bSDavid du Colombier 
4679ef1f84bSDavid du Colombier static void
etherreset(void)4689ef1f84bSDavid du Colombier etherreset(void)
4699ef1f84bSDavid du Colombier {
4709ef1f84bSDavid du Colombier 	Ether *ether;
4719ef1f84bSDavid du Colombier 	int cardno, ctlrno;
4729ef1f84bSDavid du Colombier 
4739ef1f84bSDavid du Colombier 	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
4749ef1f84bSDavid du Colombier 		if((ether = etherprobe(-1, ctlrno)) == nil)
4759ef1f84bSDavid du Colombier 			continue;
4769ef1f84bSDavid du Colombier 		etherxx[ctlrno] = ether;
4779ef1f84bSDavid du Colombier 	}
4789ef1f84bSDavid du Colombier 
4799ef1f84bSDavid du Colombier 	if(getconf("*noetherprobe"))
4809ef1f84bSDavid du Colombier 		return;
4819ef1f84bSDavid du Colombier 
4829ef1f84bSDavid du Colombier 	cardno = ctlrno = 0;
4839ef1f84bSDavid du Colombier 	while(cards[cardno].type != nil && ctlrno < MaxEther){
4849ef1f84bSDavid du Colombier 		if(etherxx[ctlrno] != nil){
4859ef1f84bSDavid du Colombier 			ctlrno++;
4869ef1f84bSDavid du Colombier 			continue;
4879ef1f84bSDavid du Colombier 		}
4889ef1f84bSDavid du Colombier 		if((ether = etherprobe(cardno, ctlrno)) == nil){
4899ef1f84bSDavid du Colombier 			cardno++;
4909ef1f84bSDavid du Colombier 			continue;
4919ef1f84bSDavid du Colombier 		}
4929ef1f84bSDavid du Colombier 		etherxx[ctlrno] = ether;
4939ef1f84bSDavid du Colombier 		ctlrno++;
4949ef1f84bSDavid du Colombier 	}
4959ef1f84bSDavid du Colombier }
4969ef1f84bSDavid du Colombier 
4979ef1f84bSDavid du Colombier static void
ethershutdown(void)4989ef1f84bSDavid du Colombier ethershutdown(void)
4999ef1f84bSDavid du Colombier {
5009ef1f84bSDavid du Colombier 	Ether *ether;
5019ef1f84bSDavid du Colombier 	int i;
5029ef1f84bSDavid du Colombier 
5039ef1f84bSDavid du Colombier 	for(i = 0; i < MaxEther; i++){
5049ef1f84bSDavid du Colombier 		ether = etherxx[i];
5059ef1f84bSDavid du Colombier 		if(ether == nil)
5069ef1f84bSDavid du Colombier 			continue;
507*252486b2SDavid du Colombier 		if(ether->irq >= 0)
508*252486b2SDavid du Colombier 			intrdisable(ether->vector);
5099ef1f84bSDavid du Colombier 		if(ether->shutdown == nil) {
510277b6efdSDavid du Colombier 			print("#l%d: no shutdown function\n", i);
5119ef1f84bSDavid du Colombier 			continue;
5129ef1f84bSDavid du Colombier 		}
5139ef1f84bSDavid du Colombier 		(*ether->shutdown)(ether);
5149ef1f84bSDavid du Colombier 	}
5159ef1f84bSDavid du Colombier }
5169ef1f84bSDavid du Colombier 
5179ef1f84bSDavid du Colombier 
5189ef1f84bSDavid du Colombier #define POLY 0xedb88320
5199ef1f84bSDavid du Colombier 
5209ef1f84bSDavid du Colombier /* really slow 32 bit crc for ethers */
5219ef1f84bSDavid du Colombier ulong
ethercrc(uchar * p,int len)5229ef1f84bSDavid du Colombier ethercrc(uchar *p, int len)
5239ef1f84bSDavid du Colombier {
5249ef1f84bSDavid du Colombier 	int i, j;
5259ef1f84bSDavid du Colombier 	ulong crc, b;
5269ef1f84bSDavid du Colombier 
5279ef1f84bSDavid du Colombier 	crc = 0xffffffff;
5289ef1f84bSDavid du Colombier 	for(i = 0; i < len; i++){
5299ef1f84bSDavid du Colombier 		b = *p++;
5309ef1f84bSDavid du Colombier 		for(j = 0; j < 8; j++){
5319ef1f84bSDavid du Colombier 			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
5329ef1f84bSDavid du Colombier 			b >>= 1;
5339ef1f84bSDavid du Colombier 		}
5349ef1f84bSDavid du Colombier 	}
5359ef1f84bSDavid du Colombier 	return crc;
5369ef1f84bSDavid du Colombier }
5379ef1f84bSDavid du Colombier 
5389ef1f84bSDavid du Colombier Dev etherdevtab = {
5399ef1f84bSDavid du Colombier 	'l',
5409ef1f84bSDavid du Colombier 	"ether",
5419ef1f84bSDavid du Colombier 
5429ef1f84bSDavid du Colombier 	etherreset,
5439ef1f84bSDavid du Colombier 	devinit,
5449ef1f84bSDavid du Colombier 	ethershutdown,
5459ef1f84bSDavid du Colombier 	etherattach,
5469ef1f84bSDavid du Colombier 	etherwalk,
5479ef1f84bSDavid du Colombier 	etherstat,
5489ef1f84bSDavid du Colombier 	etheropen,
5499ef1f84bSDavid du Colombier 	ethercreate,
5509ef1f84bSDavid du Colombier 	etherclose,
5519ef1f84bSDavid du Colombier 	etherread,
5529ef1f84bSDavid du Colombier 	etherbread,
5539ef1f84bSDavid du Colombier 	etherwrite,
5549ef1f84bSDavid du Colombier 	etherbwrite,
5559ef1f84bSDavid du Colombier 	devremove,
5569ef1f84bSDavid du Colombier 	etherwstat,
5579ef1f84bSDavid du Colombier };
558