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

1993/1113/pc/devether.c (diff list | history)

1993/0212/sys/src/9/pc/devether.c:3,481993/1113/sys/src/9/pc/devether.c:3,44 (short | long | prev | next)
1992/0403    
#include "mem.h" 
#include "dat.h" 
#include "fns.h" 
#include "../port/error.h" 
#include "io.h" 
#include "devtab.h" 
1993/1113    
#include "ureg.h" 
#include "../port/error.h" 
#include "../port/netif.h" 
1992/0403    
 
1992/1222    
#include "ether.h" 
1993/1113    
#include "etherif.h" 
1992/1222    
 
1992/0922    
/* 
 * Half-arsed attempt at a general top-level 
 * ethernet driver. Needs work: 
 *	handle multiple controllers 
 *	much tidying 
 *	set ethernet address 
1993/0212    
 *	need a ctl file passed down to card drivers 
 *	  so we can set options. 
1992/0922    
 */ 
1993/0212    
extern Card ether8003, ether503, ether2000; 
extern Card ether509; 
1993/1113    
static Ether *ether[MaxEther]; 
1992/0403    
 
1992/1222    
/* 
1993/0212    
 * The ordering here is important for those cards 
 * using the DP8390 (WD8003, 3Com503 and NE2000) as 
 * attempting to determine if a card is a NE2000 
1992/1222    
 * cannot be done passively, so it must be last to 
 * prevent scrogging one of the others. 
 */ 
1993/0212    
static Card *cards[] = { 
1992/1222    
	ðer8003, 
	ðer503, 
	ðer2000, 
1993/1113    
void 
etherinit(void) 
{ 
} 
1992/1222    
 
1992/0922    
	ðer509, 
	0 
1992/0404    
}; 
1993/1113    
Chan* 
etherattach(char *spec) 
{ 
	ulong ctlrno; 
	char *p; 
	Chan *c; 
1992/0404    
 
enum { 
1992/1222    
	NCtlr		= 1, 
1992/0424    
}; 
1993/1113    
	ctlrno = 0; 
	if(spec && *spec){ 
		ctlrno = strtoul(spec, &p, 0); 
		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther)) 
			error(Ebadarg); 
	} 
	if(ether[ctlrno] == 0) 
		error(Enodev); 
1992/0403    
 
1993/0212    
/*static */struct Ctlr *softctlr[NCtlr]; 
1992/1222    
static int nctlr; 
1993/1113    
	c = devattach('l', spec); 
	c->dev = ctlrno; 
	if(ether[ctlrno]->attach) 
		(*ether[ctlrno]->attach)(ether[ctlrno]); 
	return c; 
} 
1992/0922    
 
1992/0425    
Chan* 
1992/0424    
etherclone(Chan *c, Chan *nc) 
1993/0212/sys/src/9/pc/devether.c:53,711993/1113/sys/src/9/pc/devether.c:49,67
1992/0424    
int 
etherwalk(Chan *c, char *name) 
{ 
1992/1222    
	return netwalk(c, name, &softctlr[0]->net); 
1993/1113    
	return netifwalk(ether[c->dev], c, name); 
1992/0424    
} 
 
void 
etherstat(Chan *c, char *dp) 
{ 
1992/1222    
	netstat(c, dp, &softctlr[0]->net); 
1993/1113    
	netifstat(ether[c->dev], c, dp); 
1992/0424    
} 
 
Chan* 
etheropen(Chan *c, int omode) 
{ 
1992/1222    
	return netopen(c, omode, &softctlr[0]->net); 
1993/1113    
	return netifopen(ether[c->dev], c, omode); 
1992/0424    
} 
 
void 
1993/0212/sys/src/9/pc/devether.c:72,5821993/1113/sys/src/9/pc/devether.c:68,212
1992/0424    
ethercreate(Chan *c, char *name, int omode, ulong perm) 
{ 
1992/0711    
	USED(c, name, omode, perm); 
1992/0424    
	error(Eperm); 
} 
 
void 
etherclose(Chan *c) 
{ 
	if(c->stream) 
		streamclose(c); 
1993/1113    
	netifclose(ether[c->dev], c); 
1992/0424    
} 
 
long 
etherread(Chan *c, void *a, long n, ulong offset) 
1993/1113    
etherread(Chan *c, void *buf, long n, ulong offset) 
1992/0424    
{ 
1992/1222    
	return netread(c, a, n, offset, &softctlr[0]->net); 
1993/1113    
	return netifread(ether[c->dev], c, buf, n, offset); 
1992/0411    
} 
 
1992/0424    
long 
etherwrite(Chan *c, char *a, long n, ulong offset) 
{ 
1992/0711    
	USED(offset); 
1992/0424    
	return streamwrite(c, a, n, 0); 
} 
                 
void 
etherremove(Chan *c) 
{ 
1992/0711    
	USED(c); 
1992/0424    
	error(Eperm); 
} 
 
void 
etherwstat(Chan *c, char *dp) 
{ 
1992/1222    
	netwstat(c, dp, &softctlr[0]->net); 
1993/1113    
	netifwstat(ether[c->dev], c, dp); 
1992/0424    
} 
 
1992/0407    
static int 
1992/0424    
isobuf(void *arg) 
1993/1113    
long 
etherwrite(Chan *c, void *buf, long n, ulong offset) 
1992/0407    
{ 
1992/1222    
	Ctlr *ctlr = arg; 
1993/1113    
	Ether *ctlr; 
1992/0407    
 
1992/1222    
	return ctlr->tb[ctlr->th].owner == Host; 
1992/0407    
} 
1993/1113    
	USED(offset); 
	if(n > ETHERMAXTU) 
		error(Ebadarg); 
	ctlr = ether[c->dev]; 
1992/0407    
 
1992/0403    
static void 
etheroput(Queue *q, Block *bp) 
{ 
1992/1222    
	Ctlr *ctlr; 
	Type *type; 
	Etherpkt *pkt; 
	RingBuf *ring; 
1993/0212    
	int len, n, s; 
1992/0403    
	Block *nbp; 
1993/1113    
	if(NETTYPE(c->qid.path) != Ndataqid) 
		return netifwrite(ctlr, c, buf, n); 
1992/0403    
 
1992/1222    
	type = q->ptr; 
	ctlr = type->ctlr; 
1992/0403    
	if(bp->type == M_CTL){ 
1992/1222    
		qlock(ctlr); 
		if(streamparse("connect", bp)){ 
			if(type->type == -1) 
				ctlr->all--; 
			type->type = strtol((char*)bp->rptr, 0, 0); 
			if(type->type == -1) 
				ctlr->all++; 
		} 
1992/0403    
		else if(streamparse("promiscuous", bp)) { 
1992/1222    
			type->prom = 1; 
			ctlr->prom++; 
			if(ctlr->prom == 1) 
1993/0212    
				(*ctlr->card.mode)(ctlr, 1); 
1992/0403    
		} 
1992/1222    
		qunlock(ctlr); 
1992/0403    
		freeb(bp); 
		return; 
	} 
                 
	/* 
1992/1222    
	 * Give packet a local address, return upstream if destined for 
1992/0403    
	 * this machine. 
	 */ 
1992/0406    
	if(BLEN(bp) < ETHERHDRSIZE && (bp = pullup(bp, ETHERHDRSIZE)) == 0) 
		return; 
1992/1222    
	pkt = (Etherpkt*)bp->rptr; 
	memmove(pkt->s, ctlr->ea, sizeof(ctlr->ea)); 
	if(memcmp(ctlr->ea, pkt->d, sizeof(ctlr->ea)) == 0){ 
1992/0403    
		len = blen(bp); 
1992/1222    
		if(bp = expandb(bp, len >= ETHERMINTU ? len: ETHERMINTU)){ 
			putq(&ctlr->lbq, bp); 
			wakeup(&ctlr->rr); 
1992/0403    
		} 
		return; 
	} 
1992/1222    
	if(memcmp(ctlr->ba, pkt->d, sizeof(ctlr->ba)) == 0 || ctlr->prom || ctlr->all){ 
1992/0403    
		len = blen(bp); 
		nbp = copyb(bp, len); 
1992/0406    
		if(nbp = expandb(nbp, len >= ETHERMINTU ? len: ETHERMINTU)){ 
1992/0403    
			nbp->wptr = nbp->rptr+len; 
1992/1222    
			putq(&ctlr->lbq, nbp); 
			wakeup(&ctlr->rr); 
1992/0403    
		} 
	} 
                 
	/* 
1992/1222    
	 * Only one transmitter at a time. 
1992/0403    
	 */ 
1992/1222    
	qlock(&ctlr->tlock); 
1992/0403    
	if(waserror()){ 
1992/1222    
		qunlock(&ctlr->tlock); 
1993/0212    
		freeb(bp); 
1992/0403    
		nexterror(); 
	} 
                 
	/* 
1992/1222    
	 * Wait till we get an output buffer. 
1992/0424    
	 * should try to restart. 
1992/0403    
	 */ 
1993/0212    
	if(isobuf(ctlr) == 0){ 
		tsleep(&ctlr->tr, isobuf, ctlr, 3*1000); 
		if(isobuf(ctlr) == 0){ 
			qunlock(&ctlr->tlock); 
			freeb(bp); 
			poperror(); 
			return; 
		} 
	} 
1992/0403    
                 
1992/1222    
	ring = &ctlr->tb[ctlr->th]; 
1992/0424    
                 
1992/0403    
	/* 
1992/1222    
	 * Copy message into buffer. 
1992/0403    
	 */ 
	len = 0; 
	for(nbp = bp; nbp; nbp = nbp->next){ 
		if(sizeof(Etherpkt) - len >= (n = BLEN(nbp))){ 
1992/1222    
			memmove(ring->pkt+len, nbp->rptr, n); 
1992/0403    
			len += n; 
1992/1222    
		} 
1992/0403    
		if(bp->flags & S_DELIM) 
			break; 
	} 
                 
	/* 
1992/1222    
	 * Pad the packet (zero the pad). 
1992/0403    
	 */ 
	if(len < ETHERMINTU){ 
1992/1222    
		memset(ring->pkt+len, 0, ETHERMINTU-len); 
1992/0403    
		len = ETHERMINTU; 
	} 
                 
	/* 
1992/1222    
	 * Set up the transmit buffer and  
	 * start the transmission. 
1992/0403    
	 */ 
1993/0212    
	s = splhi(); 
1992/1222    
	ring->len = len; 
	ring->owner = Interface; 
	ctlr->th = NEXT(ctlr->th, ctlr->ntb); 
1993/0212    
	(*ctlr->card.transmit)(ctlr); 
	splx(s); 
1992/0403    
                 
1992/1222    
	qunlock(&ctlr->tlock); 
1993/0212    
	freeb(bp); 
1993/1113    
	n = (*ctlr->write)(ctlr, buf, n); 
1992/0403    
	poperror(); 
} 
1993/1113    
	qunlock(&ctlr->tlock); 
1992/0403    
 
/* 
1992/1222    
 * Open an ether line discipline. 
1992/0403    
 */ 
static void 
etherstopen(Queue *q, Stream *s) 
{ 
1992/1222    
	Ctlr *ctlr = softctlr[0]; 
	Type *type; 
1992/0403    
                 
1992/1222    
	type = &ctlr->type[s->id]; 
	RD(q)->ptr = WR(q)->ptr = type; 
	type->type = 0; 
	type->q = RD(q); 
	type->inuse = 1; 
	type->ctlr = ctlr; 
1993/1113    
	return n; 
1992/0403    
} 
 
/* 
1992/1222    
 * Close ether line discipline. 
1992/0403    
 * 
1992/1222    
 * The locking is to synchronize changing the ethertype with 
 * sending packets up the stream on interrupts. 
1992/0403    
 */ 
1992/1222    
static int 
isclosed(void *arg) 
{ 
	return ((Type*)arg)->q == 0; 
} 
                 
1992/0403    
static void 
etherstclose(Queue *q) 
1993/1113    
etherintr(Ureg *ur) 
1992/0403    
{ 
1992/1222    
	Type *type = (Type*)(q->ptr); 
	Ctlr *ctlr = type->ctlr; 
1993/1113    
	int i, irq; 
1992/0403    
 
1992/1222    
	if(type->prom){ 
		qlock(ctlr); 
		ctlr->prom--; 
		if(ctlr->prom == 0) 
1993/0212    
			(*ctlr->card.mode)(ctlr, 0); 
1992/1222    
		qunlock(ctlr); 
	} 
	if(type->type == -1){ 
		qlock(ctlr); 
		ctlr->all--; 
		qunlock(ctlr); 
	} 
                 
	/* 
	 * Mark as closing and wait for kproc 
	 * to close us. 
1993/1113    
	 * Call all ethernet interrupt routines on this IRQ. 
	 * Might be better if setvec() took an argument which 
	 * was passed down to the interrupt routine when 
	 * called. This would let us easily distinguish multiple 
	 * controllers. A hack by any other name... 
1992/1222    
	 */ 
	lock(&ctlr->clock); 
	type->clist = ctlr->clist; 
	ctlr->clist = type; 
	unlock(&ctlr->clock); 
	wakeup(&ctlr->rr); 
	sleep(&type->cr, isclosed, type); 
                 
	type->type = 0; 
	type->prom = 0; 
	type->inuse = 0; 
	netdisown(type); 
	type->ctlr = 0; 
1992/0403    
} 
                 
static Qinfo info = { 
	nullput, 
	etheroput, 
	etherstopen, 
	etherstclose, 
	"ether" 
}; 
                 
static int 
1992/0404    
clonecon(Chan *c) 
1992/0403    
{ 
1992/1222    
	Ctlr *ctlr = softctlr[0]; 
	Type *type; 
1992/0403    
                 
1992/0711    
	USED(c); 
1992/1222    
	for(type = ctlr->type; type < &ctlr->type[NType]; type++){ 
		qlock(type); 
		if(type->inuse || type->q){ 
			qunlock(type); 
1992/0403    
			continue; 
		} 
1992/1222    
		type->inuse = 1; 
		netown(type, u->p->user, 0); 
		qunlock(type); 
		return type - ctlr->type; 
1993/1113    
	irq = ur->trap-Int0vec; 
	for(i = 0; i < MaxEther; i++){ 
		if(ether[i] && ether[i]->irq == irq) 
			(*ether[i]->interrupt)(ether[i]); 
1992/0403    
	} 
	exhausted("ether channels"); 
1992/0711    
	return 0; 
1992/0403    
} 
 
static void 
1992/0404    
statsfill(Chan *c, char *p, int n) 
1992/0403    
{ 
1992/1222    
	Ctlr *ctlr = softctlr[0]; 
1992/0403    
	char buf[256]; 
1993/1113    
#define NCARD 32 
static struct { 
	char	*type; 
	int	(*reset)(Ether*); 
} cards[NCARD+1]; 
1992/0403    
 
1992/0711    
	USED(c); 
1992/0403    
	sprint(buf, "in: %d\nout: %d\ncrc errs %d\noverflows: %d\nframe errs %d\nbuff errs: %d\noerrs %d\naddr: %.02x:%.02x:%.02x:%.02x:%.02x:%.02x\n", 
1992/1222    
		ctlr->inpackets, ctlr->outpackets, ctlr->crcs, 
		ctlr->overflows, ctlr->frames, ctlr->buffs, ctlr->oerrs, 
		ctlr->ea[0], ctlr->ea[1], ctlr->ea[2], 
		ctlr->ea[3], ctlr->ea[4], ctlr->ea[5]); 
1992/0403    
	strncpy(p, buf, n); 
} 
                 
static void 
typefill(Chan *c, char *p, int n) 
1993/1113    
void 
addethercard(char *t, int (*r)(Ether*)) 
1992/0403    
{ 
	char buf[16]; 
1992/1222    
	Type *type; 
1993/1113    
	static int ncard; 
1992/0403    
 
1992/1222    
	type = &softctlr[0]->type[STREAMID(c->qid.path)]; 
	sprint(buf, "%d", type->type); 
1992/0403    
	strncpy(p, buf, n); 
1993/1113    
	if(ncard == NCARD) 
		panic("too many ether cards"); 
	cards[ncard].type = t; 
	cards[ncard].reset = r; 
	ncard++; 
1992/0403    
} 
 
static void 
1992/1222    
etherup(Ctlr *ctlr, Etherpkt *pkt, int len) 
1992/0403    
{ 
1992/0404    
	int t; 
1992/1222    
	Type *type; 
1992/0501    
	Block *bp; 
1992/0404    
                 
1992/1222    
	t = (pkt->type[0]<<8)|pkt->type[1]; 
	for(type = &ctlr->type[0]; type < &ctlr->type[NType]; type++){ 
1992/0404    
                 
		/* 
1992/1222    
		 * Check for open, the right type, and flow control. 
1992/0404    
		 */ 
1992/1222    
		if(type->q == 0) 
1992/0404    
			continue; 
1992/1222    
		if(t != type->type && type->type != -1) 
			continue; 
		if(type->q->next->len > Streamhi) 
			continue; 
1992/0404    
                 
		/* 
1992/1222    
		 * Only a trace channel gets packets destined for other machines. 
1992/0404    
		 */ 
1992/1222    
		if(type->type != -1 && pkt->d[0] != 0xFF 
		  && (*pkt->d != *ctlr->ea || memcmp(pkt->d, ctlr->ea, sizeof(pkt->d)))) 
1992/0404    
			continue; 
1992/1222    
                 
		if(waserror() == 0){ 
1992/0424    
			bp = allocb(len); 
1992/1222    
			memmove(bp->rptr, pkt, len); 
1992/0424    
			bp->wptr += len; 
1992/0404    
			bp->flags |= S_DELIM; 
1992/1222    
			PUTNEXT(type->q, bp); 
1992/1015    
			poperror(); 
1992/0404    
		} 
	} 
} 
                 
1992/0403    
static int 
isinput(void *arg) 
{ 
1992/1222    
	Ctlr *ctlr = arg; 
1992/0403    
                 
1992/1222    
	return ctlr->lbq.first || ctlr->rb[ctlr->rh].owner == Host || ctlr->clist; 
1992/0403    
} 
                 
static void 
etherkproc(void *arg) 
{ 
1992/1222    
	Ctlr *ctlr = arg; 
	RingBuf *ring; 
1992/0404    
	Block *bp; 
1992/1222    
	Type *type; 
1992/0403    
                 
	if(waserror()){ 
1992/1222    
		print("%s noted\n", ctlr->name); 
		/* fix 
1993/0212    
		if(ctlr->card.reset) 
			(*ctlr->card.reset)(ctlr); 
1992/1222    
		 */ 
		ctlr->kproc = 0; 
1992/0403    
		nexterror(); 
	} 
1992/1222    
                 
1992/0403    
	for(;;){ 
1992/1222    
		tsleep(&ctlr->rr, isinput, ctlr, 500); 
1993/0212    
		if(ctlr->card.watch) 
			(*ctlr->card.watch)(ctlr); 
1992/0424    
                 
1992/0406    
		/* 
1992/1222    
		 * Process any internal loopback packets. 
1992/0406    
		 */ 
1992/1222    
		while(bp = getq(&ctlr->lbq)){ 
			ctlr->inpackets++; 
			etherup(ctlr, (Etherpkt*)bp->rptr, BLEN(bp)); 
1992/0404    
			freeb(bp); 
		} 
1992/0408    
                 
1992/0406    
		/* 
1992/1222    
		 * Process any received packets. 
1992/0406    
		 */ 
1992/1222    
		while(ctlr->rb[ctlr->rh].owner == Host){ 
			ctlr->inpackets++; 
			ring = &ctlr->rb[ctlr->rh]; 
			etherup(ctlr, (Etherpkt*)ring->pkt, ring->len); 
			ring->owner = Interface; 
			ctlr->rh = NEXT(ctlr->rh, ctlr->nrb); 
1992/0424    
		} 
1992/1222    
                 
		/* 
		 * Close Types requesting it. 
		 */ 
		if(ctlr->clist){ 
			lock(&ctlr->clock); 
			for(type = ctlr->clist; type; type = type->clist){ 
				type->q = 0; 
				wakeup(&type->cr); 
			} 
			ctlr->clist = 0; 
			unlock(&ctlr->clock); 
		} 
1992/0424    
	} 
} 
1992/0410    
                 
1992/0922    
static void 
etherintr(Ureg *ur) 
{ 
1992/1222    
	Ctlr *ctlr = softctlr[0]; 
1992/0922    
                 
	USED(ur); 
1993/0212    
	(*ctlr->card.intr)(ctlr); 
1992/0922    
} 
                 
1992/0424    
void 
etherreset(void) 
{ 
1992/1222    
	Ctlr *ctlr; 
1993/0212    
	Card **card; 
1992/0625    
	int i; 
1993/1113    
	Ether *ctlr; 
	int i, n, ctlrno; 
	ulong irqmask; 
1992/0410    
 
1993/0212    
	if(nctlr >= NCtlr) 
		return; 
1992/1222    
	if(softctlr[nctlr] == 0) 
		softctlr[nctlr] = xalloc(sizeof(Ctlr)); 
	ctlr = softctlr[nctlr]; 
1993/0212    
	for(card = cards; *card; card++){ 
		memset(ctlr, 0, sizeof(Ctlr)); 
		ctlr->card = **card; 
		if((*ctlr->card.reset)(ctlr) == 0){ 
1992/1222    
			ctlr->present = 1; 
1993/1113    
	irqmask = 0; 
	for(ctlr = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){ 
		if(ctlr == 0) 
			ctlr = malloc(sizeof(Ether)); 
		memset(ctlr, 0, sizeof(Ether)); 
		if(isaconfig("ether", ctlrno, ctlr) == 0) 
			continue; 
		for(n = 0; cards[n].type; n++){ 
			if(strcmp(cards[n].type, ctlr->type)) 
				continue; 
			if((*cards[n].reset)(ctlr)) 
				break; 
1992/1222    
 
			/* 
			 * IRQ2 doesn't really exist, it's used to gang the interrupt 
			 * controllers together. A device set to IRQ2 will appear on 
			 * the second interrupt controller as IRQ9. 
1993/1113    
			 * If there are multiple controllers on the same IRQ, only 
			 * call setvec() for one of them, etherintr() will scan through 
			 * all the controllers looking for those at the IRQ it was 
			 * called with. This is a hack, see the comments in etherintr(). 
1992/1222    
			 */ 
1993/0212    
			if(ctlr->card.irq == 2) 
				ctlr->card.irq = 9; 
			setvec(Int0vec + ctlr->card.irq, etherintr); 
1992/0922    
			break; 
		} 
1992/0917    
	} 
1992/1222    
	if(ctlr->present == 0) 
1992/0922    
		return; 
1993/1113    
			if(ctlr->irq == 2) 
				ctlr->irq = 9; 
			if((irqmask & (1<<ctlr->irq)) == 0){ 
				setvec(Int0vec+ctlr->irq, etherintr); 
				irqmask |= 1<<ctlr->irq; 
			} 
1993/0212    
 
	print("ether%d: %s: I/O addr %lux width %d addr %lux size %d irq %d:", 
		nctlr, ctlr->card.id, 
		ctlr->card.io, ctlr->card.bit16 ? 16: 8, ctlr->card.ramstart, 
		ctlr->card.ramstop-ctlr->card.ramstart, ctlr->card.irq); 
	for(i = 0; i < sizeof(ctlr->ea); i++) 
		print(" %2.2ux", ctlr->ea[i]); 
	print("\n"); 
1993/1113    
			print("ether%d: %s: port %lux irq %d addr %lux size %d:", 
				ctlrno, ctlr->type, ctlr->port, ctlr->irq, ctlr->mem, ctlr->size); 
			for(i = 0; i < sizeof(ctlr->ea); i++) 
				print(" %2.2ux", ctlr->ea[i]); 
			print("\n"); 
1993/0212    
 
1992/1222    
	nctlr++; 
1993/1113    
			netifinit(ctlr, "ether", Ntypes, 32*1024); 
			ctlr->alen = Eaddrlen; 
			memmove(ctlr->addr, ctlr->ea, sizeof(ctlr->ea)); 
			memmove(ctlr->bcast, etherbcast, sizeof(etherbcast)); 
1992/0410    
 
1992/1222    
	if(ctlr->nrb == 0) 
		ctlr->nrb = Nrb; 
	ctlr->rb = xalloc(sizeof(RingBuf)*ctlr->nrb); 
	if(ctlr->ntb == 0) 
		ctlr->ntb = Ntb; 
	ctlr->tb = xalloc(sizeof(RingBuf)*ctlr->ntb); 
1992/0410    
                 
1992/1222    
	memset(ctlr->ba, 0xFF, sizeof(ctlr->ba)); 
                 
	ctlr->net.name = "ether"; 
	ctlr->net.nconv = NType; 
	ctlr->net.devp = &info; 
	ctlr->net.protop = 0; 
	ctlr->net.listen = 0; 
	ctlr->net.clone = clonecon; 
	ctlr->net.ninfo = 2; 
	ctlr->net.info[0].name = "stats"; 
	ctlr->net.info[0].fill = statsfill; 
	ctlr->net.info[1].name = "type"; 
	ctlr->net.info[1].fill = typefill; 
1992/0625    
	for(i = 0; i < NType; i++) 
1992/1222    
		netadd(&ctlr->net, &ctlr->type[i], i); 
1992/0403    
} 
                 
1992/0425    
void 
etherinit(void) 
1992/0403    
{ 
	int ctlrno = 0; 
1992/1222    
	Ctlr *ctlr = softctlr[ctlrno]; 
1992/0424    
	int i; 
1992/0403    
                 
1992/1222    
	if(ctlr->present == 0) 
1992/0917    
		return; 
                 
1992/1222    
	ctlr->rh = 0; 
	ctlr->ri = 0; 
	for(i = 0; i < ctlr->nrb; i++) 
		ctlr->rb[i].owner = Interface; 
1992/0424    
                 
1992/1222    
	ctlr->th = 0; 
	ctlr->ti = 0; 
	for(i = 0; i < ctlr->ntb; i++) 
		ctlr->tb[i].owner = Host; 
} 
1992/0424    
                 
1992/1222    
Chan* 
etherattach(char *spec) 
{ 
	int ctlrno = 0; 
	Ctlr *ctlr = softctlr[ctlrno]; 
                 
	if(ctlr->present == 0) 
		error(Enodev); 
                 
1992/0403    
	/* 
1992/1222    
	 * Enable the interface 
	 * and start the kproc. 
1992/0501    
	 */	 
1993/0212    
	(*ctlr->card.attach)(ctlr); 
1992/1222    
	if(ctlr->kproc == 0){ 
		sprint(ctlr->name, "ether%dkproc", ctlrno); 
		ctlr->kproc = 1; 
		kproc(ctlr->name, etherkproc, ctlr); 
1993/1113    
			ether[ctlrno] = ctlr; 
			ctlr = 0; 
		} 
1992/0403    
	} 
1992/1222    
	return devattach('l', spec); 
1993/1113    
	if(ctlr) 
		free(ctlr); 
1992/0403    
} 


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