xref: /plan9/sys/src/9/pc/devether.c (revision 6520663fb7ebac8d17e4a1dbc55d10bf525f7b14)
13e12c5d1SDavid du Colombier #include "u.h"
23e12c5d1SDavid du Colombier #include "../port/lib.h"
33e12c5d1SDavid du Colombier #include "mem.h"
43e12c5d1SDavid du Colombier #include "dat.h"
53e12c5d1SDavid du Colombier #include "fns.h"
63e12c5d1SDavid du Colombier #include "io.h"
787dfdc75SDavid du Colombier #include "pool.h"
87dd7cddfSDavid du Colombier #include "ureg.h"
97dd7cddfSDavid du Colombier #include "../port/error.h"
107dd7cddfSDavid du Colombier #include "../port/netif.h"
113e12c5d1SDavid du Colombier 
127dd7cddfSDavid du Colombier #include "etherif.h"
133e12c5d1SDavid du Colombier 
147dd7cddfSDavid du Colombier static Ether *etherxx[MaxEther];
153e12c5d1SDavid du Colombier 
163e12c5d1SDavid du Colombier Chan*
etherattach(char * spec)177dd7cddfSDavid du Colombier etherattach(char* spec)
183e12c5d1SDavid du Colombier {
197dd7cddfSDavid du Colombier 	ulong ctlrno;
207dd7cddfSDavid du Colombier 	char *p;
217dd7cddfSDavid du Colombier 	Chan *chan;
223e12c5d1SDavid du Colombier 
237dd7cddfSDavid du Colombier 	ctlrno = 0;
247dd7cddfSDavid du Colombier 	if(spec && *spec){
257dd7cddfSDavid du Colombier 		ctlrno = strtoul(spec, &p, 0);
267dd7cddfSDavid du Colombier 		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
277dd7cddfSDavid du Colombier 			error(Ebadarg);
283e12c5d1SDavid du Colombier 	}
297dd7cddfSDavid du Colombier 	if(etherxx[ctlrno] == 0)
307dd7cddfSDavid du Colombier 		error(Enodev);
313e12c5d1SDavid du Colombier 
327dd7cddfSDavid du Colombier 	chan = devattach('l', spec);
33830cad64SDavid du Colombier 	if(waserror()){
34830cad64SDavid du Colombier 		chanfree(chan);
35830cad64SDavid du Colombier 		nexterror();
36830cad64SDavid du Colombier 	}
377dd7cddfSDavid du Colombier 	chan->dev = ctlrno;
387dd7cddfSDavid du Colombier 	if(etherxx[ctlrno]->attach)
397dd7cddfSDavid du Colombier 		etherxx[ctlrno]->attach(etherxx[ctlrno]);
40830cad64SDavid du Colombier 	poperror();
417dd7cddfSDavid du Colombier 	return chan;
423e12c5d1SDavid du Colombier }
433e12c5d1SDavid du Colombier 
449a747e4fSDavid du Colombier static Walkqid*
etherwalk(Chan * chan,Chan * nchan,char ** name,int nname)459a747e4fSDavid du Colombier etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
463e12c5d1SDavid du Colombier {
479a747e4fSDavid du Colombier 	return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
483e12c5d1SDavid du Colombier }
493e12c5d1SDavid du Colombier 
509a747e4fSDavid du Colombier static int
etherstat(Chan * chan,uchar * dp,int n)519a747e4fSDavid du Colombier etherstat(Chan* chan, uchar* dp, int n)
523e12c5d1SDavid du Colombier {
539a747e4fSDavid du Colombier 	return netifstat(etherxx[chan->dev], chan, dp, n);
547dd7cddfSDavid du Colombier }
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier static Chan*
etheropen(Chan * chan,int omode)577dd7cddfSDavid du Colombier etheropen(Chan* chan, int omode)
587dd7cddfSDavid du Colombier {
597dd7cddfSDavid du Colombier 	return netifopen(etherxx[chan->dev], chan, omode);
607dd7cddfSDavid du Colombier }
617dd7cddfSDavid du Colombier 
627dd7cddfSDavid du Colombier static void
ethercreate(Chan *,char *,int,ulong)637dd7cddfSDavid du Colombier ethercreate(Chan*, char*, int, ulong)
647dd7cddfSDavid du Colombier {
657dd7cddfSDavid du Colombier }
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier static void
etherclose(Chan * chan)687dd7cddfSDavid du Colombier etherclose(Chan* chan)
697dd7cddfSDavid du Colombier {
707dd7cddfSDavid du Colombier 	netifclose(etherxx[chan->dev], chan);
717dd7cddfSDavid du Colombier }
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier static long
etherread(Chan * chan,void * buf,long n,vlong off)747dd7cddfSDavid du Colombier etherread(Chan* chan, void* buf, long n, vlong off)
757dd7cddfSDavid du Colombier {
767dd7cddfSDavid du Colombier 	Ether *ether;
777dd7cddfSDavid du Colombier 	ulong offset = off;
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier 	ether = etherxx[chan->dev];
809a747e4fSDavid du Colombier 	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
817dd7cddfSDavid du Colombier 		/*
827dd7cddfSDavid du Colombier 		 * With some controllers it is necessary to reach
837dd7cddfSDavid du Colombier 		 * into the chip to extract statistics.
847dd7cddfSDavid du Colombier 		 */
857dd7cddfSDavid du Colombier 		if(NETTYPE(chan->qid.path) == Nifstatqid)
867dd7cddfSDavid du Colombier 			return ether->ifstat(ether, buf, n, offset);
877dd7cddfSDavid du Colombier 		else if(NETTYPE(chan->qid.path) == Nstatqid)
887dd7cddfSDavid du Colombier 			ether->ifstat(ether, buf, 0, offset);
897dd7cddfSDavid du Colombier 	}
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier 	return netifread(ether, chan, buf, n, offset);
927dd7cddfSDavid du Colombier }
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier static Block*
etherbread(Chan * chan,long n,ulong offset)957dd7cddfSDavid du Colombier etherbread(Chan* chan, long n, ulong offset)
967dd7cddfSDavid du Colombier {
977dd7cddfSDavid du Colombier 	return netifbread(etherxx[chan->dev], chan, n, offset);
987dd7cddfSDavid du Colombier }
997dd7cddfSDavid du Colombier 
1009a747e4fSDavid du Colombier static int
etherwstat(Chan * chan,uchar * dp,int n)1019a747e4fSDavid du Colombier etherwstat(Chan* chan, uchar* dp, int n)
1027dd7cddfSDavid du Colombier {
1039a747e4fSDavid du Colombier 	return netifwstat(etherxx[chan->dev], chan, dp, n);
1047dd7cddfSDavid du Colombier }
1057dd7cddfSDavid du Colombier 
1067dd7cddfSDavid du Colombier static void
etherrtrace(Netfile * f,Etherpkt * pkt,int len)1077dd7cddfSDavid du Colombier etherrtrace(Netfile* f, Etherpkt* pkt, int len)
1087dd7cddfSDavid du Colombier {
1099a747e4fSDavid du Colombier 	int i, n;
1107dd7cddfSDavid du Colombier 	Block *bp;
1117dd7cddfSDavid du Colombier 
1127dd7cddfSDavid du Colombier 	if(qwindow(f->in) <= 0)
1137dd7cddfSDavid du Colombier 		return;
11459cc4ca5SDavid du Colombier 	if(len > 58)
11559cc4ca5SDavid du Colombier 		n = 58;
1167dd7cddfSDavid du Colombier 	else
1177dd7cddfSDavid du Colombier 		n = len;
1189a747e4fSDavid du Colombier 	bp = iallocb(64);
11959cc4ca5SDavid du Colombier 	if(bp == nil)
1207dd7cddfSDavid du Colombier 		return;
1217dd7cddfSDavid du Colombier 	memmove(bp->wp, pkt->d, n);
1229a747e4fSDavid du Colombier 	i = TK2MS(MACHP(0)->ticks);
1237dd7cddfSDavid du Colombier 	bp->wp[58] = len>>8;
1247dd7cddfSDavid du Colombier 	bp->wp[59] = len;
1259a747e4fSDavid du Colombier 	bp->wp[60] = i>>24;
1269a747e4fSDavid du Colombier 	bp->wp[61] = i>>16;
1279a747e4fSDavid du Colombier 	bp->wp[62] = i>>8;
1289a747e4fSDavid du Colombier 	bp->wp[63] = i;
1299a747e4fSDavid du Colombier 	bp->wp += 64;
1307dd7cddfSDavid du Colombier 	qpass(f->in, bp);
1317dd7cddfSDavid du Colombier }
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier Block*
etheriq(Ether * ether,Block * bp,int fromwire)1347dd7cddfSDavid du Colombier etheriq(Ether* ether, Block* bp, int fromwire)
1357dd7cddfSDavid du Colombier {
136bd389b36SDavid du Colombier 	Etherpkt *pkt;
1377dd7cddfSDavid du Colombier 	ushort type;
1387dd7cddfSDavid du Colombier 	int len, multi, tome, fromme;
1397dd7cddfSDavid du Colombier 	Netfile **ep, *f, **fp, *fx;
1407dd7cddfSDavid du Colombier 	Block *xbp;
1413e12c5d1SDavid du Colombier 
1427dd7cddfSDavid du Colombier 	ether->inpackets++;
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
1457dd7cddfSDavid du Colombier 	len = BLEN(bp);
1467dd7cddfSDavid du Colombier 	type = (pkt->type[0]<<8)|pkt->type[1];
1477dd7cddfSDavid du Colombier 	fx = 0;
1487dd7cddfSDavid du Colombier 	ep = &ether->f[Ntypes];
1497dd7cddfSDavid du Colombier 
1507dd7cddfSDavid du Colombier 	multi = pkt->d[0] & 1;
151d95be1c0SDavid du Colombier 	/* check for valid multicast addresses */
15208fd2d13SDavid du Colombier 	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 && ether->prom == 0){
1537dd7cddfSDavid du Colombier 		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
1547dd7cddfSDavid du Colombier 			if(fromwire){
1553e12c5d1SDavid du Colombier 				freeb(bp);
1567dd7cddfSDavid du Colombier 				bp = 0;
1573e12c5d1SDavid du Colombier 			}
1587dd7cddfSDavid du Colombier 			return bp;
1593e12c5d1SDavid du Colombier 		}
1603e12c5d1SDavid du Colombier 	}
1613e12c5d1SDavid du Colombier 
1627dd7cddfSDavid du Colombier 	/* is it for me? */
1637dd7cddfSDavid du Colombier 	tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
1647dd7cddfSDavid du Colombier 	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
1657dd7cddfSDavid du Colombier 
1663e12c5d1SDavid du Colombier 	/*
1677dd7cddfSDavid du Colombier 	 * Multiplex the packet to all the connections which want it.
1687dd7cddfSDavid du Colombier 	 * If the packet is not to be used subsequently (fromwire != 0),
1697dd7cddfSDavid du Colombier 	 * attempt to simply pass it into one of the connections, thereby
1707dd7cddfSDavid du Colombier 	 * saving a copy of the data (usual case hopefully).
1713e12c5d1SDavid du Colombier 	 */
1727dd7cddfSDavid du Colombier 	for(fp = ether->f; fp < ep; fp++){
1737dd7cddfSDavid du Colombier 		if(f = *fp)
1747dd7cddfSDavid du Colombier 		if(f->type == type || f->type < 0)
1757dd7cddfSDavid du Colombier 		if(tome || multi || f->prom){
1767dd7cddfSDavid du Colombier 			/* Don't want to hear bridged packets */
1777dd7cddfSDavid du Colombier 			if(f->bridge && !fromwire && !fromme)
1787dd7cddfSDavid du Colombier 				continue;
1797dd7cddfSDavid du Colombier 			if(!f->headersonly){
1807dd7cddfSDavid du Colombier 				if(fromwire && fx == 0)
1817dd7cddfSDavid du Colombier 					fx = f;
1827dd7cddfSDavid du Colombier 				else if(xbp = iallocb(len)){
1837dd7cddfSDavid du Colombier 					memmove(xbp->wp, pkt, len);
1847dd7cddfSDavid du Colombier 					xbp->wp += len;
185aa26ca9fSDavid du Colombier 					if(qpass(f->in, xbp) < 0){
18608cb2eb6SDavid du Colombier 						// print("soverflow for f->in\n");
1879a747e4fSDavid du Colombier 						ether->soverflows++;
1887dd7cddfSDavid du Colombier 					}
189aa26ca9fSDavid du Colombier 				}
190aa26ca9fSDavid du Colombier 				else{
19108cb2eb6SDavid du Colombier 					// print("soverflow iallocb\n");
1927dd7cddfSDavid du Colombier 					ether->soverflows++;
1937dd7cddfSDavid du Colombier 				}
194aa26ca9fSDavid du Colombier 			}
1957dd7cddfSDavid du Colombier 			else
1967dd7cddfSDavid du Colombier 				etherrtrace(f, pkt, len);
1977dd7cddfSDavid du Colombier 		}
1987dd7cddfSDavid du Colombier 	}
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 	if(fx){
201aa26ca9fSDavid du Colombier 		if(qpass(fx->in, bp) < 0){
20208cb2eb6SDavid du Colombier 			// print("soverflow for fx->in\n");
2037dd7cddfSDavid du Colombier 			ether->soverflows++;
204aa26ca9fSDavid du Colombier 		}
2057dd7cddfSDavid du Colombier 		return 0;
2067dd7cddfSDavid du Colombier 	}
2077dd7cddfSDavid du Colombier 	if(fromwire){
208219b2ee8SDavid du Colombier 		freeb(bp);
2097dd7cddfSDavid du Colombier 		return 0;
2103e12c5d1SDavid du Colombier 	}
2113e12c5d1SDavid du Colombier 
2127dd7cddfSDavid du Colombier 	return bp;
2137dd7cddfSDavid du Colombier }
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier static int
etheroq(Ether * ether,Block * bp)2167dd7cddfSDavid du Colombier etheroq(Ether* ether, Block* bp)
2177dd7cddfSDavid du Colombier {
2187dd7cddfSDavid du Colombier 	int len, loopback, s;
2197dd7cddfSDavid du Colombier 	Etherpkt *pkt;
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 	ether->outpackets++;
2227dd7cddfSDavid du Colombier 
2233e12c5d1SDavid du Colombier 	/*
2247dd7cddfSDavid du Colombier 	 * Check if the packet has to be placed back onto the input queue,
2257dd7cddfSDavid du Colombier 	 * i.e. if it's a loopback or broadcast packet or the interface is
2267dd7cddfSDavid du Colombier 	 * in promiscuous mode.
2277dd7cddfSDavid du Colombier 	 * If it's a loopback packet indicate to etheriq that the data isn't
2287dd7cddfSDavid du Colombier 	 * needed and return, etheriq will pass-on or free the block.
2297dd7cddfSDavid du Colombier 	 * To enable bridging to work, only packets that were originated
2307dd7cddfSDavid du Colombier 	 * by this interface are fed back.
2313e12c5d1SDavid du Colombier 	 */
2327dd7cddfSDavid du Colombier 	pkt = (Etherpkt*)bp->rp;
2337dd7cddfSDavid du Colombier 	len = BLEN(bp);
2347dd7cddfSDavid du Colombier 	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
2357dd7cddfSDavid du Colombier 	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
236219b2ee8SDavid du Colombier 		s = splhi();
2377dd7cddfSDavid du Colombier 		etheriq(ether, bp, 0);
238219b2ee8SDavid du Colombier 		splx(s);
2397dd7cddfSDavid du Colombier 	}
2403e12c5d1SDavid du Colombier 
2417dd7cddfSDavid du Colombier 	if(!loopback){
242aa26ca9fSDavid du Colombier 		if(qfull(ether->oq))
243aa26ca9fSDavid du Colombier 			print("etheroq: WARNING: ether->oq full!\n");
2447dd7cddfSDavid du Colombier 		qbwrite(ether->oq, bp);
2455e96a66cSDavid du Colombier 		if(ether->transmit != nil)
2467dd7cddfSDavid du Colombier 			ether->transmit(ether);
2477dd7cddfSDavid du Colombier 	} else
248219b2ee8SDavid du Colombier 		freeb(bp);
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier 	return len;
2513e12c5d1SDavid du Colombier }
2523e12c5d1SDavid du Colombier 
2537dd7cddfSDavid du Colombier static long
etherwrite(Chan * chan,void * buf,long n,vlong)2547dd7cddfSDavid du Colombier etherwrite(Chan* chan, void* buf, long n, vlong)
2553e12c5d1SDavid du Colombier {
2567dd7cddfSDavid du Colombier 	Ether *ether;
2573e12c5d1SDavid du Colombier 	Block *bp;
258fb7f0c93SDavid du Colombier 	int nn, onoff;
259fb7f0c93SDavid du Colombier 	Cmdbuf *cb;
2603e12c5d1SDavid du Colombier 
2617dd7cddfSDavid du Colombier 	ether = etherxx[chan->dev];
26259cc4ca5SDavid du Colombier 	if(NETTYPE(chan->qid.path) != Ndataqid) {
26359cc4ca5SDavid du Colombier 		nn = netifwrite(ether, chan, buf, n);
26459cc4ca5SDavid du Colombier 		if(nn >= 0)
26559cc4ca5SDavid du Colombier 			return nn;
266fb7f0c93SDavid du Colombier 		cb = parsecmd(buf, n);
267830cad64SDavid du Colombier 		if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){
268fb7f0c93SDavid du Colombier 			if(cb->nf <= 1)
269fb7f0c93SDavid du Colombier 				onoff = 1;
270fb7f0c93SDavid du Colombier 			else
271fb7f0c93SDavid du Colombier 				onoff = atoi(cb->f[1]);
272fb7f0c93SDavid du Colombier 			qnoblock(ether->oq, onoff);
273fb7f0c93SDavid du Colombier 			free(cb);
2743ff48bf5SDavid du Colombier 			return n;
2753ff48bf5SDavid du Colombier 		}
276fb7f0c93SDavid du Colombier 		free(cb);
27759cc4ca5SDavid du Colombier 		if(ether->ctl != nil)
27859cc4ca5SDavid du Colombier 			return ether->ctl(ether, buf, n);
27959cc4ca5SDavid du Colombier 
28059cc4ca5SDavid du Colombier 		error(Ebadctl);
28159cc4ca5SDavid du Colombier 	}
28259cc4ca5SDavid du Colombier 
28323173ec1SDavid du Colombier 	if(n > ether->mtu)
2847dd7cddfSDavid du Colombier 		error(Etoobig);
28559cc4ca5SDavid du Colombier 	if(n < ether->minmtu)
2867dd7cddfSDavid du Colombier 		error(Etoosmall);
2873e12c5d1SDavid du Colombier 
2887dd7cddfSDavid du Colombier 	bp = allocb(n);
2899a747e4fSDavid du Colombier 	if(waserror()){
2909a747e4fSDavid du Colombier 		freeb(bp);
2919a747e4fSDavid du Colombier 		nexterror();
2929a747e4fSDavid du Colombier 	}
2937dd7cddfSDavid du Colombier 	memmove(bp->rp, buf, n);
2947dd7cddfSDavid du Colombier 	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
2959a747e4fSDavid du Colombier 	poperror();
2967dd7cddfSDavid du Colombier 	bp->wp += n;
297bd389b36SDavid du Colombier 
2987dd7cddfSDavid du Colombier 	return etheroq(ether, bp);
2997dd7cddfSDavid du Colombier }
3003e12c5d1SDavid du Colombier 
3017dd7cddfSDavid du Colombier static long
etherbwrite(Chan * chan,Block * bp,ulong)3027dd7cddfSDavid du Colombier etherbwrite(Chan* chan, Block* bp, ulong)
3037dd7cddfSDavid du Colombier {
3047dd7cddfSDavid du Colombier 	Ether *ether;
3057dd7cddfSDavid du Colombier 	long n;
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier 	n = BLEN(bp);
3087dd7cddfSDavid du Colombier 	if(NETTYPE(chan->qid.path) != Ndataqid){
30959cc4ca5SDavid du Colombier 		if(waserror()) {
31059cc4ca5SDavid du Colombier 			freeb(bp);
31159cc4ca5SDavid du Colombier 			nexterror();
31259cc4ca5SDavid du Colombier 		}
31359cc4ca5SDavid du Colombier 		n = etherwrite(chan, bp->rp, n, 0);
31459cc4ca5SDavid du Colombier 		poperror();
3153e12c5d1SDavid du Colombier 		freeb(bp);
3167dd7cddfSDavid du Colombier 		return n;
3173e12c5d1SDavid du Colombier 	}
31859cc4ca5SDavid du Colombier 	ether = etherxx[chan->dev];
3193e12c5d1SDavid du Colombier 
32023173ec1SDavid du Colombier 	if(n > ether->mtu){
3217dd7cddfSDavid du Colombier 		freeb(bp);
32259cc4ca5SDavid du Colombier 		error(Etoobig);
3237dd7cddfSDavid du Colombier 	}
32459cc4ca5SDavid du Colombier 	if(n < ether->minmtu){
3257dd7cddfSDavid du Colombier 		freeb(bp);
3267dd7cddfSDavid du Colombier 		error(Etoosmall);
327bd389b36SDavid du Colombier 	}
328bd389b36SDavid du Colombier 
3297dd7cddfSDavid du Colombier 	return etheroq(ether, bp);
3303e12c5d1SDavid du Colombier }
3313e12c5d1SDavid du Colombier 
3327dd7cddfSDavid du Colombier static struct {
333219b2ee8SDavid du Colombier 	char*	type;
3347dd7cddfSDavid du Colombier 	int	(*reset)(Ether*);
3357dd7cddfSDavid du Colombier } cards[MaxEther+1];
336219b2ee8SDavid du Colombier 
337219b2ee8SDavid du Colombier void
addethercard(char * t,int (* r)(Ether *))3387dd7cddfSDavid du Colombier addethercard(char* t, int (*r)(Ether*))
339219b2ee8SDavid du Colombier {
340219b2ee8SDavid du Colombier 	static int ncard;
341219b2ee8SDavid du Colombier 
3427dd7cddfSDavid du Colombier 	if(ncard == MaxEther)
343219b2ee8SDavid du Colombier 		panic("too many ether cards");
344219b2ee8SDavid du Colombier 	cards[ncard].type = t;
345219b2ee8SDavid du Colombier 	cards[ncard].reset = r;
346219b2ee8SDavid du Colombier 	ncard++;
347219b2ee8SDavid du Colombier }
348219b2ee8SDavid du Colombier 
3497dd7cddfSDavid du Colombier int
parseether(uchar * to,char * from)3507dd7cddfSDavid du Colombier parseether(uchar *to, char *from)
3517dd7cddfSDavid du Colombier {
3527dd7cddfSDavid du Colombier 	char nip[4];
3537dd7cddfSDavid du Colombier 	char *p;
3547dd7cddfSDavid du Colombier 	int i;
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier 	p = from;
3573ff48bf5SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++){
3587dd7cddfSDavid du Colombier 		if(*p == 0)
3597dd7cddfSDavid du Colombier 			return -1;
3607dd7cddfSDavid du Colombier 		nip[0] = *p++;
3617dd7cddfSDavid du Colombier 		if(*p == 0)
3627dd7cddfSDavid du Colombier 			return -1;
3637dd7cddfSDavid du Colombier 		nip[1] = *p++;
3647dd7cddfSDavid du Colombier 		nip[2] = 0;
3657dd7cddfSDavid du Colombier 		to[i] = strtoul(nip, 0, 16);
3667dd7cddfSDavid du Colombier 		if(*p == ':')
3677dd7cddfSDavid du Colombier 			p++;
3687dd7cddfSDavid du Colombier 	}
3697dd7cddfSDavid du Colombier 	return 0;
3707dd7cddfSDavid du Colombier }
3717dd7cddfSDavid du Colombier 
3729a747e4fSDavid du Colombier static Ether*
etherprobe(int cardno,int ctlrno)3739a747e4fSDavid du Colombier etherprobe(int cardno, int ctlrno)
374219b2ee8SDavid du Colombier {
37587dfdc75SDavid du Colombier 	int i, lg;
37687dfdc75SDavid du Colombier 	ulong mb, bsz;
3777dd7cddfSDavid du Colombier 	Ether *ether;
3789a747e4fSDavid du Colombier 	char buf[128], name[32];
379219b2ee8SDavid du Colombier 
3807dd7cddfSDavid du Colombier 	ether = malloc(sizeof(Ether));
381aa72973aSDavid du Colombier 	if(ether == nil)
382aa72973aSDavid du Colombier 		error(Enomem);
3837dd7cddfSDavid du Colombier 	memset(ether, 0, sizeof(Ether));
3847dd7cddfSDavid du Colombier 	ether->ctlrno = ctlrno;
3857dd7cddfSDavid du Colombier 	ether->tbdf = BUSUNKNOWN;
3867dd7cddfSDavid du Colombier 	ether->mbps = 10;
38759cc4ca5SDavid du Colombier 	ether->minmtu = ETHERMINTU;
38859cc4ca5SDavid du Colombier 	ether->maxmtu = ETHERMAXTU;
38923173ec1SDavid du Colombier 	ether->mtu = ETHERMAXTU;
3909a747e4fSDavid du Colombier 
3919a747e4fSDavid du Colombier 	if(cardno < 0){
3929a747e4fSDavid du Colombier 		if(isaconfig("ether", ctlrno, ether) == 0){
3939a747e4fSDavid du Colombier 			free(ether);
3949a747e4fSDavid du Colombier 			return nil;
3959a747e4fSDavid du Colombier 		}
3969a747e4fSDavid du Colombier 		for(cardno = 0; cards[cardno].type; cardno++){
3979a747e4fSDavid du Colombier 			if(cistrcmp(cards[cardno].type, ether->type))
3987dd7cddfSDavid du Colombier 				continue;
3997dd7cddfSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
4007dd7cddfSDavid du Colombier 				if(strncmp(ether->opt[i], "ea=", 3))
4017dd7cddfSDavid du Colombier 					continue;
4029a747e4fSDavid du Colombier 				if(parseether(ether->ea, &ether->opt[i][3]))
4037dd7cddfSDavid du Colombier 					memset(ether->ea, 0, Eaddrlen);
4047dd7cddfSDavid du Colombier 			}
405219b2ee8SDavid du Colombier 			break;
4069a747e4fSDavid du Colombier 		}
4079a747e4fSDavid du Colombier 	}
4089a747e4fSDavid du Colombier 
4099a747e4fSDavid du Colombier 	if(cardno >= MaxEther || cards[cardno].type == nil){
4109a747e4fSDavid du Colombier 		free(ether);
4119a747e4fSDavid du Colombier 		return nil;
4129a747e4fSDavid du Colombier 	}
4139a747e4fSDavid du Colombier 	if(cards[cardno].reset(ether) < 0){
4149a747e4fSDavid du Colombier 		free(ether);
4159a747e4fSDavid du Colombier 		return nil;
4169a747e4fSDavid du Colombier 	}
417219b2ee8SDavid du Colombier 
418219b2ee8SDavid du Colombier 	/*
419219b2ee8SDavid du Colombier 	 * IRQ2 doesn't really exist, it's used to gang the interrupt
420219b2ee8SDavid du Colombier 	 * controllers together. A device set to IRQ2 will appear on
421219b2ee8SDavid du Colombier 	 * the second interrupt controller as IRQ9.
422219b2ee8SDavid du Colombier 	 */
4237dd7cddfSDavid du Colombier 	if(ether->irq == 2)
4247dd7cddfSDavid du Colombier 		ether->irq = 9;
4257dd7cddfSDavid du Colombier 	snprint(name, sizeof(name), "ether%d", ctlrno);
42659cc4ca5SDavid du Colombier 
4279a747e4fSDavid du Colombier 	/*
428fb7f0c93SDavid du Colombier 	 * If ether->irq is <0, it is a hack to indicate no interrupt
4299a747e4fSDavid du Colombier 	 * used by ethersink.
43059cc4ca5SDavid du Colombier 	 */
431fb7f0c93SDavid du Colombier 	if(ether->irq >= 0)
4327dd7cddfSDavid du Colombier 		intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
433219b2ee8SDavid du Colombier 
4341bdadbfaSDavid du Colombier 	i = sprint(buf, "#l%d: %s: ", ctlrno, cards[cardno].type);
4351bdadbfaSDavid du Colombier 	if(ether->mbps >= 1000)
4361bdadbfaSDavid du Colombier 		i += sprint(buf+i, "%dGbps", ether->mbps/1000);
4371bdadbfaSDavid du Colombier 	else
4381bdadbfaSDavid du Colombier 		i += sprint(buf+i, "%dMbps", ether->mbps);
4391bdadbfaSDavid du Colombier 	i += sprint(buf+i, " port 0x%luX irq %d", ether->port, ether->irq);
4407dd7cddfSDavid du Colombier 	if(ether->mem)
4414de34a7eSDavid du Colombier 		i += sprint(buf+i, " addr 0x%luX", ether->mem);
4427dd7cddfSDavid du Colombier 	if(ether->size)
4437dd7cddfSDavid du Colombier 		i += sprint(buf+i, " size 0x%luX", ether->size);
4446063170eSDavid du Colombier 	i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
4457dd7cddfSDavid du Colombier 		ether->ea[0], ether->ea[1], ether->ea[2],
4467dd7cddfSDavid du Colombier 		ether->ea[3], ether->ea[4], ether->ea[5]);
4477dd7cddfSDavid du Colombier 	sprint(buf+i, "\n");
4487dd7cddfSDavid du Colombier 	print(buf);
449219b2ee8SDavid du Colombier 
450*6520663fSDavid du Colombier 	/*
451*6520663fSDavid du Colombier 	 * input queues are allocated by ../port/netif.c:/^openfile.
452*6520663fSDavid du Colombier 	 * the size will be the last argument to netifinit() below.
453*6520663fSDavid du Colombier 	 *
454*6520663fSDavid du Colombier 	 * output queues should be small, to minimise `bufferbloat',
455*6520663fSDavid du Colombier 	 * which confuses tcp's feedback loop.  at 1Gb/s, it only takes
456*6520663fSDavid du Colombier 	 * ~15µs to transmit a full-sized non-jumbo packet.
457*6520663fSDavid du Colombier 	 */
458*6520663fSDavid du Colombier 
45987dfdc75SDavid du Colombier 	/* compute log10(ether->mbps) into lg */
46087dfdc75SDavid du Colombier 	for(lg = 0, mb = ether->mbps; mb >= 10; lg++)
46187dfdc75SDavid du Colombier 		mb /= 10;
462*6520663fSDavid du Colombier 	if (lg > 14)			/* sanity cap; 2**(14+15) = 2²⁹ */
46387dfdc75SDavid du Colombier 		lg = 14;
46487dfdc75SDavid du Colombier 
465*6520663fSDavid du Colombier 	/* allocate larger input queues for higher-speed interfaces */
466*6520663fSDavid du Colombier 	bsz = 1UL << (lg + 15);		/* 2ⁱ⁵ = 32K, bsz = 2ⁿ × 32K */
467*6520663fSDavid du Colombier 	while (bsz > mainmem->maxsize / 8 && bsz > 128*1024)	/* sanity */
468*6520663fSDavid du Colombier 		bsz /= 2;
46987dfdc75SDavid du Colombier 	netifinit(ether, name, Ntypes, bsz);
470*6520663fSDavid du Colombier 
47187dfdc75SDavid du Colombier 	if(ether->oq == nil)
472*6520663fSDavid du Colombier 		ether->oq = qopen(1 << (lg + 13), Qmsg, 0, 0);
473*6520663fSDavid du Colombier 	if(ether->oq == nil)
474*6520663fSDavid du Colombier 		panic("etherreset %s: can't allocate output queue", name);
475*6520663fSDavid du Colombier 
4767dd7cddfSDavid du Colombier 	ether->alen = Eaddrlen;
4777dd7cddfSDavid du Colombier 	memmove(ether->addr, ether->ea, Eaddrlen);
4787dd7cddfSDavid du Colombier 	memset(ether->bcast, 0xFF, Eaddrlen);
4797dd7cddfSDavid du Colombier 
4809a747e4fSDavid du Colombier 	return ether;
4819a747e4fSDavid du Colombier }
4829a747e4fSDavid du Colombier 
4839a747e4fSDavid du Colombier static void
etherreset(void)4849a747e4fSDavid du Colombier etherreset(void)
4859a747e4fSDavid du Colombier {
4869a747e4fSDavid du Colombier 	Ether *ether;
4879a747e4fSDavid du Colombier 	int cardno, ctlrno;
4889a747e4fSDavid du Colombier 
4899a747e4fSDavid du Colombier 	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
4909a747e4fSDavid du Colombier 		if((ether = etherprobe(-1, ctlrno)) == nil)
4919a747e4fSDavid du Colombier 			continue;
4927dd7cddfSDavid du Colombier 		etherxx[ctlrno] = ether;
4939a747e4fSDavid du Colombier 	}
4949a747e4fSDavid du Colombier 
495b7b24591SDavid du Colombier 	if(getconf("*noetherprobe"))
496b7b24591SDavid du Colombier 		return;
497b7b24591SDavid du Colombier 
4989a747e4fSDavid du Colombier 	cardno = ctlrno = 0;
4999a747e4fSDavid du Colombier 	while(cards[cardno].type != nil && ctlrno < MaxEther){
5009a747e4fSDavid du Colombier 		if(etherxx[ctlrno] != nil){
5019a747e4fSDavid du Colombier 			ctlrno++;
5029a747e4fSDavid du Colombier 			continue;
5039a747e4fSDavid du Colombier 		}
5049a747e4fSDavid du Colombier 		if((ether = etherprobe(cardno, ctlrno)) == nil){
5059a747e4fSDavid du Colombier 			cardno++;
5069a747e4fSDavid du Colombier 			continue;
5079a747e4fSDavid du Colombier 		}
5089a747e4fSDavid du Colombier 		etherxx[ctlrno] = ether;
5099a747e4fSDavid du Colombier 		ctlrno++;
510219b2ee8SDavid du Colombier 	}
511219b2ee8SDavid du Colombier }
5129a747e4fSDavid du Colombier 
5139a747e4fSDavid du Colombier static void
ethershutdown(void)5149a747e4fSDavid du Colombier ethershutdown(void)
5159a747e4fSDavid du Colombier {
5169a747e4fSDavid du Colombier 	Ether *ether;
5179a747e4fSDavid du Colombier 	int i;
5189a747e4fSDavid du Colombier 
5199a747e4fSDavid du Colombier 	for(i = 0; i < MaxEther; i++){
5209a747e4fSDavid du Colombier 		ether = etherxx[i];
5219a747e4fSDavid du Colombier 		if(ether == nil)
5229a747e4fSDavid du Colombier 			continue;
5239a747e4fSDavid du Colombier 		if(ether->shutdown == nil) {
52498bee55eSDavid du Colombier 			print("#l%d: no shutdown function\n", i);
5259a747e4fSDavid du Colombier 			continue;
5267dd7cddfSDavid du Colombier 		}
5279a747e4fSDavid du Colombier 		(*ether->shutdown)(ether);
5289a747e4fSDavid du Colombier 	}
5299a747e4fSDavid du Colombier }
5309a747e4fSDavid du Colombier 
531219b2ee8SDavid du Colombier 
5327dd7cddfSDavid du Colombier #define POLY 0xedb88320
5337dd7cddfSDavid du Colombier 
5347dd7cddfSDavid du Colombier /* really slow 32 bit crc for ethers */
5357dd7cddfSDavid du Colombier ulong
ethercrc(uchar * p,int len)5367dd7cddfSDavid du Colombier ethercrc(uchar *p, int len)
5373e12c5d1SDavid du Colombier {
5387dd7cddfSDavid du Colombier 	int i, j;
5397dd7cddfSDavid du Colombier 	ulong crc, b;
5403e12c5d1SDavid du Colombier 
5417dd7cddfSDavid du Colombier 	crc = 0xffffffff;
5427dd7cddfSDavid du Colombier 	for(i = 0; i < len; i++){
5437dd7cddfSDavid du Colombier 		b = *p++;
5447dd7cddfSDavid du Colombier 		for(j = 0; j < 8; j++){
5457dd7cddfSDavid du Colombier 			crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
5467dd7cddfSDavid du Colombier 			b >>= 1;
5477dd7cddfSDavid du Colombier 		}
5487dd7cddfSDavid du Colombier 	}
5497dd7cddfSDavid du Colombier 	return crc;
550bd389b36SDavid du Colombier }
551bd389b36SDavid du Colombier 
5527dd7cddfSDavid du Colombier Dev etherdevtab = {
5537dd7cddfSDavid du Colombier 	'l',
5547dd7cddfSDavid du Colombier 	"ether",
555bd389b36SDavid du Colombier 
5567dd7cddfSDavid du Colombier 	etherreset,
5577dd7cddfSDavid du Colombier 	devinit,
5589a747e4fSDavid du Colombier 	ethershutdown,
5597dd7cddfSDavid du Colombier 	etherattach,
5607dd7cddfSDavid du Colombier 	etherwalk,
5617dd7cddfSDavid du Colombier 	etherstat,
5627dd7cddfSDavid du Colombier 	etheropen,
5637dd7cddfSDavid du Colombier 	ethercreate,
5647dd7cddfSDavid du Colombier 	etherclose,
5657dd7cddfSDavid du Colombier 	etherread,
5667dd7cddfSDavid du Colombier 	etherbread,
5677dd7cddfSDavid du Colombier 	etherwrite,
5687dd7cddfSDavid du Colombier 	etherbwrite,
5699a747e4fSDavid du Colombier 	devremove,
5707dd7cddfSDavid du Colombier 	etherwstat,
5717dd7cddfSDavid du Colombier };
572