plan 9 kernel history: overview | file list | diff list

1993/0904/carrera/devether.c (diff list | history)

carrera/devether.c on 1993/0903
1993/0903    
#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" 
 
/* 
1993/0904    
 * National Semiconductor DP83932 
 * Systems-Oriented Network Interface Controller 
 * (SONIC) 
1993/0903    
 */ 
 
1993/0904    
#define SONICADDR	((Sonic*)Sonicbase) 
1993/0903    
 
1993/0904    
#define RD(rn)		(delay(1), *(ulong*)((ulong)&SONICADDR->rn^4)) 
#define WR(rn, v)	(delay(1), *(ulong*)((ulong)&SONICADDR->rn^4) = v) 
1993/0903    
 
1993/0904    
typedef struct 
{ 
	ulong	cr;		/* command */ 
	ulong	dcr;		/* data configuration */ 
	ulong	rcr;		/* receive control */ 
	ulong	tcr;		/* transmit control */ 
	ulong	imr;		/* interrupt mask */ 
	ulong	isr;		/* interrupt status */ 
	ulong	utda;		/* upper transmit descriptor address */ 
	ulong	ctda;		/* current transmit descriptor address */ 
	ulong	pad0x08[5];	/*  */ 
	ulong	urda;		/* upper receive descriptor address */ 
	ulong	crda;		/* current receive descriptor address */ 
	ulong	pad0x0F[4];	/*  */ 
	ulong	eobc;		/* end of buffer word count */ 
	ulong	urra;		/* upper receive resource address */ 
	ulong	rsa;		/* resource start address */ 
	ulong	rea;		/* resource end address */ 
	ulong	rrp;		/* resource read pointer */ 
	ulong	rwp;		/* resource write pointer */ 
	ulong	pad0x19[8];	/*  */ 
	ulong	cep;		/* CAM entry pointer */ 
	ulong	cap2;		/* CAM address port 2 */ 
	ulong	cap1;		/* CAM address port 1 */ 
	ulong	cap0;		/* CAM address port 0 */ 
	ulong	ce;		/* CAM enable */ 
	ulong	cdp;		/* CAM descriptor pointer */ 
	ulong	cdc;		/* CAM descriptor count */ 
	ulong	sr;		/* silicon revision */ 
	ulong	wt0;		/* watchdog timer 0 */ 
	ulong	wt1;		/* watchdog timer 1 */ 
	ulong	rsc;		/* receive sequence counter */ 
	ulong	crct;		/* CRC error tally */ 
	ulong	faet;		/* FAE tally */ 
	ulong	mpt;		/* missed packet tally */ 
	ulong	mdt;		/* maximum deferral timer */ 
	ulong	pad0x30[15];	/*  */ 
	ulong	dcr2;		/* data configuration 2 */ 
} Sonic; 
1993/0903    
 
enum 
{ 
1993/0904    
	Nrb		= 16,		/* receive buffers */ 
	Ntb		= 8,		/* transmit buffers */ 
}; 
1993/0903    
 
1993/0904    
enum 
{ 
	Htx	= 0x0001,	/* halt transmission */ 
	Txp	= 0x0002,	/* transmit packet(s) */ 
	Rxdis	= 0x0004,	/* receiver disable */ 
	Rxen	= 0x0008,	/* receiver enable */ 
	Stp	= 0x0010,	/* stop timer */ 
	St	= 0x0020,	/* start timer */ 
	Rst	= 0x0080,	/* software reset */ 
	Rrra	= 0x0100,	/* read RRA */ 
	Lcam	= 0x0200,	/* load CAM */ 
1993/0903    
 
1993/0904    
	Dw32	= 0x0020,	/* data width select */ 
	Sterm	= 0x0400,	/* synchronous termination */ 
	Lbr	= 0x4000,	/* latched bus retry */ 
	Efm	= 0x0010,	/* Empty fill mode */ 
	W14tf	= 0x0003,	/* 14 Word transmit fifo */ 
1993/0903    
 
1993/0904    
	Prx	= 0x0001,	/* packet received ok */ 
	Fae	= 0x0004,	/* frame alignment error */ 
	Crc	= 0x0008,	/* CRC error */ 
	Lpkt	= 0x0040,	/* last packet in rba */ 
	Bc	= 0x0080,	/* broadcast packet received */ 
	Pro	= 0x1000,	/* physical promiscuous mode */ 
	Brd	= 0x2000,	/* accept broadcast packets */ 
	Rnt	= 0x4000,	/* accept runt packets */ 
	Err	= 0x8000,	/* accept packets with errors */ 
1993/0903    
 
1993/0904    
	Ptx	= 0x0001,	/* packet transmitted ok */ 
	Pintr	= 0x8000,	/* programmable interrupt */ 
1993/0903    
 
1993/0904    
	Rfo	= 0x0001,	/* receive fifo overrun */ 
	MpTally	= 0x0002,	/* missed packet tally counter rollover */ 
	FaeTally= 0x0004,	/* frame alignment error tally counter rollover */ 
	CrcTally= 0x0008,	/* Crc tally counter rollover */ 
	Rbae	= 0x0010,	/* receive buffer area exceeded */ 
	Rbe	= 0x0020,	/* receive buffer exhausted */ 
	Rde	= 0x0040,	/* receive descriptors exhausted */ 
	Txer	= 0x0100,	/* transmit error */ 
	Txdn	= 0x0200,	/* transmission done */ 
	Pktrx	= 0x0400,	/* packet received */ 
	Pint	= 0x0800,	/* programmed interrupt */ 
	Lcd	= 0x1000,	/* load CAM done */ 
	Hbl	= 0x2000,	/* CD heartbeat lost */ 
	Br	= 0x4000,	/* bus retry occurred */ 
	AllIntr	= 0x7771,	/* all of the above */ 
1993/0903    
}; 
 
1993/0904    
/* 
 * Receive Resource Descriptor. 
 */ 
typedef struct 
{ 
	uchar	pad0[2]; 
	ushort	ptr0;		/* buffer pointer in the RRA */ 
	uchar	pad1[2]; 
	ushort	ptr1; 
	uchar	pad2[2]; 
	ushort	wc0;		/* buffer word count in the RRA */ 
	uchar	pad3[2]; 
	ushort	wc1; 
} RXrsc; 
1993/0903    
 
/* 
1993/0904    
 * Receive Packet Descriptor. 
1993/0903    
 */ 
typedef struct 
{ 
1993/0904    
	uchar	pad0[2]; 
	ushort	status;		/* receive status */ 
	uchar	pad1[2]; 
	ushort	count;		/* packet byte count */ 
	uchar	pad2[2]; 
	ushort	ptr0;		/* buffer pointer */ 
	uchar	pad3[2]; 
	ushort	ptr1; 
	uchar	pad4[2]; 
	ushort	seqno;		/*  */ 
	uchar	pad5[2]; 
	ushort	link;		/* descriptor link and EOL */ 
	uchar	pad6[2]; 
	ushort	owner;		/* in use */ 
} RXpkt; 
1993/0903    
 
1993/0904    
/* 
 * Transmit Packet Descriptor. 
 */ 
typedef struct 
{ 
	uchar	pad0[2]; 
	ushort	status;		/* transmit status */ 
	uchar	pad1[2]; 
	ushort	config;		/*  */ 
	uchar	pad2[2]; 
	ushort	size;		/* byte count of entire packet */ 
	uchar	pad3[2]; 
	ushort	count;		/* fragment count */ 
	uchar	pad4[2]; 
	ushort	ptr0;		/* packet pointer */ 
	uchar	pad5[2]; 
	ushort	ptr1; 
	uchar	pad6[2]; 
	ushort	fsize;		/* fragment size */ 
	uchar	pad7[2]; 
	ushort	link;		/* descriptor link */ 
} TXpkt; 
 
enum{ 
	Eol		= 1,	/* end of list bit in descriptor link */ 
	Host		= 0,	/* descriptor belongs to host */ 
	Interface	= -1,	/* descriptor belongs to interface */ 
 
	Nether		= 1, 
	Ntypes=		8, 
}; 
 
/* 
 * CAM Descriptor 
 */ 
typedef struct { 
	uchar	pad0[2]; 
	ushort	cep;		/* CAM entry pointer */ 
	uchar	pad1[2]; 
	ushort	cap0;		/* CAM address port 0 */ 
	uchar	pad2[2]; 
	ushort	cap1;		/* CAM address port 1 */ 
	uchar	pad3[2]; 
	ushort	cap2;		/* CAM address port 2 */ 
	uchar	pad4[2]; 
	ushort	ce;		/* CAM enable */ 
} Cam; 
 
typedef struct Ether Ether; 
1993/0903    
struct Ether 
{ 
	uchar	ea[6]; 
1993/0904    
	uchar	ba[6]; 
1993/0903    
 
1993/0904    
	Sonic	*sonic;		/* SONIC registers */ 
1993/0903    
 
	QLock	tlock;		/* lock for grabbing transmitter queue */ 
	Rendez	tr;		/* wait here for free xmit buffer */ 
1993/0904    
	int	th;		/* first transmit buffer owned by host */	 
	int	ti;		/* first transmit buffer owned by interface */ 
1993/0903    
 
1993/0904    
	int	rh;		/* first receive buffer owned by host */ 
	int	ri;		/* first receive buffer owned by interface */ 
 
	RXrsc	rra[Nrb];	/* receive resource area */ 
	RXpkt	rda[Nrb];	/* receive descriptor area */ 
	uchar	rb[Nrb][sizeof(Etherpkt)+4];	/* receive buffer area */ 
	TXpkt	tda[Ntb];	/* transmit descriptor area */ 
	uchar	tb[Ntb][sizeof(Etherpkt)];	/* transmit buffer area */ 
	Cam	cda;		/* CAM descriptor area */ 
 
1993/0903    
	Netif; 
1993/0904    
}; 
1993/0903    
 
1993/0904    
Ether *ether[Nether]; 
1993/0903    
 
1993/0904    
#define NEXT(x, l)	(((x)+1)%(l)) 
#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1) 
#define LS16(addr)	(PADDR(addr) & 0xFFFF) 
#define MS16(addr)	((PADDR(addr)>>16) & 0xFFFF) 
 
1993/0903    
static void 
1993/0904    
reset(Ether *ctlr) 
1993/0903    
{ 
1993/0904    
	int i; 
1993/0903    
 
1993/0904    
iprint("reset sonic dcr=#%lux mydcr=#%lux\n", RD(dcr), Sterm|Dw32|Lbr|Efm|W14tf); 
	/* 
	 * Reset the SONIC, toggle the Rst bit. 
	 * Set the data config register for synchronous termination 
	 * and 32-bit data-path width. 
	 * Clear the descriptor and buffer area. 
	 */ 
	WR(cr, Rst); 
	WR(dcr, Sterm|Dw32|Lbr|Efm|W14tf); 
	WR(cr, 0); 
1993/0903    
 
1993/0904    
	/* 
	 * Initialise the receive resource area (RRA) and 
	 * the receive descriptor area (RDA). 
	 * 
	 * We use a simple scheme of one packet per descriptor. 
	 * We achieve this by setting the EOBC register to be 
	 * 2 (16-bit words) less than the buffer size; 
	 * thus the size of the receive buffers must be sizeof(Etherpkt)+4. 
	 * Set up the receive descriptors as a ring. 
	 */ 
	for(i = 0; i < Nrb; i++){ 
		ctlr->rra[i].wc0 = (sizeof(ctlr->rb[0])/2) & 0xFFFF; 
		ctlr->rra[i].wc1 = ((sizeof(ctlr->rb[0])/2)>>16) & 0xFFFF; 
1993/0903    
 
1993/0904    
		ctlr->rda[i].link = LS16(&ctlr->rda[NEXT(i, Nrb)]); 
		ctlr->rda[i].owner = Interface; 
1993/0903    
 
1993/0904    
		ctlr->rra[i].ptr0 = ctlr->rda[i].ptr0 = LS16(ctlr->rb[i]); 
		ctlr->rra[i].ptr1 = ctlr->rda[i].ptr1 = MS16(ctlr->rb[i]); 
	} 
1993/0903    
 
1993/0904    
	/* 
	 * Terminate the receive descriptor ring 
	 * and load the SONIC registers to describe the RDA. 
1993/0903    
	 */ 
1993/0904    
	ctlr->rda[Nrb-1].link |= Eol; 
1993/0903    
 
1993/0904    
	WR(crda, LS16(ctlr->rda)); 
	WR(urda, MS16(ctlr->rda)); 
	WR(eobc, sizeof(ctlr->rb[0])/2 - 2); 
1993/0903    
 
1993/0904    
	/* 
	 * Load the SONIC registers to describe the RRA. 
	 * We set the rwp to beyond the area delimited by rsa and 
	 * rea. This means that since we've already allocated all 
	 * the buffers, we'll never get a 'receive buffer area 
	 * exhausted' interrupt and the rrp will just wrap round. 
	 * Tell the SONIC to load the RRA and wait for 
	 * it to complete. 
	 */ 
	WR(urra, MS16(&ctlr->rra[0])); 
	WR(rsa, LS16(&ctlr->rra[0])); 
	WR(rrp, LS16(&ctlr->rra[0])); 
	WR(rea, LS16(&ctlr->rra[Nrb])); 
	WR(rwp, LS16(&ctlr->rra[Nrb+1])); 
1993/0903    
 
1993/0904    
iprint("wait rra\n"); 
	WR(cr, Rrra); 
	while(RD(cr) & Rrra) 
		; 
iprint("rra done\n"); 
 
	/* 
	 * Initialise the transmit descriptor area (TDA). 
	 * Each descriptor describes one packet, we make no use 
	 * of having the packet in multiple fragments. 
	 * The descriptors are linked in a ring; overlapping transmission 
	 * with buffer queueing will cause some packets to 
	 * go out back-to-back. 
	 * 
	 * Load the SONIC registers to describe the TDA. 
	 */ 
	for(i = 0; i < Ntb; i++){ 
		ctlr->tda[i].status = Host; 
		ctlr->tda[i].config = 0; 
		ctlr->tda[i].count = 1; 
		ctlr->tda[i].ptr0 = LS16(ctlr->tb[i]); 
		ctlr->tda[i].ptr1 = MS16(ctlr->tb[i]); 
		ctlr->tda[i].link = LS16(&ctlr->tda[NEXT(i, Ntb)]); 
	} 
 
	WR(ctda, LS16(&ctlr->tda[0])); 
	WR(utda, MS16(&ctlr->tda[0])); 
 
	/* 
	 * Initialise the software receive and transmit 
	 * ring indexes. 
	 */ 
	ctlr->rh = 0; 
	ctlr->ri = 0; 
	ctlr->th = 0; 
	ctlr->ti = 0; 
 
	/* 
	 * Initialise the CAM descriptor area (CDA). 
	 * We only have one ethernet address to load, 
	 * broadcast is defined by the SONIC as all 1s. 
	 * 
	 * Load the SONIC registers to describe the CDA. 
	 * Tell the SONIC to load the CDA and wait for it 
	 * to complete. 
	 */ 
	ctlr->cda.cep = 0; 
	ctlr->cda.cap0 = (ctlr->ea[1]<<8)|ctlr->ea[0]; 
	ctlr->cda.cap1 = (ctlr->ea[3]<<8)|ctlr->ea[2]; 
	ctlr->cda.cap2 = (ctlr->ea[5]<<8)|ctlr->ea[4]; 
	ctlr->cda.ce = 1; 
 
	WR(cdp, LS16(&ctlr->cda)); 
	WR(cdc, 1); 
 
	WR(cr, Lcam); 
	while(RD(cr) & Lcam) 
		; 
 
	/* 
	 * Configure the receive control, transmit control 
	 * and interrupt-mask registers. 
	 * The SONIC is now initialised, but not enabled. 
	 */ 
	WR(rcr, Err|Rnt|Brd); 
	WR(tcr, 0); 
	WR(imr, AllIntr); 
iprint("reset done\n"); 
1993/0903    
} 
 
void 
etherintr(void) 
{ 
	int x; 
	ushort t; 
1993/0904    
	Ether *ctlr; 
	ulong status; 
	TXpkt *txpkt; 
	RXpkt *rxpkt; 
	Etherpkt *p; 
	Netfile *f, **fp; 
1993/0903    
 
1993/0904    
	ctlr = ether[0]; 
1993/0903    
 
1993/0904    
	for(;;) { 
		status = RD(isr) & AllIntr; 
		if(status == 0) 
			break; 
 
		WR(isr, status); 
	 
		/* 
		 * Transmission complete, for good or bad. 
		 */ 
		if(status & (Txdn|Txer)){ 
			txpkt = &ctlr->tda[ctlr->ti]; 
			while(txpkt->status != Host){ 
				if(txpkt->status == Interface){ 
					WR(ctda, LS16(txpkt)); 
					WR(cr, Txp); 
					break; 
				} 
	 
				if((txpkt->status & Ptx) == 0) 
					ctlr->oerrs++; 
	 
				txpkt->status = Host; 
				ctlr->ti = NEXT(ctlr->ti, Ntb); 
				txpkt = &ctlr->tda[ctlr->ti]; 
			} 
			status &= ~(Txdn|Txer); 
1993/0903    
		} 
1993/0904    
 
		if((status & (Pktrx|Rde)) == 0) 
			goto noinput; 
 
		/* 
		 * A packet arrived or we ran out of descriptors. 
		 */ 
		status &= ~(Pktrx|Rde); 
		rxpkt = &ctlr->rda[ctlr->rh]; 
		while(rxpkt->owner == Host){ 
			ctlr->inpackets++; 
	 
			/* 
			 * If the packet was received OK, pass it up, 
			 * otherwise log the error. 
			 * SONIC gives us the CRC in the packet, so 
			 * remember to subtract it from the length. 
			 */ 
 
			if(rxpkt->status & Prx) { 
				x = (rxpkt->count & 0xFFFF)-4; 
				p = (Etherpkt*)ctlr->rb[ctlr->rh]; 
				t = (p->type[0]<<8) | p->type[1]; 
				for(fp = ctlr->f; fp < &ctlr->f[Ntypes]; fp++){ 
					f = *fp; 
					if(f == 0) 
						continue; 
					if(f->type == t || f->type < 0) 
						qproduce(f->in, p->d, x); 
				} 
1993/0903    
			} 
1993/0904    
			else 
			if(rxpkt->status & Fae) 
				ctlr->frames++; 
			else 
			if(rxpkt->status & Crc) 
				ctlr->crcs++; 
			else 
				ctlr->buffs++; 
	 
			/* 
			 * Finished with this packet, it becomes the 
			 * last free packet in the ring, so give it Eol, 
			 * and take the Eol bit off the previous packet. 
			 * Move the ring index on. 
			 */ 
			rxpkt->link |= Eol; 
			rxpkt->owner = Interface; 
			ctlr->rda[PREV(ctlr->rh, Nrb)].link &= ~Eol; 
			ctlr->rh = NEXT(ctlr->rh, Nrb); 
	 
			rxpkt = &ctlr->rda[ctlr->rh]; 
1993/0903    
		} 
1993/0904    
		status &= ~(Pktrx|Rde); 
1993/0903    
 
1993/0904    
	noinput: 
1993/0903    
		/* 
1993/0904    
		 * We get a 'load CAM done' interrupt 
		 * after initialisation. Ignore it. 
1993/0903    
		 */ 
1993/0904    
		if(status & Lcd) 
			status &= ~Lcd; 
	 
		/* 
		 * Warnings that something is afoot. 
		 */ 
		if(status & Hbl){ 
			print("sonic: cd heartbeat lost\n"); 
			status &= ~Hbl; 
1993/0903    
		} 
1993/0904    
		if(status & Br){ 
			print("sonic: bus retry occurred\n"); 
			status &= ~Br; 
		} 
	 
		if(status & AllIntr) 
			print("sonic %ux\n", status); 
1993/0903    
	} 
} 
 
/* 
 *  turn promiscuous mode on/off 
 */ 
static void 
promiscuous(void *arg, int on) 
{ 
1993/0904    
	ushort reg; 
1993/0903    
 
	USED(arg); 
1993/0904    
 
	reg = RD(rcr); 
1993/0903    
	if(on) 
1993/0904    
		WR(rcr, reg|Pro); 
1993/0903    
	else 
1993/0904    
		WR(rcr, reg&~Pro); 
1993/0903    
} 
 
void 
etherreset(void) 
{ 
1993/0904    
	Ether *ctlr; 
1993/0903    
 
1993/0904    
	/* 
	 * Map the device registers and allocate 
	 * memory for the receive/transmit rings. 
	 * Set the physical ethernet address and 
	 * prime the interrupt handler. 
	 */ 
	if(ether[0] == 0) { 
		ether[0] = xspanalloc(sizeof(Ether), BY2PG, 64*1024); 
/*		memmove(ether[0]->ea, eeprom.ea, sizeof(ether[0]->ea)); */ 
1993/0903    
	} 
1993/0904    
	ctlr = ether[0]; 
1993/0903    
 
1993/0904    
	reset(ctlr); 
1993/0903    
 
1993/0904    
	memset(ctlr->ba, 0xFF, sizeof(ctlr->ba)); 
1993/0903    
 
	/* general network interface structure */ 
1993/0904    
	netifinit(ether[0], "ether", Ntypes, 32*1024); 
	ether[0]->alen = 6; 
	memmove(ether[0]->addr, ether[0]->ea, 6); 
	memmove(ether[0]->bcast, ctlr->ba, 6); 
	ether[0]->promiscuous = promiscuous; 
	ether[0]->arg = ether[0]; 
1993/0903    
} 
 
void 
etherinit(void) 
{ 
} 
 
Chan* 
etherattach(char *spec) 
{ 
	return devattach('l', spec); 
} 
 
Chan* 
etherclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int 
etherwalk(Chan *c, char *name) 
{ 
1993/0904    
	return netifwalk(ether[0], c, name); 
1993/0903    
} 
 
Chan* 
etheropen(Chan *c, int omode) 
{ 
1993/0904    
	return netifopen(ether[0], c, omode); 
1993/0903    
} 
 
void 
ethercreate(Chan *c, char *name, int omode, ulong perm) 
{ 
	USED(c, name, omode, perm); 
} 
 
void 
etherclose(Chan *c) 
{ 
1993/0904    
	netifclose(ether[0], c); 
1993/0903    
} 
 
long 
etherread(Chan *c, void *buf, long n, ulong offset) 
{ 
1993/0904    
	return netifread(ether[0], c, buf, n, offset); 
1993/0903    
} 
 
static int 
isoutbuf(void *arg) 
{ 
1993/0904    
	Ether *ctlr = arg; 
1993/0903    
 
1993/0904    
	return ctlr->tda[ctlr->th].status == Host; 
1993/0903    
} 
 
long 
etherwrite(Chan *c, void *buf, long n, ulong offset) 
{ 
1993/0904    
	Etherpkt *p; 
	TXpkt *txpkt; 
	Ether *ctlr = ether[0]; 
1993/0903    
 
	USED(offset); 
 
	if(n > ETHERMAXTU) 
		error(Ebadarg); 
 
	/* etherif.c handles structure */ 
	if(NETTYPE(c->qid.path) != Ndataqid) 
1993/0904    
		return netifwrite(ether[0], c, buf, n); 
1993/0903    
 
	/* we handle data */ 
1993/0904    
	qlock(&ctlr->tlock); 
	tsleep(&ctlr->tr, isoutbuf, ctlr, 10000); 
	if(!isoutbuf(ctlr)){ 
1993/0903    
		print("ether transmitter jammed\n"); 
1993/0904    
	} 
	else { 
		p =(Etherpkt*)ctlr->tb[ctlr->th]; 
1993/0903    
		memmove(p->d, buf, n); 
		if(n < 60) { 
			memset(p->d+n, 0, 60-n); 
			n = 60; 
		} 
1993/0904    
		memmove(p->s, ctlr->ea, sizeof(ctlr->ea)); 
 
		txpkt = &ctlr->tda[ctlr->th]; 
		txpkt->size = n; 
		txpkt->fsize = n; 
		txpkt->link |= Eol; 
		txpkt->status = Interface; 
		ctlr->tda[PREV(ctlr->th, Ntb)].link &= ~Eol; 
 
		ctlr->th = NEXT(ctlr->th, Ntb); 
		WR(cr, Txp); 
1993/0903    
	} 
1993/0904    
	qunlock(&ctlr->tlock); 
1993/0903    
	return n; 
} 
 
void 
etherremove(Chan *c) 
{ 
	USED(c); 
} 
 
void 
etherstat(Chan *c, char *dp) 
{ 
1993/0904    
	netifstat(ether[0], c, dp); 
1993/0903    
} 
 
void 
etherwstat(Chan *c, char *dp) 
{ 
1993/0904    
	netifwstat(ether[0], c, dp); 
1993/0903    
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)