xref: /inferno-os/os/pc/ethersmc.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 /*
2  * SMC EtherEZ (SMC91cXX chip) PCMCIA card support.
3  */
4 
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "../port/error.h"
12 #include "../port/netif.h"
13 #include "etherif.h"
14 
15 enum {
16 	IoSize		= 0x10,		/* port pool size */
17 	TxTimeout	= 150,
18 };
19 
20 enum {	/* PCMCIA related */
21 	TupleFunce	= 0x22,
22 	TfNodeId	= 0x04,
23 };
24 
25 enum {	/* bank 0 registers */
26 	Tcr		= 0x0000,	/* transmit control */
27 	Eph		= 0x0002,	/* ethernet protocol handler */
28 	Rcr		= 0x0004,	/* receiver control */
29 	Counter		= 0x0006,	/* statistics counter */
30 	MemInfo		= 0x0008,
31 	MemCfg		= 0x000A,
32 };
33 
34 enum {	/* bank 1 registers */
35 	Config		= 0x0000,
36 	BaseAddr	= 0x0002,
37 	Addr0		= 0x0004,	/* ethernet address */
38 	Addr1		= 0x0006,
39 	Addr2		= 0x0008,
40 	General		= 0x000A,
41 	Control		= 0x000C,
42 };
43 
44 enum {	/* bank 2 registers */
45 	MmuCmd		= 0x0000,
46 	PktNo		= 0x0002,
47 	AllocRes	= 0x0003,
48 	FifoPorts	= 0x0004,
49 	Pointer		= 0x0006,
50 	Data1		= 0x0008,
51 	Interrupt	= 0x000C,
52 	IntrMask	= 0x000D,
53 };
54 
55 enum {	/* bank 3 registers */
56 	Mcast0		= 0x0000,
57 	Mcast2		= 0x0002,
58 	Mcast4		= 0x0004,
59 	Mcast6		= 0x0006,
60 	Revision	= 0x000A,
61 };
62 
63 enum {
64 	BankSelect	= 0x000E	/* bank select register */
65 };
66 
67 enum {
68 	BsrMask		= 0xFF00,	/* mask for chip identification */
69 	BsrId		= 0x3300,
70 };
71 
72 
73 enum {	/* Tcr values */
74 	TcrClear	= 0x0000,
75 	TcrEnable	= 0x0001,	/* enable transmit */
76 	TcrLoop		= 0x0002,	/* enable internal analogue loopback */
77 	TcrForceCol	= 0x0004,	/* force collision on next tx */
78 	TcrPadEn	= 0x0080,	/* pad short packets to 64 bytes */
79 	TcrNoCrc	= 0x0100,	/* do not append CRC */
80 	TcrMonCns	= 0x0400,	/* monitor carrier status */
81 	TcrFduplx	= 0x0800,
82 	TcrStpSqet	= 0x1000,
83 	TcrEphLoop	= 0x2000,
84 	TcrNormal	= TcrEnable,
85 };
86 
87 enum {	/* Eph values */
88 	EphTxOk		= 0x0001,
89 	Eph1Col		= 0x0002,	/* single collision */
90 	EphMCol		= 0x0004,	/* multiple collisions */
91 	EphTxMcast	= 0x0008,	/* multicast transmit */
92 	Eph16Col	= 0x0010,	/* 16 collisions, tx disabled */
93 	EphSqet		= 0x0020,	/* SQE test failed, tx disabled */
94 	EphTxBcast	= 0x0040,	/* broadcast tx */
95 	EphDefr		= 0x0080,	/* deffered tx */
96 	EphLatCol	= 0x0200,	/* late collision, tx disabled */
97 	EphLostCarr	= 0x0400,	/* lost carrier, tx disabled */
98 	EphExcDefr	= 0x0800,	/* excessive defferals */
99 	EphCntRol	= 0x1000,	/* ECR counter(s) rolled over */
100 	EphRxOvrn	= 0x2000,	/* receiver overrun, packets dropped */
101 	EphLinkOk	= 0x4000,
102 	EphTxUnrn	= 0x8000,	/* tx underrun */
103 };
104 
105 enum {	/* Rcr values */
106 	RcrClear	= 0x0000,
107 	RcrPromisc	= 0x0002,
108 	RcrAllMcast	= 0x0004,
109 	RcrEnable	= 0x0100,
110 	RcrStripCrc	= 0x0200,
111 	RcrSoftReset	= 0x8000,
112 	RcrNormal	= RcrStripCrc | RcrEnable,
113 };
114 
115 enum { /* Counter value masks */
116 	CntColMask	= 0x000F,	/* collisions */
117 	CntMColMask	= 0x00F0,	/* multiple collisions */
118 	CntDtxMask	= 0x0F00,	/* deferred transmits */
119 	CntExDtxMask	= 0xF000,	/* excessively deferred transmits */
120 
121 	CntColShr	= 1,
122 	CntMColShr	= 4,
123 	CntDtxShr	= 8,
124 };
125 
126 enum { /* MemInfo value masks */
127 	MirTotalMask	= 0x00FF,
128 	MirFreeMask	= 0xFF00,
129 };
130 
131 enum {	/* Config values */
132 	CfgIrqSel0	= 0x0002,
133 	CfgIrqSel1	= 0x0004,
134 	CfgDisLink	= 0x0040,	/* disable 10BaseT link test */
135 	Cfg16Bit	= 0x0080,
136 	CfgAuiSelect	= 0x0100,
137 	CfgSetSqlch	= 0x0200,
138 	CfgFullStep	= 0x0400,
139 	CfgNoWait	= 0x1000,
140 	CfgMiiSelect	= 0x8000,
141 };
142 
143 enum {	/* Control values */
144 	CtlStore	= 0x0001,	/* store to EEPROM */
145 	CtlReload	= 0x0002,	/* reload EEPROM into registers */
146 	CtlEeSelect	= 0x0004,	/* select registers for reload/store */
147 	CtlTeEnable	= 0x0020,	/* tx error detection via eph irq */
148 	CtlCrEnable	= 0x0040,	/* counter rollover via eph irq */
149 	CtlLeEnable	= 0x0080,	/* link error detection via eph irq*/
150 	CtlAutoRls	= 0x0800,	/* auto release mode */
151 	CtlPowerDn	= 0x2000,
152 };
153 
154 enum {	/* MmuCmd values */
155 	McBusy		= 0x0001,
156 	McAlloc		= 0x0020,	/* | with number of 256 byte packets - 1 */
157 	McReset		= 0x0040,
158 	McRelease	= 0x0080,	/* dequeue (but not free) current rx packet */
159 	McFreePkt	= 0x00A0,	/* dequeue and free current rx packet */
160 	McEnqueue	= 0x00C0,	/* enqueue the packet for tx */
161 	McTxReset	= 0x00E0,	/* reset transmit queues */
162 };
163 
164 enum { /* AllocRes values */
165 	ArFailed	= 0x80,
166 };
167 
168 enum {	/* FifoPorts values */
169 	FpTxEmpty	= 0x0080,
170 	FpRxEmpty	= 0x8000,
171 	FpTxMask	= 0x007F,
172 	FpRxMask	= 0x7F00,
173 };
174 
175 enum {	/* Pointer values */
176 	PtrRead		= 0x2000,
177 	PtrAutoInc	= 0x4000,
178 	PtrRcv		= 0x8000,
179 };
180 
181 enum {	/* Interrupt values */
182 	IntRcv		= 0x0001,
183 	IntTxError	= 0x0002,
184 	IntTxEmpty	= 0x0004,
185 	IntAlloc	= 0x0008,
186 	IntRxOvrn	= 0x0010,
187 	IntEph		= 0x0020,
188 };
189 
190 enum { /* transmit status bits */
191 	TsSuccess	= 0x0001,
192 	Ts16Col		= 0x00A0,
193 	TsLatCol	= 0x0200,
194 	TsLostCar	= 0x0400,
195 };
196 
197 enum { /* receive status bits */
198 	RsMcast		= 0x0001,
199 	RsTooShort	= 0x0400,
200 	RsTooLong	= 0x0800,
201 	RsOddFrame	= 0x1000,
202 	RsBadCrc	= 0x2000,
203 	RsAlgnErr	= 0x8000,
204 	RsError		= RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,
205 };
206 
207 enum {
208 	RxLenMask	= 0x07FF,	/* significant rx len bits */
209 	HdrSize		= 6,		/* packet header length */
210 	PageSize	= 256,		/* page length */
211 };
212 
213 typedef struct Smc91xx Smc91xx;
214 struct Smc91xx {
215 	Lock;
216 	ushort rev;
217 	int attached;
218 	Block *txbp;
219 	ulong txtime;
220 
221 	ulong rovrn;
222 	ulong lcar;
223 	ulong col;
224 	ulong scol;
225 	ulong mcol;
226 	ulong lcol;
227 	ulong dfr;
228 };
229 
230 #define SELECT_BANK(x) outs(port + BankSelect, x)
231 
232 static int
233 readnodeid(int slot, Ether* ether)
234 {
235 	uchar data[Eaddrlen + 1];
236 	int len;
237 
238 	len = sizeof(data);
239 	if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)
240 		return -1;
241 
242 	if (data[0] != Eaddrlen)
243 		return -1;
244 
245 	memmove(ether->ea, &data[1], Eaddrlen);
246 	return 0;
247 }
248 
249 static void
250 chipreset(Ether* ether)
251 {
252 	int port;
253 	int i;
254 
255 	port = ether->port;
256 
257 	/* reset the chip */
258 	SELECT_BANK(0);
259 	outs(port + Rcr, RcrSoftReset);
260 	delay(1);
261 	outs(port + Rcr, RcrClear);
262 	outs(port + Tcr, TcrClear);
263 	SELECT_BANK(1);
264 	outs(port + Control, CtlAutoRls | CtlTeEnable |
265 		CtlCrEnable);
266 
267 	for(i = 0; i < 6; i++) {
268 		outb(port + Addr0 +  i, ether->ea[i]);
269 	}
270 
271 	SELECT_BANK(2);
272 	outs(port + MmuCmd, McReset);
273 }
274 
275 static void
276 chipenable(Ether* ether)
277 {
278 	int port;
279 
280 	port = ether->port;
281 	SELECT_BANK(0);
282 	outs(port + Tcr, TcrNormal);
283 	outs(port + Rcr, RcrNormal);
284 	SELECT_BANK(2);
285 	outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);
286 }
287 
288 static void
289 attach(Ether *ether)
290 {
291 	Smc91xx* ctlr;
292 
293 	ctlr = ether->ctlr;
294 	ilock(ctlr);
295 
296 	if (ctlr->attached) {
297 		iunlock(ctlr);
298 		return;
299 	}
300 
301 	chipenable(ether);
302 	ctlr->attached = 1;
303 	iunlock(ctlr);
304 }
305 
306 static void
307 txstart(Ether* ether)
308 {
309 	int port;
310 	Smc91xx* ctlr;
311 	Block* bp;
312 	int len, npages;
313 	int pno;
314 
315 	/* assumes ctlr is locked and bank 2 is selected */
316 	/* leaves bank 2 selected on return */
317 	port = ether->port;
318 	ctlr = ether->ctlr;
319 
320 	if (ctlr->txbp) {
321 		bp = ctlr->txbp;
322 		ctlr->txbp = 0;
323 	} else {
324 		bp = qget(ether->oq);
325 		if (bp == 0)
326 			return;
327 
328 		len = BLEN(bp);
329 		npages = (len + HdrSize) / PageSize;
330 		outs(port + MmuCmd, McAlloc | npages);
331 	}
332 
333 	pno = inb(port + AllocRes);
334 	if (pno & ArFailed) {
335 		outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);
336 		ctlr->txbp = bp;
337 		ctlr->txtime = MACHP(0)->ticks;
338 		return;
339 	}
340 
341 	outb(port + PktNo, pno);
342 	outs(port + Pointer, PtrAutoInc);
343 
344 	len = BLEN(bp);
345 	outs(port + Data1, 0);
346 	outb(port + Data1, (len + HdrSize) & 0xFF);
347 	outb(port + Data1, (len + HdrSize) >> 8);
348 	outss(port + Data1, bp->rp, len / 2);
349 	if ((len & 1) == 0) {
350 		outs(port + Data1, 0);
351 	} else {
352 		outb(port + Data1, bp->rp[len - 1]);
353 		outb(port + Data1, 0x20);	/* no info what 0x20 means */
354 	}
355 
356 	outb(port + IntrMask, inb(port + IntrMask) |
357 			IntTxError | IntTxEmpty);
358 
359 	outs(port + MmuCmd, McEnqueue);
360 	freeb(bp);
361 }
362 
363 static void
364 receive(Ether* ether)
365 {
366 	int port;
367 	Block* bp;
368 	int pktno, status, len;
369 
370 	/* assumes ctlr is locked and bank 2 is selected */
371 	/* leaves bank 2 selected on return */
372 	port = ether->port;
373 
374 	pktno = ins(port + FifoPorts);
375 	if (pktno & FpRxEmpty) {
376 		return;
377 	}
378 
379 	outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);
380 	status = ins(port + Data1);
381 	len = ins(port + Data1) & RxLenMask - HdrSize;
382 
383 	if (status & RsOddFrame)
384 		len++;
385 
386 	if ((status & RsError) || (bp = iallocb(len)) == 0) {
387 
388 		if (status & RsAlgnErr)
389 			ether->frames++;
390 		if (status & (RsTooShort | RsTooLong))
391 			ether->buffs++;
392 		if (status & RsBadCrc)
393 			ether->crcs++;
394 
395 		outs(port + MmuCmd, McRelease);
396 		return;
397 	}
398 
399 	/* packet length is padded to word */
400 	inss(port + Data1, bp->rp, len / 2);
401 	bp->wp = bp->rp + (len & ~1);
402 
403 	if (len & 1) {
404 		*bp->wp = inb(port + Data1);
405 		bp->wp++;
406 	}
407 
408 	etheriq(ether, bp, 1);
409 	ether->inpackets++;
410 	outs(port + MmuCmd, McRelease);
411 }
412 
413 static void
414 txerror(Ether* ether)
415 {
416 	int port;
417 	Smc91xx* ctlr;
418 	int save_pkt;
419 	int pktno, status;
420 
421 	/* assumes ctlr is locked and bank 2 is selected */
422 	/* leaves bank 2 selected on return */
423 	port = ether->port;
424 	ctlr = ether->ctlr;
425 
426 	save_pkt = inb(port + PktNo);
427 
428 	pktno = ins(port + FifoPorts) & FpTxMask;
429 	outb(port + PktNo, pktno);
430 	outs(port + Pointer, PtrAutoInc | PtrRead);
431 	status = ins(port + Data1);
432 
433 	if (status & TsLostCar)
434 		ctlr->lcar++;
435 
436 	if (status & TsLatCol)
437 		ctlr->lcol++;
438 
439 	if (status & Ts16Col)
440 		ctlr->scol++;
441 
442 	ether->oerrs++;
443 
444 	SELECT_BANK(0);
445 	outs(port + Tcr, ins(port + Tcr) | TcrEnable);
446 
447 	SELECT_BANK(2);
448 	outs(port + MmuCmd, McFreePkt);
449 
450 	outb(port + PktNo, save_pkt);
451 }
452 
453 static void
454 eph_irq(Ether* ether)
455 {
456 	int port;
457 	Smc91xx* ctlr;
458 	ushort status;
459 	int n;
460 
461 	/* assumes ctlr is locked and bank 2 is selected */
462 	/* leaves bank 2 selected on return */
463 	port = ether->port;
464 	ctlr = ether->ctlr;
465 
466 	SELECT_BANK(0);
467 	status = ins(port + Eph);
468 
469 	if (status & EphCntRol) {
470 		/* read the counter register even if we don't need it */
471 		/* otherwise we will keep getting this interrupt */
472 		n = ins(port + Counter);
473 		ctlr->col += (n & CntColMask) >> CntColShr;
474 		ctlr->mcol += (n & CntMColMask) >> CntMColShr;
475 		ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;
476 	}
477 
478 	/* if there was a transmit error, Tcr is disabled */
479 	outs(port + Tcr, ins(port + Tcr) | TcrEnable);
480 
481 	/* clear a link error interrupt */
482 	SELECT_BANK(1);
483 	outs(port + Control, CtlAutoRls);
484 	outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);
485 
486 	SELECT_BANK(2);
487 }
488 
489 static void
490 transmit(Ether* ether)
491 {
492 	Smc91xx* ctlr;
493 	int port, n;
494 
495 	ctlr = ether->ctlr;
496 	port = ether->port;
497 	ilock(ctlr);
498 
499 	if (ctlr->txbp) {
500 		n = TK2MS(MACHP(0)->ticks - ctlr->txtime);
501 		if (n > TxTimeout) {
502 			chipreset(ether);
503 			chipenable(ether);
504 			freeb(ctlr->txbp);
505 			ctlr->txbp = 0;
506 		}
507 		iunlock(ctlr);
508 		return;
509 	}
510 
511 	SELECT_BANK(2);
512 	txstart(ether);
513 	iunlock(ctlr);
514 }
515 
516 static void
517 interrupt(Ureg*, void *arg)
518 {
519 	int port;
520 	Smc91xx* ctlr;
521 	Ether* ether;
522 	int save_bank;
523 	int save_pointer;
524 	int mask, status;
525 
526 	ether = arg;
527 	port = ether->port;
528 	ctlr = ether->ctlr;
529 
530 	ilock(ctlr);
531 	save_bank = ins(port + BankSelect);
532 	SELECT_BANK(2);
533 	save_pointer = ins(port + Pointer);
534 
535 	mask = inb(port + IntrMask);
536 	outb(port + IntrMask, 0);
537 
538 	while ((status = inb(port + Interrupt) & mask) != 0) {
539 		if (status & IntRcv) {
540 			receive(ether);
541 		}
542 
543 		if (status & IntTxError) {
544 			txerror(ether);
545 		}
546 
547 		if (status & IntTxEmpty) {
548 			outb(port + Interrupt, IntTxEmpty);
549 			outb(port + IntrMask, mask & ~IntTxEmpty);
550 			txstart(ether);
551 			mask = inb(port + IntrMask);
552 		}
553 
554 		if (status & IntAlloc) {
555 			outb(port + IntrMask, mask & ~IntAlloc);
556 			txstart(ether);;
557 			mask = inb(port + IntrMask);
558 		}
559 
560 		if (status & IntRxOvrn) {
561 			ctlr->rovrn++;
562 			ether->misses++;
563 			outb(port + Interrupt,IntRxOvrn);
564 		}
565 
566 		if (status & IntEph)
567 			eph_irq(ether);
568 	}
569 
570 	outb(port + IntrMask, mask);
571 	outs(port + Pointer, save_pointer);
572 	outs(port + BankSelect, save_bank);
573 	iunlock(ctlr);
574 }
575 
576 static void
577 promiscuous(void* arg, int on)
578 {
579 	int port;
580 	Smc91xx *ctlr;
581 	Ether* ether;
582 	ushort x;
583 
584 	ether = arg;
585 	port = ether->port;
586 	ctlr = ether->ctlr;
587 
588 	ilock(ctlr);
589 	SELECT_BANK(0);
590 	x = ins(port + Rcr);
591 	if (on)
592 		x |= RcrPromisc;
593 	else
594 		x &= ~RcrPromisc;
595 
596 	outs(port + Rcr, x);
597 	iunlock(ctlr);
598 }
599 
600 static void
601 multicast(void* arg, uchar *addr, int on)
602 {
603 	int port;
604 	Smc91xx*ctlr;
605 	Ether *ether;
606 	ushort x;
607 
608 	USED(addr, on);
609 
610 	ether = arg;
611 	port = ether->port;
612 	ctlr = ether->ctlr;
613 	ilock(ctlr);
614 
615 	SELECT_BANK(0);
616 	x = ins(port + Rcr);
617 
618 	if (ether->nmaddr)
619 		x |= RcrAllMcast;
620 	else
621 		x &= ~RcrAllMcast;
622 
623 	outs(port + Rcr, x);
624 	iunlock(ctlr);
625 }
626 
627 static long
628 ifstat(Ether* ether, void* a, long n, ulong offset)
629 {
630 	static char *chiprev[] = {
631 		[3] 	"92",
632 		[5]	"95",
633 		[7]	"100",
634 		[8]	"100-FD",
635 		[9]	"110",
636 	};
637 
638 	Smc91xx* ctlr;
639 	char* p;
640 	int r, len;
641 	char* s;
642 
643 	if (n == 0)
644 		return 0;
645 
646 	ctlr = ether->ctlr;
647 	p = malloc(READSTR);
648 
649 	s = 0;
650 	if (ctlr->rev > 0) {
651 		r = ctlr->rev >> 4;
652 		if (r < nelem(chiprev))
653 			s = chiprev[r];
654 
655 		if (r == 4) {
656 			if ((ctlr->rev & 0x0F) >= 6)
657 				s = "96";
658 			else
659 				s = "94";
660 		}
661 	}
662 
663 	len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");
664 	len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);
665 	len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);
666 	len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);
667 	len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);
668 	len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);
669 	len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);
670 	len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);
671 	USED(len);
672 
673 	n = readstr(offset, a, n, p);
674 	free(p);
675 
676 	return n;
677 }
678 
679 static int
680 reset(Ether* ether)
681 {
682 	int port;
683 	int i, x;
684 	char* type;
685 	Smc91xx* ctlr;
686 	int slot;
687 	uchar ea[Eaddrlen];
688 
689 	if (ether->irq == 0)
690 		ether->irq = 9;
691 
692 	if (ether->port == 0)
693 		ether->port = 0x100;
694 
695 	type = "8020";
696 	for(i = 0; i < ether->nopt; i++) {
697 		if (cistrncmp(ether->opt[i], "id=", 3))
698 			continue;
699 		type = &ether->opt[i][3];
700 		break;
701 	}
702 
703 	if ((slot = pcmspecial(type, ether)) < 0)
704 		return -1;
705 
706 	if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {
707 		pcmspecialclose(slot);
708 		return -1;
709 	}
710 
711 	ether->ctlr = malloc(sizeof(Smc91xx));
712 	ctlr = ether->ctlr;
713 	if (ctlr == 0) {
714 		iofree(ether->port);
715 		pcmspecialclose(slot);
716 		return -1;
717 	}
718 
719 	ilock(ctlr);
720 	ctlr->rev = 0;
721 	ctlr->txbp = nil;
722 	ctlr->attached = 0;
723 	ctlr->rovrn = 0;
724 	ctlr->lcar = 0;
725 	ctlr->col = 0;
726 	ctlr->scol = 0;
727 	ctlr->mcol = 0;
728 	ctlr->lcol = 0;
729 	ctlr->dfr = 0;
730 
731 	port = ether->port;
732 
733 	SELECT_BANK(1);
734 	if ((ins(port + BankSelect) & BsrMask) != BsrId) {
735 		outs(port + Control, 0);	/* try powering up the chip */
736 		delay(55);
737 	}
738 
739 	outs(port + Config, ins(port + Config) | Cfg16Bit);
740 	x = ins(port + BaseAddr);
741 
742 	if (((ins(port + BankSelect) & BsrMask) != BsrId) ||
743 		((x >> 8) == (x & 0xFF))) {
744 		iunlock(ctlr);
745 		iofree(port);
746 		pcmspecialclose(slot);
747 		return -1;
748 	}
749 
750 	SELECT_BANK(3);
751 	ctlr->rev = ins(port + Revision) & 0xFF;
752 
753 	memset(ea, 0, Eaddrlen);
754 	if (memcmp(ea, ether->ea, Eaddrlen) == 0) {
755 		if (readnodeid(slot, ether) < 0) {
756 			print("Smc91cXX: cannot find ethernet address\n");
757 			iunlock(ctlr);
758 			iofree(port);
759 			pcmspecialclose(slot);
760 			return -1;
761 		}
762 	}
763 
764 	chipreset(ether);
765 
766 	ether->attach = attach;
767 	ether->transmit = transmit;
768 	ether->interrupt = interrupt;
769 	ether->ifstat = ifstat;
770 	ether->promiscuous = promiscuous;
771 	ether->multicast = multicast;
772 	ether->arg = ether;
773 	iunlock(ctlr);
774 	return 0;
775 }
776 
777 void
778 ethersmclink(void)
779 {
780 	addethercard("smc91cXX", reset);
781 }
782