|
|
1.1 root 1: /*
2: * National Semiconductor DP8390
3: * and SMC 83C90
4: * Network Interface Controller.
5: */
6: #include "u.h"
7: #include "../port/lib.h"
8: #include "mem.h"
9: #include "dat.h"
10: #include "fns.h"
11: #include "../port/error.h"
12: #include "io.h"
13: #include "devtab.h"
14:
15: #include "ether.h"
16:
17: enum {
18: Cr = 0x00, /* command register, all pages */
19:
20: Stp = 0x01, /* stop */
21: Sta = 0x02, /* start */
22: Txp = 0x04, /* transmit packet */
23: RDMAread = (1<<3), /* remote DMA read */
24: RDMAwrite = (2<<3), /* remote DMA write */
25: RDMAsend = (3<<3), /* remote DMA send packet */
26: RDMAabort = (4<<3), /* abort/complete remote DMA */
27: Ps0 = 0x40, /* page select */
28: Ps1 = 0x80, /* page select */
29: Page0 = 0x00,
30: Page1 = Ps0,
31: Page2 = Ps1,
32: };
33:
34: enum { /* Page 0, read */
35: Clda0 = 0x01, /* current local DMA address 0 */
36: Clda1 = 0x02, /* current local DMA address 1 */
37: Bnry = 0x03, /* boundary pointer (R/W) */
38: Tsr = 0x04, /* transmit status register */
39: Ncr = 0x05, /* number of collisions register */
40: Fifo = 0x06, /* FIFO */
41: Isr = 0x07, /* interrupt status register (R/W) */
42: Crda0 = 0x08, /* current remote DMA address 0 */
43: Crda1 = 0x09, /* current remote DMA address 1 */
44: Rsr = 0x0C, /* receive status register */
45: Cntr0 = 0x0D, /* frame alignment errors */
46: Cntr1 = 0x0E, /* CRC errors */
47: Cntr2 = 0x0F, /* missed packet errors */
48: };
49:
50: enum { /* Page 0, write */
51: Pstart = 0x01, /* page start register */
52: Pstop = 0x02, /* page stop register */
53: Tpsr = 0x04, /* transmit page start address */
54: Tbcr0 = 0x05, /* transmit byte count register 0 */
55: Tbcr1 = 0x06, /* transmit byte count register 1 */
56: Rsar0 = 0x08, /* remote start address register 0 */
57: Rsar1 = 0x09, /* remote start address register 1 */
58: Rbcr0 = 0x0A, /* remote byte count register 0 */
59: Rbcr1 = 0x0B, /* remote byte count register 1 */
60: Rcr = 0x0C, /* receive configuration register */
61: Tcr = 0x0D, /* transmit configuration register */
62: Dcr = 0x0E, /* data configuration register */
63: Imr = 0x0F, /* interrupt mask */
64: };
65:
66: enum { /* Page 1, read/write */
67: Par0 = 0x01, /* physical address register 0 */
68: Curr = 0x07, /* current page register */
69: Mar0 = 0x08, /* multicast address register 0 */
70: };
71:
72: enum { /* Interrupt Status Register */
73: Prx = 0x01, /* packet received */
74: Ptx = 0x02, /* packet transmitted */
75: Rxe = 0x04, /* receive error */
76: Txe = 0x08, /* transmit error */
77: Ovw = 0x10, /* overwrite warning */
78: Cnt = 0x20, /* counter overflow */
79: Rdc = 0x40, /* remote DMA complete */
80: Rst = 0x80, /* reset status */
81: };
82:
83: enum { /* Interrupt Mask Register */
84: Prxe = 0x01, /* packet received interrupt enable */
85: Ptxe = 0x02, /* packet transmitted interrupt enable */
86: Rxee = 0x04, /* receive error interrupt enable */
87: Txee = 0x08, /* transmit error interrupt enable */
88: Ovwe = 0x10, /* overwrite warning interrupt enable */
89: Cnte = 0x20, /* counter overflow interrupt enable */
90: Rdce = 0x40, /* DMA complete interrupt enable */
91: };
92:
93: enum { /* Data Configuration register */
94: Wts = 0x01, /* word transfer select */
95: Bos = 0x02, /* byte order select */
96: Las = 0x04, /* long address select */
97: Ls = 0x08, /* loopback select */
98: Arm = 0x10, /* auto-initialise remote */
99: Ft1 = (0x00<<5), /* FIFO threshhold select 1 byte/word */
100: Ft2 = (0x01<<5), /* FIFO threshhold select 2 bytes/words */
101: Ft4 = (0x02<<5), /* FIFO threshhold select 4 bytes/words */
102: Ft6 = (0x03<<5), /* FIFO threshhold select 6 bytes/words */
103: };
104:
105: enum { /* Transmit Configuration Register */
106: Crc = 0x01, /* inhibit CRC */
107: Lb = 0x02, /* internal loopback */
108: Atd = 0x08, /* auto transmit disable */
109: Ofst = 0x10, /* collision offset enable */
110: };
111:
112: enum { /* Transmit Status Register */
113: Ptxok = 0x01, /* packet transmitted */
114: Col = 0x04, /* transmit collided */
115: Abt = 0x08, /* tranmit aborted */
116: Crs = 0x10, /* carrier sense lost */
117: Fu = 0x20, /* FIFO underrun */
118: Cdh = 0x40, /* CD heartbeat */
119: Owc = 0x80, /* out of window collision */
120: };
121:
122: enum { /* Receive Configuration Register */
123: Sep = 0x01, /* save errored packets */
124: Ar = 0x02, /* accept runt packets */
125: Ab = 0x04, /* accept broadcast */
126: Am = 0x08, /* accept multicast */
127: Pro = 0x10, /* promiscuous physical */
128: Mon = 0x20, /* monitor mode */
129: };
130:
131: enum { /* Receive Status Register */
132: Prxok = 0x01, /* packet received intact */
133: Crce = 0x02, /* CRC error */
134: Fae = 0x04, /* frame alignment error */
135: Fo = 0x08, /* FIFO overrun */
136: Mpa = 0x10, /* missed packet */
137: Phy = 0x20, /* physical/multicast address */
138: Dis = 0x40, /* receiver disabled */
139: Dfr = 0x80, /* deferring */
140: };
141:
142: typedef struct {
143: uchar status;
144: uchar next;
145: uchar len0;
146: uchar len1;
147: } Hdr;
148:
149: static void
150: dp8390disable(Ctlr *ctlr)
151: {
152: ulong dp8390;
153: int timo;
154:
155: dp8390 = ctlr->card.dp8390;
156: /*
157: * Stop the chip. Set the Stp bit and wait for the chip
158: * to finish whatever was on its tiny mind before it sets
159: * the Rst bit.
160: * We need the timeout because there may not be a real
161: * chip there if this is called when probing for a device
162: * at boot.
163: */
164: dp8390outb(dp8390+Cr, Page0|RDMAabort|Stp);
165: dp8390outb(dp8390+Rbcr0, 0);
166: dp8390outb(dp8390+Rbcr1, 0);
167: for(timo = 10000; (dp8390inb(dp8390+Isr) & Rst) == 0 && timo; timo--)
168: ;
169: }
170:
171: static void
172: dp8390ring(Ctlr *ctlr)
173: {
174: ulong dp8390;
175:
176: dp8390 = ctlr->card.dp8390;
177: dp8390outb(dp8390+Pstart, ctlr->card.pstart);
178: dp8390outb(dp8390+Pstop, ctlr->card.pstop);
179: dp8390outb(dp8390+Bnry, ctlr->card.pstop-1);
180:
181: dp8390outb(dp8390+Cr, Page1|RDMAabort|Stp);
182: dp8390outb(dp8390+Curr, ctlr->card.pstart);
183: dp8390outb(dp8390+Cr, Page0|RDMAabort|Stp);
184:
185: ctlr->card.nxtpkt = ctlr->card.pstart;
186: }
187:
188: void
189: dp8390reset(Ctlr *ctlr)
190: {
191: ulong dp8390;
192:
193: dp8390 = ctlr->card.dp8390;
194: /*
195: * This is the initialisation procedure described
196: * as 'mandatory' in the datasheet, with references
197: * to the 3Com503 technical reference manual.
198: */
199: dp8390disable(ctlr);
200: if(ctlr->card.bit16)
201: dp8390outb(dp8390+Dcr, Ft4|Ls|Wts);
202: else
203: dp8390outb(dp8390+Dcr, Ft4|Ls);
204:
205: dp8390outb(dp8390+Rbcr0, 0);
206: dp8390outb(dp8390+Rbcr1, 0);
207:
208: dp8390outb(dp8390+Tcr, 0);
209: dp8390outb(dp8390+Rcr, Mon);
210:
211: /*
212: * Init the ring hardware and software ring pointers.
213: * Can't initialise ethernet address as we may not know
214: * it yet.
215: */
216: dp8390ring(ctlr);
217: dp8390outb(dp8390+Tpsr, ctlr->card.tstart);
218:
219: dp8390outb(dp8390+Isr, 0xFF);
220: dp8390outb(dp8390+Imr, Cnte|Ovwe|Txee|Rxee|Ptxe|Prxe);
221:
222: /*
223: * Leave the chip initialised,
224: * but in monitor mode.
225: */
226: dp8390outb(dp8390+Cr, Page0|RDMAabort|Sta);
227: }
228:
229: void
230: dp8390attach(Ctlr *ctlr)
231: {
232: /*
233: * Enable the chip for transmit/receive.
234: * The init routine leaves the chip in monitor
235: * mode. Clear the missed-packet counter, it
236: * increments while in monitor mode.
237: */
238: dp8390outb(ctlr->card.dp8390+Rcr, Ab);
239: dp8390inb(ctlr->card.dp8390+Cntr2);
240: }
241:
242: void
243: dp8390mode(Ctlr *ctlr, int on)
244: {
245: /*
246: * Set/reset promiscuous mode.
247: */
248: if(on)
249: dp8390outb(ctlr->card.dp8390+Rcr, Pro|Ab);
250: else
251: dp8390outb(ctlr->card.dp8390+Rcr, Ab);
252: }
253:
254: void
255: dp8390setea(Ctlr *ctlr)
256: {
257: ulong dp8390;
258: uchar cr;
259: int i;
260:
261: dp8390 = ctlr->card.dp8390;
262: /*
263: * Set the ethernet address into the chip.
264: * Take care to restore the command register
265: * afterwards. We don't care about multicast
266: * addresses as we never set the multicast
267: * enable.
268: */
269: cr = dp8390inb(dp8390+Cr) & ~Txp;
270: dp8390outb(dp8390+Cr, Page1|(~(Ps1|Ps0) & cr));
271: for(i = 0; i < sizeof(ctlr->ea); i++)
272: dp8390outb(dp8390+Par0+i, ctlr->ea[i]);
273: dp8390outb(dp8390+Cr, cr);
274: }
275:
276: void
277: dp8390getea(Ctlr *ctlr)
278: {
279: ulong dp8390;
280: uchar cr;
281: int i;
282:
283: dp8390 = ctlr->card.dp8390;
284: /*
285: * Set the ethernet address into the chip.
286: * Take care to restore the command register
287: * afterwards. We don't care about multicast
288: * addresses as we never set the multicast
289: * enable.
290: */
291: cr = dp8390inb(dp8390+Cr) & ~Txp;
292: dp8390outb(dp8390+Cr, Page1|(~(Ps1|Ps0) & cr));
293: for(i = 0; i < sizeof(ctlr->ea); i++)
294: ctlr->ea[i] = dp8390inb(dp8390+Par0+i);
295: dp8390outb(dp8390+Cr, cr);
296: }
297:
298: void*
299: dp8390read(Ctlr *ctlr, void *to, ulong from, ulong len)
300: {
301: ulong dp8390;
302: uchar cr;
303: int timo;
304:
305: dp8390 = ctlr->card.dp8390;
306: /*
307: * Read some data at offset 'from' in the card's memory
308: * using the DP8390 remote DMA facility, and place it at
309: * 'to' in main memory, via the I/O data port.
310: */
311: cr = dp8390inb(dp8390+Cr) & ~Txp;
312: dp8390outb(dp8390+Cr, Page0|RDMAabort|Sta);
313: dp8390outb(dp8390+Isr, Rdc);
314:
315: /*
316: * Set up the remote DMA address and count.
317: */
318: if(ctlr->card.bit16)
319: len = ROUNDUP(len, 2);
320: dp8390outb(dp8390+Rbcr0, len & 0xFF);
321: dp8390outb(dp8390+Rbcr1, (len>>8) & 0xFF);
322: dp8390outb(dp8390+Rsar0, from & 0xFF);
323: dp8390outb(dp8390+Rsar1, (from>>8) & 0xFF);
324:
325: /*
326: * Start the remote DMA read and suck the data
327: * out of the I/O port.
328: */
329: dp8390outb(dp8390+Cr, Page0|RDMAread|Sta);
330: if(ctlr->card.bit16)
331: inss(ctlr->card.data, to, len/2);
332: else
333: insb(ctlr->card.data, to, len);
334:
335: /*
336: * Wait for the remote DMA to complete. The timeout
337: * is necessary because we may call this routine on
338: * a non-existent chip during initialisation and, due
339: * to the miracles of the bus, we could get this far
340: * and still be talking to a slot full of nothing.
341: */
342: for(timo = 10000; (dp8390inb(dp8390+Isr) & Rdc) == 0 && timo; timo--)
343: ;
344:
345: dp8390outb(dp8390+Isr, Rdc);
346: dp8390outb(dp8390+Cr, cr);
347: return to;
348: }
349:
350: void*
351: dp8390write(Ctlr *ctlr, ulong to, void *from, ulong len)
352: {
353: ulong dp8390, crda;
354: uchar cr;
355:
356: dp8390 = ctlr->card.dp8390;
357: /*
358: * Write some data to offset 'to' in the card's memory
359: * using the DP8390 remote DMA facility, reading it at
360: * 'from' in main memory, via the I/O data port.
361: */
362: cr = dp8390inb(dp8390+Cr) & ~Txp;
363: dp8390outb(dp8390+Cr, Page0|RDMAabort|Sta);
364: dp8390outb(dp8390+Isr, Rdc);
365:
366: if(ctlr->card.bit16)
367: len = ROUNDUP(len, 2);
368:
369: /*
370: * Set up the remote DMA address and count.
371: * This is straight from the DP8390[12D] datasheet, hence
372: * the initial set up for read.
373: */
374: crda = to-1-ctlr->card.bit16;
375: dp8390outb(dp8390+Rbcr0, (len+1+ctlr->card.bit16) & 0xFF);
376: dp8390outb(dp8390+Rbcr1, ((len+1+ctlr->card.bit16)>>8) & 0xFF);
377: dp8390outb(dp8390+Rsar0, crda & 0xFF);
378: dp8390outb(dp8390+Rsar1, (crda>>8) & 0xFF);
379: dp8390outb(dp8390+Cr, Page0|RDMAread|Sta);
380:
381: for(;;){
382: crda = dp8390inb(dp8390+Crda0);
383: crda |= dp8390inb(dp8390+Crda1)<<8;
384: if(crda == to){
385: /*
386: * Start the remote DMA write and make sure
387: * the registers are correct.
388: */
389: dp8390outb(dp8390+Cr, Page0|RDMAwrite|Sta);
390:
391: crda = dp8390inb(dp8390+Crda0);
392: crda |= dp8390inb(dp8390+Crda1)<<8;
393: if(crda != to)
394: panic("crda write %d to %d\n", crda, to);
395:
396: break;
397: }
398: }
399:
400: /*
401: * Pump the data into the I/O port.
402: */
403: if(ctlr->card.bit16)
404: outss(ctlr->card.data, from, len/2);
405: else
406: outsb(ctlr->card.data, from, len);
407:
408: /*
409: * Wait for the remote DMA to finish. We'll need
410: * a timeout here if this ever gets called before
411: * we know there really is a chip there.
412: */
413: while((dp8390inb(dp8390+Isr) & Rdc) == 0)
414: ;
415:
416: dp8390outb(dp8390+Isr, Rdc);
417: dp8390outb(dp8390+Cr, cr);
418: return (void*)to;
419: }
420:
421: static uchar
422: getcurr(ulong dp8390)
423: {
424: uchar cr, curr;
425:
426: cr = dp8390inb(dp8390+Cr) & ~Txp;
427: dp8390outb(dp8390+Cr, Page1|(~(Ps1|Ps0) & cr));
428: curr = dp8390inb(dp8390+Curr);
429: dp8390outb(dp8390+Cr, cr);
430: return curr;
431: }
432:
433: static void
434: cldaquiet(ulong dp8390)
435: {
436: uchar a, b, c;
437:
438: /*
439: * Hack to keep away from the card's memory while it is receiving
440: * a packet. This is only a problem on the NCR 3170 Safari.
441: *
442: * We peek at the current local DMA registers and, if they are
443: * changing, wait.
444: */
445:
446: for(;;delay(10)){
447: a = dp8390inb(dp8390+Clda0);
448: b = dp8390inb(dp8390+Clda0);
449: if(a != b)
450: continue;
451: c = dp8390inb(dp8390+Clda0);
452: if(c != b)
453: continue;
454: break;
455: }
456: }
457:
458: void
459: dp8390receive(Ctlr *ctlr)
460: {
461: RingBuf *ring;
462: uchar curr, len1, *pkt;
463: Hdr hdr;
464: ulong dp8390, data, len;
465:
466: dp8390 = ctlr->card.dp8390;
467: for(curr = getcurr(dp8390); ctlr->card.nxtpkt != curr; curr = getcurr(dp8390)){
468: if(strcmp(arch->id, "NCRD.0") == 0)
469: cldaquiet(dp8390);
470:
471: ctlr->inpackets++;
472:
473: data = ctlr->card.nxtpkt*Dp8390BufSz;
474: (*ctlr->card.read)(ctlr, &hdr, data, sizeof(Hdr));
475:
476: /*
477: * Don't believe the upper byte count, work it
478: * out from the software next-page pointer and
479: * the current next-page pointer.
480: */
481: if(hdr.next > ctlr->card.nxtpkt)
482: len1 = hdr.next - ctlr->card.nxtpkt - 1;
483: else
484: len1 = (ctlr->card.pstop-ctlr->card.nxtpkt) + (hdr.next-ctlr->card.pstart) - 1;
485: if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
486: len1--;
487:
488: len = ((len1<<8)|hdr.len0)-4;
489:
490: /*
491: * Chip is badly scrogged, reinitialise the ring.
492: */
493: if(hdr.next < ctlr->card.pstart || hdr.next >= ctlr->card.pstop
494: || len < 60 || len > sizeof(Etherpkt)){
495: print("H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%d|",
496: hdr.status, hdr.next, hdr.len0, hdr.len1, len);
497: dp8390outb(dp8390+Cr, Page0|RDMAabort|Stp);
498: dp8390ring(ctlr);
499: dp8390outb(dp8390+Cr, Page0|RDMAabort|Sta);
500: return;
501: }
502:
503: /*
504: * If it's a good packet and we have a place to put it,
505: * read it in to the software ring.
506: * If the packet wraps round the hardware ring, read it
507: * in two pieces.
508: */
509: ring = &ctlr->rb[ctlr->ri];
510: if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && ring->owner == Interface){
511: pkt = ring->pkt;
512: data += sizeof(Hdr);
513: ring->len = len;
514:
515: if((data+len) >= ctlr->card.pstop*Dp8390BufSz){
516: ulong count = ctlr->card.pstop*Dp8390BufSz - data;
517:
518: (*ctlr->card.read)(ctlr, pkt, data, count);
519: pkt += count;
520: data = ctlr->card.pstart*Dp8390BufSz;
521: len -= count;
522: }
523: if(len)
524: (*ctlr->card.read)(ctlr, pkt, data, len);
525:
526: ring->owner = Host;
527: ctlr->ri = NEXT(ctlr->ri, ctlr->nrb);
528: }
529:
530: /*
531: * Finished woth this packet, update the
532: * hardware and software ring pointers.
533: */
534: ctlr->card.nxtpkt = hdr.next;
535:
536: hdr.next--;
537: if(hdr.next < ctlr->card.pstart)
538: hdr.next = ctlr->card.pstop-1;
539: dp8390outb(dp8390+Bnry, hdr.next);
540: }
541: }
542:
543: /*
544: * Initiate a transmission. Must be called splhi().
545: */
546: void
547: dp8390transmit(Ctlr *ctlr)
548: {
549: ulong dp8390;
550: RingBuf *ring;
551:
552: dp8390 = ctlr->card.dp8390;
553: ring = &ctlr->tb[ctlr->ti];
554: if(ctlr->tbusy == 0 && ring->owner == Interface){
555:
556: ctlr->tbusy = 1;
557:
558: (*ctlr->card.write)(ctlr, ctlr->card.tstart*Dp8390BufSz, ring->pkt, ring->len);
559:
560: dp8390outb(dp8390+Tbcr0, ring->len & 0xFF);
561: dp8390outb(dp8390+Tbcr1, (ring->len>>8) & 0xFF);
562: dp8390outb(dp8390+Cr, Page0|RDMAabort|Txp|Sta);
563: }
564: }
565:
566: void
567: dp8390overflow(Ctlr *ctlr)
568: {
569: ulong dp8390;
570: uchar txp;
571: int resend;
572:
573: dp8390 = ctlr->card.dp8390;
574: /*
575: * The following procedure is taken from the DP8390[12D] datasheet,
576: * it seems pretty adamant that this is what has to be done.
577: */
578: txp = dp8390inb(dp8390+Cr) & Txp;
579: dp8390outb(dp8390+Cr, Page0|RDMAabort|Stp);
580: delay(2);
581: dp8390outb(dp8390+Rbcr0, 0);
582: dp8390outb(dp8390+Rbcr1, 0);
583:
584: resend = 0;
585: if(txp && (dp8390inb(dp8390+Isr) & (Txe|Ptx)) == 0)
586: resend = 1;
587:
588: dp8390outb(dp8390+Tcr, Lb);
589: dp8390outb(dp8390+Cr, Page0|RDMAabort|Sta);
590: (*ctlr->card.receive)(ctlr);
591: dp8390outb(dp8390+Isr, Ovw);
592: wakeup(&ctlr->rr);
593: dp8390outb(dp8390+Tcr, 0);
594:
595: if(resend)
596: dp8390outb(dp8390+Cr, Page0|RDMAabort|Txp|Sta);
597: }
598:
599: void
600: dp8390watch(Ctlr *ctlr)
601: {
602: int s;
603:
604: /*
605: * Stub watchdog routine.
606: * Whine if a transmit takes too long.
607: */
608: s = splhi();
609: if(ctlr->tbusy){
610: ctlr->tbusy++;
611: if(ctlr->tbusy > 1 && ctlr->debug)
612: print("TB%d|", ctlr->tbusy);
613: }
614: splx(s);
615: }
616:
617: void
618: dp8390intr(Ctlr *ctlr)
619: {
620: ulong dp8390;
621: RingBuf *ring;
622: uchar isr, r;
623:
624: dp8390 = ctlr->card.dp8390;
625: /*
626: * While there is something of interest,
627: * clear all the interrupts and process.
628: */
629: dp8390outb(dp8390+Imr, 0x00);
630: while(isr = dp8390inb(dp8390+Isr)){
631:
632: if(isr & Ovw){
633: if(ctlr->card.overflow)
634: (*ctlr->card.overflow)(ctlr);
635: dp8390outb(dp8390+Isr, Ovw);
636: ctlr->overflows++;
637: }
638:
639: /*
640: * We have received packets.
641: * Take a spin round the ring and
642: * wakeup the kproc.
643: */
644: if(isr & (Rxe|Prx)){
645: (*ctlr->card.receive)(ctlr);
646: dp8390outb(dp8390+Isr, Rxe|Prx);
647: wakeup(&ctlr->rr);
648: }
649:
650: /*
651: * A packet completed transmission, successfully or
652: * not. Start transmission on the next buffered packet,
653: * and wake the output routine.
654: */
655: if(isr & (Txe|Ptx)){
656: r = dp8390inb(dp8390+Tsr);
657: if(isr & Txe){
658: if((r & (Cdh|Fu|Crs|Abt)) && ctlr->debug)
659: print("Tsr#%2.2ux|", r);
660: ctlr->oerrs++;
661: }
662:
663: dp8390outb(dp8390+Isr, Txe|Ptx);
664:
665: if(isr & Ptx)
666: ctlr->outpackets++;
667:
668: ring = &ctlr->tb[ctlr->ti];
669: ring->owner = Host;
670: ctlr->tbusy = 0;
671: ctlr->ti = NEXT(ctlr->ti, ctlr->ntb);
672: (*ctlr->card.transmit)(ctlr);
673: wakeup(&ctlr->tr);
674: }
675:
676: if(isr & Cnt){
677: ctlr->frames += dp8390inb(dp8390+Cntr0);
678: ctlr->crcs += dp8390inb(dp8390+Cntr1);
679: ctlr->buffs += dp8390inb(dp8390+Cntr2);
680: dp8390outb(dp8390+Isr, Cnt);
681: }
682: }
683: dp8390outb(dp8390+Imr, Cnte|Ovwe|Txee|Rxee|Ptxe|Prxe);
684: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.