FreeBSD/Linux Kernel Cross Reference
sys/pc/etherelnk3.c
1 /*
2 * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
3 * To do:
4 * check robustness in the face of errors (e.g. busmaster & rxUnderrun);
5 * RxEarly and busmaster;
6 * autoSelect;
7 * PCI latency timer and master enable;
8 * errata list;
9 * rewrite all initialisation.
10 */
11 #include "u.h"
12 #include "../port/lib.h"
13 #include "mem.h"
14 #include "dat.h"
15 #include "fns.h"
16 #include "io.h"
17 #include "../port/error.h"
18 #include "../port/netif.h"
19
20 #include "etherif.h"
21
22 #define XCVRDEBUG if(0)print
23
24 enum {
25 IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */
26 };
27
28 enum { /* all windows */
29 CommandR = 0x000E,
30 IntStatusR = 0x000E,
31 };
32
33 enum { /* Commands */
34 GlobalReset = 0x0000,
35 SelectRegisterWindow = 0x0001,
36 EnableDcConverter = 0x0002,
37 RxDisable = 0x0003,
38 RxEnable = 0x0004,
39 RxReset = 0x0005,
40 Stall = 0x0006, /* 3C90x */
41 TxDone = 0x0007,
42 RxDiscard = 0x0008,
43 TxEnable = 0x0009,
44 TxDisable = 0x000A,
45 TxReset = 0x000B,
46 RequestInterrupt = 0x000C,
47 AcknowledgeInterrupt = 0x000D,
48 SetInterruptEnable = 0x000E,
49 SetIndicationEnable = 0x000F, /* SetReadZeroMask */
50 SetRxFilter = 0x0010,
51 SetRxEarlyThresh = 0x0011,
52 SetTxAvailableThresh = 0x0012,
53 SetTxStartThresh = 0x0013,
54 StartDma = 0x0014, /* initiate busmaster operation */
55 StatisticsEnable = 0x0015,
56 StatisticsDisable = 0x0016,
57 DisableDcConverter = 0x0017,
58 SetTxReclaimThresh = 0x0018, /* PIO-only adapters */
59 PowerUp = 0x001B, /* not all adapters */
60 PowerDownFull = 0x001C, /* not all adapters */
61 PowerAuto = 0x001D, /* not all adapters */
62 };
63
64 enum { /* (Global|Rx|Tx)Reset command bits */
65 tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */
66 endecReset = 0x0002, /* internal Ethernet encoder/decoder */
67 networkReset = 0x0004, /* network interface logic */
68 fifoReset = 0x0008, /* FIFO control logic */
69 aismReset = 0x0010, /* autoinitialise state-machine logic */
70 hostReset = 0x0020, /* bus interface logic */
71 dmaReset = 0x0040, /* bus master logic */
72 vcoReset = 0x0080, /* on-board 10Mbps VCO */
73 updnReset = 0x0100, /* upload/download (Rx/TX) logic */
74
75 resetMask = 0x01FF,
76 };
77
78 enum { /* Stall command bits */
79 upStall = 0x0000,
80 upUnStall = 0x0001,
81 dnStall = 0x0002,
82 dnUnStall = 0x0003,
83 };
84
85 enum { /* SetRxFilter command bits */
86 receiveIndividual = 0x0001, /* match station address */
87 receiveMulticast = 0x0002,
88 receiveBroadcast = 0x0004,
89 receiveAllFrames = 0x0008, /* promiscuous */
90 };
91
92 enum { /* StartDma command bits */
93 Upload = 0x0000, /* transfer data from adapter to memory */
94 Download = 0x0001, /* transfer data from memory to adapter */
95 };
96
97 enum { /* IntStatus bits */
98 interruptLatch = 0x0001,
99 hostError = 0x0002, /* Adapter Failure */
100 txComplete = 0x0004,
101 txAvailable = 0x0008,
102 rxComplete = 0x0010,
103 rxEarly = 0x0020,
104 intRequested = 0x0040,
105 updateStats = 0x0080,
106 transferInt = 0x0100, /* Bus Master Transfer Complete */
107 dnComplete = 0x0200,
108 upComplete = 0x0400,
109 busMasterInProgress = 0x0800,
110 commandInProgress = 0x1000,
111
112 interruptMask = 0x07FE,
113 };
114
115 #define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))
116 #define STATUS(port) ins((port)+IntStatusR)
117
118 enum { /* Window 0 - setup */
119 Wsetup = 0x0000,
120 /* registers */
121 ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
122 ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
123 ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
124 AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
125 ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
126 EepromCommand = 0x000A,
127 EepromData = 0x000C,
128 /* AddressConfig Bits */
129 autoSelect9 = 0x0080,
130 xcvrMask9 = 0xC000,
131 /* ConfigControl bits */
132 Ena = 0x0001,
133 base10TAvailable9 = 0x0200,
134 coaxAvailable9 = 0x1000,
135 auiAvailable9 = 0x2000,
136 /* EepromCommand bits */
137 EepromReadRegister = 0x0080,
138 EepromReadOffRegister = 0x00B0,
139 EepromRead8bRegister = 0x0230,
140 EepromBusy = 0x8000,
141 };
142
143 #define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a))
144 #define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy)
145 #define EEPROMDATA(port) ins((port)+EepromData)
146
147 enum { /* Window 1 - operating set */
148 Wop = 0x0001,
149 /* registers */
150 Fifo = 0x0000,
151 RxError = 0x0004, /* 3C59[0257] only */
152 RxStatus = 0x0008,
153 TIMER = 0x000A,
154 TxStatus = 0x000B,
155 TxFree = 0x000C,
156 /* RxError bits */
157 rxOverrun = 0x0001,
158 runtFrame = 0x0002,
159 alignmentError = 0x0004, /* Framing */
160 crcError = 0x0008,
161 oversizedFrame = 0x0010,
162 dribbleBits = 0x0080,
163 /* RxStatus bits */
164 rxBytes = 0x1FFF, /* 3C59[0257] mask */
165 rxBytes9 = 0x07FF, /* 3C5[078]9 mask */
166 rxError9 = 0x3800, /* 3C5[078]9 error mask */
167 rxOverrun9 = 0x0000,
168 oversizedFrame9 = 0x0800,
169 dribbleBits9 = 0x1000,
170 runtFrame9 = 0x1800,
171 alignmentError9 = 0x2000, /* Framing */
172 crcError9 = 0x2800,
173 rxError = 0x4000,
174 rxIncomplete = 0x8000,
175 /* TxStatus Bits */
176 txStatusOverflow = 0x0004,
177 maxCollisions = 0x0008,
178 txUnderrun = 0x0010,
179 txJabber = 0x0020,
180 interruptRequested = 0x0040,
181 txStatusComplete = 0x0080,
182 };
183
184 enum { /* Window 2 - station address */
185 Wstation = 0x0002,
186
187 ResetOp905B = 0x000C,
188 };
189
190 enum { /* Window 3 - FIFO management */
191 Wfifo = 0x0003,
192 /* registers */
193 InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
194 OtherInt = 0x0004, /* 3C59[0257] */
195 RomControl = 0x0006, /* 3C509B, 3C59[27] */
196 MacControl = 0x0006, /* 3C59[0257] */
197 ResetOptions = 0x0008, /* 3C59[0257] */
198 MediaOptions = 0x0008, /* 3C905B */
199 RxFree = 0x000A,
200 /* InternalConfig bits */
201 disableBadSsdDetect = 0x00000100,
202 ramLocation = 0x00000200, /* 0 external, 1 internal */
203 ramPartition5to3 = 0x00000000,
204 ramPartition3to1 = 0x00010000,
205 ramPartition1to1 = 0x00020000,
206 ramPartition3to5 = 0x00030000,
207 ramPartitionMask = 0x00030000,
208 xcvr10BaseT = 0x00000000,
209 xcvrAui = 0x00100000, /* 10BASE5 */
210 xcvr10Base2 = 0x00300000,
211 xcvr100BaseTX = 0x00400000,
212 xcvr100BaseFX = 0x00500000,
213 xcvrMii = 0x00600000,
214 xcvrMask = 0x00700000,
215 autoSelect = 0x01000000,
216 /* MacControl bits */
217 deferExtendEnable = 0x0001,
218 deferTIMERSelect = 0x001E, /* mask */
219 fullDuplexEnable = 0x0020,
220 allowLargePackets = 0x0040,
221 extendAfterCollision = 0x0080, /* 3C90xB */
222 flowControlEnable = 0x0100, /* 3C90xB */
223 vltEnable = 0x0200, /* 3C90xB */
224 /* ResetOptions bits */
225 baseT4Available = 0x0001,
226 baseTXAvailable = 0x0002,
227 baseFXAvailable = 0x0004,
228 base10TAvailable = 0x0008,
229 coaxAvailable = 0x0010,
230 auiAvailable = 0x0020,
231 miiConnector = 0x0040,
232 };
233
234 enum { /* Window 4 - diagnostic */
235 Wdiagnostic = 0x0004,
236 /* registers */
237 VcoDiagnostic = 0x0002,
238 FifoDiagnostic = 0x0004,
239 NetworkDiagnostic = 0x0006,
240 PhysicalMgmt = 0x0008,
241 MediaStatus = 0x000A,
242 BadSSD = 0x000C,
243 UpperBytesOk = 0x000D,
244 /* FifoDiagnostic bits */
245 txOverrun = 0x0400,
246 rxUnderrun = 0x2000,
247 receiving = 0x8000,
248 /* PhysicalMgmt bits */
249 mgmtClk = 0x0001,
250 mgmtData = 0x0002,
251 mgmtDir = 0x0004,
252 cat5LinkTestDefeat = 0x8000,
253 /* MediaStatus bits */
254 dataRate100 = 0x0002,
255 crcStripDisable = 0x0004,
256 enableSqeStats = 0x0008,
257 collisionDetect = 0x0010,
258 carrierSense = 0x0020,
259 jabberGuardEnable = 0x0040,
260 linkBeatEnable = 0x0080,
261 jabberDetect = 0x0200,
262 polarityReversed = 0x0400,
263 linkBeatDetect = 0x0800,
264 txInProg = 0x1000,
265 dcConverterEnabled = 0x4000,
266 auiDisable = 0x8000, /* 10BaseT transceiver selected */
267 };
268
269 enum { /* Window 5 - internal state */
270 Wstate = 0x0005,
271 /* registers */
272 TxStartThresh = 0x0000,
273 TxAvailableThresh = 0x0002,
274 RxEarlyThresh = 0x0006,
275 RxFilter = 0x0008,
276 InterruptEnable = 0x000A,
277 IndicationEnable = 0x000C,
278 };
279
280 enum { /* Window 6 - statistics */
281 Wstatistics = 0x0006,
282 /* registers */
283 CarrierLost = 0x0000,
284 SqeErrors = 0x0001,
285 MultipleColls = 0x0002,
286 SingleCollFrames = 0x0003,
287 LateCollisions = 0x0004,
288 RxOverruns = 0x0005,
289 FramesXmittedOk = 0x0006,
290 FramesRcvdOk = 0x0007,
291 FramesDeferred = 0x0008,
292 UpperFramesOk = 0x0009,
293 BytesRcvdOk = 0x000A,
294 BytesXmittedOk = 0x000C,
295 };
296
297 enum { /* Window 7 - bus master operations */
298 Wmaster = 0x0007,
299 /* registers */
300 MasterAddress = 0x0000,
301 MasterLen = 0x0006,
302 MasterStatus = 0x000C,
303 /* MasterStatus bits */
304 masterAbort = 0x0001,
305 targetAbort = 0x0002,
306 targetRetry = 0x0004,
307 targetDisc = 0x0008,
308 masterDownload = 0x1000,
309 masterUpload = 0x4000,
310 masterInProgress = 0x8000,
311
312 masterMask = 0xD00F,
313 };
314
315 enum { /* 3C90x extended register set */
316 TIMER905 = 0x001A, /* 8-bits */
317 TxStatus905 = 0x001B, /* 8-bits */
318 PktStatus = 0x0020, /* 32-bits */
319 DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */
320 FragAddr = 0x0028, /* 32-bits */
321 FragLen = 0x002C, /* 16-bits */
322 ListOffset = 0x002E, /* 8-bits */
323 TxFreeThresh = 0x002F, /* 8-bits */
324 UpPktStatus = 0x0030, /* 32-bits */
325 FreeTIMER = 0x0034, /* 16-bits */
326 UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */
327
328 /* PktStatus bits */
329 fragLast = 0x00000001,
330 dnCmplReq = 0x00000002,
331 dnStalled = 0x00000004,
332 upCompleteX = 0x00000008,
333 dnCompleteX = 0x00000010,
334 upRxEarlyEnable = 0x00000020,
335 armCountdown = 0x00000040,
336 dnInProg = 0x00000080,
337 counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */
338 countdownMode = 0x00000020,
339 /* UpPktStatus bits (dpd->control) */
340 upPktLenMask = 0x00001FFF,
341 upStalled = 0x00002000,
342 upError = 0x00004000,
343 upPktComplete = 0x00008000,
344 upOverrun = 0x00010000, /* RxError<<16 */
345 upRuntFrame = 0x00020000,
346 upAlignmentError = 0x00040000,
347 upCRCError = 0x00080000,
348 upOversizedFrame = 0x00100000,
349 upDribbleBits = 0x00800000,
350 upOverflow = 0x01000000,
351
352 dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */
353
354 updnLastFrag = 0x80000000, /* (dpd->len) */
355
356 Nup = 32,
357 Ndn = 64,
358 };
359
360 /*
361 * Up/Dn Packet Descriptors.
362 * The hardware info (np, control, addr, len) must be 8-byte aligned
363 * and this structure size must be a multiple of 8.
364 */
365 typedef struct Pd Pd;
366 typedef struct Pd {
367 ulong np; /* next pointer */
368 ulong control; /* FSH or UpPktStatus */
369 ulong addr;
370 ulong len;
371
372 Pd* next;
373 Block* bp;
374 } Pd;
375
376 typedef struct Ctlr Ctlr;
377 typedef struct Ctlr {
378 int port;
379 Pcidev* pcidev;
380 int irq;
381 Ctlr* next;
382 int active;
383 int did;
384
385 Lock wlock; /* window access */
386
387 int attached;
388 int busmaster;
389 Block* rbp; /* receive buffer */
390
391 Block* txbp; /* FIFO -based transmission */
392 int txthreshold;
393 int txbusy;
394
395 int nup; /* full-busmaster -based reception */
396 void* upbase;
397 Pd* upr;
398 Pd* uphead;
399
400 int ndn; /* full-busmaster -based transmission */
401 void* dnbase;
402 Pd* dnr;
403 Pd* dnhead;
404 Pd* dntail;
405 int dnq;
406
407 long interrupts; /* statistics */
408 long bogusinterrupts;
409 long timer[2];
410 long stats[BytesRcvdOk+3];
411
412 int upqmax;
413 int upqmaxhw;
414 ulong upinterrupts;
415 ulong upqueued;
416 ulong upstalls;
417 int dnqmax;
418 int dnqmaxhw;
419 ulong dninterrupts;
420 ulong dnqueued;
421
422 int xcvr; /* transceiver type */
423 int eepromcmd; /* EEPROM read command */
424 int rxstatus9; /* old-style RxStatus register */
425 int rxearly; /* RxEarlyThreshold */
426 int ts; /* threshold shift */
427 int upenabled;
428 int dnenabled;
429 ulong cbfnpa; /* CardBus functions */
430 ulong* cbfn;
431 } Ctlr;
432
433 static Ctlr* ctlrhead;
434 static Ctlr* ctlrtail;
435
436 static void
437 init905(Ctlr* ctlr)
438 {
439 Block *bp;
440 Pd *pd, *prev;
441
442 /*
443 * Create rings for the receive and transmit sides.
444 * Take care with alignment:
445 * make sure ring base is 8-byte aligned;
446 * make sure each entry is 8-byte aligned.
447 */
448 ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
449 ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
450
451 prev = ctlr->upr;
452 for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
453 pd->np = PADDR(&prev->np);
454 pd->control = 0;
455 bp = iallocb(sizeof(Etherpkt));
456 if(bp == nil)
457 panic("can't allocate ethernet receive ring");
458 pd->addr = PADDR(bp->rp);
459 pd->len = updnLastFrag|sizeof(Etherpkt);
460
461 pd->next = prev;
462 prev = pd;
463 pd->bp = bp;
464 }
465 ctlr->uphead = ctlr->upr;
466
467 ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
468 ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
469
470 prev = ctlr->dnr;
471 for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
472 pd->next = prev;
473 prev = pd;
474 }
475 ctlr->dnhead = ctlr->dnr;
476 ctlr->dntail = ctlr->dnr;
477 ctlr->dnq = 0;
478 }
479
480 static Block*
481 rbpalloc(Block* (*f)(int))
482 {
483 Block *bp;
484 ulong addr;
485
486 /*
487 * The receive buffers must be on a 32-byte
488 * boundary for EISA busmastering.
489 */
490 if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
491 addr = (ulong)bp->base;
492 addr = ROUNDUP(addr, 32);
493 bp->rp = (uchar*)addr;
494 }
495
496 return bp;
497 }
498
499 static uchar*
500 startdma(Ether* ether, ulong address)
501 {
502 int port, status, w;
503 uchar *wp;
504
505 port = ether->port;
506
507 w = (STATUS(port)>>13) & 0x07;
508 COMMAND(port, SelectRegisterWindow, Wmaster);
509
510 wp = KADDR(inl(port+MasterAddress));
511 status = ins(port+MasterStatus);
512 if(status & (masterInProgress|targetAbort|masterAbort))
513 print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
514 outs(port+MasterStatus, masterMask);
515 outl(port+MasterAddress, address);
516 outs(port+MasterLen, sizeof(Etherpkt));
517 COMMAND(port, StartDma, Upload);
518
519 COMMAND(port, SelectRegisterWindow, w);
520 return wp;
521 }
522
523 static void
524 promiscuous(void* arg, int on)
525 {
526 int filter, port;
527 Ether *ether;
528
529 ether = (Ether*)arg;
530 port = ether->port;
531
532 filter = receiveBroadcast|receiveIndividual;
533 if(ether->nmaddr)
534 filter |= receiveMulticast;
535 if(on)
536 filter |= receiveAllFrames;
537 COMMAND(port, SetRxFilter, filter);
538 }
539
540 static void
541 multicast(void* arg, uchar *addr, int on)
542 {
543 int filter, port;
544 Ether *ether;
545
546 USED(addr, on);
547
548 ether = (Ether*)arg;
549 port = ether->port;
550
551 filter = receiveBroadcast|receiveIndividual;
552 if(ether->nmaddr)
553 filter |= receiveMulticast;
554 if(ether->prom)
555 filter |= receiveAllFrames;
556 COMMAND(port, SetRxFilter, filter);
557 }
558
559 /* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
560 static void
561 intrackcb(ulong *cbfn)
562 {
563 cbfn[1] = 0x8000;
564 }
565
566 static void
567 attach(Ether* ether)
568 {
569 int port, x;
570 Ctlr *ctlr;
571
572 ctlr = ether->ctlr;
573 ilock(&ctlr->wlock);
574 if(ctlr->attached){
575 iunlock(&ctlr->wlock);
576 return;
577 }
578
579 port = ether->port;
580
581 /*
582 * Set the receiver packet filter for this and broadcast addresses,
583 * set the interrupt masks for all interrupts, enable the receiver
584 * and transmitter.
585 */
586 promiscuous(ether, ether->prom);
587
588 x = interruptMask;
589 if(ctlr->busmaster == 1)
590 x &= ~(rxEarly|rxComplete);
591 else{
592 if(ctlr->dnenabled)
593 x &= ~transferInt;
594 if(ctlr->upenabled)
595 x &= ~(rxEarly|rxComplete);
596 }
597 COMMAND(port, SetIndicationEnable, x);
598 COMMAND(port, SetInterruptEnable, x);
599 COMMAND(port, RxEnable, 0);
600 COMMAND(port, TxEnable, 0);
601
602 /*
603 * If this is a CardBus card, acknowledge any interrupts.
604 */
605 if(ctlr->cbfn != nil)
606 intrackcb(ctlr->cbfn);
607
608 /*
609 * Prime the busmaster channel for receiving directly into a
610 * receive packet buffer if necessary.
611 */
612 if(ctlr->busmaster == 1)
613 startdma(ether, PADDR(ctlr->rbp->rp));
614 else{
615 if(ctlr->upenabled)
616 outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
617 }
618
619 ctlr->attached = 1;
620 iunlock(&ctlr->wlock);
621 }
622
623 static void
624 statistics(Ether* ether)
625 {
626 int port, i, u, w;
627 Ctlr *ctlr;
628
629 port = ether->port;
630 ctlr = ether->ctlr;
631
632 /*
633 * 3C59[27] require a read between a PIO write and
634 * reading a statistics register.
635 */
636 w = (STATUS(port)>>13) & 0x07;
637 COMMAND(port, SelectRegisterWindow, Wstatistics);
638 STATUS(port);
639
640 for(i = 0; i < UpperFramesOk; i++)
641 ctlr->stats[i] += inb(port+i) & 0xFF;
642 u = inb(port+UpperFramesOk) & 0xFF;
643 ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
644 ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
645 ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
646 ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
647
648 switch(ctlr->xcvr){
649
650 case xcvrMii:
651 case xcvr100BaseTX:
652 case xcvr100BaseFX:
653 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
654 STATUS(port);
655 ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
656 break;
657 }
658
659 COMMAND(port, SelectRegisterWindow, w);
660 }
661
662 static void
663 txstart(Ether* ether)
664 {
665 int port, len;
666 Ctlr *ctlr;
667 Block *bp;
668
669 port = ether->port;
670 ctlr = ether->ctlr;
671
672 /*
673 * Attempt to top-up the transmit FIFO. If there's room simply
674 * stuff in the packet length (unpadded to a dword boundary), the
675 * packet data (padded) and remove the packet from the queue.
676 * If there's no room post an interrupt for when there is.
677 * This routine is called both from the top level and from interrupt
678 * level and expects to be called with ctlr->wlock already locked
679 * and the correct register window (Wop) in place.
680 */
681 for(;;){
682 if(ctlr->txbp){
683 bp = ctlr->txbp;
684 ctlr->txbp = 0;
685 }
686 else{
687 bp = qget(ether->oq);
688 if(bp == nil)
689 break;
690 }
691
692 len = ROUNDUP(BLEN(bp), 4);
693 if(len+4 <= ins(port+TxFree)){
694 outl(port+Fifo, BLEN(bp));
695 outsl(port+Fifo, bp->rp, len/4);
696
697 freeb(bp);
698
699 ether->outpackets++;
700 }
701 else{
702 ctlr->txbp = bp;
703 if(ctlr->txbusy == 0){
704 ctlr->txbusy = 1;
705 COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
706 }
707 break;
708 }
709 }
710 }
711
712 static void
713 txstart905(Ether* ether)
714 {
715 Ctlr *ctlr;
716 int port, stalled, timeo;
717 Block *bp;
718 Pd *pd;
719
720 ctlr = ether->ctlr;
721 port = ether->port;
722
723 /*
724 * Free any completed packets.
725 */
726 pd = ctlr->dntail;
727 while(ctlr->dnq){
728 if(PADDR(&pd->np) == inl(port+DnListPtr))
729 break;
730 if(pd->bp){
731 freeb(pd->bp);
732 pd->bp = nil;
733 }
734 ctlr->dnq--;
735 pd = pd->next;
736 }
737 ctlr->dntail = pd;
738
739 stalled = 0;
740 while(ctlr->dnq < (ctlr->ndn-1)){
741 bp = qget(ether->oq);
742 if(bp == nil)
743 break;
744
745 pd = ctlr->dnhead->next;
746 pd->np = 0;
747 pd->control = dnIndicate|BLEN(bp);
748 pd->addr = PADDR(bp->rp);
749 pd->len = updnLastFrag|BLEN(bp);
750 pd->bp = bp;
751
752 if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
753 COMMAND(port, Stall, dnStall);
754 for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
755 ;
756 if(timeo == 0)
757 print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
758 stalled = 1;
759 }
760
761 coherence();
762 ctlr->dnhead->np = PADDR(&pd->np);
763 ctlr->dnhead->control &= ~dnIndicate;
764 ctlr->dnhead = pd;
765 if(ctlr->dnq == 0)
766 ctlr->dntail = pd;
767 ctlr->dnq++;
768
769 ctlr->dnqueued++;
770 }
771
772 if(ctlr->dnq > ctlr->dnqmax)
773 ctlr->dnqmax = ctlr->dnq;
774
775 /*
776 * If the adapter is not currently processing anything
777 * and there is something on the queue, start it processing.
778 */
779 if(inl(port+DnListPtr) == 0 && ctlr->dnq)
780 outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
781 if(stalled)
782 COMMAND(port, Stall, dnUnStall);
783 }
784
785 static void
786 transmit(Ether* ether)
787 {
788 Ctlr *ctlr;
789 int port, w;
790
791 port = ether->port;
792 ctlr = ether->ctlr;
793
794 ilock(&ctlr->wlock);
795 if(ctlr->dnenabled)
796 txstart905(ether);
797 else{
798 w = (STATUS(port)>>13) & 0x07;
799 COMMAND(port, SelectRegisterWindow, Wop);
800 txstart(ether);
801 COMMAND(port, SelectRegisterWindow, w);
802 }
803 iunlock(&ctlr->wlock);
804 }
805
806 static void
807 receive905(Ether* ether)
808 {
809 Ctlr *ctlr;
810 int len, port, q;
811 Pd *pd;
812 Block *bp;
813
814 ctlr = ether->ctlr;
815 port = ether->port;
816
817 if(inl(port+UpPktStatus) & upStalled)
818 ctlr->upstalls++;
819 q = 0;
820 for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
821 if(pd->control & upError){
822 if(pd->control & upOverrun)
823 ether->overflows++;
824 if(pd->control & (upOversizedFrame|upRuntFrame))
825 ether->buffs++;
826 if(pd->control & upAlignmentError)
827 ether->frames++;
828 if(pd->control & upCRCError)
829 ether->crcs++;
830 }
831 else if(bp = iallocb(sizeof(Etherpkt)+4)){
832 len = pd->control & rxBytes;
833 pd->bp->wp = pd->bp->rp+len;
834 etheriq(ether, pd->bp, 1);
835 pd->bp = bp;
836 pd->addr = PADDR(bp->rp);
837 coherence();
838 }
839
840 pd->control = 0;
841 COMMAND(port, Stall, upUnStall);
842
843 q++;
844 }
845 ctlr->uphead = pd;
846
847 ctlr->upqueued += q;
848 if(q > ctlr->upqmax)
849 ctlr->upqmax = q;
850 }
851
852 static void
853 receive(Ether* ether)
854 {
855 int len, port, rxerror, rxstatus;
856 Ctlr *ctlr;
857 Block *bp;
858
859 port = ether->port;
860 ctlr = ether->ctlr;
861
862 while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
863 if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
864 break;
865
866 /*
867 * If there was an error, log it and continue.
868 * Unfortunately the 3C5[078]9 has the error info in the status register
869 * and the 3C59[0257] implement a separate RxError register.
870 */
871 if(rxstatus & rxError){
872 if(ctlr->rxstatus9){
873 switch(rxstatus & rxError9){
874
875 case rxOverrun9:
876 ether->overflows++;
877 break;
878
879 case oversizedFrame9:
880 case runtFrame9:
881 ether->buffs++;
882 break;
883
884 case alignmentError9:
885 ether->frames++;
886 break;
887
888 case crcError9:
889 ether->crcs++;
890 break;
891
892 }
893 }
894 else{
895 rxerror = inb(port+RxError);
896 if(rxerror & rxOverrun)
897 ether->overflows++;
898 if(rxerror & (oversizedFrame|runtFrame))
899 ether->buffs++;
900 if(rxerror & alignmentError)
901 ether->frames++;
902 if(rxerror & crcError)
903 ether->crcs++;
904 }
905 }
906
907 /*
908 * If there was an error or a new receive buffer can't be
909 * allocated, discard the packet and go on to the next.
910 */
911 if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
912 COMMAND(port, RxDiscard, 0);
913 while(STATUS(port) & commandInProgress)
914 ;
915
916 if(ctlr->busmaster == 1)
917 startdma(ether, PADDR(ctlr->rbp->rp));
918
919 continue;
920 }
921
922 /*
923 * A valid receive packet awaits:
924 * if using PIO, read it into the buffer;
925 * discard the packet from the FIFO;
926 * if using busmastering, start a new transfer for
927 * the next packet and as a side-effect get the
928 * end-pointer of the one just received;
929 * pass the packet on to whoever wants it.
930 */
931 if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
932 len = (rxstatus & rxBytes9);
933 ctlr->rbp->wp = ctlr->rbp->rp + len;
934 insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
935 }
936
937 COMMAND(port, RxDiscard, 0);
938 while(STATUS(port) & commandInProgress)
939 ;
940
941 if(ctlr->busmaster == 1)
942 ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));
943
944 etheriq(ether, ctlr->rbp, 1);
945 ctlr->rbp = bp;
946 }
947 }
948
949 static int
950 ejectable(int did)
951 {
952 switch (did) {
953 case 0x5157:
954 return 1;
955
956 default:
957 return 0;
958 }
959 }
960
961 static void
962 interrupt(Ureg*, void* arg)
963 {
964 Ether *ether;
965 int port, status, s, txstatus, w, x;
966 Ctlr *ctlr;
967
968 ether = arg;
969 port = ether->port;
970 ctlr = ether->ctlr;
971
972 ilock(&ctlr->wlock);
973 status = STATUS(port);
974 if(!(status & (interruptMask|interruptLatch))){
975 ctlr->bogusinterrupts++;
976 iunlock(&ctlr->wlock);
977 return;
978 }
979 w = (status>>13) & 0x07;
980 COMMAND(port, SelectRegisterWindow, Wop);
981
982 ctlr->interrupts++;
983 if(ctlr->busmaster == 2)
984 ctlr->timer[0] += inb(port+TIMER905) & 0xFF;
985 else
986 ctlr->timer[0] += inb(port+TIMER) & 0xFF;
987
988 do{
989 if(status & hostError){
990 /*
991 * Adapter failure, try to find out why, reset if
992 * necessary. What happens if Tx is active and a reset
993 * occurs, need to retransmit? This probably isn't right.
994 */
995 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
996 x = ins(port+FifoDiagnostic);
997 COMMAND(port, SelectRegisterWindow, Wop);
998
999 if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {
1000 print("#l%d: Card ejected?\n", ether->ctlrno);
1001 iunlock(&ctlr->wlock);
1002 return;
1003 }
1004
1005 print("#l%d: status 0x%uX, diag 0x%uX\n",
1006 ether->ctlrno, status, x);
1007
1008 if(x & txOverrun){
1009 if(ctlr->busmaster == 0)
1010 COMMAND(port, TxReset, 0);
1011 else
1012 COMMAND(port, TxReset, (updnReset|dmaReset));
1013 COMMAND(port, TxEnable, 0);
1014 }
1015
1016 if(x & rxUnderrun){
1017 /*
1018 * This shouldn't happen...
1019 * Reset the receiver and restore the filter and RxEarly
1020 * threshold before re-enabling.
1021 * Need to restart any busmastering?
1022 */
1023 COMMAND(port, SelectRegisterWindow, Wstate);
1024 s = (port+RxFilter) & 0x000F;
1025 COMMAND(port, SelectRegisterWindow, Wop);
1026 COMMAND(port, RxReset, 0);
1027 while(STATUS(port) & commandInProgress)
1028 ;
1029 COMMAND(port, SetRxFilter, s);
1030 COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
1031 COMMAND(port, RxEnable, 0);
1032 }
1033
1034 status &= ~hostError;
1035 }
1036
1037 if(status & (transferInt|rxComplete)){
1038 receive(ether);
1039 status &= ~(transferInt|rxComplete);
1040 }
1041
1042 if(status & (upComplete)){
1043 COMMAND(port, AcknowledgeInterrupt, upComplete);
1044 receive905(ether);
1045 status &= ~upComplete;
1046 ctlr->upinterrupts++;
1047 }
1048
1049 if(status & txComplete){
1050 /*
1051 * Pop the TxStatus stack, accumulating errors.
1052 * Adjust the TX start threshold if there was an underrun.
1053 * If there was a Jabber or Underrun error, reset
1054 * the transmitter, taking care not to reset the dma logic
1055 * as a busmaster receive may be in progress.
1056 * For all conditions enable the transmitter.
1057 */
1058 if(ctlr->busmaster == 2)
1059 txstatus = port+TxStatus905;
1060 else
1061 txstatus = port+TxStatus;
1062 s = 0;
1063 do{
1064 if(x = inb(txstatus))
1065 outb(txstatus, 0);
1066 s |= x;
1067 }while(STATUS(port) & txComplete);
1068
1069 if(s & txUnderrun){
1070 if(ctlr->dnenabled){
1071 while(inl(port+PktStatus) & dnInProg)
1072 ;
1073 }
1074 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
1075 while(ins(port+MediaStatus) & txInProg)
1076 ;
1077 COMMAND(port, SelectRegisterWindow, Wop);
1078 if(ctlr->txthreshold < ETHERMAXTU)
1079 ctlr->txthreshold += ETHERMINTU;
1080 }
1081
1082 /*
1083 * According to the manual, maxCollisions does not require
1084 * a TxReset, merely a TxEnable. However, evidence points to
1085 * it being necessary on the 3C905. The jury is still out.
1086 * On busy or badly configured networks maxCollisions can
1087 * happen frequently enough for messages to be annoying so
1088 * keep quiet about them by popular request.
1089 */
1090 if(s & (txJabber|txUnderrun|maxCollisions)){
1091 if(ctlr->busmaster == 0)
1092 COMMAND(port, TxReset, 0);
1093 else
1094 COMMAND(port, TxReset, (updnReset|dmaReset));
1095 while(STATUS(port) & commandInProgress)
1096 ;
1097 COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
1098 if(ctlr->busmaster == 2)
1099 outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
1100 if(ctlr->dnenabled)
1101 status |= dnComplete;
1102 }
1103
1104 if(s & ~(txStatusComplete|maxCollisions))
1105 print("#l%d: txstatus 0x%uX, threshold %d\n",
1106 ether->ctlrno, s, ctlr->txthreshold);
1107 COMMAND(port, TxEnable, 0);
1108 ether->oerrs++;
1109 status &= ~txComplete;
1110 status |= txAvailable;
1111 }
1112
1113 if(status & txAvailable){
1114 COMMAND(port, AcknowledgeInterrupt, txAvailable);
1115 ctlr->txbusy = 0;
1116 txstart(ether);
1117 status &= ~txAvailable;
1118 }
1119
1120 if(status & dnComplete){
1121 COMMAND(port, AcknowledgeInterrupt, dnComplete);
1122 txstart905(ether);
1123 status &= ~dnComplete;
1124 ctlr->dninterrupts++;
1125 }
1126
1127 if(status & updateStats){
1128 statistics(ether);
1129 status &= ~updateStats;
1130 }
1131
1132 /*
1133 * Currently, this shouldn't happen.
1134 */
1135 if(status & rxEarly){
1136 COMMAND(port, AcknowledgeInterrupt, rxEarly);
1137 status &= ~rxEarly;
1138 }
1139
1140 /*
1141 * Panic if there are any interrupts not dealt with.
1142 */
1143 if(status & interruptMask)
1144 panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
1145
1146 COMMAND(port, AcknowledgeInterrupt, interruptLatch);
1147 if(ctlr->cbfn != nil)
1148 intrackcb(ctlr->cbfn);
1149
1150 }while((status = STATUS(port)) & (interruptMask|interruptLatch));
1151
1152 if(ctlr->busmaster == 2)
1153 ctlr->timer[1] += inb(port+TIMER905) & 0xFF;
1154 else
1155 ctlr->timer[1] += inb(port+TIMER) & 0xFF;
1156
1157 COMMAND(port, SelectRegisterWindow, w);
1158 iunlock(&ctlr->wlock);
1159 }
1160
1161 static long
1162 ifstat(Ether* ether, void* a, long n, ulong offset)
1163 {
1164 char *p;
1165 int len;
1166 Ctlr *ctlr;
1167
1168 if(n == 0)
1169 return 0;
1170
1171 ctlr = ether->ctlr;
1172
1173 ilock(&ctlr->wlock);
1174 statistics(ether);
1175 iunlock(&ctlr->wlock);
1176
1177 p = malloc(READSTR);
1178 len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
1179 len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts);
1180 len += snprint(p+len, READSTR-len, "timer: %lud %lud\n",
1181 ctlr->timer[0], ctlr->timer[1]);
1182 len += snprint(p+len, READSTR-len, "carrierlost: %lud\n",
1183 ctlr->stats[CarrierLost]);
1184 len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n",
1185 ctlr->stats[SqeErrors]);
1186 len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n",
1187 ctlr->stats[MultipleColls]);
1188 len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n",
1189 ctlr->stats[SingleCollFrames]);
1190 len += snprint(p+len, READSTR-len, "latecollisions: %lud\n",
1191 ctlr->stats[LateCollisions]);
1192 len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n",
1193 ctlr->stats[RxOverruns]);
1194 len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n",
1195 ctlr->stats[FramesXmittedOk]);
1196 len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n",
1197 ctlr->stats[FramesRcvdOk]);
1198 len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n",
1199 ctlr->stats[FramesDeferred]);
1200 len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n",
1201 ctlr->stats[BytesRcvdOk]);
1202 len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n",
1203 ctlr->stats[BytesRcvdOk+1]);
1204
1205 if(ctlr->upenabled){
1206 if(ctlr->upqmax > ctlr->upqmaxhw)
1207 ctlr->upqmaxhw = ctlr->upqmax;
1208 len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
1209 ctlr->upqueued, ctlr->upinterrupts,
1210 ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
1211 ctlr->upqmax = 0;
1212 }
1213 if(ctlr->dnenabled){
1214 if(ctlr->dnqmax > ctlr->dnqmaxhw)
1215 ctlr->dnqmaxhw = ctlr->dnqmax;
1216 len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
1217 ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
1218 ctlr->dnqmax = 0;
1219 }
1220
1221 snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);
1222
1223 n = readstr(offset, a, n, p);
1224 free(p);
1225
1226 return n;
1227 }
1228
1229 static void
1230 txrxreset(int port)
1231 {
1232 COMMAND(port, TxReset, 0);
1233 while(STATUS(port) & commandInProgress)
1234 ;
1235 COMMAND(port, RxReset, 0);
1236 while(STATUS(port) & commandInProgress)
1237 ;
1238 }
1239
1240 static Ctlr*
1241 tcmadapter(int port, int irq, Pcidev* pcidev)
1242 {
1243 Ctlr *ctlr;
1244
1245 ctlr = malloc(sizeof(Ctlr));
1246 ctlr->port = port;
1247 ctlr->irq = irq;
1248 ctlr->pcidev = pcidev;
1249 ctlr->eepromcmd = EepromReadRegister;
1250
1251 if(ctlrhead != nil)
1252 ctlrtail->next = ctlr;
1253 else
1254 ctlrhead = ctlr;
1255 ctlrtail = ctlr;
1256
1257 return ctlr;
1258 }
1259
1260 /*
1261 * Write two 0 bytes to identify the IDport and then reset the
1262 * ID sequence. Then send the ID sequence to the card to get
1263 * the card into command state.
1264 */
1265 static void
1266 idseq(void)
1267 {
1268 int i;
1269 uchar al;
1270 static int reset, untag;
1271
1272 /*
1273 * One time only:
1274 * reset any adapters listening
1275 */
1276 if(reset == 0){
1277 outb(IDport, 0);
1278 outb(IDport, 0);
1279 outb(IDport, 0xC0);
1280 delay(20);
1281 reset = 1;
1282 }
1283
1284 outb(IDport, 0);
1285 outb(IDport, 0);
1286 for(al = 0xFF, i = 0; i < 255; i++){
1287 outb(IDport, al);
1288 if(al & 0x80){
1289 al <<= 1;
1290 al ^= 0xCF;
1291 }
1292 else
1293 al <<= 1;
1294 }
1295
1296 /*
1297 * One time only:
1298 * write ID sequence to get the attention of all adapters;
1299 * untag all adapters.
1300 * If a global reset is done here on all adapters it will confuse
1301 * any ISA cards configured for EISA mode.
1302 */
1303 if(untag == 0){
1304 outb(IDport, 0xD0);
1305 untag = 1;
1306 }
1307 }
1308
1309 static ulong
1310 activate(void)
1311 {
1312 int i;
1313 ushort x, acr;
1314
1315 /*
1316 * Do the little configuration dance:
1317 *
1318 * 2. write the ID sequence to get to command state.
1319 */
1320 idseq();
1321
1322 /*
1323 * 3. Read the Manufacturer ID from the EEPROM.
1324 * This is done by writing the IDPort with 0x87 (0x80
1325 * is the 'read EEPROM' command, 0x07 is the offset of
1326 * the Manufacturer ID field in the EEPROM).
1327 * The data comes back 1 bit at a time.
1328 * A delay seems necessary between reading the bits.
1329 *
1330 * If the ID doesn't match, there are no more adapters.
1331 */
1332 outb(IDport, 0x87);
1333 delay(20);
1334 for(x = 0, i = 0; i < 16; i++){
1335 delay(20);
1336 x <<= 1;
1337 x |= inb(IDport) & 0x01;
1338 }
1339 if(x != 0x6D50)
1340 return 0;
1341
1342 /*
1343 * 3. Read the Address Configuration from the EEPROM.
1344 * The Address Configuration field is at offset 0x08 in the EEPROM).
1345 */
1346 outb(IDport, 0x88);
1347 for(acr = 0, i = 0; i < 16; i++){
1348 delay(20);
1349 acr <<= 1;
1350 acr |= inb(IDport) & 0x01;
1351 }
1352
1353 return (acr & 0x1F)*0x10 + 0x200;
1354 }
1355
1356 static void
1357 tcm509isa(void)
1358 {
1359 int irq, port;
1360
1361 /*
1362 * Attempt to activate all adapters. If adapter is set for
1363 * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
1364 * it fully.
1365 */
1366 while(port = activate()){
1367 if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
1368 print("tcm509isa: port 0x%uX in use\n", port);
1369 continue;
1370 }
1371
1372 /*
1373 * 6. Tag the adapter so it won't respond in future.
1374 */
1375 outb(IDport, 0xD1);
1376 if(port == 0x3F0){
1377 iofree(port);
1378 continue;
1379 }
1380
1381 /*
1382 * 6. Activate the adapter by writing the Activate command
1383 * (0xFF).
1384 */
1385 outb(IDport, 0xFF);
1386 delay(20);
1387
1388 /*
1389 * 8. Can now talk to the adapter's I/O base addresses.
1390 * Use the I/O base address from the acr just read.
1391 *
1392 * Enable the adapter and clear out any lingering status
1393 * and interrupts.
1394 */
1395 while(STATUS(port) & commandInProgress)
1396 ;
1397 COMMAND(port, SelectRegisterWindow, Wsetup);
1398 outs(port+ConfigControl, Ena);
1399
1400 txrxreset(port);
1401 COMMAND(port, AcknowledgeInterrupt, 0xFF);
1402
1403 irq = (ins(port+ResourceConfig)>>12) & 0x0F;
1404 tcmadapter(port, irq, nil);
1405 }
1406 }
1407
1408 static void
1409 tcm5XXeisa(void)
1410 {
1411 ushort x;
1412 int irq, port, slot;
1413
1414 /*
1415 * Check if this is an EISA machine.
1416 * If not, nothing to do.
1417 */
1418 if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
1419 return;
1420
1421 /*
1422 * Continue through the EISA slots looking for a match on both
1423 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
1424 * If an adapter is found, select window 0, enable it and clear
1425 * out any lingering status and interrupts.
1426 */
1427 for(slot = 1; slot < MaxEISA; slot++){
1428 port = slot*0x1000;
1429 if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
1430 print("tcm5XXeisa: port 0x%uX in use\n", port);
1431 continue;
1432 }
1433 if(ins(port+0xC80+ManufacturerID) != 0x6D50){
1434 iofree(port);
1435 continue;
1436 }
1437 x = ins(port+0xC80+ProductID);
1438 if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){
1439 iofree(port);
1440 continue;
1441 }
1442
1443 COMMAND(port, SelectRegisterWindow, Wsetup);
1444 outs(port+ConfigControl, Ena);
1445
1446 txrxreset(port);
1447 COMMAND(port, AcknowledgeInterrupt, 0xFF);
1448
1449 irq = (ins(port+ResourceConfig)>>12) & 0x0F;
1450 tcmadapter(port, irq, nil);
1451 }
1452 }
1453
1454 static void
1455 tcm59Xpci(void)
1456 {
1457 Pcidev *p;
1458 Ctlr *ctlr;
1459 int irq, port;
1460
1461 p = nil;
1462 while(p = pcimatch(p, 0x10B7, 0)){
1463 if(p->ccrb != 0x02 || p->ccru != 0)
1464 continue;
1465 /*
1466 * Not prepared to deal with memory-mapped
1467 * devices yet.
1468 */
1469 if(!(p->mem[0].bar & 0x01))
1470 continue;
1471 port = p->mem[0].bar & ~0x01;
1472 if((port = ioalloc((port == 0)? -1: port, p->mem[0].size,
1473 0, "tcm59Xpci")) < 0){
1474 print("tcm59Xpci: port 0x%uX in use\n", port);
1475 continue;
1476 }
1477 irq = p->intl;
1478
1479 txrxreset(port);
1480 COMMAND(port, AcknowledgeInterrupt, 0xFF);
1481
1482 ctlr = tcmadapter(port, irq, p);
1483 switch(p->did){
1484 default:
1485 break;
1486 case 0x5157:
1487 ctlr->eepromcmd = EepromRead8bRegister;
1488 ctlr->cbfnpa = p->mem[2].bar&~0x0F;
1489 ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
1490 break;
1491 case 0x6056:
1492 ctlr->eepromcmd = EepromReadOffRegister;
1493 ctlr->cbfnpa = p->mem[2].bar&~0x0F;
1494 ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
1495 break;
1496 }
1497 pcisetbme(p);
1498 }
1499 }
1500
1501 static char* tcmpcmcia[] = {
1502 "3C589", /* 3COM 589[ABCD] */
1503 "3C562", /* 3COM 562 */
1504 "589E", /* 3COM Megahertz 589E */
1505 nil,
1506 };
1507
1508 static Ctlr*
1509 tcm5XXpcmcia(Ether* ether)
1510 {
1511 int i;
1512 Ctlr *ctlr;
1513
1514 if(ether->type == nil)
1515 return nil;
1516
1517 for(i = 0; tcmpcmcia[i] != nil; i++){
1518 if(cistrcmp(ether->type, tcmpcmcia[i]))
1519 continue;
1520 ctlr = tcmadapter(ether->port, ether->irq, nil);
1521 ctlr->active = 1;
1522 return ctlr;
1523 }
1524
1525 return nil;
1526 }
1527
1528 static void
1529 setxcvr(Ctlr* ctlr, int xcvr)
1530 {
1531 int port, x;
1532
1533 port = ctlr->port;
1534 if(ctlr->rxstatus9){
1535 COMMAND(port, SelectRegisterWindow, Wsetup);
1536 x = ins(port+AddressConfig) & ~xcvrMask9;
1537 x |= (xcvr>>20)<<14;
1538 outs(port+AddressConfig, x);
1539 }
1540 else{
1541 COMMAND(port, SelectRegisterWindow, Wfifo);
1542 x = inl(port+InternalConfig) & ~xcvrMask;
1543 x |= xcvr;
1544 outl(port+InternalConfig, x);
1545 }
1546
1547 txrxreset(port);
1548 }
1549
1550 static void
1551 setfullduplex(int port)
1552 {
1553 int x;
1554
1555 COMMAND(port, SelectRegisterWindow, Wfifo);
1556 x = ins(port+MacControl);
1557 outs(port+MacControl, fullDuplexEnable|x);
1558
1559 txrxreset(port);
1560 }
1561
1562 static int
1563 miimdi(int port, int n)
1564 {
1565 int data, i;
1566
1567 /*
1568 * Read n bits from the MII Management Register.
1569 */
1570 data = 0;
1571 for(i = n-1; i >= 0; i--){
1572 if(ins(port) & mgmtData)
1573 data |= (1<<i);
1574 microdelay(1);
1575 outs(port, mgmtClk);
1576 microdelay(1);
1577 outs(port, 0);
1578 microdelay(1);
1579 }
1580
1581 return data;
1582 }
1583
1584 static void
1585 miimdo(int port, int bits, int n)
1586 {
1587 int i, mdo;
1588
1589 /*
1590 * Write n bits to the MII Management Register.
1591 */
1592 for(i = n-1; i >= 0; i--){
1593 if(bits & (1<<i))
1594 mdo = mgmtDir|mgmtData;
1595 else
1596 mdo = mgmtDir;
1597 outs(port, mdo);
1598 microdelay(1);
1599 outs(port, mdo|mgmtClk);
1600 microdelay(1);
1601 outs(port, mdo);
1602 microdelay(1);
1603 }
1604 }
1605
1606 static int
1607 miir(int port, int phyad, int regad)
1608 {
1609 int data, w;
1610
1611 w = (STATUS(port)>>13) & 0x07;
1612 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
1613 port += PhysicalMgmt;
1614
1615 /*
1616 * Preamble;
1617 * ST+OP+PHYAD+REGAD;
1618 * TA + 16 data bits.
1619 */
1620 miimdo(port, 0xFFFFFFFF, 32);
1621 miimdo(port, 0x1800|(phyad<<5)|regad, 14);
1622 data = miimdi(port, 18);
1623
1624 port -= PhysicalMgmt;
1625 COMMAND(port, SelectRegisterWindow, w);
1626
1627 if(data & 0x10000)
1628 return -1;
1629
1630 return data & 0xFFFF;
1631 }
1632
1633 static int
1634 scanphy(int port)
1635 {
1636 int i, x;
1637
1638 for(i = 0; i < 32; i++){
1639 if((x = miir(port, i, 2)) == -1 || x == 0)
1640 continue;
1641 x <<= 6;
1642 x |= miir(port, i, 3)>>10;
1643 XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
1644 USED(x);
1645
1646 return i;
1647 }
1648 return 24;
1649 }
1650
1651 static struct {
1652 char *name;
1653 int avail;
1654 int xcvr;
1655 } media[] = {
1656 "10BaseT", base10TAvailable, xcvr10BaseT,
1657 "10Base2", coaxAvailable, xcvr10Base2,
1658 "100BaseTX", baseTXAvailable, xcvr100BaseTX,
1659 "100BaseFX", baseFXAvailable, xcvr100BaseFX,
1660 "aui", auiAvailable, xcvrAui,
1661 "mii", miiConnector, xcvrMii
1662 };
1663
1664 static int
1665 autoselect(Ctlr* ctlr)
1666 {
1667 int media, port, x;
1668
1669 /*
1670 * Pathetic attempt at automatic media selection.
1671 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
1672 * cards operational.
1673 * It's a bonus if it works for anything else.
1674 */
1675 port = ctlr->port;
1676 if(ctlr->rxstatus9){
1677 COMMAND(port, SelectRegisterWindow, Wsetup);
1678 x = ins(port+ConfigControl);
1679 media = 0;
1680 if(x & base10TAvailable9)
1681 media |= base10TAvailable;
1682 if(x & coaxAvailable9)
1683 media |= coaxAvailable;
1684 if(x & auiAvailable9)
1685 media |= auiAvailable;
1686 }
1687 else{
1688 COMMAND(port, SelectRegisterWindow, Wfifo);
1689 media = ins(port+ResetOptions);
1690 }
1691 XCVRDEBUG("autoselect: media %uX\n", media);
1692
1693 if(media & miiConnector)
1694 return xcvrMii;
1695
1696 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
1697 XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
1698
1699 if(media & baseTXAvailable){
1700 /*
1701 * Must have InternalConfig register.
1702 */
1703 setxcvr(ctlr, xcvr100BaseTX);
1704
1705 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
1706 x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
1707 outs(port+MediaStatus, linkBeatEnable|x);
1708 delay(10);
1709
1710 if(ins(port+MediaStatus) & linkBeatDetect)
1711 return xcvr100BaseTX;
1712 outs(port+MediaStatus, x);
1713 }
1714
1715 if(media & base10TAvailable){
1716 setxcvr(ctlr, xcvr10BaseT);
1717
1718 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
1719 x = ins(port+MediaStatus) & ~dcConverterEnabled;
1720 outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
1721 delay(100);
1722
1723 XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
1724 if(ins(port+MediaStatus) & linkBeatDetect)
1725 return xcvr10BaseT;
1726 outs(port+MediaStatus, x);
1727 }
1728
1729 /*
1730 * Botch.
1731 */
1732 return autoSelect;
1733 }
1734
1735 static int
1736 eepromdata(Ctlr* ctlr, int offset)
1737 {
1738 int port;
1739
1740 port = ctlr->port;
1741
1742 COMMAND(port, SelectRegisterWindow, Wsetup);
1743 while(EEPROMBUSY(port))
1744 ;
1745 EEPROMCMD(port, ctlr->eepromcmd, offset);
1746 while(EEPROMBUSY(port))
1747 ;
1748 return EEPROMDATA(port);
1749 }
1750
1751 static void
1752 resetctlr(Ctlr *ctlr)
1753 {
1754 int x, port = ctlr->port;
1755
1756 txrxreset(port);
1757 x = ins(port+ResetOp905B);
1758 XCVRDEBUG("905[BC] reset ops 0x%uX\n", x);
1759 x &= ~0x4010;
1760 if(ctlr->did == 0x5157){
1761 x |= 0x0010; /* Invert LED */
1762 outs(port+ResetOp905B, x);
1763 }
1764 if(ctlr->did == 0x6056){
1765 x |= 0x4000;
1766 outs(port+ResetOp905B, x);
1767
1768 COMMAND(port, SelectRegisterWindow, Wsetup);
1769 outs(port, 0x0800);
1770 }
1771 }
1772
1773 static void
1774 shutdown(Ether *ether)
1775 {
1776 print("etherelnk3 shutting down\n");
1777 resetctlr(ether->ctlr);
1778 }
1779
1780 int
1781 etherelnk3reset(Ether* ether)
1782 {
1783 char *p;
1784 Ctlr *ctlr;
1785 uchar ea[Eaddrlen];
1786 static int scandone;
1787 int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;
1788
1789 /*
1790 * Scan for adapter on PCI, EISA and finally
1791 * using the little ISA configuration dance.
1792 */
1793 if(scandone == 0){
1794 tcm59Xpci();
1795 tcm5XXeisa();
1796 tcm509isa();
1797 scandone = 1;
1798 }
1799
1800 /*
1801 * Any adapter matches if no ether->port is supplied,
1802 * otherwise the ports must match.
1803 */
1804 for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
1805 if(ctlr->active)
1806 continue;
1807 if(ether->port == 0 || ether->port == ctlr->port){
1808 ctlr->active = 1;
1809 break;
1810 }
1811 }
1812 if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)
1813 return -1;
1814
1815 ether->ctlr = ctlr;
1816 port = ctlr->port;
1817 ether->port = port;
1818 ether->irq = ctlr->irq;
1819 if(ctlr->pcidev != nil)
1820 ether->tbdf = ctlr->pcidev->tbdf;
1821 else
1822 ether->tbdf = BUSUNKNOWN;
1823
1824 /*
1825 * Read the DeviceID from the EEPROM, it's at offset 0x03,
1826 * and do something depending on capabilities.
1827 */
1828 switch(ctlr->did = eepromdata(ctlr, 0x03)){
1829 case 0x5157: /* 3C575 Cyclone */
1830 case 0x6056:
1831 /*FALLTHROUGH*/
1832 case 0x4500: /* 3C450 HomePNA Tornado */
1833 case 0x7646: /* 3CSOHO100-TX */
1834 case 0x9055: /* 3C905B-TX */
1835 case 0x9200: /* 3C905C-TX */
1836 case 0x9201: /* 3C920 */
1837 case 0x9805: /* 3C9805: 3C980-TX Python-T 10/100baseTX */
1838 /*FALLTHROUGH*/
1839 case 0x9000: /* 3C900-TPO */
1840 case 0x9001: /* 3C900-COMBO */
1841 case 0x9005: /* 3C900B-COMBO */
1842 case 0x9050: /* 3C905-TX */
1843 case 0x9051: /* 3C905-T4 */
1844 if(BUSTYPE(ether->tbdf) != BusPCI)
1845 goto buggery;
1846 ctlr->busmaster = 2;
1847 goto vortex;
1848 case 0x5900: /* 3C590-[TP|COMBO|TPO] */
1849 case 0x5920: /* 3C592-[TP|COMBO|TPO] */
1850 case 0x5950: /* 3C595-TX */
1851 case 0x5951: /* 3C595-T4 */
1852 case 0x5952: /* 3C595-MII */
1853 case 0x5970: /* 3C597-TX */
1854 case 0x5971: /* 3C597-T4 */
1855 case 0x5972: /* 3C597-MII */
1856 ctlr->busmaster = 1;
1857 vortex:
1858 COMMAND(port, SelectRegisterWindow, Wfifo);
1859 ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
1860 ctlr->rxearly = 8188;
1861 ctlr->rxstatus9 = 0;
1862 break;
1863 buggery:
1864 default:
1865 ctlr->busmaster = 0;
1866 COMMAND(port, SelectRegisterWindow, Wsetup);
1867 x = ins(port+AddressConfig);
1868 ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;
1869 if(x & autoSelect9)
1870 ctlr->xcvr |= autoSelect;
1871 ctlr->rxearly = 2044;
1872 ctlr->rxstatus9 = 1;
1873 break;
1874 }
1875 if(ctlr->rxearly >= 2048)
1876 ctlr->ts = 2;
1877
1878 /*
1879 * Check if the adapter's station address is to be overridden.
1880 * If not, read it from the EEPROM and set in ether->ea prior to
1881 * loading the station address in Wstation.
1882 * The EEPROM returns 16-bits at a time.
1883 */
1884 memset(ea, 0, Eaddrlen);
1885 if(memcmp(ea, ether->ea, Eaddrlen) == 0){
1886 for(i = 0; i < Eaddrlen/2; i++){
1887 x = eepromdata(ctlr, i);
1888 ether->ea[2*i] = x>>8;
1889 ether->ea[2*i+1] = x;
1890 }
1891 }
1892
1893 COMMAND(port, SelectRegisterWindow, Wstation);
1894 for(i = 0; i < Eaddrlen; i++)
1895 outb(port+i, ether->ea[i]);
1896
1897 /*
1898 * Enable the transceiver if necessary and determine whether
1899 * busmastering can be used. Due to bugs in the first revision
1900 * of the 3C59[05], don't use busmastering at 10Mbps.
1901 */
1902 XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);
1903
1904 /*
1905 * Allow user to specify desired media in plan9.ini
1906 */
1907 for(i = 0; i < ether->nopt; i++){
1908 if(cistrncmp(ether->opt[i], "media=", 6) != 0)
1909 continue;
1910 p = ether->opt[i]+6;
1911 for(j = 0; j < nelem(media); j++)
1912 if(cistrcmp(p, media[j].name) == 0)
1913 ctlr->xcvr = media[j].xcvr;
1914 }
1915
1916 /*
1917 * forgive me, but i am weak
1918 */
1919 switch(ctlr->did){
1920 default:
1921 if(ctlr->xcvr & autoSelect)
1922 ctlr->xcvr = autoselect(ctlr);
1923 break;
1924 case 0x5157:
1925 case 0x6056:
1926 case 0x4500:
1927 case 0x7646:
1928 case 0x9055:
1929 case 0x9200:
1930 case 0x9201:
1931 case 0x9805:
1932 ctlr->xcvr = xcvrMii;
1933 resetctlr(ctlr);
1934 break;
1935 }
1936 XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);
1937
1938 switch(ctlr->xcvr){
1939 case xcvrMii:
1940 /*
1941 * Quick hack.
1942 */
1943 if(ctlr->did == 0x5157)
1944 phyaddr = 0;
1945 else if(ctlr->did == 0x6056)
1946 phyaddr = scanphy(port);
1947 else
1948 phyaddr = 24;
1949 for(i = 0; i < 7; i++)
1950 XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
1951 XCVRDEBUG("\n");
1952
1953 for(timeo = 0; timeo < 30; timeo++){
1954 phystat = miir(port, phyaddr, 0x01);
1955 if(phystat & 0x20)
1956 break;
1957 XCVRDEBUG(" %2.2uX", phystat);
1958 delay(100);
1959 }
1960 XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
1961 XCVRDEBUG("\n");
1962
1963 anar = miir(port, phyaddr, 0x04);
1964 anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
1965 anar &= anlpar;
1966 miir(port, phyaddr, 0x00);
1967 XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
1968 anar, anlpar, miir(port, phyaddr, 0x00),
1969 miir(port, phyaddr, 0x01));
1970 for(i = 0; i < ether->nopt; i++){
1971 if(cistrcmp(ether->opt[i], "fullduplex") == 0)
1972 anar |= 0x0100;
1973 else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
1974 anar |= 0x0100;
1975 else if(cistrcmp(ether->opt[i], "force100") == 0)
1976 anar |= 0x0080;
1977 }
1978 XCVRDEBUG("mii anar: %uX\n", anar);
1979 if(anar & 0x0100){ /* 100BASE-TXFD */
1980 ether->mbps = 100;
1981 setfullduplex(port);
1982 }
1983 else if(anar & 0x0200){ /* 100BASE-T4 */
1984 /* nothing to do */
1985 }
1986 else if(anar & 0x0080) /* 100BASE-TX */
1987 ether->mbps = 100;
1988 else if(anar & 0x0040) /* 10BASE-TFD */
1989 setfullduplex(port);
1990 else{ /* 10BASE-T */
1991 /* nothing to do */
1992 }
1993 break;
1994 case xcvr100BaseTX:
1995 case xcvr100BaseFX:
1996 COMMAND(port, SelectRegisterWindow, Wfifo);
1997 x = inl(port+InternalConfig) & ~ramPartitionMask;
1998 outl(port+InternalConfig, x|ramPartition1to1);
1999
2000 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
2001 x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
2002 x |= linkBeatEnable;
2003 outs(port+MediaStatus, x);
2004
2005 if(x & dataRate100)
2006 ether->mbps = 100;
2007 break;
2008 case xcvr10BaseT:
2009 /*
2010 * Enable Link Beat and Jabber to start the
2011 * transceiver.
2012 */
2013 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
2014 x = ins(port+MediaStatus) & ~dcConverterEnabled;
2015 x |= linkBeatEnable|jabberGuardEnable;
2016 outs(port+MediaStatus, x);
2017
2018 if((ctlr->did & 0xFF00) == 0x5900)
2019 ctlr->busmaster = 0;
2020 break;
2021 case xcvr10Base2:
2022 COMMAND(port, SelectRegisterWindow, Wdiagnostic);
2023 x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
2024 outs(port+MediaStatus, x);
2025
2026 /*
2027 * Start the DC-DC converter.
2028 * Wait > 800 microseconds.
2029 */
2030 COMMAND(port, EnableDcConverter, 0);
2031 delay(1);
2032 break;
2033 }
2034
2035 /*
2036 * Wop is the normal operating register set.
2037 * The 3C59[0257] adapters allow access to more than one register window
2038 * at a time, but there are situations where switching still needs to be
2039 * done, so just do it.
2040 * Clear out any lingering Tx status.
2041 */
2042 COMMAND(port, SelectRegisterWindow, Wop);
2043 if(ctlr->busmaster == 2)
2044 x = port+TxStatus905;
2045 else
2046 x = port+TxStatus;
2047 while(inb(x))
2048 outb(x, 0);
2049
2050 /*
2051 * Clear out the
2052 * adapter statistics, clear the statistics logged into ctlr
2053 * and enable statistics collection.
2054 */
2055 ilock(&ctlr->wlock);
2056 statistics(ether);
2057 memset(ctlr->stats, 0, sizeof(ctlr->stats));
2058
2059 COMMAND(port, StatisticsEnable, 0);
2060
2061 /*
2062 * Allocate any receive buffers.
2063 */
2064 switch(ctlr->busmaster){
2065 case 2:
2066 ctlr->dnenabled = 1;
2067
2068 /*
2069 * 10MUpldBug.
2070 * Disabling is too severe, can use receive busmastering at
2071 * 100Mbps OK, but how to tell which rate is actually being used -
2072 * the 3c905 always seems to have dataRate100 set?
2073 * Believe the bug doesn't apply if upRxEarlyEnable is set
2074 * and the threshold is set such that uploads won't start
2075 * until the whole packet has been received.
2076 */
2077 ctlr->upenabled = 1;
2078 x = eepromdata(ctlr, 0x0F);
2079 if(!(x & 0x01))
2080 outl(port+PktStatus, upRxEarlyEnable);
2081
2082 if(ctlr->upenabled || ctlr->dnenabled){
2083 ctlr->nup = Nup;
2084 ctlr->ndn = Ndn;
2085 init905(ctlr);
2086 }
2087 else {
2088 ctlr->rbp = rbpalloc(iallocb);
2089 if(ctlr->rbp == nil)
2090 panic("can't reset ethernet: out of memory");
2091 }
2092 outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
2093 break;
2094 default:
2095 ctlr->rbp = rbpalloc(iallocb);
2096 if(ctlr->rbp == nil)
2097 panic("can't reset ethernet: out of memory");
2098 break;
2099 }
2100
2101 /*
2102 * Set a base TxStartThresh which will be incremented
2103 * if any txUnderrun errors occur and ensure no RxEarly
2104 * interrupts happen.
2105 */
2106 ctlr->txthreshold = ETHERMAXTU/2;
2107 COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
2108 COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
2109
2110 iunlock(&ctlr->wlock);
2111
2112 /*
2113 * Linkage to the generic ethernet driver.
2114 */
2115 ether->attach = attach;
2116 ether->transmit = transmit;
2117 ether->interrupt = interrupt;
2118 ether->ifstat = ifstat;
2119
2120 ether->promiscuous = promiscuous;
2121 ether->multicast = multicast;
2122 ether->shutdown = shutdown;
2123 ether->arg = ether;
2124
2125 return 0;
2126 }
2127
2128 void
2129 etherelnk3link(void)
2130 {
2131 addethercard("elnk3", etherelnk3reset);
2132 addethercard("3C509", etherelnk3reset);
2133 addethercard("3C575", etherelnk3reset);
2134 }
Cache object: d66da89fc987fc760b24f7bf337956ee
|