The Pedigree Project 0.1
3Com90x.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
25/*
26 * 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
27 * by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
28 * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
29 *
30 * This program Copyright (C) 1999 LightSys Technology Services, Inc.
31 * Portions Copyright (C) 1999 Steve Smith
32 *
33 * This program may be re-distributed in source or binary form, modified,
34 * sold, or copied for any purpose, provided that the above copyright message
35 * and this text are included with all source copies or derivative works, and
36 * provided that the above copyright message and this text are included in the
37 * documentation of any binary-only distributions. This program is distributed
38 * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
39 * PURPOSE or MERCHANTABILITY. Please read the associated documentation
40 * "3c90x.txt" before compiling and using this driver.
41 *
42 * --------
43 *
44 * Program written with the assistance of the 3com documentation for
45 * the 3c905B-TX card, as well as with some assistance from the 3c59x
46 * driver Donald Becker wrote for the Linux kernel, and with some assistance
47 * from the remainder of the Etherboot distribution.
48 *
49 * REVISION HISTORY:
50 *
51 * v0.10 1-26-1998 GRB Initial implementation.
52 * v0.90 1-27-1998 GRB System works.
53 * v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
54 * v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
55 * Re-wrote poll and transmit for
56 * better error recovery and heavy
57 * network traffic operation
58 * v2.01 5-26-2993 NN Fixed driver alignment issue which
59 * caused system lockups if driver structures
60 * not 8-byte aligned.
61 *
62 */
63
64#include "3Com90x.h"
65#include "pedigree/kernel/Log.h"
66#include "pedigree/kernel/compiler.h"
67#include "pedigree/kernel/machine/Device.h"
68#include "pedigree/kernel/machine/IrqManager.h"
69#include "pedigree/kernel/machine/Machine.h"
70#include "pedigree/kernel/machine/Network.h"
71#include "pedigree/kernel/network/IpAddress.h"
72#include "pedigree/kernel/network/MacAddress.h"
73#include "pedigree/kernel/processor/IoBase.h"
74#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
75#include "pedigree/kernel/processor/VirtualAddressSpace.h"
76#include "pedigree/kernel/time/Time.h"
77#include "pedigree/kernel/utilities/utility.h"
78
79#include "3Com90xConstants.h"
80#include "modules/drivers/common/DmaBuffer.h"
81#include "modules/system/network-stack/NetworkStack.h"
82
83namespace {
84constexpr size_t CommandPollLimit = 100;
85constexpr size_t ResetCommandPollLimit = 5000;
86constexpr size_t DownloadPollLimit = 100;
87constexpr size_t EepromPollLimit = 100;
88constexpr uint32_t UpPacketComplete = 1U << 15U;
89constexpr uint32_t UpPacketError = 1U << 14U;
90constexpr uint32_t UpPacketLengthMask = 0x1FFF;
91constexpr size_t Dma32Constraints =
93} // namespace
94
95bool Nic3C90x::issueCommand(int cmd, int param) {
96 const size_t pollLimit =
97 (cmd == cmdGlobalReset || cmd == cmdRxReset) ? ResetCommandPollLimit : CommandPollLimit;
98 const auto waitUntilIdle = [this, pollLimit]() {
99 for (size_t poll = 0; poll < pollLimit; ++poll) {
100 if (!(m_pBase->read16(regCommandIntStatus_w) & INT_CMDINPROGRESS))
101 return true;
102
103 if (!Time::delay(Time::Multiplier::Millisecond))
104 break;
105 }
106 return false;
107 };
108
109 // A timed-out predecessor may still own the command register. Never
110 // overwrite it while trying to recover or quiesce the controller.
111 if (!waitUntilIdle()) {
112 ERROR("3C90x: command register remained busy before command " << Hex << cmd);
113 return false;
114 }
115
117 uint32_t val = cmd;
118 val <<= 11;
119 val |= param;
120
122 m_pBase->write16(val, regCommandIntStatus_w);
123
125 if (waitUntilIdle())
126 return true;
127
128 ERROR("3C90x: command " << Hex << cmd << " timed out");
129 return false;
130}
131
132int Nic3C90x::setWindow(int window) {
134 if (m_CurrentWindow == window)
135 return 0;
136
138 if (!issueCommand(cmdSelectRegisterWindow, window))
139 return -1;
140 m_CurrentWindow = window;
141
142 return 0;
143}
144
145bool Nic3C90x::waitForEepromReady() {
146 for (size_t poll = 0; poll < EepromPollLimit; ++poll) {
147 if (!(m_pBase->read16(regEepromCommand_0_w) & (1U << 15U)))
148 return true;
149
150 if (!Time::delay(Time::Multiplier::Millisecond))
151 break;
152 }
153
154 ERROR("3C90x: EEPROM command timed out");
155 return false;
156}
157
158bool Nic3C90x::readEeprom(int address, uint16_t& value) {
160 if (setWindow(winEepromBios0) < 0)
161 return false;
162
164 if (!waitForEepromReady())
165 return false;
166
168 m_pBase->write16(address + (0x02 << 6), regEepromCommand_0_w);
169 if (!waitForEepromReady())
170 return false;
171
172 value = m_pBase->read16(regEepromData_0_w);
173 return true;
174}
175
176int Nic3C90x::writeEepromWord(int address, uint16_t value) {
178 if (setWindow(winEepromBios0) < 0)
179 return -1;
180
182 if (!waitForEepromReady())
183 return -1;
184
186 m_pBase->write16(0x30, regEepromCommand_0_w);
187 if (!waitForEepromReady())
188 return -1;
189
191 m_pBase->write16(address + (0x03 << 6), regEepromCommand_0_w);
192 if (!waitForEepromReady())
193 return -1;
194
196 m_pBase->write16(value, regEepromData_0_w);
197 m_pBase->write16(0x30, regEepromCommand_0_w);
198 if (!waitForEepromReady())
199 return -1;
200
202 m_pBase->write16(address + (0x01 << 6), regEepromCommand_0_w);
203 if (!waitForEepromReady())
204 return -1;
205
206 return 0;
207}
208
209int Nic3C90x::writeEeprom(int address, uint16_t value) {
210 int cksum = 0;
211 uint16_t v = 0;
212 int i;
213 int maxAddress, cksumAddress;
214
215 if (m_isBrev) {
216 maxAddress = 0x1f;
217 cksumAddress = 0x20;
218 } else {
219 maxAddress = 0x16;
220 cksumAddress = 0x17;
221 }
222
224 if (writeEepromWord(address, value) == -1)
225 return -1;
226
228 for (i = 0; i <= maxAddress; i++) {
229 if (!readEeprom(i, v))
230 return -1;
231 cksum ^= (v & 0xff);
232 cksum ^= ((v >> 8) & 0xff);
233 }
234
236 if (writeEepromWord(cksumAddress, cksum) == -1)
237 return -1;
238
239 return 0;
240}
241
243#ifdef CFG_3C90X_PRESERVE_XCVR
244 int cfg;
245
247 if (setWindow(winTxRxOptions3) < 0)
248 return false;
249 cfg = m_pBase->read32(regInternalConfig_3_l);
250#endif
251
253 NOTICE("3C90x: Issuing RESET");
254 if (!issueCommand(cmdGlobalReset, 0))
255 return false;
256
257 // Global reset selects window zero independently of the software cache.
258 m_CurrentWindow = 0;
259
263 if (setWindow(winAddressing2) < 0)
264 return false;
265 m_pBase->write16(0, regStationAddress_2_3w + 0);
266 m_pBase->write16(0, regStationAddress_2_3w + 2);
267 m_pBase->write16(0, regStationAddress_2_3w + 4);
268
269#ifdef CFG_3C90X_PRESERVE_XCVR
271 if (setWindow(winTxRxOptions3) < 0)
272 return false;
273 m_pBase->write32(cfg, regInternalConfig_3_l);
274
276 if ((cfg & 0x0300) == 0x0300)
277 if (!issueCommand(cmdEnableDcConverter, 0))
278 return false;
279#endif
280
282 if (!issueCommand(cmdTxReset, 0))
283 return false;
284
285 if (!m_isBrev)
286 m_pBase->write8(0x01, regTxFreeThresh_b);
287 if (!issueCommand(cmdTxEnable, 0))
288 return false;
289
293 if (m_isBrev) {
294 if (!issueCommand(cmdRxReset, 0x04))
295 return false;
296 } else {
297 if (!issueCommand(cmdRxReset, 0x00))
298 return false;
299 }
300
301 if (!issueCommand(cmdRxEnable, 0))
302 return false;
303
305 if (!issueCommand(cmdSetInterruptEnable, 0) ||
306 !issueCommand(cmdSetIndicationEnable, ENABLED_INTS) ||
307 !issueCommand(cmdAcknowledgeInterrupt, 0xff)) {
308 return false;
309 }
310
311 return true;
312}
313
314bool Nic3C90x::quiesce() {
315 if (!issueCommand(cmdSetInterruptEnable, 0) || !issueCommand(cmdSetIndicationEnable, 0)) {
316 return false;
317 }
318
319 // Stop both bus-master engines before their descriptor regions can leave
320 // this object's lifetime.
321 if (!issueCommand(cmdRxDisable, 0) || !issueCommand(cmdTxDisable, 0) ||
322 !issueCommand(cmdStallCtl, 0) || !issueCommand(cmdStallCtl, 2)) {
323 return false;
324 }
325
326 m_pBase->write32(0, regUpListPtr_l);
327 m_pBase->write32(0, regDnListPtr_l);
328 return true;
329}
330
331bool Nic3C90x::stopDeviceLocked() {
332 if (m_Stopping)
333 return true;
334
335 // Publish callback closure only after this device can no longer assert the
336 // shared line or access its descriptor and packet storage.
337 if (!quiesce())
338 return false;
339
340 m_Active = false;
341 m_Stopping = true;
342 m_TxSuccessful = false;
343 m_TxMutex.release();
344 return true;
345}
346
347bool Nic3C90x::send(size_t nBytes, uintptr_t buffer) {
348 if (!buffer || !nBytes || nBytes > UpPacketLengthMask) {
349 ERROR("3C90x: invalid transmit packet length " << Dec << nBytes << Hex);
350 return false;
351 }
352
353 LockGuard<Mutex> sendGuard(m_SendLock);
354
355 {
356 LockGuard<Mutex> deviceGuard(m_DeviceLock);
357 if (!m_Active || m_Stopping)
358 return false;
359
361 if (!issueCommand(cmdStallCtl, 2)) {
362 if (!stopDeviceLocked())
363 FATAL("3C90x could not halt DMA after a command timeout.");
364 return false;
365 }
366
367 // A completion belongs only to the descriptor published below.
368 // Serialised senders should leave no credit, but drain defensively so a
369 // late error status can never release a future caller's buffer.
370 const size_t discardedCompletions = m_TxMutex.drainAvailable();
371 (void)discardedCompletions;
372 m_TxSuccessful = false;
373
374 // The 3C90x descriptor has one 32-bit data pointer, not scatter/gather.
375 // Keep arbitrary caller mappings out of that contract by using the
376 // controller-owned contiguous bounce buffer for every transmission.
377 MemoryCopy(m_pTxBuffVirt, reinterpret_cast<void*>(buffer), nBytes);
378 const physical_uintptr_t destPtr = m_pTxBuffPhys;
379
381 m_TransmitDPD->DnNextPtr = 0;
382
384 m_TransmitDPD->FrameStartHeader = nBytes | 0x8000;
385 // m_TransmitDPD->HdrAddr = m_pTxBuffPhys;
386 // m_TransmitDPD->HdrLength = Ethernet::instance().ethHeaderSize();
387 m_TransmitDPD->DataAddr =
388 static_cast<uint32_t>(destPtr); // m_pTxBuffPhys; // + m_TransmitDPD->HdrLength;
389 m_TransmitDPD->DataLength = (nBytes /* - m_TransmitDPD->HdrLength */) + (1U << 31U);
390
392 FENCE();
393 m_pBase->write32(m_pDPD, regDnListPtr_l);
394
396 if (!issueCommand(cmdStallCtl, 3)) {
397 if (!stopDeviceLocked())
398 FATAL("3C90x could not halt DMA after a command timeout.");
399 return false;
400 }
401
402 bool downloadComplete = false;
403 for (size_t poll = 0; poll < DownloadPollLimit; ++poll) {
404 if (!m_pBase->read32(regDnListPtr_l)) {
405 downloadComplete = true;
406 break;
407 }
408
409 if (!Time::delay(Time::Multiplier::Millisecond))
410 break;
411 }
412 if (!downloadComplete) {
413 ERROR("3C90x: packet download timed out");
414 if (!stopDeviceLocked())
415 FATAL("3C90x could not halt DMA after a download timeout.");
416 return false;
417 }
418 }
419
420 // The card may still DMA from the caller's buffer after an interruptible
421 // wake. Do not return until the IRQ has transferred completion ownership.
422 if (!m_TxMutex.acquireForCompletion(1, 1, 0)) {
423 LockGuard<Mutex> deviceGuard(m_DeviceLock);
424 if (m_Active && !m_Stopping && !stopDeviceLocked())
425 FATAL("3C90x could not halt DMA after a transmit timeout.");
426 return false;
427 }
428
429 LockGuard<Mutex> deviceGuard(m_DeviceLock);
430 return m_Active && !m_Stopping && m_TxSuccessful;
431}
432
434 : Network(pDev),
435 m_pBase(0),
436 m_isBrev(0),
437 m_CurrentWindow(0),
438 m_pRxBuffVirt(0),
439 m_pTxBuffVirt(0),
440 m_pRxBuffPhys(0),
441 m_pTxBuffPhys(0),
442 m_RxBuffMR("3c90x-rxbuffer"),
443 m_TxBuffMR("3c90x-txbuffer"),
444 m_pDPD(0),
445 m_DPDMR("3c90x-dpd"),
446 m_pUPD(0),
447 m_UPDMR("3c90x-upd"),
448 m_TransmitDPD(0),
449 m_ReceiveUPD(0),
450 m_TxMutex(0),
451 m_DeviceLock(),
452 m_SendLock(),
453 m_RxConsumerIndex(0),
454 m_IrqId(0),
455 m_Active(false),
456 m_Stopping(false),
457 m_TxSuccessful(false),
458 m_Initialised(false) {
459 setSpecificType(String("3c90x-card"));
460
461 int i, c;
462 uint16_t eeprom[0x21];
463 uint32_t cfg;
464 uint32_t mopt;
465 uint32_t mstat;
466 uint16_t linktype;
467#define HWADDR_OFFSET 10
468
469 // allocate the rx and tx buffers
470 const size_t packetBufferPages = DriverDma::pageCountForBytes(MAX_PACKET_SIZE);
471 if (!PhysicalMemoryManager::instance().allocateRegion(
472 m_RxBuffMR, packetBufferPages, Dma32Constraints, VirtualAddressSpace::Write, -1)) {
473 ERROR("3C90x: Couldn't allocate Rx Buffer!");
474 return;
475 }
476 if (!PhysicalMemoryManager::instance().allocateRegion(
477 m_TxBuffMR, packetBufferPages, Dma32Constraints, VirtualAddressSpace::Write, -1)) {
478 ERROR("3C90x: Couldn't allocate Tx Buffer!");
479 return;
480 }
481 m_pRxBuffVirt = static_cast<uint8_t*>(m_RxBuffMR.virtualAddress());
482 m_pTxBuffVirt = static_cast<uint8_t*>(m_TxBuffMR.virtualAddress());
483 m_pRxBuffPhys = m_RxBuffMR.physicalAddress();
484 m_pTxBuffPhys = m_TxBuffMR.physicalAddress();
485
486 if (!PhysicalMemoryManager::instance().allocateRegion(m_DPDMR, 2, Dma32Constraints,
488 ERROR("3C90x: Couldn't allocated buffer for DPD\n");
489 return;
490 }
491 if (!PhysicalMemoryManager::instance().allocateRegion(m_UPDMR, 2, Dma32Constraints,
493 ERROR("3C90x: Couldn't allocated buffer for UPD\n");
494 return;
495 }
496 m_pDPD = m_DPDMR.physicalAddress();
497 m_pUPD = m_UPDMR.physicalAddress();
498 m_TransmitDPD = reinterpret_cast<TXD*>(m_DPDMR.virtualAddress());
499 m_ReceiveUPD = reinterpret_cast<RXD*>(m_UPDMR.virtualAddress());
500
501 // configure the UPD... turn it into a list with a well-defined beginning
502 // and end
503 for (size_t iUpd = 0; iUpd < NUM_UPDS; iUpd++) {
504 if ((iUpd + 1) == NUM_UPDS)
505 m_ReceiveUPD[iUpd].UpNextPtr = 0;
506 else
507 m_ReceiveUPD[iUpd].UpNextPtr = m_pUPD + ((iUpd + 1) * sizeof(RXD));
508 m_ReceiveUPD[iUpd].UpPktStatus = 0;
509 m_ReceiveUPD[iUpd].DataAddr = m_pRxBuffPhys + (iUpd * 1536);
510 m_ReceiveUPD[iUpd].DataLength = 1536 + (1U << 31U);
511 }
512
513 // grab the IO ports
514 m_pBase = m_Addresses[0]->m_Io;
515
516 m_CurrentWindow = 255;
517
518 if (!reset()) {
519 ERROR("3C90x: controller reset timed out");
520 return;
521 }
522
523 uint16_t productId = 0;
524 if (!readEeprom(0x03, productId)) {
525 ERROR("3C90x: failed to read product ID from EEPROM");
526 return;
527 }
528
529 switch (productId) {
530 case 0x9000:
531 case 0x9001:
532 case 0x9050:
533 case 0x9051:
534 m_isBrev = 0;
535 break;
536
537 case 0x9004:
538 case 0x9005:
539 case 0x9006:
540 case 0x900A:
541 case 0x9055:
542 case 0x9056:
543 case 0x905A:
544 default:
545 m_isBrev = 1;
546 break;
547 }
548
550 if (m_isBrev) {
551 for (i = 0; i < 0x20; i++) {
552 if (!readEeprom(i, eeprom[i])) {
553 ERROR("3C90x: failed to load EEPROM contents");
554 return;
555 }
556 }
557
558#ifdef CFG_3C90X_BOOTROM_FIX
560 /* only necessary for 3c905b revision cards with boot PROM bug!!! */
561 if (writeEeprom(0x13, 0x0160) < 0)
562 return;
563#endif
564
565#ifdef CFG_3C90X_XCVR
567 if (CFG_3C90X_XCVR == 255) {
568 if (writeEeprom(0x16, 0) < 0)
569 return;
570 }
571
575 else {
576 if (writeEeprom(0x16, XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F)) < 0)
577 return;
578 }
579#endif
580 } else {
581 for (i = 0; i < 0x17; i++) {
582 if (!readEeprom(i, eeprom[i])) {
583 ERROR("3C90x: failed to load EEPROM contents");
584 return;
585 }
586 }
587 }
588
590 m_StationInfo.mac.setMac(eeprom, true);
591 NOTICE("3C90x MAC: " << m_StationInfo.mac[0] << ":" << m_StationInfo.mac[1] << ":"
592 << m_StationInfo.mac[2] << ":" << m_StationInfo.mac[3] << ":"
593 << m_StationInfo.mac[4] << ":" << m_StationInfo.mac[5] << ".");
594
595 /* Test if the link is good, if so continue */
596 if (setWindow(winDiagnostics4) < 0)
597 return;
598 mstat = m_pBase->read16(regMediaStatus_4_w);
599 if ((mstat & (1 << 11)) == 0) {
600 ERROR("3C90x: Valid link not established");
601 return;
602 }
603
605 if (setWindow(winAddressing2) < 0)
606 return;
607 m_pBase->write16(HOST_TO_BIG16(eeprom[HWADDR_OFFSET + 0]), regStationAddress_2_3w);
608 m_pBase->write16(HOST_TO_BIG16(eeprom[HWADDR_OFFSET + 1]), regStationAddress_2_3w + 2);
609 m_pBase->write16(HOST_TO_BIG16(eeprom[HWADDR_OFFSET + 2]), regStationAddress_2_3w + 4);
610 m_pBase->write16(0, regStationMask_2_3w);
611 m_pBase->write16(0, regStationMask_2_3w + 2);
612 m_pBase->write16(0, regStationMask_2_3w + 4);
613
620 if (setWindow(winTxRxOptions3) < 0)
621 return;
622 mopt = m_pBase->read16(regResetMediaOptions_3_w);
623
625 if (!m_isBrev)
626 mopt &= 0x7f;
627
628 NOTICE("3C90x connectors present:");
629 c = 0;
630 linktype = 0x0008;
631 if (mopt & 0x01) {
632 NOTICE(((c++) ? ", " : "") << "100BASE-T4");
633 linktype = 0x0006;
634 }
635 if (mopt & 0x04) {
636 NOTICE(((c++) ? ", " : "") << "100BASE-FX");
637 linktype = 0x0005;
638 }
639 if (mopt & 0x10) {
640 NOTICE(((c++) ? ", " : "") << "10BASE2");
641 linktype = 0x0003;
642 }
643 if (mopt & 0x20) {
644 NOTICE(((c++) ? ", " : "") << "AUI");
645 linktype = 0x0001;
646 }
647 if (mopt & 0x40) {
648 NOTICE(((c++) ? ", " : "") << "MII");
649 linktype = 0x0006;
650 }
651 if ((mopt & 0xA) == 0xA) {
652 NOTICE(((c++) ? ", " : "") << "10BASE-T / 100BASE-TX");
653 linktype = 0x0008;
654 } else if ((mopt & 0xa) == 0x2) {
655 NOTICE(((c++) ? ", " : "") << "100BASE-TX");
656 linktype = 0x0008;
657 } else if ((mopt & 0xa) == 0x8) {
658 NOTICE(((c++) ? ", " : "") << "10BASE-T");
659 linktype = 0x0008;
660 }
661
665 if (m_isBrev) {
666 if ((eeprom[0x16] & 0xff00) == XCVR_MAGIC)
667 linktype = eeprom[0x16] & 0x000f;
668 } else {
669#ifdef CFG_3C90X_XCVR
670 if (CFG_3C90X_XCVR != 255)
671 linktype = CFG_3C90X_XCVR;
672#endif
673
674 if (linktype == 0x0009) {
675 if (m_isBrev)
676 WARNING(
677 "3C90x: MII External MAC mode only supported on "
678 "B-revision cards! Falling back to MII mode.");
679 linktype = 0x0006;
680 }
681 }
682
684 if (linktype == 0x0003 && !issueCommand(cmdEnableDcConverter, 0))
685 return;
686
688 if (setWindow(winTxRxOptions3) < 0)
689 return;
690 cfg = m_pBase->read32(regInternalConfig_3_l);
691 cfg &= ~(0xF << 20);
692 cfg |= (linktype << 20);
693 m_pBase->write32(cfg, regInternalConfig_3_l);
694
696 if (!issueCommand(cmdTxReset, 0))
697 return;
698
699 if (!m_isBrev)
700 m_pBase->write8(0x01, regTxFreeThresh_b);
701 if (!issueCommand(cmdTxEnable, 0))
702 return;
703
707 if (m_isBrev) {
708 if (!issueCommand(cmdRxReset, 0x04))
709 return;
710 } else {
711 if (!issueCommand(cmdRxReset, 0x00))
712 return;
713 }
714
717 if (!issueCommand(cmdSetRxFilter, 0x01 + 0x02 + 0x04) || !issueCommand(cmdRxEnable, 0)) {
718 return;
719 }
720
722 if (!issueCommand(cmdSetInterruptEnable, 0) ||
723 !issueCommand(cmdSetIndicationEnable, ENABLED_INTS) ||
724 !issueCommand(cmdAcknowledgeInterrupt, 0xff)) {
725 return;
726 }
727
728 // Set the location for the UPD
729 FENCE();
730 m_pBase->write32(m_pUPD, regUpListPtr_l);
731
732 // install the IRQ
733 m_IrqId = Machine::instance().getIrqManager()->registerPciIrqHandler(
734 static_cast<IrqHandler*>(this), this, IrqPolicy::pciIntxThreaded());
735 if (!m_IrqId) {
736 ERROR("3C90x: could not register its PCI interrupt");
737 if (!quiesce())
738 FATAL("3C90x could not halt DMA after IRQ registration failed.");
739 m_Stopping = true;
740 return;
741 }
742
744 bool enabled = false;
745 {
746 LockGuard<Mutex> deviceGuard(m_DeviceLock);
747 m_Active = true;
748 enabled = issueCommand(cmdSetInterruptEnable, ENABLED_INTS);
749 if (enabled)
750 m_Initialised = true;
751 else if (!stopDeviceLocked())
752 FATAL("3C90x could not halt DMA after interrupt enable failed.");
753 }
754
755 if (!enabled) {
756 if (!Machine::instance().getIrqManager()->unregisterHandler(m_IrqId, this)) {
757 FATAL("3C90x could not unregister a failed IRQ callback.");
758 }
759 m_IrqId = 0;
761 }
762}
763
764Nic3C90x::~Nic3C90x() {
765 if (!m_Initialised) {
766 return;
767 }
768
769 {
770 LockGuard<Mutex> deviceGuard(m_DeviceLock);
771 if (!stopDeviceLocked())
772 FATAL("3C90x teardown could not establish a DMA halt boundary.");
773 }
774 if (m_IrqId && !Machine::instance().getIrqManager()->unregisterHandler(m_IrqId, this)) {
775 FATAL("3C90x teardown could not unregister its IRQ callback.");
776 }
777 m_IrqId = 0;
778
780 {
781 LockGuard<Mutex> sendGuard(m_SendLock);
782 }
783 m_Initialised = false;
784}
785
787 struct ReceivedPacket {
788 uint8_t* data;
789 size_t length;
790 };
791
792 constexpr size_t PassLimit = 16;
793 ReceivedPacket receivedPackets[NUM_UPDS] = {};
794 size_t receivedPacketCount = 0;
795 bool handled = false;
796 (void)number;
797
798 {
799 LockGuard<Mutex> deviceGuard(m_DeviceLock);
800 if (m_Stopping)
802 if (!m_Active)
803 return IrqDisposition::NotHandled;
804
805 uint16_t status = m_pBase->read16(regCommandIntStatus_w);
806 if ((status & ENABLED_INTS) == 0)
807 return IrqDisposition::NotHandled;
808
809 // Indications continue to latch while the source is gated. A cause
810 // which arrives after its acknowledgement is therefore owned by a
811 // later pass.
812 if (!issueCommand(cmdSetInterruptEnable, 0)) {
813 if (!stopDeviceLocked())
814 FATAL("3C90x could not quiesce after IRQ gating failed.");
815 return IrqDisposition::Handled;
816 }
817
818 for (size_t pass = 0; pass < PassLimit; ++pass) {
819 status = m_pBase->read16(regCommandIntStatus_w);
820
821 // check that one of the enabled IRQs is triggered
822 if ((status & ENABLED_INTS) == 0)
823 break;
824 handled = true;
825
826 // Acknowledge the captured cause before inspecting descriptors so
827 // a completion which arrives during the scan remains latched for
828 // the next bounded pass.
829 if (!issueCommand(cmdAcknowledgeInterrupt, (status & ENABLED_INTS))) {
830 if (!stopDeviceLocked())
831 FATAL("3C90x could not quiesce after IRQ ACK failed.");
832 break;
833 }
834
835 bool receiveBatch = false;
836 if (status & INT_UPCOMPLETE) {
837 receiveBatch = true;
838 bool wrapped = false;
839 for (size_t scanned = 0; scanned < NUM_UPDS; ++scanned) {
840 RXD& usedUpd = m_ReceiveUPD[m_RxConsumerIndex];
841 const uint32_t packetStatus = usedUpd.UpPktStatus;
842 if (!(packetStatus & UpPacketComplete))
843 break;
844 FENCE();
845
846 const size_t packetLength = packetStatus & UpPacketLengthMask;
847 if (packetStatus & UpPacketError) {
848 ERROR("3C90x: receive error, UpPktStatus = " << packetStatus << ".");
849 } else if (!packetLength || packetLength > ReceiveSlotSize) {
850 ERROR("3C90x: invalid received packet length " << packetLength);
851 } else {
852 uint8_t* packet = m_ReceiveStaging + (receivedPacketCount * ReceiveSlotSize);
853 MemoryCopy(packet, m_pRxBuffVirt + (m_RxConsumerIndex * ReceiveSlotSize), packetLength);
854 receivedPackets[receivedPacketCount++] = {packet, packetLength};
855 }
856
857 usedUpd.UpPktStatus = 0;
858 ++m_RxConsumerIndex;
859 if (m_RxConsumerIndex == NUM_UPDS) {
860 m_RxConsumerIndex = 0;
861 wrapped = true;
862 break;
863 }
864 }
865
866 if (wrapped) {
867 const bool uploadStalled = issueCommand(cmdStallCtl, 0);
868 if (uploadStalled) {
869 FENCE();
870 m_pBase->write32(m_pUPD, regUpListPtr_l);
871 }
872 if (!uploadStalled || !issueCommand(cmdStallCtl, 1)) {
873 if (!stopDeviceLocked()) {
874 FATAL(
875 "3C90x could not halt DMA after receive-list "
876 "restart failed.");
877 }
878 }
879 }
880 }
881
882 if (status & INT_HOSTERROR) {
883 ERROR("3C90x: host error IRQ");
884 if (!stopDeviceLocked())
885 FATAL("3C90x could not halt DMA after a host error.");
886 } else if (status & INT_TXCOMPLETE) {
887 const uint8_t txStatus = m_pBase->read8(regTxStatus_b);
888
889 // TxComplete advances through the status FIFO rather than the
890 // command-register acknowledgement path.
891 m_pBase->write8(0, regTxStatus_b);
892 m_TxSuccessful = (txStatus & 0xbf) == 0x80;
893
894 if (!m_TxSuccessful) {
895 if (txStatus & 0x02)
896 ERROR("3C90x: TX Reclaim Error");
897 else if (txStatus & 0x04)
898 ERROR("3C90x: TX Status Overflow");
899 else if (txStatus & 0x08)
900 ERROR("3C90x: TX Max Collisions");
901 else if (txStatus & 0x10)
902 ERROR("3C90x: TX Underrun");
903 else if (txStatus & 0x20)
904 ERROR("3C90x: TX Jabber");
905 else
906 ERROR(
907 "3C90x: Internal Error - Incomplete "
908 "Transmission");
909
910 if (!stopDeviceLocked()) {
911 FATAL(
912 "3C90x could not halt DMA after a transmit "
913 "error.");
914 }
915 } else {
916 m_TxMutex.release();
917 }
918 }
919
920 if (m_Stopping || receiveBatch)
921 break;
922 }
923
924 // Re-enable this device source only after every captured cause has
925 // either transferred ownership or forced the device offline.
926 if (m_Active && !m_Stopping && !issueCommand(cmdSetInterruptEnable, ENABLED_INTS)) {
927 if (!stopDeviceLocked())
928 FATAL("3C90x could not quiesce after IRQ rearm failed.");
929 }
930 }
931
932 for (size_t i = 0; i < receivedPacketCount; ++i) {
933 NetworkStack::instance().receive(receivedPackets[i].length,
934 reinterpret_cast<uintptr_t>(receivedPackets[i].data), this, 0);
935 }
936
937 /*
938 XL_SEL_WIN(7);
939
940 if (ifp->if_snd.ifq_head != NULL)
941 xl_start(ifp);
942 */
943
944 return handled ? IrqDisposition::Handled : IrqDisposition::NotHandled;
945}
946
948 // free the old DNS servers list, if there is one
949 if (m_StationInfo.dnsServers)
950 delete[] m_StationInfo.dnsServers;
951
952 // MAC isn't changeable, so set it all manually
953 m_StationInfo.ipv4 = info.ipv4;
954 NOTICE("3C90x: Setting ipv4, " << info.ipv4.toString() << ", " << m_StationInfo.ipv4.toString()
955 << "...");
956 m_StationInfo.ipv6 = info.ipv6;
957
958 m_StationInfo.subnetMask = info.subnetMask;
959 NOTICE("3C90x: Setting subnet mask, " << info.subnetMask.toString() << ", "
960 << m_StationInfo.subnetMask.toString() << "...");
961 m_StationInfo.gateway = info.gateway;
962 NOTICE("3C90x: Setting gateway, " << info.gateway.toString() << ", "
963 << m_StationInfo.gateway.toString() << "...");
964
965 // Callers do not free their dnsServers memory
966 m_StationInfo.dnsServers = info.dnsServers;
967 m_StationInfo.nDnsServers = info.nDnsServers;
968 NOTICE("3C90x: Setting DNS servers [" << Dec << m_StationInfo.nDnsServers << Hex
969 << " servers being set]...");
970
971 return true;
972}
973
975 return m_StationInfo;
976}
virtual void setSpecificType(String str)
Definition Device.h:182
Vector< Address * > m_Addresses
Definition Device.h:332
virtual uint8_t read8(size_t offset=0)=0
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
virtual void write8(uint8_t value, size_t offset=0)=0
virtual void write16(uint16_t value, size_t offset=0)=0
virtual uint16_t read16(size_t offset=0)=0
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &policy)=0
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
void deRegisterDevice(Network *pDevice)
static NetworkStack & instance()
void receive(size_t nBytes, uintptr_t packet, Network *pCard, uint32_t offset)
void registerDevice(Network *pDevice)
virtual const StationInfo & getStationInfo()
Definition 3Com90x.cc:974
int writeEepromWord(int address, uint16_t value)
Definition 3Com90x.cc:176
bool reset()
Definition 3Com90x.cc:242
int setWindow(int window)
Definition 3Com90x.cc:132
uint8_t m_isBrev
Definition 3Com90x.h:80
virtual IrqDisposition irq(irq_id_t number)
Definition 3Com90x.cc:786
virtual bool send(size_t nBytes, uintptr_t buffer)
Definition 3Com90x.cc:347
int writeEeprom(int address, uint16_t value)
Definition 3Com90x.cc:209
virtual bool setStationInfo(const StationInfo &info)
Definition 3Com90x.cc:947
bool readEeprom(int address, uint16_t &value)
Definition 3Com90x.cc:158
Nic3C90x(Network *pDev)
Definition 3Com90x.cc:433
bool issueCommand(int cmd, int param)
Definition 3Com90x.cc:95
static PhysicalMemoryManager & instance()
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
void release(size_t n=1)
Definition Semaphore.cc:546
MUST_USE_RESULT size_t drainAvailable()
Definition Semaphore.cc:528
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:369
size_t nDnsServers
Can contain IPv6 addresses.
IpAddress gateway
Automatically calculated?
@ Dec
Definition Log.h:144
@ Hex
Definition Log.h:142
IrqDisposition
Definition IrqHandler.h:31
Definition cmd.h:30