[93560] | 1 | /* $Id: DevDP8390.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * DevDP8390 - National Semiconductor DP8390-based Ethernet Adapter Emulation.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2022-2023 Oracle and/or its affiliates.
|
---|
[93560] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[93560] | 26 | */
|
---|
| 27 |
|
---|
| 28 | /** @page pg_dev_dp8390 NatSemi DP8390-Based Ethernet NIC Emulation.
|
---|
| 29 | *
|
---|
| 30 | * This software was written based on the following documents:
|
---|
| 31 | *
|
---|
| 32 | * - National Semiconductor DP8390/NS32490 Network Interface Controller,
|
---|
| 33 | * 1986
|
---|
| 34 | * - National Semiconductor DP8390D/NS32490D NIC Network Interface
|
---|
| 35 | * Controller datasheet, July 1995
|
---|
| 36 | * - National Semiconductor Application Note 729, DP839EB-ATN IBM PC-AT
|
---|
| 37 | * Compatible DP83901 SNIC Serial Network Interface Controller
|
---|
| 38 | * Evaluation Board, 1993
|
---|
| 39 | * - National Semiconductor Application Note 842, The Design and Operation
|
---|
| 40 | * of a Low Cost, 8-Bit PC-XT Compatible Ethernet Adapter Using
|
---|
| 41 | * the DP83902, May 1993
|
---|
| 42 | * - National Semiconductor Application Note 858, Guide to Loopback Using
|
---|
| 43 | * the DP8390 Chip Set, October 1992
|
---|
| 44 | * - National Semiconductor Application Note 875, DP83905EB-AT AT/LANTIC
|
---|
| 45 | * Evaluation Board, June 1993
|
---|
| 46 | * - Western Digital WD83C584 Bus Interface Controller Device datasheet,
|
---|
| 47 | * October 29, 1990
|
---|
| 48 | * - Western Digital WD83C690 Ethernet LAN Controller datasheet,
|
---|
| 49 | * November 2, 1990
|
---|
| 50 | * - 3Com EtherLink II Adapter Technical Reference Manual,
|
---|
| 51 | * March 1991
|
---|
| 52 | *
|
---|
| 53 | * This emulation is compatible with drivers for:
|
---|
| 54 | * - Novell/Eagle/Anthem NE1000 (8-bit)
|
---|
| 55 | * - Novell/Eagle/Anthem NE2000 (16-bit)
|
---|
| 56 | * - Western Digital/SMC WD8003E (8-bit)
|
---|
| 57 | * - Western Digital/SMC WD8013EBT (16-bit)
|
---|
| 58 | * - 3Com EtherLink II 3C503 (8-bit)
|
---|
| 59 | *
|
---|
| 60 | *
|
---|
| 61 | * The National Semiconductor DP8390 was an early (circa 1986) low-cost
|
---|
| 62 | * Ethernet controller, typically accompanied by the DP8391 Serial Network
|
---|
| 63 | * Interface and the DP8392 Coaxial Transceiver Interface.
|
---|
| 64 | *
|
---|
| 65 | * Due to its relatively low cost, the DP8390 NIC was chosen for several
|
---|
| 66 | * very widespread early PC Ethernet designs, namely the Novell NE1000/NE2000,
|
---|
| 67 | * Western Digital (later SMC) WD8003 EtherCard Plus, and 3Com EtherLink II.
|
---|
| 68 | * The popularity of these cards, especially the NE2000, in turn spawned
|
---|
| 69 | * a bevy of compatible chips from National Semiconductor and many others.
|
---|
| 70 | *
|
---|
| 71 | * All common DP8390-based cards have onboard memory. The initial WD8003E and
|
---|
| 72 | * NE1000 cards have one 8Kx8 SRAM; 16-bit cards like WD8013E or NE2000 have
|
---|
| 73 | * two 8Kx8 SRAMs wired in 8Kx16 configuration to enable 16-bit wide transfers.
|
---|
| 74 | * The DP8390 can address up to 64K or local memory and uses "Local DMA"
|
---|
| 75 | * (similar to bus mastering) to access it. Some newer cards had 32K or more
|
---|
| 76 | * onboard RAM. Note that an NE2000 in 8-bit mode can only address 8K local
|
---|
| 77 | * memory, effectively reverting to an NE1000.
|
---|
| 78 | *
|
---|
| 79 | * The DP8390 uses "Remote DMA" to move data between local memory and the host
|
---|
| 80 | * system. Remote DMA is quite similar to 8237-style third party DMA, except
|
---|
| 81 | * the DMA controller is on the DP8390 chip in this case.
|
---|
| 82 | *
|
---|
| 83 | * The DP8390 has a control bit (DCR.WTS) which selects whether all DMA (both
|
---|
| 84 | * Local and Remote) transfers are 8-bit or 16-bit. Word-wide transfers can
|
---|
| 85 | * generally only be used on a 16-bit card in a 16-bit slot, because only then
|
---|
| 86 | * can the host drive 16-bit I/O cycles to the data ports. That is why
|
---|
| 87 | * an NE2000 in an 8-bit slot can only use half of its local RAM -- remote DMA
|
---|
| 88 | * simply cannot access half of the 8Kx16 SRAM.
|
---|
| 89 | *
|
---|
| 90 | * The DP8390 maps its internal registers as sixteen 8-bit wide I/O ports.
|
---|
| 91 | * There are four register pages, selectable through the Command Register (CR)
|
---|
| 92 | * which is accessible at offset 0 in all pages.
|
---|
| 93 | *
|
---|
| 94 | * The NE1000/NE2000 cards only use I/O and IRQ resources, not memory
|
---|
| 95 | * or DMA. In contrast, the Western Digital cards use memory-mapped buffers.
|
---|
| 96 | * Later AT/LANTIC (DP83905) based NE2000-compatible cards can optionally
|
---|
| 97 | * use memory as well. The 3Com EtherLink II (3C503) uses a custom gate array
|
---|
| 98 | * in addition to the DP8390 and can use programmed I/O, 8237 DMA, as well
|
---|
| 99 | * as optional direct memory mapping.
|
---|
| 100 | *
|
---|
| 101 | * Address decoding is typically incomplete, which causes the buffer RAM and
|
---|
| 102 | * possibly PROM to be aliased multiple times in the DP8390's address space.
|
---|
| 103 | *
|
---|
| 104 | * Buffer overflow handling is slightly tricky. The DP8390 assumes that if
|
---|
| 105 | * the receiver is enabled, there is space for at least one page (256 bytes).
|
---|
| 106 | * Once it fills up the page and advances the CURR pointer, the DP8390 checks
|
---|
| 107 | * whether CURR equals BNRY and if so, triggers an overflow condition. Note
|
---|
| 108 | * that after the NIC is initialized, CURR *will* normally equal BNRY, with
|
---|
| 109 | * both pointing at the beginning of the receive ring (PSTART). An overflow
|
---|
| 110 | * is only triggered when CURR equals BNRY right after advancing.
|
---|
| 111 | *
|
---|
| 112 | * The documentation of the Send Packet command mentions that when CRDA crosses
|
---|
| 113 | * the PSTOP register, the current remote DMA address (i.e. CRDA) is set to
|
---|
| 114 | * the PSTART value, which is rather convenient when reading received packets
|
---|
| 115 | * out of the ring buffer using remote DMA. The documentation does not mention
|
---|
| 116 | * that the same logic applies for all remote DMA reads, a feature that several
|
---|
| 117 | * NE1000/NE2000 drivers (packet drivers, Novell ODI) rely on. This is logical,
|
---|
| 118 | * because reading out of the receive ring buffer address range always implies
|
---|
| 119 | * reading received packets, and then the PSTOP->PSTART wraparound becomes
|
---|
| 120 | * desirable. It is unclear whether the same wraparound handling also applies
|
---|
| 121 | * for remote DMA writes within the receive ring buffer.
|
---|
| 122 | *
|
---|
| 123 | * The documentation is not very clear on how the CRDA register is managed.
|
---|
| 124 | * One might be led to believe that starting remote DMA copies the remote DMA
|
---|
| 125 | * start address (i.e. RSAR) to the CRDA register. However, the NE1000 ODI
|
---|
| 126 | * driver for OS/2 1.0 (NE1000.SYS from early 1988) relies on restarting remote
|
---|
| 127 | * DMA and continuing where it left off. The DP8390D datasheet only mentions
|
---|
| 128 | * this in a passing fashion at the end of the "Remote Write with High Speed
|
---|
| 129 | * Buses" section, saying that if a dummy remote read is executed before a
|
---|
| 130 | * remote write, RSAR can be set up for the dummy read such that the CRDA
|
---|
| 131 | * register contains the desired value for the following write.
|
---|
| 132 | *
|
---|
| 133 | * Conversely, it is not spelled out that writing RSAR also updates CRDA, but
|
---|
| 134 | * at least Novell's NE2000 ODI driver v2.12 is known to rely on this behavior
|
---|
| 135 | * and checks that a write to RSAR is reflected in CRDA.
|
---|
| 136 | *
|
---|
| 137 | * Loopback operation is limited in the DP8390. Because it is a half-duplex
|
---|
| 138 | * device, it cannot truly transmit and receive simultaneously. When loopback
|
---|
| 139 | * is in effect, the received data is *not* written into memory. Only the last
|
---|
| 140 | * few bytes of the packet are visible in the FIFO.
|
---|
| 141 | *
|
---|
| 142 | * Likewise due to its half-duplex nature, the CRC circuitry during loopback
|
---|
| 143 | * works either only on the transmit side (FCS is generated but not checked)
|
---|
| 144 | * or the receive side (FCS is checked but not generated).
|
---|
| 145 | *
|
---|
| 146 | * The loopback behavior is even stranger when DCR.WTS is set to enabled 16-bit
|
---|
| 147 | * DMA transfers. Even though the chip reads 16 bits at a time, only 8 bits are
|
---|
| 148 | * actually transmitted; the DCR.BOS bit determines whether the low or high
|
---|
| 149 | * 8 bits of each words are transmitted. As a consequence, the programmed length
|
---|
| 150 | * of the transmit is also halved.
|
---|
| 151 | *
|
---|
| 152 | * Because loopback operation is so different from normal send/receive, loopback
|
---|
| 153 | * packets are not run through the normal receive path and are treated specially
|
---|
| 154 | * instead. The WD and especially 3C503 diagnostics exercise the loopback
|
---|
| 155 | * functionality fairly thoroughly.
|
---|
| 156 | *
|
---|
| 157 | *
|
---|
| 158 | * NE1000 and NE2000
|
---|
| 159 | * -----------------
|
---|
| 160 | *
|
---|
| 161 | * Common NE1000/NE2000 configurations in Novell drivers:
|
---|
| 162 | * I/O Base = 300h, IRQ = 3 (default)
|
---|
| 163 | * I/O Base = 320h, IRQ = 2
|
---|
| 164 | * I/O Base = 340h, IRQ = 4
|
---|
| 165 | * I/O Base = 360h, IRQ = 5
|
---|
| 166 | * The I/O base can be set to 300h/320h/340h/360h; the IRQ to 2, 3, 4, 5.
|
---|
| 167 | * No memory or DMA is used.
|
---|
| 168 | *
|
---|
| 169 | * The NE1000/NE2000 adds a data register and a reset register to the I/O
|
---|
| 170 | * space. A PROM containing the node address is mapped into the DP8390's local
|
---|
| 171 | * address space.
|
---|
| 172 | *
|
---|
| 173 | * The mapping of the 32x8 PROM on an NE2000 card is quite non-obvious but
|
---|
| 174 | * fortunately well explained in the AN-729 Application Note. Address lines
|
---|
| 175 | * A4-A1 of the internal bus are connected to lines A3-A0 of the PROM
|
---|
| 176 | * (enabling 16 distinct bytes of the 32-byte PROM to be addressed). However,
|
---|
| 177 | * the negated EN16 signal, which is active when the NE2000 is in a 16-bit
|
---|
| 178 | * slot, is connected to the PROM's address line A4. That means an NE2000 in
|
---|
| 179 | * a 16-bit slot reads different PROM bytes than when the same card is in an
|
---|
| 180 | * 8-bit slot. The PROM is structured such that an NE2000 in an 8-bit slot
|
---|
| 181 | * reads a 'BB' signature (same as NE1000) at PROM offset 1Eh/1Fh, while
|
---|
| 182 | * an NE2000 in a 16-bit slot returns a 'WW' signature from PROM offset
|
---|
| 183 | * 0Eh/0Fh instead.
|
---|
| 184 | *
|
---|
| 185 | * The original NE1000 boards Assy. #950-054401 actually only had 6 bytes of
|
---|
| 186 | * MAC address in the PROM, the rest was unused (0FFh). Software supporting the
|
---|
| 187 | * NE1000 thus should not examine the PROM contents beyond the first 6 bytes.
|
---|
| 188 | *
|
---|
| 189 | * Novell's old OUI was 00:00:D8 but drivers are not known to check for it.
|
---|
| 190 | *
|
---|
| 191 | * Newer DP83905 AT/LANTIC based NE2000plus cards were optionally capable of
|
---|
| 192 | * using shared RAM in a manner very similar to the WD8003/WD8013.
|
---|
| 193 | *
|
---|
| 194 | *
|
---|
| 195 | * WD8003 and WD8013 EtherCard Plus
|
---|
| 196 | * --------------------------------
|
---|
| 197 | *
|
---|
| 198 | * Common WD8013 configurations:
|
---|
| 199 | * I/O Base = 280h, IRQ = 3, RAM D000-D3FF (default)
|
---|
| 200 | * I/O Base = 330h, IRQ = 10, RAM CC00-CFFF
|
---|
| 201 | * I/O Base = 240h, IRQ/RAM soft-configurable
|
---|
| 202 | * The I/O base can be set anywhere in the 2xxh-3xxh range in 20h increments.
|
---|
| 203 | * The IRQs available on a WD8013 are 2, 3, 4, 5, 7, 10, 11, 15. The shared
|
---|
| 204 | * RAM can be anywhere between 80000h (512K) to FFC000h (16M-16K) in 16K
|
---|
| 205 | * increments.
|
---|
| 206 | *
|
---|
| 207 | * The Western Digital WD8003E appeared at around the same time as Novell's
|
---|
| 208 | * NE1000 (1987). It is likewise a short 8-bit ISA card with 8Kx8 onboard
|
---|
| 209 | * SRAM. The major difference is that rather than using remote DMA to move
|
---|
| 210 | * data between the host and local RAM, the WD8003 directly mapps the onboard
|
---|
| 211 | * memory to the host's address space (often called shared memory). A later
|
---|
| 212 | * 16-bit WD8013 model used 8Kx16 SRAM, and there were follow-on WD8003 models
|
---|
| 213 | * with 16K or 32K local RAM.
|
---|
| 214 | *
|
---|
| 215 | * Instead of mapping the PROM into the DP8390's local address space, the
|
---|
| 216 | * WD8003/WD8013 exposes the node address through the I/O space; the DP8390's
|
---|
| 217 | * local address space only contains buffer RAM.
|
---|
| 218 | *
|
---|
| 219 | * The WD8003 cannot use remote DMA at all; the host must use shared memory.
|
---|
| 220 | * Remote DMA can be programmed but there is no way to trigger RDMA transfers.
|
---|
| 221 | *
|
---|
| 222 | * Western Digital's brand name for WD8003/WD8013 was EtherCard. Circa 1991,
|
---|
| 223 | * WD sold the networking business to SMC; SMC continued to sell and further
|
---|
| 224 | * develop the cards under the Elite brand name, also designated as the
|
---|
| 225 | * SMC8000 series.
|
---|
| 226 | *
|
---|
| 227 | * The original WD8003E/EBT/WT uses very simple glue logic around the DP8390
|
---|
| 228 | * and must be configured through jumpers. Newer WD8003EB/EP/EW/W/WC uses an
|
---|
| 229 | * interface chip (WD83C583, WD83C584, or later) with an EEPROM and can be
|
---|
| 230 | * configured through a software utility.
|
---|
| 231 | *
|
---|
| 232 | * Similarly the 16-bit WD8013EBT is configured only though jumpers, while
|
---|
| 233 | * the newer WD8013EB/W/EW/EWC/WC/EPC are software configurable.
|
---|
| 234 | *
|
---|
| 235 | * The "Board ID" byte (at offset 6 in the PROM) is used to distinguish
|
---|
| 236 | * between the various models.
|
---|
| 237 | *
|
---|
| 238 | * Newer WD cards use the WD83C690 controller rather than DP8390. The
|
---|
| 239 | * WD83C690 is close enough to DP8390 that old WD drivers should work with
|
---|
| 240 | * it, but it has a number of differences. It has no support for Remote DMA
|
---|
| 241 | * whatsoever, and does not implement multicast filtering.
|
---|
| 242 | *
|
---|
| 243 | * The WD83C690 also handles receive buffer overflows somewhat differently;
|
---|
| 244 | * the DP8390 never fills the last remaining buffer page, meaning that
|
---|
| 245 | * CURR=BNRY indicates an empty buffer while CURR=BNRY-1 means buffer full.
|
---|
| 246 | * The WD83C690 can fill all pages and decides whether it is full or empty
|
---|
| 247 | * based on whether CURR or BNRY was changed more recently.
|
---|
| 248 | *
|
---|
| 249 | * Old Western Digital utilities/drivers may require the card to have WD's
|
---|
| 250 | * old OUI of 00:00:0C and refuse to recognize the hardware otherwise.
|
---|
| 251 | *
|
---|
| 252 | * The emulation passes WD diagnostics with no errors (DIAGNOSE.EXE Ver 1.11,
|
---|
| 253 | * dated 12/12/1989).
|
---|
| 254 | *
|
---|
| 255 | *
|
---|
| 256 | * 3C503 EtherLink II
|
---|
| 257 | * ------------------
|
---|
| 258 | *
|
---|
| 259 | * Common 3C503 configurations in Novell drivers:
|
---|
| 260 | * I/O Base = 300h, IRQ = 3 (default)
|
---|
| 261 | * The I/O base can be set via jumpers to 2E0h, 2A0h, 280h, 250h, 350h, 330h,
|
---|
| 262 | * 310h, or 300h (default). The ROM/RAM can be optionally mapped to one of
|
---|
| 263 | * DC000-DFFFF, D8000-DBFFF, CC000-CFFFF, or C8000-CBFFF, again configured
|
---|
| 264 | * through jumpers. The available IRQs are 2, 3, 4, or 5, and DRQs 1, 2, or 3,
|
---|
| 265 | * both soft-configurable (no IRQ/DRQ jumpers).
|
---|
| 266 | *
|
---|
| 267 | * Yet another design based on the DP8390 was the 3Com 3C503 EtherLink II,
|
---|
| 268 | * available sometime in 1988. Unlike Novell and WD, 3Com added a custom
|
---|
| 269 | * host interface ASIC ("Gate Array") which handles all transfers to and from
|
---|
| 270 | * the 8Kx8 onboard SRAM. The 3C503 can map the card's local RAM directly
|
---|
| 271 | * into the host's address space, alternatively software can use either PIO
|
---|
| 272 | * or 8-bit DMA to transfer data.
|
---|
| 273 | *
|
---|
| 274 | * For reasons that are not entirely clear, 3Com decided that the Remote DMA
|
---|
| 275 | * implementation on the DP3890 (successfully used by the NE1000/NE2000) was
|
---|
| 276 | * too buggy and the Gate Array essentially duplicates the Remote DMA
|
---|
| 277 | * functionality, while also adding 8327 style DMA support (like the DP839EB
|
---|
| 278 | * had) and optional shared RAM.
|
---|
| 279 | *
|
---|
| 280 | * Just like the NE1000/NE2000 and WD8003/WD8013, the 3C503 exists in an
|
---|
| 281 | * 8-bit variant (EtherLink II) and a 16-bit variant (EtherLink II/16),
|
---|
| 282 | * although both types are called 3C503.
|
---|
| 283 | *
|
---|
| 284 | * Since the 3C503 does not require shared RAM to operate, 3Com decided to
|
---|
| 285 | * use a single memory mapping for both a boot ROM (if present) and shared
|
---|
| 286 | * RAM. It is possible to boot from the ROM utilizing PIO or DMA for data
|
---|
| 287 | * transfers, and later switch to shared RAM. However, 3Com needed to add
|
---|
| 288 | * a hack for warm boot; the Vector Pointer Registers (VPTR0/1/2) contain
|
---|
| 289 | * a 20-bit address and the Gate Array monitors the ISA bus for a read cycle
|
---|
| 290 | * to that address. When a read cycle from the VPTR address occurs, the
|
---|
| 291 | * memory mapping is switched from RAM to ROM. The VPTR registers are meant
|
---|
| 292 | * to be programmed with the warm boot vector (often F000:FFF0 or FFFF0h).
|
---|
| 293 | *
|
---|
| 294 | * Some UNIX 3C503 drivers may require the card to have 3Com's old OUI
|
---|
| 295 | * of 02:60:8C and refuse to detect the hardware otherwise. Likewise the
|
---|
| 296 | * 3C503 diagnostics fail if the OUI is not 3Com's.
|
---|
| 297 | *
|
---|
| 298 | * The emulation passes 3Com diagnostics with flying colors (3C503.EXE Version
|
---|
| 299 | * 1.5, dated 11/26/1991).
|
---|
| 300 | *
|
---|
| 301 | *
|
---|
| 302 | * Linux Drivers
|
---|
| 303 | *
|
---|
| 304 | * The DP8390 driver (shared by NE1000/NE2000, WD8003/WD8013, and 3C503 drivers)
|
---|
| 305 | * in Linux has severe bugs in the receive path. The driver clears receive
|
---|
| 306 | * interrupts *after* going through the receive ring; that causes it to race
|
---|
| 307 | * against the DP8390 chip and sometimes dismiss receive interrupts without
|
---|
| 308 | * handling them. The driver also only receives at most 9 packets at a time,
|
---|
| 309 | * which again can cause already received packets to be "hanging" in the receive
|
---|
| 310 | * queue without the driver processing them.
|
---|
| 311 | * In addition, prior to Linux 1.3.47, the driver incorrectly cleared the
|
---|
| 312 | * overflow warning interrupt after any receive, causing it to potentially
|
---|
| 313 | * miss overflow interrupts.
|
---|
| 314 | *
|
---|
| 315 | * The above bugs cause received packets to be lost or retransmitted by sender,
|
---|
| 316 | * causing major TCP/IP performance issues when the DP8390 receives packets
|
---|
| 317 | * very quickly. Other operating systems do not exhibit these bugs.
|
---|
| 318 | *
|
---|
| 319 | *
|
---|
| 320 | * BSD Drivers
|
---|
| 321 | *
|
---|
| 322 | * For reasons that are not obvious, BSD drivers have configuration defaults far
|
---|
| 323 | * off from the hardware defaults. For NE2000 (ne1), it is I/O base 300h and
|
---|
| 324 | * IRQ 10. For WD8003E (we0), it is I/O base 280h, IRQ 9, memory D0000-D1FFF.
|
---|
| 325 | * For 3C503 (ec0), it is I/O base 250h, IRQ 9, memory D8000-D9FFF (no DMA).
|
---|
| 326 | *
|
---|
| 327 | * The resource assigments are difficult to configure (sometimes impossible on
|
---|
| 328 | * installation CDs) and the high IRQs may clash with PCI devices.
|
---|
| 329 | *
|
---|
| 330 | */
|
---|
| 331 |
|
---|
| 332 |
|
---|
| 333 | /*********************************************************************************************************************************
|
---|
| 334 | * Header Files *
|
---|
| 335 | *********************************************************************************************************************************/
|
---|
| 336 | #define LOG_GROUP LOG_GROUP_DEV_DP8390
|
---|
| 337 | #include <VBox/vmm/pdmdev.h>
|
---|
| 338 | #include <VBox/vmm/pdmnetifs.h>
|
---|
| 339 | #include <VBox/vmm/pgm.h>
|
---|
| 340 | #include <VBox/version.h>
|
---|
| 341 | #include <iprt/asm.h>
|
---|
| 342 | #include <iprt/assert.h>
|
---|
| 343 | #include <iprt/critsect.h>
|
---|
| 344 | #include <iprt/net.h>
|
---|
| 345 | #include <iprt/string.h>
|
---|
| 346 | #include <iprt/time.h>
|
---|
| 347 | #ifdef IN_RING3
|
---|
| 348 | # include <iprt/mem.h>
|
---|
| 349 | # include <iprt/semaphore.h>
|
---|
| 350 | # include <iprt/uuid.h>
|
---|
| 351 | #endif
|
---|
| 352 |
|
---|
| 353 | #include "VBoxDD.h"
|
---|
| 354 |
|
---|
| 355 |
|
---|
| 356 | /*********************************************************************************************************************************
|
---|
| 357 | * Defined Constants And Macros *
|
---|
| 358 | *********************************************************************************************************************************/
|
---|
| 359 |
|
---|
| 360 | #define DPNIC_SAVEDSTATE_VERSION 1
|
---|
| 361 |
|
---|
| 362 | /** Maximum number of times we report a link down to the guest (failure to send frame) */
|
---|
| 363 | #define DPNIC_MAX_LINKDOWN_REPORTED 3
|
---|
| 364 |
|
---|
[93675] | 365 | /** Maximum number of times we postpone restoring a link that is temporarily down. */
|
---|
| 366 | #define DPNIC_MAX_LINKRST_POSTPONED 3
|
---|
| 367 |
|
---|
[93560] | 368 | /** Maximum frame size we handle */
|
---|
| 369 | #define MAX_FRAME 1536
|
---|
| 370 |
|
---|
| 371 | /* Size of the local RAM. */
|
---|
| 372 | #define DPNIC_MEM_SIZE 16384u
|
---|
| 373 |
|
---|
| 374 | #define DPNIC_MEM_MASK (DPNIC_MEM_SIZE - 1)
|
---|
| 375 |
|
---|
| 376 | /* Although it is a 16-bit adapter, the EtherLink II only supports 8-bit DMA
|
---|
| 377 | * and therefore DMA channels 1 to 3 are available.
|
---|
| 378 | */
|
---|
| 379 | #define ELNKII_MIN_VALID_DMA 1
|
---|
| 380 | #define ELNKII_MAX_VALID_DMA 3
|
---|
| 381 |
|
---|
| 382 | /* EtherLink II Gate Array revision. */
|
---|
| 383 | #define ELNKII_GA_REV 1
|
---|
| 384 |
|
---|
| 385 |
|
---|
| 386 | /*********************************************************************************************************************************
|
---|
| 387 | * Structures and Typedefs *
|
---|
| 388 | *********************************************************************************************************************************/
|
---|
| 389 |
|
---|
| 390 |
|
---|
| 391 | /**
|
---|
| 392 | * Emulated device types.
|
---|
| 393 | */
|
---|
| 394 | enum DP8390_DEVICE_TYPE
|
---|
| 395 | {
|
---|
| 396 | DEV_NE1000 = 0, /* Novell NE1000 compatible (8-bit). */
|
---|
| 397 | DEV_NE2000 = 1, /* Novell NE2000 compatible (16-bit). */
|
---|
| 398 | DEV_WD8003 = 2, /* Western Digital WD8003 EtherCard Plus compatible (8-bit). */
|
---|
| 399 | DEV_WD8013 = 3, /* Western Digital WD8013 EtherCard Plus compatible (16-bit). */
|
---|
| 400 | DEV_3C503 = 4 /* 3Com 3C503 EtherLink II compatible. */
|
---|
| 401 | };
|
---|
| 402 |
|
---|
| 403 | /** WD8003/WD80013 specific register offsets. */
|
---|
| 404 | #define WDR_CTRL1 0 /* Control register 1. */
|
---|
| 405 | #define WDR_ATDET 1 /* 16-bit slot detect. */
|
---|
| 406 | #define WDR_IOBASE 2 /* I/O base register. */
|
---|
| 407 | #define WDR_CTRL2 5 /* Control register 2. */
|
---|
| 408 | #define WDR_JP 6 /* Jumper settings. */
|
---|
| 409 | #define WDR_PROM 8 /* PROM offset in I/O space. */
|
---|
| 410 |
|
---|
| 411 | /** WD8013 Control Register 1. */
|
---|
| 412 | typedef struct WD_CTRL1 {
|
---|
| 413 | uint8_t A13_18 : 6; /* Shared memory decoding A13-A18. */
|
---|
| 414 | uint8_t MEME : 1; /* Enable memory access. */
|
---|
| 415 | uint8_t RESET : 1; /* Reset NIC core. */
|
---|
| 416 | } WD_CTRL1;
|
---|
| 417 | AssertCompile(sizeof(WD_CTRL1) == sizeof(uint8_t));
|
---|
| 418 |
|
---|
| 419 | /** WD8013 Control Register 2. */
|
---|
| 420 | typedef struct WD_CTRL2 {
|
---|
| 421 | uint8_t A19_23 : 5; /* Shared memory decoding A19-A23. */
|
---|
| 422 | uint8_t res : 1; /* Reserved. */
|
---|
| 423 | uint8_t MEMW : 1; /* Memory width (16-bit wide if set). */
|
---|
| 424 | uint8_t M16 : 1; /* Allow 16-bit host memory cycles if set. */
|
---|
| 425 | } WD_CTRL2;
|
---|
| 426 | AssertCompile(sizeof(WD_CTRL2) == sizeof(uint8_t));
|
---|
| 427 |
|
---|
| 428 |
|
---|
| 429 | /** 3C503 EtherLink II specific register offsets. */
|
---|
| 430 | #define GAR_PSTR 0
|
---|
| 431 | #define GAR_PSPR 1
|
---|
| 432 | #define GAR_DQTR 2
|
---|
| 433 | #define GAR_R_BCFR 3
|
---|
| 434 | #define GAR_R_PCFR 4
|
---|
| 435 | #define GAR_GACFR 5
|
---|
| 436 | #define GAR_GACR 6
|
---|
| 437 | #define GAR_STREG 7
|
---|
| 438 | #define GAR_IDCFR 8
|
---|
| 439 | #define GAR_DAMSB 9
|
---|
| 440 | #define GAR_DALSB 10
|
---|
| 441 | #define GAR_VPTR2 11
|
---|
| 442 | #define GAR_VPTR1 12
|
---|
| 443 | #define GAR_VPTR0 13
|
---|
| 444 | #define GAR_RFMSB 14
|
---|
| 445 | #define GAR_RFLSB 15
|
---|
| 446 |
|
---|
| 447 | /** 3C503 EtherLink II Gate Array registers. */
|
---|
| 448 |
|
---|
| 449 | /** Gate Array DRQ Timer Register. */
|
---|
| 450 | typedef struct EL_DQTR {
|
---|
| 451 | uint8_t tb : 5; /* Timer bits; should be multiple of 4. */
|
---|
| 452 | uint8_t res : 3; /* Reserved. */
|
---|
| 453 | } GA_DQTR;
|
---|
| 454 | AssertCompile(sizeof(GA_DQTR) == sizeof(uint8_t));
|
---|
| 455 |
|
---|
| 456 | /** Gate Array Configuration Register. */
|
---|
| 457 | typedef struct EL_GACFR {
|
---|
| 458 | uint8_t mbs : 3; /* Memory Bank Select. */
|
---|
| 459 | uint8_t rsel : 1; /* RAM Select. */
|
---|
| 460 | uint8_t test : 1; /* Makes GA counters run at 10 MHz. */
|
---|
| 461 | uint8_t ows : 1; /* 0 Wait State for Gate Array. */
|
---|
| 462 | uint8_t tcm : 1; /* Terminal Count Mask for DMA (block interrupt if set). */
|
---|
| 463 | uint8_t nim : 1; /* NIC Interrupt Mask (block interrupt if set). */
|
---|
| 464 | } GA_GACFR;
|
---|
| 465 | AssertCompile(sizeof(GA_GACFR) == sizeof(uint8_t));
|
---|
| 466 |
|
---|
| 467 | /** Gate Array Configuration Register. */
|
---|
| 468 | typedef struct EL_GACR {
|
---|
| 469 | uint8_t rst : 1; /* Hard reset GA/NIC. */
|
---|
| 470 | uint8_t xsel : 1; /* Transceiver Select. */
|
---|
| 471 | uint8_t ealo : 1; /* Window low 16 bytes of PROM to I/O space. */
|
---|
| 472 | uint8_t eahi : 1; /* Window high 16 bytes of PROM to I/O space. */
|
---|
| 473 | uint8_t share : 1; /* Enable interrupt sharing. */
|
---|
| 474 | uint8_t dbsel : 1; /* Double Buffer Select for FIFOs. */
|
---|
| 475 | uint8_t ddir : 1; /* DMA Direction (1=host to adapter). */
|
---|
| 476 | uint8_t start : 1; /* Start Gate Array DMA. */
|
---|
| 477 | } GA_GACR;
|
---|
| 478 | AssertCompile(sizeof(GA_GACR) == sizeof(uint8_t));
|
---|
| 479 |
|
---|
| 480 | /** Gate Array Status Register. */
|
---|
| 481 | typedef struct EL_STREG {
|
---|
| 482 | uint8_t rev : 3; /* Gate Array Revision. */
|
---|
| 483 | uint8_t dip : 1; /* DMA In Progress. */
|
---|
| 484 | uint8_t dtc : 1; /* DMA Terminal Count. */
|
---|
| 485 | uint8_t oflw : 1; /* Data Overflow. */
|
---|
| 486 | uint8_t uflw : 1; /* Data Underflow. */
|
---|
| 487 | uint8_t dprdy : 1; /* Data Port Ready. */
|
---|
| 488 | } GA_STREG;
|
---|
| 489 | AssertCompile(sizeof(GA_STREG) == sizeof(uint8_t));
|
---|
| 490 |
|
---|
| 491 | /** Gate Array Interrupt/DMA Configuration. */
|
---|
| 492 | typedef struct EL_IDCFR {
|
---|
| 493 | uint8_t drq1 : 1; /* Enable DRQ 1. */
|
---|
| 494 | uint8_t drq2 : 1; /* Enable DRQ 2. */
|
---|
| 495 | uint8_t drq3 : 1; /* Enable DRQ 3. */
|
---|
| 496 | uint8_t res : 1; /* Unused. */
|
---|
| 497 | uint8_t irq2 : 1; /* Enable IRQ 2. */
|
---|
| 498 | uint8_t irq3 : 1; /* Enable IRQ 3. */
|
---|
| 499 | uint8_t irq4 : 1; /* Enable IRQ 4. */
|
---|
| 500 | uint8_t irq5 : 1; /* Enable IRQ 5. */
|
---|
| 501 | } GA_IDCFR;
|
---|
| 502 | AssertCompile(sizeof(GA_IDCFR) == sizeof(uint8_t));
|
---|
| 503 |
|
---|
| 504 | /** Current DMA Address. */
|
---|
| 505 | typedef struct EL_CDADR {
|
---|
| 506 | uint8_t cdadr_lsb; /* Current DMA Address LSB. */
|
---|
| 507 | uint8_t cdadr_msb; /* Current DMA Address MSB. */
|
---|
| 508 | } GA_CDADR;
|
---|
| 509 | AssertCompile(sizeof(GA_CDADR) == sizeof(uint16_t));
|
---|
| 510 |
|
---|
| 511 | /** 3C503 Gate Array state. */
|
---|
| 512 | typedef struct EL_GA_s {
|
---|
| 513 | uint8_t PSTR; /* Page Start Register. */
|
---|
| 514 | uint8_t PSPR; /* Page Stop Register. */
|
---|
| 515 | union {
|
---|
| 516 | uint8_t DQTR; /* DRQ Timer Register. */
|
---|
| 517 | GA_DQTR dqtr;
|
---|
| 518 | };
|
---|
| 519 | uint8_t BCFR; /* Base Configuration Register (R/O). */
|
---|
| 520 | uint8_t PCFR; /* Boot PROM Configuration Register (R/O). */
|
---|
| 521 | union {
|
---|
| 522 | uint8_t GACFR;
|
---|
| 523 | GA_GACFR gacfr; /* Gate Array Configuration Register. */
|
---|
| 524 | };
|
---|
| 525 | union {
|
---|
| 526 | uint8_t GACR; /* Gate Array Control Register. */
|
---|
| 527 | GA_GACR gacr;
|
---|
| 528 | };
|
---|
| 529 | union {
|
---|
| 530 | uint8_t STREG; /* Gate Array Status Register (R/O). */
|
---|
| 531 | GA_STREG streg;
|
---|
| 532 | };
|
---|
| 533 | union {
|
---|
| 534 | uint8_t IDCFR; /* Interrupt/DMA Configuration Register. */
|
---|
| 535 | GA_IDCFR idcfr;
|
---|
| 536 | };
|
---|
| 537 | uint8_t DAMSB; /* DMA Address MSB. */
|
---|
| 538 | uint8_t DALSB; /* DMA Address LSB. */
|
---|
| 539 | uint8_t VPTR2; /* Vector Pointer 2. */
|
---|
| 540 | uint8_t VPTR1; /* Vector Pointer 1. */
|
---|
| 541 | uint8_t VPTR0; /* Vector Pointer 0. */
|
---|
| 542 | union {
|
---|
| 543 | uint16_t CDADR; /* Current DMA address (internal state). */
|
---|
| 544 | GA_CDADR cdadr;
|
---|
| 545 | };
|
---|
| 546 | bool fGaIrq; /* Gate Array IRQ (internal state). */
|
---|
| 547 | } EL_GA, *PEL_GA;
|
---|
| 548 |
|
---|
| 549 | /** DP8390 core register offsets. */
|
---|
| 550 | #define DPR_CR 0
|
---|
| 551 |
|
---|
| 552 | #define DPR_P0_R_CLDA0 1
|
---|
| 553 | #define DPR_P0_W_PSTART 1
|
---|
| 554 | #define DPR_P0_R_CLDA1 2
|
---|
| 555 | #define DPR_P0_W_PSTOP 2
|
---|
| 556 | #define DPR_P0_BNRY 3
|
---|
| 557 | #define DPR_P0_R_TSR 4
|
---|
| 558 | #define DPR_P0_W_TPSR 4
|
---|
| 559 | #define DPR_P0_R_NCR 5
|
---|
| 560 | #define DPR_P0_W_TBCR0 5
|
---|
| 561 | #define DPR_P0_R_FIFO 6
|
---|
| 562 | #define DPR_P0_W_TBCR1 6
|
---|
| 563 | #define DPR_P0_ISR 7
|
---|
| 564 | #define DPR_P0_R_CRDA0 8
|
---|
| 565 | #define DPR_P0_W_RSAR0 8
|
---|
| 566 | #define DPR_P0_R_CRDA1 9
|
---|
| 567 | #define DPR_P0_W_RSAR1 9
|
---|
| 568 | #define DPR_P0_W_RBCR0 10
|
---|
| 569 | #define DPR_P0_W_RBCR1 11
|
---|
| 570 | #define DPR_P0_R_RSR 12
|
---|
| 571 | #define DPR_P0_W_RCR 12
|
---|
| 572 | #define DPR_P0_R_CNTR0 13
|
---|
| 573 | #define DPR_P0_W_TCR 13
|
---|
| 574 | #define DPR_P0_R_CNTR1 14
|
---|
| 575 | #define DPR_P0_W_DCR 14
|
---|
| 576 | #define DPR_P0_R_CNTR2 15
|
---|
| 577 | #define DPR_P0_W_IMR 15
|
---|
| 578 |
|
---|
| 579 | #define DPR_P1_CURR 7
|
---|
| 580 |
|
---|
| 581 | #define DPR_P2_R_PSTART 1
|
---|
| 582 | #define DPR_P2_W_CLDA0 1
|
---|
| 583 | #define DPR_P2_R_PSTOP 2
|
---|
| 584 | #define DPR_P2_W_CLDA1 2
|
---|
| 585 | #define DPR_P2_RNXTPP 3 /* Remote Next Packet Pointer. */
|
---|
| 586 | #define DPR_P2_R_TPSR 4
|
---|
| 587 | #define DPR_P2_LNXTPP 5 /* Local Next Packet Pointer. */
|
---|
| 588 | #define DPR_P2_ADRCU 6 /* Address Counter (Upper). */
|
---|
| 589 | #define DPR_P2_ADRCL 7 /* Address Counter (Lower). */
|
---|
| 590 | #define DPR_P2_R_RCR 12
|
---|
| 591 | #define DPR_P2_R_TCR 13
|
---|
| 592 | #define DPR_P2_R_DCR 14
|
---|
| 593 | #define DPR_P2_R_IMR 15
|
---|
| 594 |
|
---|
| 595 |
|
---|
| 596 | /** DP8390 Packet Header. */
|
---|
| 597 | typedef struct DP_PKT_HDR {
|
---|
| 598 | uint8_t rcv_stat; /* Receive Status. */
|
---|
| 599 | uint8_t next_ptr; /* Next Packet Pointer. */
|
---|
| 600 | uint16_t byte_cnt; /* Receive byte count. */
|
---|
| 601 | } DP_PKT_HDR;
|
---|
| 602 |
|
---|
| 603 | /** Select values for CR.RD field. */
|
---|
| 604 | #define DP_CR_RDMA_INVL 0 /* Invalid value. */
|
---|
| 605 | #define DP_CR_RDMA_RD 1 /* Remote Read. */
|
---|
| 606 | #define DP_CR_RDMA_WR 2 /* Remote Write. */
|
---|
| 607 | #define DP_CR_RDMA_SP 3 /* Send Packet. */
|
---|
| 608 | #define DP_CR_RDMA_ABRT 4 /* Abort Remote DMA. */
|
---|
| 609 |
|
---|
| 610 | /** DP8390 Command Register (CR). */
|
---|
| 611 | typedef struct DP_CR {
|
---|
| 612 | uint8_t STP : 1; /* Stop. */
|
---|
| 613 | uint8_t STA : 1; /* Start. */
|
---|
| 614 | uint8_t TXP : 1; /* Transmit Packet. */
|
---|
| 615 | uint8_t RD : 3; /* Remote DMA Command. */
|
---|
| 616 | uint8_t PS : 2; /* Page Select. */
|
---|
| 617 | } DP_CR;
|
---|
| 618 | AssertCompile(sizeof(DP_CR) == sizeof(uint8_t));
|
---|
| 619 |
|
---|
| 620 | /** DP8390 Interrupt Status Register (ISR). */
|
---|
| 621 | typedef struct DP_ISR {
|
---|
| 622 | uint8_t PRX : 1; /* Packet Received. */
|
---|
| 623 | uint8_t PTX : 1; /* Packet Transmitted. */
|
---|
| 624 | uint8_t RXE : 1; /* Receive Error. */
|
---|
| 625 | uint8_t TXE : 1; /* Transmit Error. */
|
---|
| 626 | uint8_t OVW : 1; /* Overwrite Warning (no receive buffers). */
|
---|
| 627 | uint8_t CNT : 1; /* Counter Overflow. */
|
---|
| 628 | uint8_t RDC : 1; /* Remote DMA Complete. */
|
---|
| 629 | uint8_t RST : 1; /* Reset Status. */
|
---|
| 630 | } DP_ISR;
|
---|
| 631 | AssertCompile(sizeof(DP_ISR) == sizeof(uint8_t));
|
---|
| 632 |
|
---|
| 633 | /** DP8390 Interrupt Mask Register (IMR). */
|
---|
| 634 | typedef struct DP_IMR {
|
---|
| 635 | uint8_t PRXE : 1; /* Packet Received Interrupt Enable. */
|
---|
| 636 | uint8_t PTXE : 1; /* Packet Transmitted Interrupt Enable. */
|
---|
| 637 | uint8_t RXEE : 1; /* Receive Error Interrupt Enable. */
|
---|
| 638 | uint8_t TXEE : 1; /* Transmit Error Interrupt Enable. */
|
---|
| 639 | uint8_t OVWE : 1; /* Overwrite Warning Interrupt Enable. */
|
---|
| 640 | uint8_t CNTE : 1; /* Counter Overflow Interrupt Enable. */
|
---|
| 641 | uint8_t RDCE : 1; /* DMA Complete Interrupt Enable. */
|
---|
| 642 | uint8_t res : 1; /* Reserved. */
|
---|
| 643 | } DP_IMR;
|
---|
| 644 | AssertCompile(sizeof(DP_IMR) == sizeof(uint8_t));
|
---|
| 645 |
|
---|
| 646 | /** DP8390 Data Configuration Register (DCR). */
|
---|
| 647 | typedef struct DP_DCR {
|
---|
| 648 | uint8_t WTS : 1; /* Word Transfer Select. */
|
---|
| 649 | uint8_t BOS : 1; /* Byte Order Select. */
|
---|
| 650 | uint8_t LAS : 1; /* Long Address Select. */
|
---|
| 651 | uint8_t LS : 1; /* Loopback Select. */
|
---|
| 652 | uint8_t ARM : 1; /* Auto-Initialize Remote. */
|
---|
| 653 | uint8_t FT : 2; /* Fifo Threshold Select. */
|
---|
| 654 | uint8_t res : 1; /* Reserved. */
|
---|
| 655 | } DP_DCR;
|
---|
| 656 | AssertCompile(sizeof(DP_DCR) == sizeof(uint8_t));
|
---|
| 657 |
|
---|
| 658 | /** Transmit Configuration Register (TCR). */
|
---|
| 659 | typedef struct DP_TCR {
|
---|
| 660 | uint8_t CRC : 1; /* Inhibit CRC. */
|
---|
| 661 | uint8_t LB : 2; /* Loopback Control. */
|
---|
| 662 | uint8_t ATD : 1; /* Auto Transmit Disable. */
|
---|
| 663 | uint8_t OFST : 1; /* Collision Offset Enable. */
|
---|
| 664 | uint8_t res : 3; /* Reserved. */
|
---|
| 665 | } DP_TCR;
|
---|
| 666 | AssertCompile(sizeof(DP_TCR) == sizeof(uint8_t));
|
---|
| 667 |
|
---|
| 668 | /** Transmit Status Register (TSR). */
|
---|
| 669 | typedef struct DP_TSR {
|
---|
| 670 | uint8_t PTX : 1; /* Packet Transmitted. */
|
---|
| 671 | uint8_t DFR : 1; /* Non-Deferred Transmission (reserved in DP83901A). */
|
---|
| 672 | uint8_t COL : 1; /* Transmit Collided. */
|
---|
| 673 | uint8_t ABT : 1; /* Transmit Aborted. */
|
---|
| 674 | uint8_t CRS : 1; /* Carrier Sense Lost. */
|
---|
| 675 | uint8_t FU : 1; /* FIFO Underrun. */
|
---|
| 676 | uint8_t CDH : 1; /* CD Heartbeat. */
|
---|
| 677 | uint8_t OWC : 1; /* Out of Window Collision. */
|
---|
| 678 | } DP_TSR;
|
---|
| 679 | AssertCompile(sizeof(DP_TSR) == sizeof(uint8_t));
|
---|
| 680 |
|
---|
| 681 | /** Receive Configuration Register (RCR). */
|
---|
| 682 | typedef struct DP_RCR {
|
---|
| 683 | uint8_t SEP : 1; /* Save Errored Packets. */
|
---|
| 684 | uint8_t AR : 1; /* Accept Runt Packets. */
|
---|
| 685 | uint8_t AB : 1; /* Accept Broadcast. */
|
---|
| 686 | uint8_t AM : 1; /* Accept Multicast. */
|
---|
| 687 | uint8_t PRO : 1; /* Promiscuous Physical. */
|
---|
| 688 | uint8_t MON : 1; /* Monitor Mode. */
|
---|
| 689 | uint8_t res : 2; /* Reserved. */
|
---|
| 690 | } DP_RCR;
|
---|
| 691 | AssertCompile(sizeof(DP_RCR) == sizeof(uint8_t));
|
---|
| 692 |
|
---|
| 693 | /** Receive Status Register (RSR). */
|
---|
| 694 | typedef struct DP_RSR {
|
---|
| 695 | uint8_t PRX : 1; /* Packet Received Intact. */
|
---|
| 696 | uint8_t CRC : 1; /* CRC Error. */
|
---|
| 697 | uint8_t FAE : 1; /* Frame Alignment Error. */
|
---|
| 698 | uint8_t FO : 1; /* FIFO Overrun. */
|
---|
| 699 | uint8_t MPA : 1; /* Missed Packet. */
|
---|
| 700 | uint8_t PHY : 1; /* Physical/Multicast Address. */
|
---|
| 701 | uint8_t DIS : 1; /* Receiver Disabled. */
|
---|
| 702 | uint8_t DFR : 1; /* Deferring. */
|
---|
| 703 | } DP_RSR;
|
---|
| 704 | AssertCompile(sizeof(DP_RSR) == sizeof(uint8_t));
|
---|
| 705 |
|
---|
| 706 | /** Transmit Byte Count Register. */
|
---|
| 707 | typedef struct DP_TBCR {
|
---|
| 708 | uint8_t TBCR0;
|
---|
| 709 | uint8_t TBCR1;
|
---|
| 710 | } DP_TBCR;
|
---|
| 711 | AssertCompile(sizeof(DP_TBCR) == sizeof(uint16_t));
|
---|
| 712 |
|
---|
| 713 | /** Current Local DMA Address. */
|
---|
| 714 | typedef struct DP_CLDA {
|
---|
| 715 | uint8_t CLDA0;
|
---|
| 716 | uint8_t CLDA1;
|
---|
| 717 | } DP_CLDA;
|
---|
| 718 | AssertCompile(sizeof(DP_CLDA) == sizeof(uint16_t));
|
---|
| 719 |
|
---|
| 720 | /** Remote Start Address Register. */
|
---|
| 721 | typedef struct DP_RSAR {
|
---|
| 722 | uint8_t RSAR0;
|
---|
| 723 | uint8_t RSAR1;
|
---|
| 724 | } DP_RSAR;
|
---|
| 725 | AssertCompile(sizeof(DP_RSAR) == sizeof(uint16_t));
|
---|
| 726 |
|
---|
| 727 | /** Remote Byte Count Register. */
|
---|
| 728 | typedef struct DP_RBCR {
|
---|
| 729 | uint8_t RBCR0;
|
---|
| 730 | uint8_t RBCR1;
|
---|
| 731 | } DP_RBCR;
|
---|
| 732 | AssertCompile(sizeof(DP_RBCR) == sizeof(uint16_t));
|
---|
| 733 |
|
---|
| 734 | /** Current Remote DMA Address. */
|
---|
| 735 | typedef struct DP_CRDA {
|
---|
| 736 | uint8_t CRDA0;
|
---|
| 737 | uint8_t CRDA1;
|
---|
| 738 | } DP_CRDA;
|
---|
| 739 | AssertCompile(sizeof(DP_CRDA) == sizeof(uint16_t));
|
---|
| 740 |
|
---|
| 741 | /** Page 1 registers. */
|
---|
| 742 | /* All registers read/write without side effects, unlike pages 0/2. */
|
---|
| 743 | typedef struct DP_PG1 {
|
---|
| 744 | uint8_t dummy_cr;
|
---|
| 745 | uint8_t PAR[6]; /* Physical Address PAR0-PAR5. */
|
---|
| 746 | uint8_t dummy_curr; /* Current Page Register. */
|
---|
| 747 | uint8_t MAR[8]; /* Multicast Address Register MAR0-MAR7. */
|
---|
| 748 | } DP_PG1;
|
---|
| 749 | AssertCompile(sizeof(DP_PG1) == 16);
|
---|
| 750 |
|
---|
| 751 | /** DP8390 FIFO. Not all of the state is explicitly accessible. */
|
---|
| 752 | typedef struct DP_FIFO {
|
---|
| 753 | uint8_t rp; /* Read pointer. */
|
---|
| 754 | uint8_t wp; /* Write pointer. */
|
---|
| 755 | uint8_t fifo[16]; /* 16 bytes of FIFO. */
|
---|
| 756 | } DP_FIFO;
|
---|
| 757 |
|
---|
| 758 | /**
|
---|
| 759 | * Core DP8390 chip state.
|
---|
| 760 | */
|
---|
| 761 | typedef struct DP8390CORE
|
---|
| 762 | {
|
---|
| 763 | union {
|
---|
| 764 | uint8_t CR; /* Command Register. */
|
---|
| 765 | DP_CR cr;
|
---|
| 766 | };
|
---|
| 767 | union {
|
---|
| 768 | uint8_t DCR; /* Data Control Register. */
|
---|
| 769 | DP_DCR dcr;
|
---|
| 770 | };
|
---|
| 771 | /* Interrupt control. */
|
---|
| 772 | union {
|
---|
| 773 | uint8_t ISR; /* Interrupt Status Register. */
|
---|
| 774 | DP_ISR isr;
|
---|
| 775 | };
|
---|
| 776 | union {
|
---|
| 777 | uint8_t IMR; /* Interrupt Mask Register. */
|
---|
| 778 | DP_IMR imr;
|
---|
| 779 | };
|
---|
| 780 | /* Receive state. */
|
---|
| 781 | union {
|
---|
| 782 | uint8_t RCR; /* Receive Control Register. */
|
---|
| 783 | DP_RCR rcr;
|
---|
| 784 | };
|
---|
| 785 | union {
|
---|
| 786 | uint8_t RSR; /* Receive Status register. */
|
---|
| 787 | DP_RSR rsr;
|
---|
| 788 | };
|
---|
| 789 | /* Transmit State. */
|
---|
| 790 | union {
|
---|
| 791 | uint8_t TCR; /* Transmit Control Register. */
|
---|
| 792 | DP_TCR tcr;
|
---|
| 793 | };
|
---|
| 794 | union {
|
---|
| 795 | uint8_t TSR; /* Transmit Status register. */
|
---|
| 796 | DP_TSR tsr;
|
---|
| 797 | };
|
---|
| 798 | uint8_t NCR; /* Number of Collisions Register. */
|
---|
| 799 | /* Local DMA transmit state. */
|
---|
| 800 | uint8_t TPSR; /* Transmit Page Start. */
|
---|
| 801 | union {
|
---|
| 802 | uint16_t TBCR; /* Transmit Byte Count. */
|
---|
| 803 | DP_TBCR tbcr;
|
---|
| 804 | };
|
---|
| 805 | /* Local DMA receive state. */
|
---|
| 806 | union {
|
---|
| 807 | uint16_t CLDA; /* Current Local DMA Address. */
|
---|
| 808 | DP_CLDA clda;
|
---|
| 809 | };
|
---|
| 810 | uint8_t PSTART; /* Page Start. */
|
---|
| 811 | uint8_t PSTOP; /* Page Stop. */
|
---|
| 812 | uint8_t CURR; /* Current Page. */
|
---|
| 813 | uint8_t BNRY; /* Boundary Page. Also spelled BNDRY. */
|
---|
| 814 | /* Remote DMA state. */
|
---|
| 815 | union {
|
---|
| 816 | uint16_t RSAR; /* Remote Start Address Register. */
|
---|
| 817 | DP_RSAR rsar;
|
---|
| 818 | };
|
---|
| 819 | union {
|
---|
| 820 | uint16_t RBCR; /* Remote Byte Count Register. */
|
---|
| 821 | DP_RBCR rbcr;
|
---|
| 822 | };
|
---|
| 823 | union {
|
---|
| 824 | uint16_t CRDA; /* Current Remote DMA Address. */
|
---|
| 825 | DP_CRDA crda;
|
---|
| 826 | };
|
---|
| 827 | /* Miscellaneous state. */
|
---|
| 828 | uint8_t lnxtpp; /* Local Next Packet Pointer. */
|
---|
| 829 | uint8_t rnxtpp; /* Remote Next Packet Pointer. */
|
---|
| 830 | /* Tally counters. */
|
---|
| 831 | uint8_t CNTR0; /* Frame Alignment Errors. */
|
---|
| 832 | uint8_t CNTR1; /* CRC Errors. */
|
---|
| 833 | uint8_t CNTR2; /* Missed Packet Errors. */
|
---|
| 834 | union {
|
---|
| 835 | uint8_t PG1[sizeof(DP_PG1)];
|
---|
| 836 | DP_PG1 pg1; /* All Page 1 Registers. */
|
---|
| 837 | };
|
---|
| 838 | DP_FIFO fifo; /* The internal FIFO. */
|
---|
| 839 | } DP8390CORE, *PDP8390CORE;
|
---|
| 840 |
|
---|
| 841 | /**
|
---|
| 842 | * DP8390-based card state.
|
---|
| 843 | */
|
---|
| 844 | typedef struct DPNICSTATE
|
---|
| 845 | {
|
---|
| 846 | /** Restore timer.
|
---|
| 847 | * This is used to disconnect and reconnect the link after a restore. */
|
---|
| 848 | TMTIMERHANDLE hTimerRestore;
|
---|
| 849 |
|
---|
| 850 | /** Transmit signaller. */
|
---|
| 851 | PDMTASKHANDLE hXmitTask;
|
---|
| 852 | /** Receive ready signaller. */
|
---|
| 853 | PDMTASKHANDLE hCanRxTask;
|
---|
| 854 |
|
---|
| 855 | /** Emulated device type. */
|
---|
| 856 | uint8_t uDevType;
|
---|
| 857 | /** State of the card's interrupt request signal. */
|
---|
| 858 | bool fNicIrqActive;
|
---|
| 859 |
|
---|
| 860 | /** Core DP8390 chip state. */
|
---|
| 861 | DP8390CORE core;
|
---|
| 862 |
|
---|
| 863 | /** WD80x3 Control Register 1. */
|
---|
| 864 | union {
|
---|
| 865 | uint8_t CTRL1;
|
---|
| 866 | WD_CTRL1 ctrl1;
|
---|
| 867 | };
|
---|
| 868 | /** WD80x3 Control Register 2. */
|
---|
| 869 | union {
|
---|
| 870 | uint8_t CTRL2;
|
---|
| 871 | WD_CTRL2 ctrl2;
|
---|
| 872 | };
|
---|
| 873 |
|
---|
| 874 | /** 3C503 Gate Array state. */
|
---|
| 875 | EL_GA ga;
|
---|
| 876 | /** The 3C503 soft-configured ISA DMA channel. */
|
---|
| 877 | uint8_t uElIsaDma;
|
---|
| 878 |
|
---|
| 879 | /** The PROM contents. 32 bytes addressable, R/O. */
|
---|
| 880 | uint8_t aPROM[32];
|
---|
| 881 |
|
---|
| 882 | /** Shared RAM base. */
|
---|
| 883 | RTGCPHYS MemBase;
|
---|
| 884 | /** Shared RAM MMIO region handle. */
|
---|
| 885 | PGMMMIO2HANDLE hSharedMem;
|
---|
| 886 | /** Shared RAM size. */
|
---|
| 887 | RTGCPHYS cbMemSize;
|
---|
| 888 |
|
---|
| 889 | /** Base port of the I/O space region. */
|
---|
| 890 | RTIOPORT IOPortBase;
|
---|
| 891 | /** The configured ISA IRQ. */
|
---|
| 892 | uint8_t uIsaIrq;
|
---|
| 893 | /** The configured ISA DMA channel. */
|
---|
| 894 | uint8_t uIsaDma;
|
---|
| 895 | /** If set the link is currently up. */
|
---|
| 896 | bool fLinkUp;
|
---|
| 897 | /** If set the link is temporarily down because of a saved state load. */
|
---|
| 898 | bool fLinkTempDown;
|
---|
| 899 | /** Number of times we've reported the link down. */
|
---|
| 900 | uint16_t cLinkDownReported;
|
---|
[93675] | 901 | /** Number of times we've postponed the link restore. */
|
---|
| 902 | uint16_t cLinkRestorePostponed;
|
---|
[93560] | 903 |
|
---|
| 904 | /** The "hardware" MAC address. */
|
---|
| 905 | RTMAC MacConfigured;
|
---|
| 906 |
|
---|
[93601] | 907 | /** Set if DPNICSTATER3::pDrv is not NULL. */
|
---|
| 908 | bool fDriverAttached;
|
---|
[93560] | 909 | /** The LED. */
|
---|
| 910 | PDMLED Led;
|
---|
| 911 | /** Status LUN: The LED ports. */
|
---|
| 912 | PDMILEDPORTS ILeds;
|
---|
| 913 | /** Partner of ILeds. */
|
---|
| 914 | R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
|
---|
| 915 |
|
---|
| 916 | /** Access critical section. */
|
---|
| 917 | PDMCRITSECT CritSect;
|
---|
| 918 | /** Event semaphore for blocking on receive. */
|
---|
| 919 | RTSEMEVENT hEventOutOfRxSpace;
|
---|
| 920 | /** We are waiting/about to start waiting for more receive buffers. */
|
---|
| 921 | bool volatile fMaybeOutOfSpace;
|
---|
| 922 |
|
---|
| 923 | /* MS to wait before we enable the link. */
|
---|
| 924 | uint32_t cMsLinkUpDelay;
|
---|
| 925 | /** The device instance number (for logging). */
|
---|
| 926 | uint32_t iInstance;
|
---|
| 927 |
|
---|
| 928 | STAMCOUNTER StatReceiveBytes;
|
---|
| 929 | STAMCOUNTER StatTransmitBytes;
|
---|
| 930 | #ifdef VBOX_WITH_STATISTICS
|
---|
| 931 | STAMPROFILEADV StatIOReadRZ;
|
---|
| 932 | STAMPROFILEADV StatIOReadR3;
|
---|
| 933 | STAMPROFILEADV StatIOWriteRZ;
|
---|
| 934 | STAMPROFILEADV StatIOWriteR3;
|
---|
| 935 | STAMPROFILEADV StatReceive;
|
---|
| 936 | STAMPROFILEADV StatTransmitR3;
|
---|
| 937 | STAMPROFILEADV StatTransmitRZ;
|
---|
| 938 | STAMPROFILE StatTransmitSendR3;
|
---|
| 939 | STAMPROFILE StatTransmitSendRZ;
|
---|
| 940 | STAMPROFILE StatRxOverflow;
|
---|
| 941 | STAMCOUNTER StatRxOverflowWakeup;
|
---|
| 942 | STAMCOUNTER StatRxCanReceiveNow;
|
---|
| 943 | STAMCOUNTER StatRxCannotReceiveNow;
|
---|
| 944 | STAMPROFILEADV StatInterrupt;
|
---|
| 945 | STAMCOUNTER StatDropPktMonitor;
|
---|
| 946 | STAMCOUNTER StatDropPktRcvrDis;
|
---|
| 947 | STAMCOUNTER StatDropPktVeryShort;
|
---|
| 948 | STAMCOUNTER StatDropPktVMNotRunning;
|
---|
| 949 | STAMCOUNTER StatDropPktNoLink;
|
---|
| 950 | STAMCOUNTER StatDropPktNoMatch;
|
---|
| 951 | STAMCOUNTER StatDropPktNoBuffer;
|
---|
| 952 | #endif /* VBOX_WITH_STATISTICS */
|
---|
| 953 |
|
---|
| 954 | /** NIC-specific ISA I/O ports. */
|
---|
| 955 | IOMIOPORTHANDLE hIoPortsNic;
|
---|
| 956 |
|
---|
| 957 | /** Common DP8390 core I/O ports. */
|
---|
| 958 | IOMIOPORTHANDLE hIoPortsCore;
|
---|
| 959 |
|
---|
| 960 | /** The runt pad buffer (only really needs 60 bytes). */
|
---|
| 961 | uint8_t abRuntBuf[64];
|
---|
| 962 |
|
---|
| 963 | /** The packet buffer. */
|
---|
| 964 | uint8_t abLocalRAM[DPNIC_MEM_SIZE];
|
---|
| 965 |
|
---|
| 966 | /** The loopback transmit buffer (avoid stack allocations). */
|
---|
| 967 | uint8_t abLoopBuf[DPNIC_MEM_SIZE]; /// @todo Can this be smaller?
|
---|
| 968 | } DPNICSTATE, *PDPNICSTATE;
|
---|
| 969 |
|
---|
| 970 |
|
---|
[93601] | 971 | /**
|
---|
| 972 | * DP8390 state for ring-3.
|
---|
| 973 | *
|
---|
| 974 | * @implements PDMIBASE
|
---|
| 975 | * @implements PDMINETWORKDOWN
|
---|
| 976 | * @implements PDMINETWORKCONFIG
|
---|
| 977 | * @implements PDMILEDPORTS
|
---|
| 978 | */
|
---|
| 979 | typedef struct DPNICSTATER3
|
---|
| 980 | {
|
---|
| 981 | /** Pointer to the device instance. */
|
---|
| 982 | PPDMDEVINSR3 pDevIns;
|
---|
| 983 | /** Pointer to the connector of the attached network driver. */
|
---|
| 984 | PPDMINETWORKUPR3 pDrv;
|
---|
| 985 | /** Pointer to the attached network driver. */
|
---|
| 986 | R3PTRTYPE(PPDMIBASE) pDrvBase;
|
---|
| 987 | /** LUN\#0 + status LUN: The base interface. */
|
---|
| 988 | PDMIBASE IBase;
|
---|
| 989 | /** LUN\#0: The network port interface. */
|
---|
| 990 | PDMINETWORKDOWN INetworkDown;
|
---|
| 991 | /** LUN\#0: The network config port interface. */
|
---|
| 992 | PDMINETWORKCONFIG INetworkConfig;
|
---|
| 993 |
|
---|
| 994 | /** Status LUN: The LED ports. */
|
---|
| 995 | PDMILEDPORTS ILeds;
|
---|
| 996 | /** Partner of ILeds. */
|
---|
| 997 | R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
|
---|
| 998 | } DPNICSTATER3;
|
---|
| 999 | /** Pointer to a DP8390 state structure for ring-3. */
|
---|
| 1000 | typedef DPNICSTATER3 *PDPNICSTATER3;
|
---|
| 1001 |
|
---|
| 1002 |
|
---|
| 1003 | /**
|
---|
| 1004 | * DP8390 state for ring-0.
|
---|
| 1005 | */
|
---|
| 1006 | typedef struct DPNICSTATER0
|
---|
| 1007 | {
|
---|
| 1008 | /** Pointer to the connector of the attached network driver. */
|
---|
| 1009 | PPDMINETWORKUPR0 pDrv;
|
---|
| 1010 | } DPNICSTATER0;
|
---|
| 1011 | /** Pointer to a DP8390 state structure for ring-0. */
|
---|
| 1012 | typedef DPNICSTATER0 *PDPNICSTATER0;
|
---|
| 1013 |
|
---|
| 1014 |
|
---|
| 1015 | /**
|
---|
| 1016 | * DP8390 state for raw-mode.
|
---|
| 1017 | */
|
---|
| 1018 | typedef struct DPNICSTATERC
|
---|
| 1019 | {
|
---|
| 1020 | /** Pointer to the connector of the attached network driver. */
|
---|
| 1021 | PPDMINETWORKUPRC pDrv;
|
---|
| 1022 | } DPNICSTATERC;
|
---|
| 1023 | /** Pointer to a DP8390 state structure for raw-mode. */
|
---|
| 1024 | typedef DPNICSTATERC *PDPNICSTATERC;
|
---|
| 1025 |
|
---|
| 1026 |
|
---|
| 1027 | /** The DP8390 state structure for the current context. */
|
---|
| 1028 | typedef CTX_SUFF(DPNICSTATE) DPNICSTATECC;
|
---|
| 1029 | /** Pointer to a DP8390 state structure for the current
|
---|
| 1030 | * context. */
|
---|
| 1031 | typedef CTX_SUFF(PDPNICSTATE) PDPNICSTATECC;
|
---|
| 1032 |
|
---|
| 1033 |
|
---|
[93560] | 1034 | #ifndef VBOX_DEVICE_STRUCT_TESTCASE
|
---|
| 1035 |
|
---|
| 1036 |
|
---|
| 1037 | /*********************************************************************************************************************************
|
---|
| 1038 | * Internal Functions *
|
---|
| 1039 | *********************************************************************************************************************************/
|
---|
| 1040 |
|
---|
[93601] | 1041 | static int dp8390CoreAsyncXmitLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PDPNICSTATECC pThisCC, bool fOnWorkerThread);
|
---|
[93560] | 1042 |
|
---|
| 1043 | /**
|
---|
| 1044 | * Checks if the link is up.
|
---|
| 1045 | * @returns true if the link is up.
|
---|
| 1046 | * @returns false if the link is down.
|
---|
| 1047 | */
|
---|
| 1048 | DECLINLINE(bool) dp8390IsLinkUp(PDPNICSTATE pThis)
|
---|
| 1049 | {
|
---|
[93601] | 1050 | return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
|
---|
[93560] | 1051 | }
|
---|
| 1052 |
|
---|
| 1053 |
|
---|
| 1054 | /* Table and macro borrowed from DevPCNet.cpp. */
|
---|
| 1055 | #define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
|
---|
| 1056 |
|
---|
| 1057 | /* generated using the AUTODIN II polynomial
|
---|
| 1058 | * x^32 + x^26 + x^23 + x^22 + x^16 +
|
---|
| 1059 | * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
|
---|
| 1060 | */
|
---|
| 1061 | static const uint32_t crctab[256] =
|
---|
| 1062 | {
|
---|
| 1063 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
---|
| 1064 | 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
---|
| 1065 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
---|
| 1066 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
---|
| 1067 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
---|
| 1068 | 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
---|
| 1069 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
---|
| 1070 | 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
---|
| 1071 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
---|
| 1072 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
---|
| 1073 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
---|
| 1074 | 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
---|
| 1075 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
---|
| 1076 | 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
---|
| 1077 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
---|
| 1078 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
---|
| 1079 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
---|
| 1080 | 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
---|
| 1081 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
---|
| 1082 | 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
---|
| 1083 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
---|
| 1084 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
---|
| 1085 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
---|
| 1086 | 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
---|
| 1087 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
---|
| 1088 | 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
---|
| 1089 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
---|
| 1090 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
---|
| 1091 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
---|
| 1092 | 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
---|
| 1093 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
---|
| 1094 | 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
---|
| 1095 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
---|
| 1096 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
---|
| 1097 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
---|
| 1098 | 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
---|
| 1099 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
---|
| 1100 | 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
---|
| 1101 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
---|
| 1102 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
---|
| 1103 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
---|
| 1104 | 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
---|
| 1105 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
---|
| 1106 | 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
---|
| 1107 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
---|
| 1108 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
---|
| 1109 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
---|
| 1110 | 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
---|
| 1111 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
---|
| 1112 | 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
---|
| 1113 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
---|
| 1114 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
---|
| 1115 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
---|
| 1116 | 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
---|
| 1117 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
---|
| 1118 | 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
---|
| 1119 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
---|
| 1120 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
---|
| 1121 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
---|
| 1122 | 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
---|
| 1123 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
---|
| 1124 | 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
---|
| 1125 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
---|
| 1126 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
---|
| 1127 | };
|
---|
| 1128 |
|
---|
| 1129 |
|
---|
| 1130 | #ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
|
---|
| 1131 | #define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
|
---|
| 1132 | #endif
|
---|
| 1133 |
|
---|
| 1134 |
|
---|
| 1135 | /**
|
---|
| 1136 | * Check if incoming frame matches the station address.
|
---|
| 1137 | */
|
---|
| 1138 | DECLINLINE(int) padr_match(PDPNICSTATE pThis, const uint8_t *buf)
|
---|
| 1139 | {
|
---|
| 1140 | RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
|
---|
| 1141 | int result;
|
---|
| 1142 |
|
---|
| 1143 | /* Checks own address only; always enabled if receiver on. */
|
---|
| 1144 | result = !memcmp(hdr->DstMac.au8, pThis->core.pg1.PAR, 6);
|
---|
| 1145 |
|
---|
| 1146 | return result;
|
---|
| 1147 | }
|
---|
| 1148 |
|
---|
| 1149 |
|
---|
| 1150 | /**
|
---|
| 1151 | * Check if incoming frame is an accepted broadcast frame.
|
---|
| 1152 | */
|
---|
| 1153 | DECLINLINE(int) padr_bcast(PDPNICSTATE pThis, const uint8_t *buf)
|
---|
| 1154 | {
|
---|
| 1155 | static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
---|
| 1156 | RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
|
---|
| 1157 | int result = pThis->core.rcr.AB && !memcmp(hdr->DstMac.au8, aBCAST, 6);
|
---|
| 1158 | return result;
|
---|
| 1159 | }
|
---|
| 1160 |
|
---|
| 1161 |
|
---|
| 1162 | /**
|
---|
| 1163 | * Check if incoming frame is an accepted multicast frame.
|
---|
| 1164 | */
|
---|
| 1165 | DECLINLINE(int) padr_mcast(PDPNICSTATE pThis, const uint8_t *buf, int *mcast_type)
|
---|
| 1166 | {
|
---|
| 1167 | uint32_t crc = UINT32_MAX;
|
---|
| 1168 | RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
|
---|
| 1169 | int result = 0;
|
---|
| 1170 |
|
---|
| 1171 | /* If multicast addresses are enabled, and the destination
|
---|
| 1172 | * address is in fact multicast, the address must be run through
|
---|
| 1173 | * the CRC generator and matched against the multicast filter
|
---|
| 1174 | * array.
|
---|
| 1175 | */
|
---|
| 1176 | if (pThis->core.rcr.AM && ETHER_IS_MULTICAST(hdr->DstMac.au8))
|
---|
| 1177 | {
|
---|
[93562] | 1178 | unsigned i;
|
---|
[93560] | 1179 | const uint8_t *p = buf;
|
---|
| 1180 | unsigned crc_frag, crc_rev;
|
---|
| 1181 | unsigned ma_bit_mask, ma_byte_idx;
|
---|
| 1182 |
|
---|
| 1183 | /* Indicate to caller that the address is a multicast one, regardless
|
---|
| 1184 | * of whether it's accepted or not.
|
---|
| 1185 | */
|
---|
| 1186 | *mcast_type = 1;
|
---|
| 1187 |
|
---|
| 1188 | for (i = 0; i < sizeof(hdr->DstMac); ++i)
|
---|
| 1189 | CRC(crc, *p++);
|
---|
| 1190 |
|
---|
| 1191 | /* The top 6 bits of the CRC calculated from the destination address
|
---|
| 1192 | * becomes an index into the 64-bit multicast address register. Sadly
|
---|
| 1193 | * our CRC algorithm is bit-reversed (Ethernet shifts bits out MSB first)
|
---|
| 1194 | * so instead of the top 6 bits of the CRC we have to take the bottom 6
|
---|
| 1195 | * and reverse the bits.
|
---|
| 1196 | */
|
---|
| 1197 | crc_frag = crc & 63;
|
---|
| 1198 |
|
---|
| 1199 | for (i = 0, crc_rev = 0; i < 6; ++i)
|
---|
| 1200 | crc_rev |= ((crc_frag >> i) & 1) * (0x20 >> i);
|
---|
| 1201 |
|
---|
| 1202 | ma_bit_mask = 1 << (crc_rev & 7);
|
---|
| 1203 | ma_byte_idx = crc_rev / 8;
|
---|
| 1204 | Log3Func(("crc=%08X, crc_frag=%u, crc_rev=%u, ma_byte_idx=%u, ma_bit_mask=%02X\n", crc, crc_frag, crc_rev, ma_byte_idx, ma_bit_mask));
|
---|
| 1205 | Log3Func(("MAR: %02X:%02X:%02X:%02X %02X:%02X:%02X:%02X\n", pThis->core.pg1.MAR[0], pThis->core.pg1.MAR[1], pThis->core.pg1.MAR[2], pThis->core.pg1.MAR[3], pThis->core.pg1.MAR[4], pThis->core.pg1.MAR[5], pThis->core.pg1.MAR[6], pThis->core.pg1.MAR[7]));
|
---|
| 1206 |
|
---|
| 1207 | /* The multicast filtering logic is fairly extensively
|
---|
| 1208 | * verified by EtherLink II diagnostics (3C503.EXE).
|
---|
| 1209 | */
|
---|
| 1210 | if (pThis->core.pg1.MAR[ma_byte_idx] & ma_bit_mask)
|
---|
| 1211 | {
|
---|
| 1212 | Log3Func(("Passed multicast filter\n"));
|
---|
| 1213 | result = 1;
|
---|
| 1214 | }
|
---|
| 1215 | }
|
---|
| 1216 |
|
---|
| 1217 | return result;
|
---|
| 1218 | }
|
---|
| 1219 |
|
---|
| 1220 |
|
---|
| 1221 | /**
|
---|
| 1222 | * Check if incoming frame is an accepted promiscuous frame.
|
---|
| 1223 | */
|
---|
| 1224 | DECLINLINE(int) padr_promi(PDPNICSTATE pThis, const uint8_t *buf)
|
---|
| 1225 | {
|
---|
| 1226 | RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
|
---|
| 1227 | int result = pThis->core.rcr.PRO && !ETHER_IS_MULTICAST(hdr->DstMac.au8);
|
---|
| 1228 | return result;
|
---|
| 1229 | }
|
---|
| 1230 |
|
---|
| 1231 |
|
---|
| 1232 | /**
|
---|
| 1233 | * Update the device IRQ line based on internal state.
|
---|
| 1234 | */
|
---|
[93601] | 1235 | static void dp8390CoreUpdateIrq(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 1236 | {
|
---|
| 1237 | bool fCoreIrqActive = false;
|
---|
| 1238 | bool fNicIrqActive = false;
|
---|
| 1239 |
|
---|
| 1240 | STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
|
---|
| 1241 |
|
---|
| 1242 | /* Set the ISR.CNT bit based on the counter state (top counter bits ANDed together). */
|
---|
| 1243 | pThis->core.isr.CNT = (pThis->core.CNTR0 & pThis->core.CNTR1 & pThis->core.CNTR2) >> 7;
|
---|
| 1244 |
|
---|
| 1245 | /* IRQ is active if a bit is set in ISR and the corresponding bit
|
---|
| 1246 | * is set in IMR. No additional internal state needed.
|
---|
| 1247 | */
|
---|
| 1248 | Assert(!pThis->core.imr.res);
|
---|
| 1249 | if (pThis->core.ISR & pThis->core.IMR)
|
---|
| 1250 | fCoreIrqActive = true;
|
---|
| 1251 |
|
---|
| 1252 | /* The 3C503 has additional interrupt sources and control. For other device
|
---|
| 1253 | * types, the extras magically work out to be a no-op.
|
---|
| 1254 | */
|
---|
| 1255 | pThis->ga.fGaIrq = pThis->ga.streg.dtc && !pThis->ga.gacfr.tcm;
|
---|
| 1256 | fNicIrqActive = (fCoreIrqActive && !pThis->ga.gacfr.nim) || (pThis->ga.streg.dtc && !pThis->ga.gacfr.tcm);
|
---|
| 1257 |
|
---|
[93601] | 1258 | Log2Func(("#%d set irq fNicIrqActive=%d (fCoreIrqActive=%d, fGaIrq=%d)\n", pThis->iInstance, fNicIrqActive, fCoreIrqActive, pThis->ga.fGaIrq));
|
---|
[93560] | 1259 |
|
---|
| 1260 | /* The IRQ line typically does not change. */
|
---|
| 1261 | if (RT_UNLIKELY(fNicIrqActive != pThis->fNicIrqActive))
|
---|
| 1262 | {
|
---|
[93601] | 1263 | LogFunc(("#%d IRQ=%d, state=%d\n", pThis->iInstance, pThis->uIsaIrq, fNicIrqActive));
|
---|
[93560] | 1264 | /// @todo Handle IRQ 2/9 elsewhere
|
---|
[93601] | 1265 | PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq == 2 ? 9 : pThis->uIsaIrq, fNicIrqActive);
|
---|
[93560] | 1266 | pThis->fNicIrqActive = fNicIrqActive;
|
---|
| 1267 | }
|
---|
| 1268 | STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
|
---|
| 1269 | }
|
---|
| 1270 |
|
---|
| 1271 |
|
---|
| 1272 | /**
|
---|
| 1273 | * Perform a software reset of the NIC.
|
---|
| 1274 | */
|
---|
[93601] | 1275 | static void dp8390CoreReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 1276 | {
|
---|
[93601] | 1277 | LogFlowFunc(("#%d:\n", pThis->iInstance));
|
---|
[93560] | 1278 |
|
---|
| 1279 | /* DP8390 or DP83901A datasheet, section 11.0. */
|
---|
| 1280 | pThis->core.cr.TXP = 0;
|
---|
| 1281 | pThis->core.cr.STA = 0;
|
---|
| 1282 | pThis->core.cr.STP = 1;
|
---|
| 1283 | pThis->core.cr.RD = DP_CR_RDMA_ABRT;
|
---|
| 1284 | pThis->core.isr.RST = 1;
|
---|
| 1285 | pThis->core.IMR = 0;
|
---|
| 1286 | pThis->core.dcr.LAS = 0;
|
---|
| 1287 | pThis->core.tcr.LB = 0;
|
---|
| 1288 |
|
---|
| 1289 | /// @todo Check if this really happens on soft reset
|
---|
| 1290 | /* Clear the internal FIFO including r/w pointers. */
|
---|
| 1291 | memset(&pThis->core.fifo, 0, sizeof(pThis->core.fifo));
|
---|
| 1292 |
|
---|
| 1293 | /* Make sure the IRQ line us updated. */
|
---|
[93601] | 1294 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 1295 | }
|
---|
| 1296 |
|
---|
| 1297 | #ifdef IN_RING3
|
---|
| 1298 |
|
---|
[93601] | 1299 | static DECLCALLBACK(void) dp8390R3WakeupReceive(PPDMDEVINS pDevIns)
|
---|
[93560] | 1300 | {
|
---|
[93601] | 1301 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
| 1302 | LogFlowFunc(("#%d\n", pThis->iInstance));
|
---|
[93560] | 1303 | STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
|
---|
| 1304 | if (pThis->hEventOutOfRxSpace != NIL_RTSEMEVENT)
|
---|
| 1305 | RTSemEventSignal(pThis->hEventOutOfRxSpace);
|
---|
| 1306 | }
|
---|
| 1307 |
|
---|
| 1308 | /**
|
---|
| 1309 | * @callback_method_impl{FNPDMTASKDEV,
|
---|
| 1310 | * Signal to R3 that NIC is ready to receive a packet.
|
---|
| 1311 | */
|
---|
| 1312 | static DECLCALLBACK(void) dpNicR3CanRxTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
|
---|
| 1313 | {
|
---|
| 1314 | RT_NOREF(pvUser);
|
---|
[93601] | 1315 | dp8390R3WakeupReceive(pDevIns);
|
---|
[93560] | 1316 | }
|
---|
| 1317 |
|
---|
| 1318 | #endif /* IN_RING3 */
|
---|
| 1319 |
|
---|
| 1320 | /**
|
---|
| 1321 | * Read up to 256 bytes from a single page of local RAM.
|
---|
| 1322 | */
|
---|
| 1323 | static void dpLocalRAMReadBuf(PDPNICSTATE pThis, uint16_t addr, unsigned cb, uint8_t *pDst)
|
---|
| 1324 | {
|
---|
| 1325 | if ((RT_LOBYTE(addr) + cb) > 256)
|
---|
| 1326 | {
|
---|
[93601] | 1327 | LogFunc(("#%d: addr=%04X, cb=%X, cb!!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1328 | cb = 256 - RT_LOBYTE(addr);
|
---|
| 1329 | }
|
---|
| 1330 |
|
---|
| 1331 | /* A single page is always either entirely inside or outside local RAM. */
|
---|
| 1332 | if (pThis->uDevType == DEV_NE1000)
|
---|
| 1333 | {
|
---|
| 1334 | /* Only 14 bits of address are decoded. */
|
---|
| 1335 | addr &= 0x3fff;
|
---|
| 1336 | if (addr >= 0x2000)
|
---|
| 1337 | {
|
---|
| 1338 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 1339 | addr -= 0x2000;
|
---|
| 1340 | memcpy(pDst, &pThis->abLocalRAM[addr], cb);
|
---|
| 1341 | }
|
---|
| 1342 | else
|
---|
[93601] | 1343 | LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1344 | }
|
---|
| 1345 | else if (pThis->uDevType == DEV_NE2000)
|
---|
| 1346 | {
|
---|
| 1347 | /* Only 15 bits of address are decoded. */
|
---|
| 1348 | addr &= 0x7fff;
|
---|
| 1349 | if (addr >= 0x4000)
|
---|
| 1350 | {
|
---|
| 1351 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 1352 | addr -= 0x4000;
|
---|
| 1353 | memcpy(pDst, &pThis->abLocalRAM[addr], cb);
|
---|
| 1354 | }
|
---|
| 1355 | else
|
---|
[93601] | 1356 | LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1357 | }
|
---|
| 1358 | else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
|
---|
| 1359 | {
|
---|
| 1360 | /* Local RAM is mapped starting at address zero. */
|
---|
| 1361 | addr &= DPNIC_MEM_MASK;
|
---|
| 1362 | if (addr + cb <= DPNIC_MEM_SIZE)
|
---|
| 1363 | memcpy(pDst, &pThis->abLocalRAM[addr], cb);
|
---|
| 1364 | else
|
---|
[93601] | 1365 | LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1366 | }
|
---|
| 1367 | else if (pThis->uDevType == DEV_3C503)
|
---|
| 1368 | {
|
---|
| 1369 | /* Only 14 bits of address are decoded. */
|
---|
| 1370 | /// @todo Is there any internal wrap-around in the 3C503 too?
|
---|
| 1371 | addr &= 0x3fff;
|
---|
| 1372 | if (addr >= 0x2000)
|
---|
| 1373 | {
|
---|
| 1374 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 1375 | addr -= 0x2000;
|
---|
| 1376 | memcpy(pDst, &pThis->abLocalRAM[addr], cb);
|
---|
| 1377 | }
|
---|
| 1378 | else
|
---|
[93601] | 1379 | LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1380 | }
|
---|
| 1381 | else
|
---|
| 1382 | {
|
---|
| 1383 | Assert(0);
|
---|
| 1384 | }
|
---|
| 1385 | }
|
---|
| 1386 |
|
---|
| 1387 |
|
---|
| 1388 | #ifdef IN_RING3
|
---|
| 1389 |
|
---|
| 1390 | /**
|
---|
| 1391 | * Write up to 256 bytes into a single page of local RAM.
|
---|
| 1392 | */
|
---|
| 1393 | static void dpLocalRAMWriteBuf(PDPNICSTATE pThis, uint16_t addr, unsigned cb, const uint8_t *pSrc)
|
---|
| 1394 | {
|
---|
| 1395 | if ((RT_LOBYTE(addr) + cb) > 256)
|
---|
| 1396 | {
|
---|
[93601] | 1397 | LogFunc(("#%d: addr=%04X, cb=%X, cb!!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1398 | cb = 256 - RT_LOBYTE(addr);
|
---|
| 1399 | }
|
---|
| 1400 |
|
---|
| 1401 | /* A single page is always either entirely inside or outside local RAM. */
|
---|
| 1402 | if (pThis->uDevType == DEV_NE1000)
|
---|
| 1403 | {
|
---|
| 1404 | /* Only 14 bits of address are decoded. */
|
---|
| 1405 | addr &= 0x3fff;
|
---|
| 1406 | if (addr >= 0x2000)
|
---|
| 1407 | {
|
---|
| 1408 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 1409 | addr -= 0x2000;
|
---|
| 1410 | memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
|
---|
| 1411 | }
|
---|
| 1412 | else
|
---|
[93601] | 1413 | LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1414 | }
|
---|
| 1415 | else if (pThis->uDevType == DEV_NE2000)
|
---|
| 1416 | {
|
---|
| 1417 | /* Only 14 bits of address are decoded. */
|
---|
| 1418 | addr &= 0x7fff;
|
---|
| 1419 | if (addr >= 0x4000)
|
---|
| 1420 | {
|
---|
| 1421 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 1422 | addr -= 0x4000;
|
---|
| 1423 | memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
|
---|
| 1424 | }
|
---|
| 1425 | else
|
---|
[93601] | 1426 | LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1427 | }
|
---|
| 1428 | else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
|
---|
| 1429 | {
|
---|
| 1430 | /* Local RAM is mapped starting at address zero. */
|
---|
| 1431 | addr &= DPNIC_MEM_MASK;
|
---|
| 1432 | if (addr + cb <= DPNIC_MEM_SIZE)
|
---|
| 1433 | memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
|
---|
| 1434 | else
|
---|
[93601] | 1435 | LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1436 | }
|
---|
| 1437 | else if (pThis->uDevType == DEV_3C503)
|
---|
| 1438 | {
|
---|
| 1439 | /* Only 14 bits of address are decoded. */
|
---|
| 1440 | /// @todo Is there any internal wrap-around in the 3C503 too?
|
---|
| 1441 | addr &= 0x3fff;
|
---|
| 1442 | if (addr >= 0x2000)
|
---|
| 1443 | {
|
---|
| 1444 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 1445 | addr -= 0x2000;
|
---|
| 1446 | memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
|
---|
| 1447 | }
|
---|
| 1448 | else
|
---|
[93601] | 1449 | LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
|
---|
[93560] | 1450 | }
|
---|
| 1451 | else
|
---|
| 1452 | {
|
---|
| 1453 | Assert(0);
|
---|
| 1454 | }
|
---|
| 1455 | }
|
---|
| 1456 |
|
---|
| 1457 |
|
---|
| 1458 | /**
|
---|
| 1459 | * Receive an arbitrarily long buffer into the receive ring starting at CLDA.
|
---|
| 1460 | * Update RSR, CLDA, and other state in the process.
|
---|
| 1461 | */
|
---|
| 1462 | static void dp8390CoreReceiveBuf(PDPNICSTATE pThis, DP_RSR *pRsr, const uint8_t *src, unsigned cbLeft, bool fLast)
|
---|
| 1463 | {
|
---|
[93601] | 1464 | LogFlow(("#%d: Initial CURR=%02X00 CLDA=%04X\n", pThis->iInstance, pThis->core.CURR, pThis->core.CLDA));
|
---|
[93560] | 1465 |
|
---|
| 1466 | while (cbLeft)
|
---|
| 1467 | {
|
---|
| 1468 | unsigned cbWrite;
|
---|
| 1469 | unsigned cbPage;
|
---|
| 1470 |
|
---|
| 1471 | /* Write at most up to the end of a page. */
|
---|
| 1472 | cbPage = cbWrite = 256 - pThis->core.clda.CLDA0;
|
---|
| 1473 | if (cbWrite > cbLeft)
|
---|
| 1474 | cbWrite = cbLeft;
|
---|
[93601] | 1475 | Log2Func(("#%d: cbLeft=%d CURR=%02X00 CLDA=%04X\n", pThis->iInstance, cbLeft, pThis->core.CURR, pThis->core.CLDA));
|
---|
[93560] | 1476 | dpLocalRAMWriteBuf(pThis, pThis->core.CLDA, cbWrite, src);
|
---|
| 1477 | src += cbWrite;
|
---|
| 1478 |
|
---|
| 1479 | /* If this is the last fragment of a received frame, we need to
|
---|
| 1480 | * round CLDA up to the next page boundary to correctly evaluate
|
---|
| 1481 | * buffer overflows and the next pointer. Otherwise we just
|
---|
| 1482 | * add however much data we had so that we can continue writing
|
---|
| 1483 | * at the CLDA position.
|
---|
| 1484 | */
|
---|
| 1485 | if (fLast && (cbWrite == cbLeft))
|
---|
| 1486 | {
|
---|
[93601] | 1487 | Log3Func(("#%d: Round up: CLDA=%04X cbPage=%X\n", pThis->iInstance, pThis->core.CLDA, cbPage));
|
---|
[93560] | 1488 | pThis->core.CLDA += cbPage;
|
---|
| 1489 | }
|
---|
| 1490 | else
|
---|
| 1491 | pThis->core.CLDA += cbWrite;
|
---|
| 1492 |
|
---|
[93601] | 1493 | Log3Func(("#%d: Final CURR=%02X00 CLDA=%04X\n", pThis->iInstance, pThis->core.CURR, pThis->core.CLDA));
|
---|
[93560] | 1494 | /* If at end of ring, wrap around. */
|
---|
| 1495 | if (pThis->core.clda.CLDA1 == pThis->core.PSTOP)
|
---|
| 1496 | pThis->core.clda.CLDA1 = pThis->core.PSTART;
|
---|
| 1497 |
|
---|
| 1498 | /* Check for buffer overflow. */
|
---|
| 1499 | if (pThis->core.clda.CLDA1 == pThis->core.BNRY)
|
---|
| 1500 | {
|
---|
| 1501 | pThis->core.isr.OVW = 1;
|
---|
| 1502 | pThis->core.isr.RST = 1;
|
---|
| 1503 | pRsr->MPA = 1; /* Indicates to caller that receive was aborted. */
|
---|
| 1504 | STAM_COUNTER_INC(&pThis->StatDropPktNoBuffer);
|
---|
[93601] | 1505 | Log3Func(("#%d: PSTART=%02X00 PSTOP=%02X00 BNRY=%02X00 CURR=%02X00 -- overflow!\n", pThis->iInstance, pThis->core.PSTART, pThis->core.PSTOP, pThis->core.BNRY, pThis->core.CURR));
|
---|
[93560] | 1506 | break;
|
---|
| 1507 | }
|
---|
| 1508 | cbLeft -= cbWrite;
|
---|
| 1509 | }
|
---|
| 1510 | }
|
---|
| 1511 |
|
---|
| 1512 | /**
|
---|
| 1513 | * Write incoming data into the packet buffer.
|
---|
| 1514 | */
|
---|
[93601] | 1515 | static void dp8390CoreReceiveLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, const uint8_t *src, size_t cbToRecv)
|
---|
[93560] | 1516 | {
|
---|
| 1517 | int is_padr = 0, is_bcast = 0, is_mcast = 0, is_prom = 0;
|
---|
| 1518 | int mc_type = 0;
|
---|
| 1519 |
|
---|
| 1520 | /*
|
---|
| 1521 | * Drop all packets if the VM is not running yet/anymore.
|
---|
| 1522 | */
|
---|
| 1523 | VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
|
---|
| 1524 | if ( enmVMState != VMSTATE_RUNNING
|
---|
| 1525 | && enmVMState != VMSTATE_RUNNING_LS)
|
---|
| 1526 | {
|
---|
| 1527 | STAM_COUNTER_INC(&pThis->StatDropPktVMNotRunning);
|
---|
| 1528 | return;
|
---|
| 1529 | }
|
---|
| 1530 |
|
---|
| 1531 | /*
|
---|
| 1532 | * Drop all packets if the cable is not connected.
|
---|
| 1533 | */
|
---|
| 1534 | if (RT_UNLIKELY(!dp8390IsLinkUp(pThis)))
|
---|
| 1535 | {
|
---|
| 1536 | STAM_COUNTER_INC(&pThis->StatDropPktNoLink);
|
---|
| 1537 | return;
|
---|
| 1538 | }
|
---|
| 1539 |
|
---|
| 1540 | /*
|
---|
| 1541 | * Drop everything if NIC is not started or in reset.
|
---|
| 1542 | */
|
---|
| 1543 | if (RT_UNLIKELY(!pThis->core.cr.STA || pThis->core.cr.STP))
|
---|
| 1544 | {
|
---|
| 1545 | STAM_COUNTER_INC(&pThis->StatDropPktRcvrDis);
|
---|
| 1546 | return;
|
---|
| 1547 | }
|
---|
| 1548 |
|
---|
| 1549 | /* Drop impossibly short packets. The DP8390 requires a packet to have
|
---|
| 1550 | * at least 8 bytes to even qualify as a runt. We can also assume that
|
---|
| 1551 | * there is a complete destination address at that point.
|
---|
| 1552 | */
|
---|
| 1553 | if (RT_UNLIKELY(cbToRecv < 8))
|
---|
| 1554 | {
|
---|
| 1555 | STAM_COUNTER_INC(&pThis->StatDropPktVeryShort);
|
---|
| 1556 | return;
|
---|
| 1557 | }
|
---|
| 1558 |
|
---|
[93601] | 1559 | LogFlowFunc(("#%d: size on wire=%d\n", pThis->iInstance, cbToRecv));
|
---|
[93560] | 1560 |
|
---|
| 1561 | /*
|
---|
| 1562 | * Perform address matching. Packets which do not pass any address
|
---|
| 1563 | * matching logic are ignored.
|
---|
| 1564 | */
|
---|
| 1565 | if ( (is_padr = padr_match(pThis, src))
|
---|
| 1566 | || (is_bcast = padr_bcast(pThis, src))
|
---|
| 1567 | || (is_mcast = padr_mcast(pThis, src, &mc_type))
|
---|
| 1568 | || (is_prom = padr_promi(pThis, src)))
|
---|
| 1569 | {
|
---|
| 1570 | union {
|
---|
| 1571 | uint8_t nRSR;
|
---|
| 1572 | DP_RSR nRsr;
|
---|
| 1573 | };
|
---|
| 1574 | uint32_t fcs = 0;
|
---|
| 1575 |
|
---|
| 1576 | nRSR = 0;
|
---|
[93601] | 1577 | Log2Func(("#%d Packet passed address filter (is_padr=%d, is_bcast=%d, is_mcast=%d, is_prom=%d), size=%d\n", pThis->iInstance, is_padr, is_bcast, is_mcast, is_prom, cbToRecv));
|
---|
[93560] | 1578 |
|
---|
| 1579 | if (is_bcast || mc_type)
|
---|
| 1580 | nRsr.PHY = 1;
|
---|
| 1581 |
|
---|
| 1582 | /* In Monitor Mode, just increment the tally counter. */
|
---|
| 1583 | if (RT_UNLIKELY(pThis->core.rcr.MON))
|
---|
| 1584 | {
|
---|
| 1585 | STAM_COUNTER_INC(&pThis->StatDropPktMonitor);
|
---|
| 1586 | nRsr.MPA = 1;
|
---|
| 1587 | if (pThis->core.CNTR2 <= 192)
|
---|
| 1588 | pThis->core.CNTR2++; /* Relies on UpdateIrq to be run. */
|
---|
| 1589 | }
|
---|
| 1590 | else
|
---|
| 1591 | {
|
---|
| 1592 | /* Error detection: FCS and frame alignment errors cannot happen,
|
---|
| 1593 | * likewise FIFO overruns can't.
|
---|
| 1594 | * Runts are padded up to the required minimum. Note that the DP8390
|
---|
| 1595 | * documentation considers packets smaller than 64 bytes to be runts,
|
---|
| 1596 | * but that includes 32 bits of FCS.
|
---|
| 1597 | */
|
---|
| 1598 |
|
---|
| 1599 | /* See if we need to pad, and how much. Note that if there's any
|
---|
| 1600 | * room left in the receive buffers, a runt will fit even after padding.
|
---|
| 1601 | */
|
---|
| 1602 | if (RT_UNLIKELY(cbToRecv < 60))
|
---|
| 1603 | {
|
---|
| 1604 | /// @todo This really is kind of stupid. We shouldn't be doing any
|
---|
| 1605 | /// padding here, it should be done by the sending side!
|
---|
| 1606 | memset(pThis->abRuntBuf, 0, sizeof(pThis->abRuntBuf));
|
---|
| 1607 | memcpy(pThis->abRuntBuf, src, cbToRecv);
|
---|
| 1608 | cbToRecv = 60;
|
---|
| 1609 | src = pThis->abRuntBuf;
|
---|
| 1610 | }
|
---|
| 1611 |
|
---|
[93601] | 1612 | LogFlowFunc(("#%d: PSTART=%02X00 PSTOP=%02X00 BNRY=%02X00 CURR=%02X00\n", pThis->iInstance, pThis->core.PSTART, pThis->core.PSTOP, pThis->core.BNRY, pThis->core.CURR));
|
---|
[93560] | 1613 |
|
---|
| 1614 | /* All packets that passed the address filter are copied to local RAM.
|
---|
| 1615 | * Since the DP8390 does not know how long the frame is until it detects
|
---|
| 1616 | * end of frame, it can only detect an out-of-buffer condition after
|
---|
| 1617 | * filling up all available space. It then reports an error and rewinds
|
---|
| 1618 | * back to where it was before.
|
---|
| 1619 | *
|
---|
| 1620 | * We do not limit the incoming frame size except by available buffer space. /// @todo Except we do??
|
---|
| 1621 | */
|
---|
| 1622 |
|
---|
| 1623 | STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbToRecv);
|
---|
| 1624 |
|
---|
| 1625 | /* Copy incoming data to the packet buffer. Start by setting CLDA
|
---|
| 1626 | * to CURR + 4, leaving room for header.
|
---|
| 1627 | */
|
---|
| 1628 | pThis->core.CLDA = RT_MAKE_U16(4, pThis->core.CURR);
|
---|
| 1629 |
|
---|
| 1630 | /* Receive the incoming frame. */
|
---|
| 1631 | Assert(cbToRecv < MAX_FRAME); /// @todo Can we actually do bigger?
|
---|
| 1632 | dp8390CoreReceiveBuf(pThis, &nRsr, src, (unsigned)cbToRecv, false);
|
---|
| 1633 | /// @todo Use the same method for runt padding?
|
---|
| 1634 |
|
---|
| 1635 | /* If there was no overflow, add the FCS. */
|
---|
| 1636 | if (!nRsr.MPA)
|
---|
| 1637 | {
|
---|
| 1638 | fcs = 0xBADF00D; // Just fake it, does anyone care?
|
---|
| 1639 | dp8390CoreReceiveBuf(pThis, &nRsr, (uint8_t *)&fcs, sizeof(fcs), true);
|
---|
| 1640 | }
|
---|
| 1641 |
|
---|
| 1642 | /* Error-free packets are considered intact. */
|
---|
| 1643 | if (!nRsr.CRC && !nRsr.FAE && !nRsr.FO && !nRsr.MPA)
|
---|
| 1644 | {
|
---|
| 1645 | nRsr.PRX = 1;
|
---|
| 1646 | pThis->core.isr.PRX = 1;
|
---|
| 1647 | }
|
---|
| 1648 | else
|
---|
| 1649 | pThis->core.isr.RXE = 1;
|
---|
| 1650 |
|
---|
| 1651 | /* For 'intact' packets, write the packet header. */
|
---|
| 1652 | if (nRsr.PRX)
|
---|
| 1653 | {
|
---|
| 1654 | DP_PKT_HDR header;
|
---|
| 1655 |
|
---|
| 1656 | /* Round up CLDA to the next page. */
|
---|
| 1657 | if (pThis->core.clda.CLDA0)
|
---|
| 1658 | pThis->core.CLDA = RT_MAKE_U16(0, pThis->core.clda.CLDA1 + 1);
|
---|
| 1659 |
|
---|
| 1660 | /* If entire frame was successfully received, write the packet header at the old CURR. */
|
---|
| 1661 | header.rcv_stat = nRSR;
|
---|
| 1662 | header.next_ptr = pThis->core.clda.CLDA1;
|
---|
| 1663 | /// @todo big endian (WTS)
|
---|
| 1664 | header.byte_cnt = (uint16_t)cbToRecv + sizeof(fcs);
|
---|
| 1665 |
|
---|
| 1666 | pThis->core.CLDA = RT_MAKE_U16(0, pThis->core.CURR);
|
---|
| 1667 | dpLocalRAMWriteBuf(pThis, pThis->core.CLDA, sizeof(header), (uint8_t *)&header);
|
---|
| 1668 | pThis->core.CLDA += sizeof(header);
|
---|
| 1669 |
|
---|
| 1670 | pThis->core.CURR = header.next_ptr;
|
---|
| 1671 | }
|
---|
| 1672 | }
|
---|
| 1673 |
|
---|
| 1674 | pThis->core.RSR = nRSR;
|
---|
| 1675 |
|
---|
| 1676 | Log2Func(("Receive completed, size=%d, CURR=%02X00, RSR=%02X, ISR=%02X\n", cbToRecv, pThis->core.CURR, pThis->core.RSR, pThis->core.ISR));
|
---|
[93601] | 1677 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 1678 | }
|
---|
| 1679 | else
|
---|
| 1680 | {
|
---|
[93601] | 1681 | Log3Func(("#%d Packet did not pass address filter, size=%d\n", pThis->iInstance, cbToRecv));
|
---|
[93560] | 1682 | STAM_COUNTER_INC(&pThis->StatDropPktNoMatch);
|
---|
| 1683 | }
|
---|
| 1684 | }
|
---|
| 1685 |
|
---|
| 1686 | #endif /* IN_RING3 */
|
---|
| 1687 |
|
---|
| 1688 |
|
---|
| 1689 | /**
|
---|
| 1690 | * Transmit a packet from local memory.
|
---|
| 1691 | *
|
---|
| 1692 | * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
|
---|
| 1693 | *
|
---|
[93601] | 1694 | * @param pDevIns The device instance data.
|
---|
| 1695 | * @param pThis The device state data.
|
---|
[93560] | 1696 | * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
|
---|
| 1697 | */
|
---|
[93601] | 1698 | static int dp8390CoreXmitPacket(PPDMDEVINS pDevIns, PDPNICSTATE pThis, bool fOnWorkerThread)
|
---|
[93560] | 1699 | {
|
---|
[93601] | 1700 | PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
|
---|
[93560] | 1701 | RT_NOREF_PV(fOnWorkerThread);
|
---|
| 1702 | int rc;
|
---|
| 1703 |
|
---|
| 1704 | /*
|
---|
| 1705 | * Grab the xmit lock of the driver as well as the DP8390 device state.
|
---|
| 1706 | */
|
---|
[93601] | 1707 | PPDMINETWORKUP pDrv = pThisCC->pDrv;
|
---|
[93560] | 1708 | if (pDrv)
|
---|
| 1709 | {
|
---|
| 1710 | rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
|
---|
| 1711 | if (RT_FAILURE(rc))
|
---|
| 1712 | return rc;
|
---|
| 1713 | }
|
---|
| 1714 | rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
|
---|
| 1715 | if (RT_SUCCESS(rc))
|
---|
| 1716 | {
|
---|
| 1717 | /*
|
---|
| 1718 | * Do the transmitting.
|
---|
| 1719 | */
|
---|
[93601] | 1720 | int rc2 = dp8390CoreAsyncXmitLocked(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
|
---|
[93560] | 1721 | AssertReleaseRC(rc2);
|
---|
| 1722 |
|
---|
| 1723 | /*
|
---|
| 1724 | * Release the locks.
|
---|
| 1725 | */
|
---|
| 1726 | PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
|
---|
| 1727 | }
|
---|
| 1728 | else
|
---|
| 1729 | AssertLogRelRC(rc);
|
---|
| 1730 | if (pDrv)
|
---|
| 1731 | pDrv->pfnEndXmit(pDrv);
|
---|
| 1732 |
|
---|
| 1733 | return rc;
|
---|
| 1734 | }
|
---|
| 1735 |
|
---|
| 1736 |
|
---|
| 1737 | #ifdef IN_RING3
|
---|
| 1738 |
|
---|
| 1739 | /**
|
---|
| 1740 | * @callback_method_impl{FNPDMTASKDEV,
|
---|
| 1741 | * This is just a very simple way of delaying sending to R3.
|
---|
| 1742 | */
|
---|
| 1743 | static DECLCALLBACK(void) dpNicR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
|
---|
| 1744 | {
|
---|
[93601] | 1745 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
[93560] | 1746 | NOREF(pvUser);
|
---|
| 1747 |
|
---|
| 1748 | /*
|
---|
| 1749 | * Transmit if we can.
|
---|
| 1750 | */
|
---|
[93601] | 1751 | dp8390CoreXmitPacket(pDevIns, pThis, true /*fOnWorkerThread*/);
|
---|
[93560] | 1752 | }
|
---|
| 1753 |
|
---|
| 1754 | #endif /* IN_RING3 */
|
---|
| 1755 |
|
---|
| 1756 |
|
---|
| 1757 | /**
|
---|
| 1758 | * Allocates a scatter/gather buffer for a transfer.
|
---|
| 1759 | *
|
---|
| 1760 | * @returns See PPDMINETWORKUP::pfnAllocBuf.
|
---|
| 1761 | * @param pThis The device instance.
|
---|
[93601] | 1762 | * @param pThisCC The device state for current context.
|
---|
[93560] | 1763 | * @param cbMin The minimum buffer size.
|
---|
| 1764 | * @param fLoopback Set if we're in loopback mode.
|
---|
| 1765 | * @param pSgLoop Pointer to stack storage for the loopback SG.
|
---|
| 1766 | * @param ppSgBuf Where to return the SG buffer descriptor on success.
|
---|
| 1767 | * Always set.
|
---|
| 1768 | */
|
---|
[93601] | 1769 | DECLINLINE(int) dp8390XmitAllocBuf(PDPNICSTATE pThis, PDPNICSTATECC pThisCC, size_t cbMin, bool fLoopback,
|
---|
[93560] | 1770 | PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
|
---|
| 1771 | {
|
---|
| 1772 | int rc;
|
---|
| 1773 |
|
---|
| 1774 | if (!fLoopback)
|
---|
| 1775 | {
|
---|
[93601] | 1776 | PPDMINETWORKUP pDrv = pThisCC->pDrv;
|
---|
[93560] | 1777 | if (RT_LIKELY(pDrv))
|
---|
| 1778 | {
|
---|
| 1779 | rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
|
---|
| 1780 | AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
|
---|
| 1781 | if (RT_FAILURE(rc))
|
---|
| 1782 | *ppSgBuf = NULL;
|
---|
| 1783 | }
|
---|
| 1784 | else
|
---|
| 1785 | {
|
---|
| 1786 | rc = VERR_NET_DOWN;
|
---|
| 1787 | *ppSgBuf = NULL;
|
---|
| 1788 | }
|
---|
| 1789 | }
|
---|
| 1790 | else
|
---|
| 1791 | {
|
---|
| 1792 | /* Fake loopback allocator. */
|
---|
| 1793 | pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
|
---|
| 1794 | pSgLoop->cbUsed = 0;
|
---|
| 1795 | pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
|
---|
| 1796 | pSgLoop->pvAllocator = pThis;
|
---|
| 1797 | pSgLoop->pvUser = NULL;
|
---|
| 1798 | pSgLoop->cSegs = 1;
|
---|
| 1799 | pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
|
---|
| 1800 | pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
|
---|
| 1801 | *ppSgBuf = pSgLoop;
|
---|
| 1802 | rc = VINF_SUCCESS;
|
---|
| 1803 | }
|
---|
| 1804 | return rc;
|
---|
| 1805 | }
|
---|
| 1806 |
|
---|
| 1807 |
|
---|
| 1808 | /**
|
---|
| 1809 | * Sends the scatter/gather buffer.
|
---|
| 1810 | *
|
---|
| 1811 | * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
|
---|
| 1812 | *
|
---|
| 1813 | * @returns See PDMINETWORKUP::pfnSendBuf.
|
---|
[93601] | 1814 | * @param pDevIns The device instance.
|
---|
| 1815 | * @param pThisCC The current context device state.
|
---|
[93560] | 1816 | * @param fLoopback Set if we're in loopback mode.
|
---|
| 1817 | * @param pSgBuf The SG to send.
|
---|
| 1818 | * @param fOnWorkerThread Set if we're being called on a work thread. Clear
|
---|
| 1819 | * if an EMT.
|
---|
| 1820 | */
|
---|
[93601] | 1821 | DECLINLINE(int) dp8390CoreXmitSendBuf(PPDMDEVINS pDevIns, PDPNICSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
|
---|
[93560] | 1822 | {
|
---|
[93601] | 1823 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
[93560] | 1824 | int rc;
|
---|
| 1825 | STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
|
---|
| 1826 | if (!fLoopback)
|
---|
| 1827 | {
|
---|
| 1828 | STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
|
---|
| 1829 | if (pSgBuf->cbUsed > 70) /* unqualified guess */
|
---|
| 1830 | pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
|
---|
| 1831 |
|
---|
[93601] | 1832 | PPDMINETWORKUP pDrv = pThisCC->pDrv;
|
---|
[93560] | 1833 | if (RT_LIKELY(pDrv))
|
---|
| 1834 | {
|
---|
| 1835 | rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
|
---|
| 1836 | AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
|
---|
| 1837 | }
|
---|
| 1838 | else
|
---|
| 1839 | rc = VERR_NET_DOWN;
|
---|
| 1840 |
|
---|
| 1841 | pThis->Led.Actual.s.fWriting = 0;
|
---|
| 1842 | STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
|
---|
| 1843 | }
|
---|
| 1844 | else
|
---|
| 1845 | {
|
---|
| 1846 | PDP8390CORE pCore = &pThis->core;
|
---|
| 1847 | union {
|
---|
| 1848 | uint8_t nRSR;
|
---|
| 1849 | DP_RSR nRsr;
|
---|
| 1850 | };
|
---|
| 1851 | unsigned ofs;
|
---|
| 1852 | uint32_t fcs = UINT32_MAX;
|
---|
| 1853 |
|
---|
| 1854 | nRSR = 0;
|
---|
| 1855 |
|
---|
| 1856 | /* Loopback on the DP8390 is so strange that it must be handled specially. */
|
---|
| 1857 | Assert(pSgBuf->pvAllocator == (void *)pThis);
|
---|
| 1858 | pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
|
---|
| 1859 |
|
---|
[93601] | 1860 | LogFlowFunc(("#%d: loopback (DCR=%02X LB=%u TCR=%02X RCR=%02X, %u bytes)\n", pThis->iInstance, pCore->DCR, pCore->tcr.LB, pCore->TCR, pCore->RCR, pSgBuf->cbUsed));
|
---|
[93560] | 1861 | for (ofs = 0; ofs < pSgBuf->cbUsed; ofs += 16)
|
---|
| 1862 | Log((" %04X: %.*Rhxs\n", ofs, ofs + 16 < pSgBuf->cbUsed ? 16 : pSgBuf->cbUsed - ofs, &pThis->abLoopBuf[ofs]));
|
---|
| 1863 |
|
---|
| 1864 | /* A packet shorter than 8 bytes is ignored by the receiving side. */
|
---|
| 1865 | if (pSgBuf->cbUsed < 8)
|
---|
| 1866 | return VINF_SUCCESS;
|
---|
| 1867 |
|
---|
| 1868 | /* The loopback mode affects transmit status bits. */
|
---|
| 1869 | switch (pCore->tcr.LB)
|
---|
| 1870 | {
|
---|
| 1871 | case 1: /* Internal loopback within DP8390. */
|
---|
| 1872 | pCore->tsr.CDH = 1;
|
---|
| 1873 | pCore->tsr.CRS = 1;
|
---|
| 1874 | break;
|
---|
| 1875 | case 2: /* Loopback through serializer. */
|
---|
| 1876 | pCore->tsr.CDH = 1;
|
---|
| 1877 | break;
|
---|
| 1878 | case 3: /* External loopback. Requires a cable. */
|
---|
| 1879 | break;
|
---|
| 1880 | default:
|
---|
| 1881 | Assert(0);
|
---|
| 1882 | }
|
---|
| 1883 |
|
---|
| 1884 | /* The CRC Inhibit controls whether transmit or receive path uses the
|
---|
| 1885 | * CRC circuitry. If transmit side uses CRC, receive always fails.
|
---|
| 1886 | * We always need to calculate the FCS because either the sending or
|
---|
| 1887 | * the receiving side uses it.
|
---|
| 1888 | */
|
---|
| 1889 | uint8_t *p;
|
---|
| 1890 | uint8_t *pktbuf = pThis->abLoopBuf; /// @todo Point into sgbuf instead?
|
---|
| 1891 | uint16_t pktlen = (uint16_t)pSgBuf->cbUsed;
|
---|
| 1892 | uint16_t fcslen = pktlen;
|
---|
| 1893 | uint8_t abFcs[4];
|
---|
| 1894 | bool fAddrMatched = true;
|
---|
| 1895 |
|
---|
| 1896 | /* If the receiver side is calculating FCS, it needs to skip the last
|
---|
| 1897 | * bytes (which are the transmit-side FCS).
|
---|
| 1898 | */
|
---|
| 1899 | if (pCore->tcr.CRC && (pktlen > 4))
|
---|
| 1900 | fcslen -= 4;
|
---|
| 1901 |
|
---|
| 1902 | p = pktbuf;
|
---|
| 1903 | while (p != &pktbuf[fcslen])
|
---|
| 1904 | CRC(fcs, *p++);
|
---|
| 1905 |
|
---|
| 1906 | fcs = ~fcs;
|
---|
| 1907 | Log3Func(("FCS: %08X\n", fcs));
|
---|
| 1908 | for (ofs = 0; ofs < sizeof(abFcs); ++ofs)
|
---|
| 1909 | {
|
---|
| 1910 | abFcs[ofs] = (uint8_t)fcs;
|
---|
| 1911 | fcs >>= 8;
|
---|
| 1912 | }
|
---|
| 1913 |
|
---|
| 1914 | /* The FIFO write pointer gets zeroed on each receive,
|
---|
| 1915 | * but the read pointer does not.
|
---|
| 1916 | */
|
---|
| 1917 | pCore->fifo.wp = 0;
|
---|
| 1918 |
|
---|
| 1919 | if (pCore->tcr.CRC)
|
---|
| 1920 | {
|
---|
| 1921 | bool fGoodFcs = true;
|
---|
| 1922 | int is_padr = 0, is_bcast = 0, is_mcast = 0, is_prom = 0;
|
---|
| 1923 | int mc_type = 0;
|
---|
| 1924 |
|
---|
| 1925 | /* Always put the first 8 bytes of the packet in the FIFO. */
|
---|
| 1926 | for (ofs = 0; ofs < 8; ++ofs)
|
---|
| 1927 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
|
---|
| 1928 |
|
---|
| 1929 |
|
---|
| 1930 | /* If the receiving side uses the CRC circuitry, it also performs
|
---|
| 1931 | * destination address matching.
|
---|
| 1932 | */
|
---|
| 1933 | if ( (is_padr = padr_match(pThis, pktbuf))
|
---|
| 1934 | || (is_bcast = padr_bcast(pThis, pktbuf))
|
---|
| 1935 | || (is_mcast = padr_mcast(pThis, pktbuf, &mc_type))
|
---|
| 1936 | || (is_prom = padr_promi(pThis, pktbuf)))
|
---|
| 1937 | {
|
---|
| 1938 | /* Receiving side checks the FCS. */
|
---|
| 1939 | fGoodFcs = !memcmp(&pktbuf[pktlen - 4], abFcs, sizeof(abFcs));
|
---|
[93601] | 1940 | Log2Func(("#%d: Address matched (is_padr=%d, is_bcast=%d, is_mcast=%d, is_prom=%d), checking FCS (fGoodFcs=%RTbool)\n", pThis->iInstance, is_padr, is_bcast, is_mcast, is_prom, fGoodFcs));
|
---|
[93560] | 1941 |
|
---|
| 1942 | /* Now we have to update the FIFO. Since only 8 bytes are visible
|
---|
| 1943 | * in the FIFO after a receive, we can skip most of it.
|
---|
| 1944 | */
|
---|
| 1945 | for ( ; ofs < pktlen; ++ofs)
|
---|
| 1946 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
|
---|
| 1947 |
|
---|
| 1948 | }
|
---|
| 1949 | else
|
---|
| 1950 | {
|
---|
| 1951 | nRsr.PRX = 1; /* Weird but true, for non-matching address only! */
|
---|
| 1952 | fAddrMatched = false;
|
---|
[93601] | 1953 | Log3Func(("#%d: Address NOT matched, ignoring FCS errors.\n", pThis->iInstance));
|
---|
[93560] | 1954 | }
|
---|
| 1955 |
|
---|
| 1956 | /* The PHY bit is set when when an enabled broadcast packet is accepted,
|
---|
| 1957 | * but also when an enabled multicast packet arrives regardless of whether
|
---|
| 1958 | * it passes the MAR filter or not.
|
---|
| 1959 | */
|
---|
| 1960 | if (is_bcast || mc_type)
|
---|
| 1961 | nRsr.PHY = 1;
|
---|
| 1962 |
|
---|
| 1963 | if (!fGoodFcs)
|
---|
| 1964 | nRsr.CRC = 1;
|
---|
| 1965 | }
|
---|
| 1966 | else
|
---|
| 1967 | {
|
---|
| 1968 | nRsr.CRC = 1; /* Always report CRC error if receiver isn't checking. */
|
---|
| 1969 |
|
---|
| 1970 | /* Now we have to update the FIFO. Since only 8 bytes are visible
|
---|
| 1971 | * in the FIFO after a receive, we can skip most of it.
|
---|
| 1972 | */
|
---|
| 1973 | for (ofs = 0; ofs < pktlen; ++ofs)
|
---|
| 1974 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
|
---|
| 1975 |
|
---|
| 1976 | /* Stuff the generated FCS in the FIFO. */
|
---|
| 1977 | for (ofs = 0; ofs < sizeof(abFcs); ++ofs)
|
---|
| 1978 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = abFcs[ofs];
|
---|
| 1979 | }
|
---|
| 1980 |
|
---|
| 1981 | /* And now put the packet length in the FIFO. */
|
---|
| 1982 | if (fAddrMatched || 1)
|
---|
| 1983 | {
|
---|
| 1984 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_LOBYTE(pktlen);
|
---|
| 1985 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_HIBYTE(pktlen);
|
---|
| 1986 | pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_HIBYTE(pktlen); /* Yes, written twice! */
|
---|
| 1987 | }
|
---|
| 1988 |
|
---|
| 1989 | Log(("FIFO: rp=%u, wp=%u\n", pCore->fifo.rp & 7, pCore->fifo.wp & 7));
|
---|
| 1990 | Log((" %Rhxs\n", &pCore->fifo.fifo));
|
---|
| 1991 |
|
---|
| 1992 | if (nRsr.CRC)
|
---|
| 1993 | pCore->isr.RXE = 1;
|
---|
| 1994 | pCore->RSR = nRSR;
|
---|
| 1995 |
|
---|
| 1996 | pThis->Led.Actual.s.fReading = 0;
|
---|
| 1997 |
|
---|
| 1998 | /* Return success so that caller sets TSR.PTX and ISR.PTX. */
|
---|
| 1999 | rc = VINF_SUCCESS;
|
---|
| 2000 | }
|
---|
| 2001 | return rc;
|
---|
| 2002 | }
|
---|
| 2003 |
|
---|
| 2004 |
|
---|
| 2005 | /**
|
---|
| 2006 | * Reads the entire frame into the scatter gather buffer.
|
---|
| 2007 | */
|
---|
[93601] | 2008 | DECLINLINE(void) dp8390CoreXmitRead(PPDMDEVINS pDevIns, const unsigned uLocalAddr, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf, bool fLoopback)
|
---|
[93560] | 2009 | {
|
---|
[93601] | 2010 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
| 2011 | unsigned uOfs = 0;
|
---|
| 2012 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
|
---|
[93560] | 2013 | Assert(pSgBuf->cbAvailable >= cbFrame);
|
---|
| 2014 |
|
---|
| 2015 | pSgBuf->cbUsed = cbFrame;
|
---|
| 2016 |
|
---|
[93601] | 2017 | LogFlowFunc(("#%d: uLocalAddr=%04X cbFrame=%d\n", pThis->iInstance, uLocalAddr, cbFrame));
|
---|
[93560] | 2018 | /* Have to figure out where the address is in local RAM. */
|
---|
| 2019 | if (pThis->uDevType == DEV_NE1000)
|
---|
| 2020 | {
|
---|
| 2021 | /* Only 14 bits of address are decoded. */
|
---|
| 2022 | uOfs = uLocalAddr & 0x3fff;
|
---|
| 2023 | if (uOfs >= 0x2000)
|
---|
| 2024 | {
|
---|
| 2025 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 2026 | uOfs -= 0x2000;
|
---|
| 2027 | }
|
---|
| 2028 | else
|
---|
| 2029 | {
|
---|
| 2030 | /// @todo What are we supposed to do?!
|
---|
[93601] | 2031 | LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
|
---|
[93560] | 2032 | }
|
---|
| 2033 | }
|
---|
| 2034 | else if (pThis->uDevType == DEV_NE2000)
|
---|
| 2035 | {
|
---|
| 2036 | /* Only 15 bits of address are decoded. */
|
---|
| 2037 | uOfs = uLocalAddr & 0x7fff;
|
---|
| 2038 | if (uOfs >= 0x4000)
|
---|
| 2039 | {
|
---|
| 2040 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 2041 | uOfs -= 0x4000;
|
---|
| 2042 | }
|
---|
| 2043 | else
|
---|
| 2044 | {
|
---|
| 2045 | /// @todo What are we supposed to do?!
|
---|
[93601] | 2046 | LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
|
---|
[93560] | 2047 | }
|
---|
| 2048 | }
|
---|
| 2049 | else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
|
---|
| 2050 | {
|
---|
| 2051 | /* Not much to do, WD was nice enough to put the RAM at the start of DP8390's address space. */
|
---|
| 2052 | uOfs = uLocalAddr & DPNIC_MEM_MASK;
|
---|
| 2053 | }
|
---|
| 2054 | else if (pThis->uDevType == DEV_3C503)
|
---|
| 2055 | {
|
---|
| 2056 | /* Only 14 bits of address are decoded. */
|
---|
| 2057 | uOfs = uLocalAddr & 0x3fff;
|
---|
| 2058 | if (uOfs >= 0x2000)
|
---|
| 2059 | {
|
---|
| 2060 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 2061 | uOfs -= 0x2000;
|
---|
| 2062 | }
|
---|
| 2063 | else
|
---|
| 2064 | {
|
---|
| 2065 | /// @todo What are we supposed to do?!
|
---|
[93601] | 2066 | LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
|
---|
[93560] | 2067 | }
|
---|
| 2068 | }
|
---|
| 2069 | else
|
---|
| 2070 | {
|
---|
| 2071 | Assert(0);
|
---|
| 2072 | }
|
---|
| 2073 |
|
---|
| 2074 | if (!fLoopback)
|
---|
| 2075 | {
|
---|
| 2076 | /* Fast path for normal transmit, ignores DCR.WTS. */
|
---|
| 2077 | if (uOfs + cbFrame <= sizeof(pThis->abLocalRAM))
|
---|
| 2078 | memcpy(pSgBuf->aSegs[0].pvSeg, &pThis->abLocalRAM[uOfs], cbFrame);
|
---|
| 2079 | else
|
---|
| 2080 | memset(pSgBuf->aSegs[0].pvSeg, 0xEE, cbFrame);
|
---|
| 2081 | }
|
---|
| 2082 | else
|
---|
| 2083 | {
|
---|
| 2084 | /* If DCR.WTS is set, only every other byte actually goes through loopback. */
|
---|
| 2085 | const uint8_t *src = &pThis->abLocalRAM[uOfs];
|
---|
| 2086 | uint8_t *dst = (uint8_t *)pSgBuf->aSegs[0].pvSeg;
|
---|
| 2087 | int cbDst = cbFrame;
|
---|
| 2088 | int step = 1 << pThis->core.dcr.WTS;
|
---|
| 2089 |
|
---|
| 2090 | /* Depending on DCR.BOS, take either odd or even bytes when DCR.WTS is set. */
|
---|
| 2091 | if (pThis->core.dcr.WTS && !pThis->core.dcr.BOS)
|
---|
| 2092 | ++src;
|
---|
| 2093 |
|
---|
| 2094 | while (cbDst-- && (src <= &pThis->abLocalRAM[DPNIC_MEM_SIZE]))
|
---|
| 2095 | {
|
---|
| 2096 | *dst++ = *src;
|
---|
| 2097 | src += step;
|
---|
| 2098 | }
|
---|
| 2099 |
|
---|
| 2100 | /* The address should perhaps wrap around -- depends on card design. */
|
---|
| 2101 | if (cbDst != -1)
|
---|
| 2102 | {
|
---|
| 2103 | while (cbDst--)
|
---|
| 2104 | *dst++ = 0xEE;
|
---|
| 2105 | }
|
---|
| 2106 | Assert(cbDst == -1);
|
---|
| 2107 | }
|
---|
| 2108 | }
|
---|
| 2109 |
|
---|
| 2110 | /**
|
---|
| 2111 | * Try to transmit a frame.
|
---|
| 2112 | */
|
---|
[93601] | 2113 | static void dp8390CoreStartTransmit(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 2114 | {
|
---|
| 2115 | /*
|
---|
| 2116 | * Transmit the packet if possible, defer it if we cannot do it
|
---|
| 2117 | * in the current context.
|
---|
| 2118 | */
|
---|
| 2119 | pThis->core.TSR = 0; /* Clear transmit status. */
|
---|
| 2120 | pThis->core.NCR = 0; /* Clear collision counter. */
|
---|
| 2121 | #if defined(IN_RING0) || defined(IN_RC)
|
---|
[93601] | 2122 | PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
|
---|
| 2123 | if (!pThisCC->pDrv)
|
---|
[93560] | 2124 | {
|
---|
[93601] | 2125 | int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
|
---|
[93560] | 2126 | AssertRC(rc);
|
---|
| 2127 | }
|
---|
| 2128 | else
|
---|
| 2129 | #endif
|
---|
| 2130 | {
|
---|
[93601] | 2131 | int rc = dp8390CoreXmitPacket(pDevIns, pThis, false /*fOnWorkerThread*/);
|
---|
[93560] | 2132 | if (rc == VERR_TRY_AGAIN)
|
---|
| 2133 | rc = VINF_SUCCESS;
|
---|
| 2134 | AssertRC(rc);
|
---|
| 2135 | }
|
---|
| 2136 | }
|
---|
| 2137 |
|
---|
| 2138 |
|
---|
| 2139 | /**
|
---|
| 2140 | * If a packet is waiting, poke the receiving machinery.
|
---|
| 2141 | *
|
---|
| 2142 | * @threads EMT.
|
---|
| 2143 | */
|
---|
[93601] | 2144 | static void dp8390CoreKickReceive(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 2145 | {
|
---|
| 2146 | if (pThis->fMaybeOutOfSpace)
|
---|
| 2147 | {
|
---|
| 2148 | LogFlow(("Poking receive thread.\n"));
|
---|
| 2149 | #ifdef IN_RING3
|
---|
[93601] | 2150 | dp8390R3WakeupReceive(pDevIns);
|
---|
[93560] | 2151 | #else
|
---|
[93601] | 2152 | int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hCanRxTask);
|
---|
[93560] | 2153 | AssertRC(rc);
|
---|
| 2154 | #endif
|
---|
| 2155 | }
|
---|
| 2156 | }
|
---|
| 2157 |
|
---|
| 2158 | /**
|
---|
| 2159 | * Try transmitting a frame.
|
---|
| 2160 | *
|
---|
| 2161 | * @threads TX or EMT.
|
---|
| 2162 | */
|
---|
[93601] | 2163 | static int dp8390CoreAsyncXmitLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PDPNICSTATECC pThisCC, bool fOnWorkerThread)
|
---|
[93560] | 2164 | {
|
---|
[93601] | 2165 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
|
---|
[93560] | 2166 |
|
---|
| 2167 | /*
|
---|
| 2168 | * Just drop it if not transmitting. Can happen with delayed transmits
|
---|
| 2169 | * if transmit was disabled in the meantime.
|
---|
| 2170 | */
|
---|
| 2171 | if (RT_UNLIKELY(!pThis->core.cr.TXP))
|
---|
| 2172 | {
|
---|
[93601] | 2173 | LogFunc(("#%d: Nope, CR.TXP is off (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
|
---|
[93560] | 2174 | return VINF_SUCCESS;
|
---|
| 2175 | }
|
---|
| 2176 |
|
---|
| 2177 | /*
|
---|
| 2178 | * Blast out data from the packet buffer.
|
---|
| 2179 | */
|
---|
| 2180 | int rc;
|
---|
| 2181 | STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
|
---|
| 2182 | do
|
---|
| 2183 | {
|
---|
| 2184 | /* Don't send anything when the link is down. */
|
---|
| 2185 | if (RT_UNLIKELY( !dp8390IsLinkUp(pThis)
|
---|
| 2186 | && pThis->cLinkDownReported > DPNIC_MAX_LINKDOWN_REPORTED)
|
---|
| 2187 | )
|
---|
| 2188 | break;
|
---|
| 2189 |
|
---|
| 2190 | bool const fLoopback = pThis->core.tcr.LB != 0;
|
---|
| 2191 | PDMSCATTERGATHER SgLoop;
|
---|
| 2192 | PPDMSCATTERGATHER pSgBuf;
|
---|
| 2193 |
|
---|
| 2194 | /*
|
---|
| 2195 | * Sending is easy peasy, there is by definition always
|
---|
| 2196 | * a complete packet on hand.
|
---|
| 2197 | */
|
---|
| 2198 | unsigned cb = pThis->core.TBCR; /* Packet size. */
|
---|
| 2199 | const int adr = RT_MAKE_U16(0, pThis->core.TPSR);
|
---|
[93601] | 2200 | LogFunc(("#%d: cb=%d, adr=%04X\n", pThis->iInstance, cb, adr));
|
---|
[93560] | 2201 |
|
---|
| 2202 | if (RT_LIKELY(dp8390IsLinkUp(pThis) || fLoopback))
|
---|
| 2203 | {
|
---|
| 2204 | if (RT_LIKELY(cb <= MAX_FRAME))
|
---|
| 2205 | {
|
---|
| 2206 | /* Loopback fun! */
|
---|
| 2207 | if (RT_UNLIKELY(fLoopback && pThis->core.dcr.WTS))
|
---|
| 2208 | {
|
---|
| 2209 | cb /= 2;
|
---|
| 2210 | Log(("Loopback with DCR.WTS set -> cb=%d\n", cb));
|
---|
| 2211 | }
|
---|
| 2212 |
|
---|
[93601] | 2213 | rc = dp8390XmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
|
---|
[93560] | 2214 | if (RT_SUCCESS(rc))
|
---|
| 2215 | {
|
---|
[93601] | 2216 | dp8390CoreXmitRead(pDevIns, adr, cb, pSgBuf, fLoopback);
|
---|
| 2217 | rc = dp8390CoreXmitSendBuf(pDevIns, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
|
---|
| 2218 | Log2Func(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
|
---|
[93560] | 2219 | }
|
---|
| 2220 | else if (rc == VERR_TRY_AGAIN)
|
---|
| 2221 | {
|
---|
| 2222 | STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
|
---|
[93601] | 2223 | LogFunc(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
|
---|
[93560] | 2224 | return VINF_SUCCESS;
|
---|
| 2225 | }
|
---|
| 2226 | if (RT_SUCCESS(rc))
|
---|
| 2227 | {
|
---|
| 2228 | pThis->core.tsr.PTX = 1;
|
---|
| 2229 | pThis->core.isr.PTX = 1;
|
---|
| 2230 | }
|
---|
| 2231 | else
|
---|
| 2232 | {
|
---|
| 2233 | pThis->core.tsr.COL = 1; /* Pretend there was a collision. */
|
---|
| 2234 | pThis->core.isr.TXE = 1;
|
---|
| 2235 | }
|
---|
| 2236 | }
|
---|
| 2237 | else
|
---|
| 2238 | {
|
---|
| 2239 | /* Signal error, as this violates the Ethernet specs. Note that the DP8390
|
---|
| 2240 | * hardware does *not* limit the packet length.
|
---|
| 2241 | */
|
---|
[93601] | 2242 | LogRel(("DPNIC#%d: Attempt to transmit illegal giant frame (%u bytes) -> signaling error\n", pThis->iInstance, cb));
|
---|
[93560] | 2243 | pThis->core.tsr.OWC = 1; /* Pretend there was an out-of-window collision. */
|
---|
| 2244 | pThis->core.isr.TXE = 1;
|
---|
| 2245 | }
|
---|
| 2246 | }
|
---|
| 2247 | else
|
---|
| 2248 | {
|
---|
| 2249 | /* Signal a transmit error pretending there was a collision. */
|
---|
| 2250 | pThis->core.tsr.COL = 1;
|
---|
| 2251 | pThis->core.isr.TXE = 1;
|
---|
| 2252 | pThis->cLinkDownReported++;
|
---|
| 2253 | }
|
---|
| 2254 | /* Transmit officially done, update register state. */
|
---|
| 2255 | pThis->core.cr.TXP = 0;
|
---|
| 2256 | pThis->core.TBCR = 0;
|
---|
[93601] | 2257 | LogFlowFunc(("#%d: TSR=%02X, ISR=%02X\n", pThis->iInstance, pThis->core.TSR, pThis->core.ISR));
|
---|
[93560] | 2258 |
|
---|
| 2259 | } while (0); /* No loop, because there isn't ever more than one packet to transmit. */
|
---|
| 2260 |
|
---|
[93601] | 2261 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2262 |
|
---|
| 2263 | /* If there's anything waiting, this should be a good time to recheck. */
|
---|
[93601] | 2264 | dp8390CoreKickReceive(pDevIns, pThis);
|
---|
[93560] | 2265 |
|
---|
| 2266 | STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
|
---|
| 2267 |
|
---|
| 2268 | return VINF_SUCCESS;
|
---|
| 2269 | }
|
---|
| 2270 |
|
---|
| 2271 | /* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
|
---|
| 2272 |
|
---|
| 2273 |
|
---|
[93601] | 2274 | static uint32_t dp8390CoreRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int ofs)
|
---|
[93560] | 2275 | {
|
---|
| 2276 | uint8_t val;
|
---|
| 2277 |
|
---|
| 2278 | /* The 3C503 can read the PROM instead of the DP8390 registers. */
|
---|
| 2279 | if (pThis->ga.gacr.ealo)
|
---|
| 2280 | return pThis->aPROM[ofs % 0xf];
|
---|
| 2281 | else if (pThis->ga.gacr.eahi)
|
---|
| 2282 | return pThis->aPROM[16 + (ofs % 0xf)];
|
---|
| 2283 |
|
---|
| 2284 | /* Command Register exists in all pages. */
|
---|
| 2285 | if (ofs == DPR_CR)
|
---|
| 2286 | return pThis->core.CR;
|
---|
| 2287 |
|
---|
| 2288 | if (pThis->core.cr.PS == 0)
|
---|
| 2289 | {
|
---|
| 2290 | switch (ofs)
|
---|
| 2291 | {
|
---|
| 2292 | case DPR_P0_R_CLDA0:
|
---|
| 2293 | return pThis->core.clda.CLDA0;
|
---|
| 2294 | case DPR_P0_R_CLDA1:
|
---|
| 2295 | return pThis->core.clda.CLDA1;
|
---|
| 2296 | case DPR_P0_BNRY:
|
---|
| 2297 | return pThis->core.BNRY;
|
---|
| 2298 | case DPR_P0_R_TSR:
|
---|
| 2299 | return pThis->core.TSR;
|
---|
| 2300 | case DPR_P0_R_NCR:
|
---|
| 2301 | return pThis->core.NCR;
|
---|
| 2302 | case DPR_P0_R_FIFO:
|
---|
| 2303 | return pThis->core.fifo.fifo[pThis->core.fifo.rp++ & 7]; /// @todo Abstract the mask somehow?
|
---|
| 2304 | case DPR_P0_ISR:
|
---|
| 2305 | return pThis->core.ISR;
|
---|
| 2306 | case DPR_P0_R_CRDA0:
|
---|
| 2307 | return pThis->core.crda.CRDA0;
|
---|
| 2308 | case DPR_P0_R_CRDA1:
|
---|
| 2309 | return pThis->core.crda.CRDA1;
|
---|
| 2310 | case DPR_P0_R_RSR:
|
---|
| 2311 | return pThis->core.RSR;
|
---|
| 2312 | case DPR_P0_R_CNTR0:
|
---|
| 2313 | val = pThis->core.CNTR0;
|
---|
| 2314 | pThis->core.CNTR0 = 0; /* Cleared by reading. */
|
---|
[93601] | 2315 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2316 | return val;
|
---|
| 2317 | case DPR_P0_R_CNTR1:
|
---|
| 2318 | val = pThis->core.CNTR1;
|
---|
| 2319 | pThis->core.CNTR1 = 0; /* Cleared by reading. */
|
---|
[93601] | 2320 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2321 | return val;
|
---|
| 2322 | case DPR_P0_R_CNTR2:
|
---|
| 2323 | val = pThis->core.CNTR2;
|
---|
| 2324 | pThis->core.CNTR2 = 0; /* Cleared by reading. */
|
---|
[93601] | 2325 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2326 | return val;
|
---|
| 2327 | default:
|
---|
| 2328 | return 0; /// @todo or 0xFF? or something else?
|
---|
| 2329 | }
|
---|
| 2330 | }
|
---|
| 2331 | else if (pThis->core.cr.PS == 1)
|
---|
| 2332 | {
|
---|
| 2333 | /* Page 1 is easy, most registers are stored directly. */
|
---|
| 2334 | if (ofs == DPR_P1_CURR)
|
---|
| 2335 | return pThis->core.CURR;
|
---|
| 2336 | else
|
---|
| 2337 | return pThis->core.PG1[ofs];
|
---|
| 2338 | }
|
---|
| 2339 | else if (pThis->core.cr.PS == 2)
|
---|
| 2340 | {
|
---|
| 2341 | /* Page 2 is for diagnostics. Reads many registers that
|
---|
| 2342 | * are write-only in Page 0.
|
---|
| 2343 | */
|
---|
| 2344 | switch (ofs)
|
---|
| 2345 | {
|
---|
| 2346 | case DPR_P2_R_PSTART:
|
---|
| 2347 | return pThis->core.PSTART;
|
---|
| 2348 | case DPR_P2_R_PSTOP:
|
---|
| 2349 | return pThis->core.PSTOP;
|
---|
| 2350 | case DPR_P2_RNXTPP:
|
---|
| 2351 | return pThis->core.rnxtpp;
|
---|
| 2352 | case DPR_P2_R_TPSR:
|
---|
| 2353 | return pThis->core.TPSR;
|
---|
| 2354 | case DPR_P2_LNXTPP:
|
---|
| 2355 | return pThis->core.lnxtpp;
|
---|
| 2356 | case DPR_P2_ADRCU:
|
---|
| 2357 | case DPR_P2_ADRCL:
|
---|
| 2358 | return 0; /// @todo What's this?
|
---|
| 2359 | case DPR_P2_R_RCR:
|
---|
| 2360 | return pThis->core.RCR;
|
---|
| 2361 | case DPR_P2_R_TCR:
|
---|
| 2362 | return pThis->core.TCR;
|
---|
| 2363 | case DPR_P2_R_DCR:
|
---|
| 2364 | return pThis->core.DCR;
|
---|
| 2365 | case DPR_P2_R_IMR:
|
---|
| 2366 | return pThis->core.IMR;
|
---|
| 2367 | default:
|
---|
| 2368 | return 0; /// @todo Or 0xFF? Or something else?
|
---|
| 2369 | }
|
---|
| 2370 | }
|
---|
| 2371 | else
|
---|
| 2372 | {
|
---|
| 2373 | /* Page 3 is undocumented and unimplemented. */
|
---|
[93565] | 2374 | LogFunc(("Reading page 3 register: ofs=%X!\n", ofs));
|
---|
[93560] | 2375 | return 0;
|
---|
| 2376 | }
|
---|
| 2377 | }
|
---|
| 2378 |
|
---|
| 2379 |
|
---|
[93601] | 2380 | static int dp8390CoreWriteCR(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t val)
|
---|
[93560] | 2381 | {
|
---|
| 2382 | union {
|
---|
| 2383 | uint8_t nCR;
|
---|
| 2384 | DP_CR nCr;
|
---|
| 2385 | };
|
---|
| 2386 |
|
---|
| 2387 | nCR = val;
|
---|
| 2388 | LogFlow(("val=%02X, old=%02X\n", val, pThis->core.CR));
|
---|
| 2389 | if (nCr.STP != pThis->core.cr.STP)
|
---|
| 2390 | {
|
---|
| 2391 | if (nCr.STP)
|
---|
| 2392 | {
|
---|
| 2393 | /* Stop the engine -- software reset. */
|
---|
| 2394 | pThis->core.cr.STP = 1;
|
---|
| 2395 | pThis->core.isr.RST = 1;
|
---|
| 2396 | }
|
---|
| 2397 | else
|
---|
| 2398 | {
|
---|
| 2399 | /* Clear the stop condition. */
|
---|
| 2400 | pThis->core.cr.STP = 0;
|
---|
| 2401 |
|
---|
| 2402 | /* And possibly start up right away. */
|
---|
| 2403 | if (nCr.STA)
|
---|
| 2404 | pThis->core.cr.STA = 1;
|
---|
| 2405 |
|
---|
| 2406 | /* The STA bit may have been set all along. */
|
---|
| 2407 | if (pThis->core.cr.STA)
|
---|
| 2408 | pThis->core.isr.RST = 0;
|
---|
| 2409 | }
|
---|
| 2410 |
|
---|
| 2411 | /* Unblock receive thread if necessary, possibly drop any packets. */
|
---|
[93601] | 2412 | dp8390CoreKickReceive(pDevIns, pThis);
|
---|
[93560] | 2413 | }
|
---|
| 2414 | if (nCr.STA && !pThis->core.cr.STA)
|
---|
| 2415 | {
|
---|
| 2416 | /* Start the engine. It is not clearly documented but the STA bit is
|
---|
| 2417 | * sticky, and once it's set only a hard reset can clear it. Setting the
|
---|
| 2418 | * STP bit doesn't clear it.
|
---|
| 2419 | */
|
---|
| 2420 | pThis->core.cr.STA = 1;
|
---|
| 2421 | pThis->core.isr.RST = 0;
|
---|
| 2422 |
|
---|
| 2423 | /* Unblock receive thread. */
|
---|
[93601] | 2424 | dp8390CoreKickReceive(pDevIns, pThis);
|
---|
[93560] | 2425 | }
|
---|
| 2426 | if (nCr.TXP && !pThis->core.cr.TXP)
|
---|
| 2427 | {
|
---|
| 2428 | /* Kick off a transmit. */
|
---|
| 2429 | pThis->core.cr.TXP = 1; /* Indicate transmit in progress. */
|
---|
[93601] | 2430 | dp8390CoreStartTransmit(pDevIns, pThis);
|
---|
[93560] | 2431 | }
|
---|
| 2432 |
|
---|
| 2433 | /* It is not possible to write a zero (invalid value) to the RD bits. */
|
---|
| 2434 | if (nCr.RD == DP_CR_RDMA_INVL)
|
---|
| 2435 | nCr.RD = DP_CR_RDMA_ABRT;
|
---|
| 2436 |
|
---|
| 2437 | if (nCr.RD != pThis->core.cr.RD)
|
---|
| 2438 | {
|
---|
| 2439 | /* Remote DMA state change. */
|
---|
| 2440 | if (nCr.RD & DP_CR_RDMA_ABRT)
|
---|
| 2441 | {
|
---|
| 2442 | /* Abort. */
|
---|
[93565] | 2443 | LogFunc(("RDMA Abort! RD=%d RSAR=%04X RBCR=%04X CRDA=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR, pThis->core.CRDA));
|
---|
[93560] | 2444 | }
|
---|
| 2445 | else if (nCr.RD == DP_CR_RDMA_SP)
|
---|
| 2446 | {
|
---|
| 2447 | DP_PKT_HDR header;
|
---|
| 2448 |
|
---|
| 2449 | /* Read a packet header from memory at BNRY. */
|
---|
| 2450 | dpLocalRAMReadBuf(pThis, pThis->core.BNRY, sizeof(header), (uint8_t*)&header);
|
---|
| 2451 |
|
---|
| 2452 | pThis->core.CRDA = RT_MAKE_U16(0, pThis->core.BNRY);
|
---|
| 2453 | pThis->core.RBCR = header.byte_cnt;
|
---|
| 2454 |
|
---|
[93565] | 2455 | LogFunc(("RDMA SP: RD=%d RSAR=%04X RBCR=%04X CRDA=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR, pThis->core.CRDA));
|
---|
[93560] | 2456 | }
|
---|
| 2457 | else
|
---|
| 2458 | {
|
---|
| 2459 | /* Starting remote DMA read or write. */
|
---|
[93565] | 2460 | LogFunc(("RDMA: RD=%d RSAR=%04X RBCR=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR));
|
---|
[93560] | 2461 | }
|
---|
| 2462 | pThis->core.cr.RD = nCr.RD;
|
---|
| 2463 | /* NB: The current DMA address (CRDA) is not modified here. */
|
---|
| 2464 | }
|
---|
| 2465 | /* Set the page select bits. */
|
---|
| 2466 | pThis->core.cr.PS = nCr.PS;
|
---|
| 2467 |
|
---|
| 2468 | return VINF_SUCCESS;
|
---|
| 2469 | }
|
---|
| 2470 |
|
---|
[93601] | 2471 | static int dp8390CoreWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int ofs, uint32_t val)
|
---|
[93560] | 2472 | {
|
---|
| 2473 | int rc = VINF_SUCCESS;
|
---|
| 2474 | bool fUpdateIRQ = false;
|
---|
| 2475 |
|
---|
[93601] | 2476 | Log2Func(("#%d: page=%d reg=%X val=%02X\n", pThis->iInstance, pThis->core.cr.PS, ofs, val));
|
---|
[93560] | 2477 |
|
---|
| 2478 | /* Command Register exists in all pages. */
|
---|
| 2479 | if (ofs == DPR_CR)
|
---|
| 2480 | {
|
---|
[93601] | 2481 | rc = dp8390CoreWriteCR(pDevIns, pThis, val);
|
---|
[93560] | 2482 | }
|
---|
| 2483 | else if (pThis->core.cr.PS == 0)
|
---|
| 2484 | {
|
---|
| 2485 | switch (ofs)
|
---|
| 2486 | {
|
---|
| 2487 | case DPR_P0_W_PSTART:
|
---|
| 2488 | pThis->core.PSTART = val;
|
---|
| 2489 | pThis->core.CURR = val;
|
---|
| 2490 | break;
|
---|
| 2491 | case DPR_P0_W_PSTOP:
|
---|
| 2492 | pThis->core.PSTOP = val;
|
---|
| 2493 | break;
|
---|
| 2494 | case DPR_P0_BNRY:
|
---|
| 2495 | if (pThis->core.BNRY != val)
|
---|
| 2496 | {
|
---|
| 2497 | pThis->core.BNRY = val;
|
---|
| 2498 | /* Probably made more room in receive ring. */
|
---|
[93601] | 2499 | dp8390CoreKickReceive(pDevIns, pThis);
|
---|
[93560] | 2500 | }
|
---|
| 2501 | break;
|
---|
| 2502 | case DPR_P0_W_TPSR:
|
---|
| 2503 | pThis->core.TPSR = val;
|
---|
| 2504 | break;
|
---|
| 2505 | case DPR_P0_W_TBCR0:
|
---|
| 2506 | pThis->core.tbcr.TBCR0 = val;
|
---|
| 2507 | break;
|
---|
| 2508 | case DPR_P0_W_TBCR1:
|
---|
| 2509 | pThis->core.tbcr.TBCR1 = val;
|
---|
| 2510 | break;
|
---|
| 2511 | case DPR_P0_ISR:
|
---|
| 2512 | /* Bits are cleared by writing 1 to them, except for bit 7 (RST). */
|
---|
| 2513 | pThis->core.ISR &= ~val | RT_BIT(7);
|
---|
| 2514 | fUpdateIRQ = true;
|
---|
| 2515 | break;
|
---|
| 2516 | case DPR_P0_W_RSAR0:
|
---|
| 2517 | /* NE2000 ODI driver v2.12 detects card presence by writing RSAR0
|
---|
| 2518 | * and checking if CRDA0 changes to the same value.
|
---|
| 2519 | */
|
---|
| 2520 | pThis->core.rsar.RSAR0 = val;
|
---|
| 2521 | pThis->core.crda.CRDA0 = val;
|
---|
| 2522 | break;
|
---|
| 2523 | case DPR_P0_W_RSAR1:
|
---|
| 2524 | pThis->core.rsar.RSAR1 = val;
|
---|
| 2525 | pThis->core.crda.CRDA1 = val;
|
---|
| 2526 | break;
|
---|
| 2527 | case DPR_P0_W_RBCR0:
|
---|
| 2528 | pThis->core.rbcr.RBCR0 = val;
|
---|
| 2529 | break;
|
---|
| 2530 | case DPR_P0_W_RBCR1:
|
---|
| 2531 | pThis->core.rbcr.RBCR1 = val;
|
---|
| 2532 | break;
|
---|
| 2533 | case DPR_P0_W_RCR:
|
---|
| 2534 | pThis->core.RCR = val;
|
---|
| 2535 | pThis->core.rsr.DIS = pThis->core.rcr.MON;
|
---|
| 2536 | break;
|
---|
| 2537 | case DPR_P0_W_TCR:
|
---|
| 2538 | pThis->core.TCR = val;
|
---|
| 2539 | break;
|
---|
| 2540 | case DPR_P0_W_DCR:
|
---|
| 2541 | pThis->core.DCR = val;
|
---|
| 2542 | break;
|
---|
| 2543 | case DPR_P0_W_IMR:
|
---|
| 2544 | pThis->core.IMR = val & 0x7f; /* Don't let the high bit get set. */
|
---|
| 2545 | fUpdateIRQ = true;
|
---|
| 2546 | break;
|
---|
| 2547 | default:
|
---|
| 2548 | Assert(0);
|
---|
| 2549 | break;
|
---|
| 2550 | }
|
---|
| 2551 | }
|
---|
| 2552 | else if (pThis->core.cr.PS == 1)
|
---|
| 2553 | {
|
---|
| 2554 | /* Page 1 is easy, most registers are stored directly. */
|
---|
| 2555 | if (ofs == DPR_P1_CURR)
|
---|
| 2556 | {
|
---|
| 2557 | pThis->core.CURR = val;
|
---|
| 2558 | }
|
---|
| 2559 | else
|
---|
| 2560 | pThis->core.PG1[ofs] = val;
|
---|
| 2561 | }
|
---|
| 2562 | else if (pThis->core.cr.PS == 2)
|
---|
| 2563 | {
|
---|
| 2564 | switch (ofs)
|
---|
| 2565 | {
|
---|
| 2566 | case DPR_P2_W_CLDA0:
|
---|
| 2567 | pThis->core.clda.CLDA0 = val;
|
---|
| 2568 | break;
|
---|
| 2569 | case DPR_P2_W_CLDA1:
|
---|
| 2570 | pThis->core.clda.CLDA1 = val;
|
---|
| 2571 | break;
|
---|
| 2572 | case DPR_P2_RNXTPP:
|
---|
| 2573 | pThis->core.rnxtpp = val;
|
---|
| 2574 | break;
|
---|
| 2575 | case DPR_P2_LNXTPP:
|
---|
| 2576 | pThis->core.lnxtpp = val;
|
---|
| 2577 | break;
|
---|
| 2578 | case DPR_P2_ADRCU:
|
---|
| 2579 | case DPR_P2_ADRCL:
|
---|
| 2580 | /// @todo What are these?
|
---|
| 2581 | break;
|
---|
| 2582 | default:
|
---|
| 2583 | LogFunc(("Writing unimplemented register: Page 2, offset=%d, val=%02X!\n", ofs, val));
|
---|
| 2584 | break;
|
---|
| 2585 | }
|
---|
| 2586 | }
|
---|
| 2587 | else
|
---|
| 2588 | {
|
---|
| 2589 | /* Page 3 is undocumented and unimplemented. */
|
---|
| 2590 | LogFunc(("Writing page 3 register: offset=%d, val=%02X!\n", ofs, val));
|
---|
| 2591 | }
|
---|
| 2592 |
|
---|
| 2593 | if (fUpdateIRQ)
|
---|
[93601] | 2594 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2595 |
|
---|
| 2596 | return rc;
|
---|
| 2597 | }
|
---|
| 2598 |
|
---|
| 2599 |
|
---|
| 2600 | static void neLocalRAMWrite8(PDPNICSTATE pThis, uint16_t addr, uint8_t val)
|
---|
| 2601 | {
|
---|
| 2602 | if (pThis->uDevType == DEV_NE1000)
|
---|
| 2603 | {
|
---|
| 2604 | /* Only 14 bits of address are decoded. */
|
---|
| 2605 | addr &= 0x3fff;
|
---|
| 2606 | if (addr >= 0x2000)
|
---|
| 2607 | {
|
---|
| 2608 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 2609 | addr -= 0x2000;
|
---|
| 2610 | pThis->abLocalRAM[addr] = val;
|
---|
| 2611 | }
|
---|
| 2612 | }
|
---|
| 2613 | else if (pThis->uDevType == DEV_NE2000)
|
---|
| 2614 | {
|
---|
| 2615 | /* Only 15 bits of address are decoded. */
|
---|
| 2616 | addr &= 0x7fff;
|
---|
| 2617 | if (addr >= 0x4000)
|
---|
| 2618 | {
|
---|
| 2619 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 2620 | addr -= 0x4000;
|
---|
| 2621 | pThis->abLocalRAM[addr] = val;
|
---|
| 2622 | }
|
---|
| 2623 | }
|
---|
| 2624 | else
|
---|
| 2625 | {
|
---|
| 2626 | Assert(0);
|
---|
| 2627 | }
|
---|
| 2628 | }
|
---|
| 2629 |
|
---|
| 2630 |
|
---|
| 2631 | static void neLocalRAMWrite16(PDPNICSTATE pThis, uint16_t addr, uint16_t val)
|
---|
| 2632 | {
|
---|
| 2633 | if (pThis->uDevType == DEV_NE2000)
|
---|
| 2634 | {
|
---|
| 2635 | /* Only 14 bits of address are decoded, word aligned. */
|
---|
| 2636 | addr &= 0x7ffe;
|
---|
| 2637 | if (addr >= 0x4000)
|
---|
| 2638 | {
|
---|
| 2639 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 2640 | addr -= 0x4000;
|
---|
| 2641 | pThis->abLocalRAM[addr+0] = RT_LOBYTE(val);
|
---|
| 2642 | pThis->abLocalRAM[addr+1] = RT_HIBYTE(val);
|
---|
| 2643 | }
|
---|
| 2644 | }
|
---|
| 2645 | else
|
---|
| 2646 | {
|
---|
| 2647 | Assert(0);
|
---|
| 2648 | }
|
---|
| 2649 | }
|
---|
| 2650 |
|
---|
| 2651 |
|
---|
| 2652 | static uint8_t neLocalRAMRead8(PDPNICSTATE pThis, uint16_t addr)
|
---|
| 2653 | {
|
---|
| 2654 | uint8_t val = 0xff;
|
---|
| 2655 |
|
---|
| 2656 | if (pThis->uDevType == DEV_NE1000)
|
---|
| 2657 | {
|
---|
| 2658 | /* Only 14 bits of address are decoded. */
|
---|
| 2659 | addr &= 0x3fff;
|
---|
| 2660 | if (addr >= 0x2000)
|
---|
| 2661 | {
|
---|
| 2662 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 2663 | addr -= 0x2000;
|
---|
| 2664 | val = pThis->abLocalRAM[addr];
|
---|
| 2665 | }
|
---|
| 2666 | else
|
---|
| 2667 | {
|
---|
| 2668 | /* The PROM is mapped below 2000h, effectively only 4 bits decoded.
|
---|
| 2669 | * NE1000 emulation uses top 16 bytes of the PROM.
|
---|
| 2670 | */
|
---|
| 2671 | val = pThis->aPROM[(addr & 0x0f) + 16]; /// @todo Use a constant
|
---|
| 2672 | }
|
---|
| 2673 | }
|
---|
| 2674 | else if (pThis->uDevType == DEV_NE2000)
|
---|
| 2675 | {
|
---|
| 2676 | /* Only 15 bits of address are decoded. */
|
---|
| 2677 | addr &= 0x7fff;
|
---|
| 2678 | if (addr >= 0x4000)
|
---|
| 2679 | {
|
---|
| 2680 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 2681 | addr -= 0x4000;
|
---|
| 2682 | val = pThis->abLocalRAM[addr];
|
---|
| 2683 | }
|
---|
| 2684 | else
|
---|
| 2685 | {
|
---|
| 2686 | /* The PROM is mapped below 4000h, effectively only 4 bits decoded.
|
---|
| 2687 | * Address bits 1:4 from the bus are connected to address pins 0:3
|
---|
| 2688 | * on the PROM.
|
---|
| 2689 | */
|
---|
| 2690 | val = pThis->aPROM[(addr & 0x1f) >> 1]; /// @todo use a constant
|
---|
| 2691 | }
|
---|
| 2692 | }
|
---|
| 2693 | else
|
---|
| 2694 | {
|
---|
| 2695 | Assert(0);
|
---|
| 2696 | }
|
---|
| 2697 | return val;
|
---|
| 2698 | }
|
---|
| 2699 |
|
---|
| 2700 |
|
---|
| 2701 | static uint16_t neLocalRAMRead16(PDPNICSTATE pThis, uint16_t addr)
|
---|
| 2702 | {
|
---|
| 2703 | uint16_t val = 0xffff;
|
---|
| 2704 |
|
---|
| 2705 | if (pThis->uDevType == DEV_NE2000)
|
---|
| 2706 | {
|
---|
| 2707 | /* Only 14 bits of address are decoded, word aligned. */
|
---|
| 2708 | addr &= 0x7ffe;
|
---|
| 2709 | if (addr >= 0x4000)
|
---|
| 2710 | {
|
---|
| 2711 | /* Local RAM is mapped at 4000h-7FFFh. */
|
---|
| 2712 | addr -= 0x4000;
|
---|
| 2713 | val = RT_MAKE_U16(pThis->abLocalRAM[addr], pThis->abLocalRAM[addr+1]);
|
---|
| 2714 | }
|
---|
| 2715 | else
|
---|
| 2716 | {
|
---|
| 2717 | uint8_t uPromByte;
|
---|
| 2718 |
|
---|
| 2719 | /* The PROM is mapped below 4000h, effectively only 4 bits decoded.
|
---|
| 2720 | * Address bits 1:4 from the bus are connected to address pins 0:3
|
---|
| 2721 | * on the PROM.
|
---|
| 2722 | */
|
---|
| 2723 | uPromByte = pThis->aPROM[(addr & 0x1f) >> 1];
|
---|
| 2724 | val = RT_MAKE_U16(uPromByte, uPromByte);
|
---|
| 2725 | }
|
---|
| 2726 | }
|
---|
| 2727 | else
|
---|
| 2728 | {
|
---|
| 2729 | Assert(0);
|
---|
| 2730 | }
|
---|
| 2731 | return val;
|
---|
| 2732 | }
|
---|
| 2733 |
|
---|
| 2734 |
|
---|
[93601] | 2735 | static int neDataPortWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint16_t val)
|
---|
[93560] | 2736 | {
|
---|
| 2737 | /* Remote Write; ignored if Remote DMA command is not 'Write'. */
|
---|
| 2738 | if (pThis->core.cr.RD == DP_CR_RDMA_WR)
|
---|
| 2739 | {
|
---|
| 2740 | /// @todo Also do nothing if DCR.LAS set?
|
---|
| 2741 | if (pThis->core.dcr.WTS)
|
---|
| 2742 | {
|
---|
| 2743 | Log3Func(("RDMA16 write %04X to local addr %04X\n", val, pThis->core.CRDA));
|
---|
| 2744 | neLocalRAMWrite16(pThis, pThis->core.CRDA, val);
|
---|
| 2745 | }
|
---|
| 2746 | else
|
---|
| 2747 | {
|
---|
| 2748 | Log3Func(("RDMA8 write %02X to local addr %04X\n", val, pThis->core.CRDA));
|
---|
| 2749 | neLocalRAMWrite8(pThis, pThis->core.CRDA, val);
|
---|
| 2750 | }
|
---|
| 2751 | pThis->core.CRDA += 1 << pThis->core.dcr.WTS;
|
---|
| 2752 | if ((pThis->core.crda.CRDA1 == pThis->core.PSTOP) && (pThis->core.PSTOP != pThis->core.PSTART))
|
---|
| 2753 | {
|
---|
| 2754 | LogFunc(("RDMA wrap / write!! (CRDA=%04X PSTOP=%02X00 PSTART=%02X00)\n", pThis->core.CRDA, pThis->core.PSTOP, pThis->core.PSTART));
|
---|
| 2755 | Assert(!pThis->core.crda.CRDA0); /// @todo Can misalignment actually happen?
|
---|
| 2756 | pThis->core.crda.CRDA1 = pThis->core.PSTART;
|
---|
| 2757 | }
|
---|
| 2758 | pThis->core.RBCR -= 1;
|
---|
| 2759 |
|
---|
| 2760 | /* Carefully decrement if WTS set so we don't overshoot and miss EOP. */
|
---|
| 2761 | if (pThis->core.dcr.WTS && pThis->core.RBCR)
|
---|
| 2762 | pThis->core.RBCR -= 1;
|
---|
| 2763 |
|
---|
| 2764 | if (!pThis->core.RBCR)
|
---|
| 2765 | {
|
---|
| 2766 | LogFunc(("RDMA EOP / write\n"));
|
---|
| 2767 | pThis->core.isr.RDC = 1;
|
---|
| 2768 | pThis->core.cr.RD = 0;
|
---|
[93601] | 2769 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2770 | }
|
---|
| 2771 | }
|
---|
| 2772 | return VINF_SUCCESS;
|
---|
| 2773 | }
|
---|
| 2774 |
|
---|
| 2775 |
|
---|
[93601] | 2776 | static uint16_t neDataPortRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 2777 | {
|
---|
| 2778 | uint16_t val = 0x1234;
|
---|
| 2779 |
|
---|
| 2780 | /* Remote Read; ignored if Remote DMA command is not 'Read'. */
|
---|
| 2781 | if (pThis->core.cr.RD == DP_CR_RDMA_RD)
|
---|
| 2782 | {
|
---|
| 2783 | /// @todo Also do nothing if DCR.LAS set?
|
---|
| 2784 | if (pThis->core.dcr.WTS)
|
---|
| 2785 | {
|
---|
| 2786 | val = neLocalRAMRead16(pThis, pThis->core.CRDA);
|
---|
| 2787 | Log3Func(("RDMA16 read from local addr %04X: %04X\n", pThis->core.CRDA, val));
|
---|
| 2788 | }
|
---|
| 2789 | else
|
---|
| 2790 | {
|
---|
| 2791 | val = neLocalRAMRead8(pThis, pThis->core.CRDA);
|
---|
| 2792 | Log3Func(("RDMA8 read from local addr %04X: %02X\n", pThis->core.CRDA, val));
|
---|
| 2793 | }
|
---|
| 2794 | pThis->core.CRDA += 1 << pThis->core.dcr.WTS;
|
---|
| 2795 | /// @todo explain that PSTOP=PSTART check is only to reduce logging/busywork
|
---|
| 2796 | if ((pThis->core.crda.CRDA1 == pThis->core.PSTOP) && (pThis->core.PSTOP != pThis->core.PSTART))
|
---|
| 2797 | {
|
---|
| 2798 | Log3Func(("RDMA wrap / read (CRDA=%04X PSTOP=%02X00 PSTART=%02X00)\n", pThis->core.CRDA, pThis->core.PSTOP, pThis->core.PSTART));
|
---|
| 2799 | Assert(!pThis->core.crda.CRDA0); /// @todo can misalignment happen?
|
---|
| 2800 | pThis->core.crda.CRDA1 = pThis->core.PSTART;
|
---|
| 2801 | }
|
---|
| 2802 | pThis->core.RBCR -= 1;
|
---|
| 2803 |
|
---|
| 2804 | /* Carefully decrement if WTS set so we don't overshoot and miss EOP. */
|
---|
| 2805 | if (pThis->core.dcr.WTS && pThis->core.RBCR)
|
---|
| 2806 | pThis->core.RBCR -= 1;
|
---|
| 2807 |
|
---|
| 2808 | if (!pThis->core.RBCR)
|
---|
| 2809 | {
|
---|
| 2810 | LogFunc(("RDMA EOP / read\n"));
|
---|
| 2811 | pThis->core.isr.RDC = 1;
|
---|
| 2812 | pThis->core.cr.RD = 0;
|
---|
[93601] | 2813 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 2814 | }
|
---|
| 2815 | }
|
---|
| 2816 | return val;
|
---|
| 2817 | }
|
---|
| 2818 |
|
---|
| 2819 |
|
---|
[93601] | 2820 | static int neResetPortWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 2821 | {
|
---|
| 2822 | LogFlowFunc(("\n"));
|
---|
[93601] | 2823 | dp8390CoreReset(pDevIns, pThis);
|
---|
[93560] | 2824 | return VINF_SUCCESS;
|
---|
| 2825 | }
|
---|
| 2826 |
|
---|
| 2827 |
|
---|
[93601] | 2828 | static int dpNeIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr, uint32_t val)
|
---|
[93560] | 2829 | {
|
---|
| 2830 | int reg = addr & 0x0f;
|
---|
| 2831 | int rc = VINF_SUCCESS;
|
---|
| 2832 |
|
---|
[93601] | 2833 | Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
|
---|
[93560] | 2834 |
|
---|
| 2835 | /* The NE2000 has 8 bytes of data port followed by 8 bytes of reset port.
|
---|
| 2836 | * In contrast, the NE1000 has 4 bytes of data port followed by 4 bytes
|
---|
| 2837 | * of reset port, aliased twice within the 16-byte range.
|
---|
| 2838 | */
|
---|
| 2839 | if (pThis->uDevType == DEV_NE2000)
|
---|
| 2840 | reg >>= 1;
|
---|
| 2841 | if (reg & 0x04)
|
---|
[93601] | 2842 | rc = neResetPortWrite(pDevIns, pThis);
|
---|
[93560] | 2843 | else
|
---|
[93601] | 2844 | rc = neDataPortWrite(pDevIns, pThis, val);
|
---|
[93560] | 2845 |
|
---|
| 2846 | return rc;
|
---|
| 2847 | }
|
---|
| 2848 |
|
---|
| 2849 |
|
---|
[93601] | 2850 | static uint32_t neIoRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr)
|
---|
[93560] | 2851 | {
|
---|
| 2852 | uint32_t val = UINT32_MAX;
|
---|
| 2853 | int reg = addr & 0x0f;
|
---|
| 2854 |
|
---|
| 2855 | /* The NE2000 has 8 bytes of data port followed by 8 bytes of reset port.
|
---|
| 2856 | * In contrast, the NE1000 has 4 bytes of data port followed by 4 bytes
|
---|
| 2857 | * of reset port, aliased twice within the 16-byte range.
|
---|
| 2858 | */
|
---|
| 2859 | if (pThis->uDevType == DEV_NE2000)
|
---|
| 2860 | reg >>= 1;
|
---|
| 2861 | if (reg & 0x04)
|
---|
| 2862 | val = 0x52; /// @todo Check what really happens
|
---|
| 2863 | else
|
---|
[93601] | 2864 | val = neDataPortRead(pDevIns, pThis);
|
---|
[93560] | 2865 |
|
---|
[93601] | 2866 | Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
|
---|
[93560] | 2867 | return val;
|
---|
| 2868 | }
|
---|
| 2869 |
|
---|
| 2870 |
|
---|
[93601] | 2871 | static int wdIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr, uint32_t val)
|
---|
[93560] | 2872 | {
|
---|
| 2873 | int reg = addr & 0xf;
|
---|
| 2874 | int rc = VINF_SUCCESS;
|
---|
| 2875 | union {
|
---|
| 2876 | uint8_t nCTRL1;
|
---|
| 2877 | WD_CTRL1 nCtrl1;
|
---|
| 2878 | };
|
---|
| 2879 | union {
|
---|
| 2880 | uint8_t nCTRL2;
|
---|
| 2881 | WD_CTRL2 nCtrl2;
|
---|
| 2882 | };
|
---|
| 2883 |
|
---|
[93601] | 2884 | Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
|
---|
[93560] | 2885 |
|
---|
| 2886 | switch (reg)
|
---|
| 2887 | {
|
---|
| 2888 | case WDR_CTRL1:
|
---|
| 2889 | nCTRL1 = val;
|
---|
| 2890 | if (nCtrl1.MEME != pThis->ctrl1.MEME)
|
---|
| 2891 | {
|
---|
| 2892 | LogFunc(("CTRL1.MEME=%u\n", nCtrl1.MEME));
|
---|
| 2893 | pThis->ctrl1.MEME = nCtrl1.MEME;
|
---|
| 2894 | }
|
---|
| 2895 | if (nCtrl1.RESET)
|
---|
| 2896 | {
|
---|
[93601] | 2897 | dp8390CoreReset(pDevIns, pThis);
|
---|
[93560] | 2898 | pThis->CTRL1 = 0;
|
---|
| 2899 | }
|
---|
| 2900 | break;
|
---|
| 2901 | case WDR_CTRL2:
|
---|
| 2902 | /* NYI. */
|
---|
| 2903 | nCTRL2 = val;
|
---|
| 2904 | if (nCTRL2 != pThis->CTRL2)
|
---|
| 2905 | {
|
---|
| 2906 | LogFunc(("CTRL2=%02X, new=%02X\n", pThis->CTRL2, nCTRL2));
|
---|
| 2907 | pThis->CTRL2 = nCTRL2;
|
---|
| 2908 | }
|
---|
| 2909 | break;
|
---|
| 2910 | default:
|
---|
| 2911 | /* Most of the WD registers are read-only. */
|
---|
| 2912 | break;
|
---|
| 2913 | }
|
---|
| 2914 |
|
---|
| 2915 | return rc;
|
---|
| 2916 | }
|
---|
| 2917 |
|
---|
| 2918 |
|
---|
| 2919 | static uint32_t wdIoRead(PDPNICSTATE pThis, uint32_t addr)
|
---|
| 2920 | {
|
---|
| 2921 | uint32_t val = UINT32_MAX;
|
---|
| 2922 | int reg = addr & 0x0f;
|
---|
| 2923 |
|
---|
| 2924 | if (reg >= WDR_PROM)
|
---|
| 2925 | {
|
---|
| 2926 | val = pThis->aPROM[reg & 7];
|
---|
| 2927 | }
|
---|
| 2928 | else
|
---|
| 2929 | {
|
---|
| 2930 | if (pThis->uDevType == DEV_WD8013)
|
---|
| 2931 | {
|
---|
| 2932 | switch (reg)
|
---|
| 2933 | {
|
---|
| 2934 | case WDR_CTRL1:
|
---|
| 2935 | val = pThis->CTRL1;
|
---|
| 2936 | break;
|
---|
| 2937 | case WDR_ATDET:
|
---|
| 2938 | val = pThis->uDevType == DEV_WD8013 ? 1 : 0;
|
---|
| 2939 | break;
|
---|
| 2940 | case WDR_IOBASE:
|
---|
| 2941 | val = pThis->aPROM[WDR_IOBASE]; //val = pThis->IOPortBase >> 5;
|
---|
| 2942 | break;
|
---|
| 2943 | case WDR_CTRL2:
|
---|
| 2944 | val = pThis->CTRL2;
|
---|
| 2945 | break;
|
---|
| 2946 | case WDR_JP:
|
---|
| 2947 | val = 0xa0;
|
---|
| 2948 | break;
|
---|
| 2949 | default:
|
---|
| 2950 | val = 0x00; /// @todo What should it be really?
|
---|
| 2951 | break;
|
---|
| 2952 | }
|
---|
| 2953 | }
|
---|
| 2954 | else
|
---|
| 2955 | {
|
---|
| 2956 | /* Old WD adapters (including 8003E) aliased the PROM for
|
---|
| 2957 | * unimplemented control register reads.
|
---|
| 2958 | */
|
---|
| 2959 | switch (reg)
|
---|
| 2960 | {
|
---|
| 2961 | case WDR_CTRL2:
|
---|
| 2962 | val = 1; //pThis->CTRL2;
|
---|
| 2963 | break;
|
---|
| 2964 | case WDR_JP:
|
---|
| 2965 | val = 0xa0;
|
---|
| 2966 | break;
|
---|
| 2967 | default:
|
---|
| 2968 | val = pThis->aPROM[reg & 7];
|
---|
| 2969 | break;
|
---|
| 2970 | }
|
---|
| 2971 | }
|
---|
| 2972 |
|
---|
| 2973 | }
|
---|
| 2974 |
|
---|
[93601] | 2975 | Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
|
---|
[93560] | 2976 | return val;
|
---|
| 2977 | }
|
---|
| 2978 |
|
---|
| 2979 |
|
---|
| 2980 | static uint8_t elGetIrqFromIdcfr(uint8_t val)
|
---|
| 2981 | {
|
---|
| 2982 | union {
|
---|
| 2983 | uint8_t IDCFR;
|
---|
| 2984 | EL_IDCFR idcfr;
|
---|
| 2985 | };
|
---|
| 2986 | uint8_t irq = 0;
|
---|
| 2987 |
|
---|
| 2988 | IDCFR = val;
|
---|
| 2989 |
|
---|
| 2990 | /* Lowest set IRQ bit wins (might not match hardware).
|
---|
| 2991 | * NB: It is valid to not enable any IRQ line!
|
---|
| 2992 | */
|
---|
| 2993 | if (idcfr.irq2)
|
---|
| 2994 | irq = 2;
|
---|
| 2995 | else if (idcfr.irq3)
|
---|
| 2996 | irq = 3;
|
---|
| 2997 | else if (idcfr.irq4)
|
---|
| 2998 | irq = 4;
|
---|
| 2999 | else if (idcfr.irq5)
|
---|
| 3000 | irq = 5;
|
---|
| 3001 |
|
---|
| 3002 | return irq;
|
---|
| 3003 | }
|
---|
| 3004 |
|
---|
| 3005 | static uint8_t elGetDrqFromIdcfr(uint8_t val)
|
---|
| 3006 | {
|
---|
| 3007 | union {
|
---|
| 3008 | uint8_t IDCFR;
|
---|
| 3009 | EL_IDCFR idcfr;
|
---|
| 3010 | };
|
---|
| 3011 | uint8_t drq = 0;
|
---|
| 3012 |
|
---|
| 3013 | IDCFR = val;
|
---|
| 3014 |
|
---|
| 3015 | /* Lowest set DRQ bit wins; it is valid to not set any. */
|
---|
| 3016 | if (idcfr.drq1)
|
---|
| 3017 | drq = 1;
|
---|
| 3018 | else if (idcfr.drq2)
|
---|
| 3019 | drq = 2;
|
---|
| 3020 | else if (idcfr.drq3)
|
---|
| 3021 | drq = 3;
|
---|
| 3022 |
|
---|
| 3023 | return drq;
|
---|
| 3024 | }
|
---|
| 3025 |
|
---|
[93601] | 3026 | static void elWriteIdcfr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
|
---|
[93560] | 3027 | {
|
---|
| 3028 | uint8_t uOldIrq = pThis->uIsaIrq;
|
---|
| 3029 | uint8_t uNewIrq;
|
---|
| 3030 | uint8_t uOldDrq = pThis->uElIsaDma;
|
---|
| 3031 | uint8_t uNewDrq;
|
---|
| 3032 |
|
---|
| 3033 | /* If the IRQ is currently active, have to switch it. */
|
---|
| 3034 | uNewIrq = elGetIrqFromIdcfr(val);
|
---|
| 3035 | if (uOldIrq != uNewIrq)
|
---|
| 3036 | {
|
---|
[93601] | 3037 | LogFunc(("#%d Switching IRQ=%d -> IRQ=%d\n", pThis->iInstance, uOldIrq, uNewIrq));
|
---|
[93560] | 3038 | if (pThis->fNicIrqActive)
|
---|
| 3039 | {
|
---|
| 3040 | /* This probably isn't supposed to happen. */
|
---|
[93601] | 3041 | LogFunc(("#%d Moving active IRQ!\n", pThis->iInstance));
|
---|
[93560] | 3042 | if (uOldIrq)
|
---|
[93601] | 3043 | PDMDevHlpISASetIrq(pDevIns, uOldIrq, 0);
|
---|
[93560] | 3044 | if (uNewIrq)
|
---|
[93601] | 3045 | PDMDevHlpISASetIrq(pDevIns, uNewIrq, 1);
|
---|
[93560] | 3046 | }
|
---|
| 3047 | pThis->uIsaIrq = uNewIrq;
|
---|
| 3048 | }
|
---|
| 3049 |
|
---|
| 3050 | /* And now the same dance for DMA. */
|
---|
| 3051 | uNewDrq = elGetDrqFromIdcfr(val);
|
---|
| 3052 | if (uOldDrq != uNewDrq)
|
---|
| 3053 | {
|
---|
| 3054 | /// @todo We can't really move the DRQ, what can we do?
|
---|
[93601] | 3055 | LogFunc(("#%d Switching DRQ=%d -> DRQ=%d\n", pThis->iInstance, uOldDrq, uNewDrq));
|
---|
[93560] | 3056 | pThis->uElIsaDma = uNewDrq;
|
---|
| 3057 | }
|
---|
| 3058 |
|
---|
| 3059 | pGa->IDCFR = val;
|
---|
| 3060 | }
|
---|
| 3061 |
|
---|
| 3062 |
|
---|
[93601] | 3063 | static void elWriteGacfr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
|
---|
[93560] | 3064 | {
|
---|
| 3065 | union {
|
---|
| 3066 | uint8_t nGACFR;
|
---|
| 3067 | GA_GACFR nGacfr;
|
---|
| 3068 | };
|
---|
| 3069 |
|
---|
| 3070 | nGACFR = val;
|
---|
| 3071 |
|
---|
| 3072 | if (nGacfr.nim != pGa->gacfr.nim)
|
---|
| 3073 | {
|
---|
| 3074 | /// @todo Should we just run UpdateInterrupts?
|
---|
| 3075 | if (pThis->fNicIrqActive && !nGacfr.nim)
|
---|
| 3076 | {
|
---|
[93601] | 3077 | LogFunc(("#%d: Unmasking active IRQ!\n", pThis->iInstance));
|
---|
| 3078 | PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, 1);
|
---|
[93560] | 3079 | }
|
---|
| 3080 | else if (pThis->fNicIrqActive && nGacfr.nim)
|
---|
| 3081 | {
|
---|
[93601] | 3082 | LogFunc(("#%d: Masking active IRQ\n", pThis->iInstance));
|
---|
| 3083 | PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, 0);
|
---|
[93560] | 3084 | }
|
---|
| 3085 | }
|
---|
| 3086 |
|
---|
| 3087 | /// @todo rsel/mbs bit change?
|
---|
| 3088 | if (nGacfr.rsel != pGa->gacfr.rsel)
|
---|
| 3089 | {
|
---|
[93601] | 3090 | LogFunc(("#%d: rsel=%u mbs=%u\n", pThis->iInstance, nGacfr.rsel, nGacfr.mbs));
|
---|
[93560] | 3091 | }
|
---|
| 3092 |
|
---|
| 3093 | pGa->GACFR = nGACFR;
|
---|
| 3094 | }
|
---|
| 3095 |
|
---|
| 3096 |
|
---|
[93601] | 3097 | static void elSoftReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
|
---|
[93560] | 3098 | {
|
---|
| 3099 | PEL_GA pGa = &pThis->ga;
|
---|
| 3100 |
|
---|
| 3101 | LogFlow(("Resetting ASIC GA\n"));
|
---|
| 3102 | /* Most GA registers are zeroed. */
|
---|
| 3103 | pGa->PSTR = pGa->PSPR = 0;
|
---|
| 3104 | pGa->DQTR = 0;
|
---|
[93601] | 3105 | elWriteGacfr(pDevIns, pThis, pGa, 0);
|
---|
[93560] | 3106 | pGa->STREG = ELNKII_GA_REV;
|
---|
| 3107 | pGa->VPTR0 = pGa->VPTR1 = pGa->VPTR2 = 0;
|
---|
| 3108 | pGa->DALSB = pGa->DAMSB = 0;
|
---|
[93601] | 3109 | elWriteIdcfr(pDevIns, pThis, pGa, 0);
|
---|
[93560] | 3110 | pGa->GACR = 0x0B; /* Low bit set = in reset state. */
|
---|
| 3111 | pGa->fGaIrq = false;
|
---|
| 3112 |
|
---|
| 3113 | /* Reset the NIC core. */
|
---|
[93601] | 3114 | dp8390CoreReset(pDevIns, pThis);
|
---|
[93560] | 3115 | }
|
---|
| 3116 |
|
---|
| 3117 |
|
---|
[93601] | 3118 | static int elWriteGacr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
|
---|
[93560] | 3119 | {
|
---|
| 3120 | union {
|
---|
| 3121 | uint8_t nGACR;
|
---|
| 3122 | GA_GACR nGacr;
|
---|
| 3123 | };
|
---|
| 3124 |
|
---|
| 3125 | nGACR = val;
|
---|
| 3126 |
|
---|
| 3127 | if (nGacr.rst != pGa->gacr.rst)
|
---|
| 3128 | {
|
---|
| 3129 | /* When going out of reset, only clear the rst bit. 3C503 diagnostics checks for this. */
|
---|
| 3130 | if (nGacr.rst)
|
---|
[93601] | 3131 | elSoftReset(pDevIns, pThis);
|
---|
[93560] | 3132 | else
|
---|
| 3133 | pGa->gacr.rst = 0;
|
---|
| 3134 | }
|
---|
| 3135 | else
|
---|
| 3136 | {
|
---|
| 3137 | #ifdef IN_RING0
|
---|
| 3138 | /* Force a trip to R3. */
|
---|
| 3139 | if (pThis->uElIsaDma == pThis->uIsaDma)
|
---|
| 3140 | return VINF_IOM_R3_IOPORT_WRITE;
|
---|
| 3141 | #endif
|
---|
| 3142 |
|
---|
| 3143 | /* Make the data registers "ready" as long as transfers are started. */
|
---|
| 3144 | if (nGacr.start)
|
---|
| 3145 | {
|
---|
| 3146 | pGa->cdadr.cdadr_lsb = pGa->DALSB;
|
---|
| 3147 | pGa->cdadr.cdadr_msb = pGa->DAMSB;
|
---|
| 3148 | LogFunc(("DMA started, ddir=%u, cdadr=%04X\n", pGa->gacr.ddir, pGa->CDADR));
|
---|
| 3149 | pGa->streg.dprdy = 1;
|
---|
| 3150 | pGa->streg.dip = 1;
|
---|
| 3151 | pGa->streg.dtc = 0;
|
---|
| 3152 | }
|
---|
| 3153 | else
|
---|
| 3154 | {
|
---|
| 3155 | pGa->streg.dprdy = 0;
|
---|
| 3156 | pGa->streg.dip = 0;
|
---|
| 3157 | }
|
---|
| 3158 |
|
---|
| 3159 | /* Only do anything if the software configured DMA channel matches the emulation config. */
|
---|
| 3160 | if (pThis->uElIsaDma == pThis->uIsaDma)
|
---|
| 3161 | {
|
---|
| 3162 | #ifdef IN_RING3
|
---|
[93601] | 3163 | PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, pGa->streg.dprdy);
|
---|
[93560] | 3164 | if (pGa->streg.dprdy)
|
---|
[93601] | 3165 | PDMDevHlpDMASchedule(pDevIns);
|
---|
| 3166 | LogFunc(("#%d: DREQ for channel %u set to %u\n", pThis->iInstance, pThis->uIsaDma, pGa->streg.dprdy));
|
---|
[93560] | 3167 | #else
|
---|
| 3168 | /* Must not get here. */
|
---|
| 3169 | Assert(0);
|
---|
| 3170 | #endif
|
---|
| 3171 | }
|
---|
| 3172 |
|
---|
| 3173 | pGa->GACR = nGACR;
|
---|
| 3174 | LogFunc(("GACR=%02X ealo=%u eahi=%u\n", pGa->GACR, pGa->gacr.ealo, pGa->gacr.eahi));
|
---|
| 3175 | }
|
---|
| 3176 |
|
---|
| 3177 | return VINF_SUCCESS;
|
---|
| 3178 | }
|
---|
| 3179 |
|
---|
| 3180 |
|
---|
| 3181 | static int elGaDataWrite(PDPNICSTATE pThis, PEL_GA pGa, uint16_t val)
|
---|
| 3182 | {
|
---|
| 3183 | /* Data write; ignored if not started and in "download" mode. */
|
---|
| 3184 | if (pGa->gacr.start && pGa->gacr.ddir)
|
---|
| 3185 | {
|
---|
| 3186 | uint16_t addr = pGa->CDADR;
|
---|
| 3187 |
|
---|
| 3188 | addr &= 0x3fff;
|
---|
| 3189 | if (addr >= 0x2000)
|
---|
| 3190 | {
|
---|
| 3191 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 3192 | addr -= 0x2000;
|
---|
| 3193 | pThis->abLocalRAM[addr] = val;
|
---|
| 3194 | }
|
---|
| 3195 |
|
---|
| 3196 | pGa->CDADR++;
|
---|
| 3197 | /// @todo Does this really apply to writes or only reads?
|
---|
| 3198 | if ((pGa->cdadr.cdadr_msb == pGa->PSPR) && (pGa->PSPR != pGa->PSTR))
|
---|
| 3199 | {
|
---|
| 3200 | LogFunc(("GA DMA wrap / write!! (cdadr=%04X PSPR=%02X00 PSTR=%02X00)\n", pGa->CDADR, pGa->PSPR, pGa->PSTR));
|
---|
| 3201 | pGa->cdadr.cdadr_msb = pGa->PSTR;
|
---|
| 3202 | }
|
---|
| 3203 | }
|
---|
| 3204 | return VINF_SUCCESS;
|
---|
| 3205 | }
|
---|
| 3206 |
|
---|
| 3207 |
|
---|
| 3208 | static uint8_t elGaDataRead(PDPNICSTATE pThis, PEL_GA pGa)
|
---|
| 3209 | {
|
---|
| 3210 | uint8_t val = 0xcd;
|
---|
| 3211 |
|
---|
| 3212 | /* Data read; ignored if not started and in "upload" mode. */
|
---|
| 3213 | if (pGa->gacr.start && !pGa->gacr.ddir)
|
---|
| 3214 | {
|
---|
| 3215 | uint16_t addr = pGa->CDADR;
|
---|
| 3216 |
|
---|
| 3217 | addr &= 0x3fff;
|
---|
| 3218 | if (addr >= 0x2000)
|
---|
| 3219 | {
|
---|
| 3220 | /* Local RAM is mapped at 2000h-3FFFh. */
|
---|
| 3221 | addr -= 0x2000;
|
---|
| 3222 | val = pThis->abLocalRAM[addr];
|
---|
| 3223 | }
|
---|
| 3224 |
|
---|
| 3225 | pGa->CDADR++;
|
---|
| 3226 | if ((pGa->cdadr.cdadr_msb == pGa->PSPR) && (pGa->PSPR != pGa->PSTR))
|
---|
| 3227 | {
|
---|
| 3228 | LogFunc(("GA DMA wrap / read!! (cdadr=%04X PSPR=%02X00 PSTR=%02X00)\n", pGa->CDADR, pGa->PSPR, pGa->PSTR));
|
---|
| 3229 | pGa->cdadr.cdadr_msb = pGa->PSTR;
|
---|
| 3230 | }
|
---|
| 3231 | }
|
---|
| 3232 | return val;
|
---|
| 3233 | }
|
---|
| 3234 |
|
---|
| 3235 |
|
---|
[93601] | 3236 | static int elGaIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr, uint32_t val)
|
---|
[93560] | 3237 | {
|
---|
| 3238 | int reg = addr & 0xf;
|
---|
| 3239 | int rc = VINF_SUCCESS;
|
---|
| 3240 | PEL_GA pGa = &pThis->ga;
|
---|
| 3241 |
|
---|
[93601] | 3242 | Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
|
---|
[93560] | 3243 |
|
---|
| 3244 | switch (reg)
|
---|
| 3245 | {
|
---|
| 3246 | case GAR_PSTR:
|
---|
| 3247 | pGa->PSTR = val;
|
---|
| 3248 | break;
|
---|
| 3249 | case GAR_PSPR:
|
---|
| 3250 | pGa->PSPR = val;
|
---|
| 3251 | break;
|
---|
| 3252 | case GAR_DQTR:
|
---|
| 3253 | pGa->DQTR = val;
|
---|
| 3254 | break;
|
---|
| 3255 | case GAR_GACFR:
|
---|
[93601] | 3256 | elWriteGacfr(pDevIns, pThis, pGa, val);
|
---|
[93560] | 3257 | break;
|
---|
| 3258 | case GAR_GACR:
|
---|
[93601] | 3259 | rc = elWriteGacr(pDevIns, pThis, pGa, val);
|
---|
[93560] | 3260 | break;
|
---|
| 3261 | case GAR_STREG:
|
---|
| 3262 | /* Writing anything to STREG clears ASIC interrupt. */
|
---|
| 3263 | pThis->ga.streg.dtc = 0;
|
---|
| 3264 | pThis->ga.fGaIrq = false;
|
---|
[93601] | 3265 | dp8390CoreUpdateIrq(pDevIns, pThis);
|
---|
[93560] | 3266 | break;
|
---|
| 3267 | case GAR_IDCFR:
|
---|
[93601] | 3268 | elWriteIdcfr(pDevIns, pThis, pGa, val);
|
---|
[93560] | 3269 | break;
|
---|
| 3270 | case GAR_DAMSB:
|
---|
| 3271 | pGa->DAMSB = val;
|
---|
| 3272 | break;
|
---|
| 3273 | case GAR_DALSB:
|
---|
| 3274 | pGa->DALSB = val;
|
---|
| 3275 | break;
|
---|
| 3276 | case GAR_VPTR2:
|
---|
| 3277 | pGa->VPTR2 = val;
|
---|
| 3278 | break;
|
---|
| 3279 | case GAR_VPTR1:
|
---|
| 3280 | pGa->VPTR1 = val;
|
---|
| 3281 | break;
|
---|
| 3282 | case GAR_VPTR0:
|
---|
| 3283 | pGa->VPTR0 = val;
|
---|
| 3284 | break;
|
---|
| 3285 | case GAR_RFMSB:
|
---|
| 3286 | case GAR_RFLSB:
|
---|
| 3287 | elGaDataWrite(pThis, pGa, val);
|
---|
| 3288 | break;
|
---|
| 3289 | case GAR_R_BCFR:
|
---|
| 3290 | case GAR_R_PCFR:
|
---|
| 3291 | /* Read-only registers, ignored. */
|
---|
| 3292 | break;
|
---|
| 3293 | default:
|
---|
| 3294 | Assert(0);
|
---|
| 3295 | break;
|
---|
| 3296 | }
|
---|
| 3297 |
|
---|
| 3298 | return rc;
|
---|
| 3299 | }
|
---|
| 3300 |
|
---|
| 3301 |
|
---|
| 3302 | static uint32_t elGaIoRead(PDPNICSTATE pThis, uint32_t addr)
|
---|
| 3303 | {
|
---|
| 3304 | uint32_t val = UINT32_MAX;
|
---|
| 3305 | int reg = addr & 0x0f;
|
---|
| 3306 | PEL_GA pGa = &pThis->ga;
|
---|
| 3307 |
|
---|
| 3308 | switch (reg)
|
---|
| 3309 | {
|
---|
| 3310 | case GAR_PSTR:
|
---|
| 3311 | val = pGa->PSTR;
|
---|
| 3312 | break;
|
---|
| 3313 | case GAR_PSPR:
|
---|
| 3314 | val = pGa->PSPR;
|
---|
| 3315 | break;
|
---|
| 3316 | case GAR_DQTR:
|
---|
| 3317 | val = pGa->DQTR;
|
---|
| 3318 | break;
|
---|
| 3319 | case GAR_R_BCFR:
|
---|
| 3320 | val = pGa->BCFR;
|
---|
| 3321 | break;
|
---|
| 3322 | case GAR_R_PCFR:
|
---|
| 3323 | val = pGa->PCFR;
|
---|
| 3324 | break;
|
---|
| 3325 | case GAR_GACFR:
|
---|
| 3326 | val = pGa->GACFR;
|
---|
| 3327 | break;
|
---|
| 3328 | case GAR_GACR:
|
---|
| 3329 | val = pGa->GACR;
|
---|
| 3330 | break;
|
---|
| 3331 | case GAR_STREG:
|
---|
| 3332 | val = pGa->STREG;
|
---|
| 3333 | break;
|
---|
| 3334 | case GAR_IDCFR:
|
---|
| 3335 | val = pGa->IDCFR;
|
---|
| 3336 | break;
|
---|
| 3337 | case GAR_DAMSB:
|
---|
| 3338 | val = pGa->DAMSB;
|
---|
| 3339 | break;
|
---|
| 3340 | case GAR_DALSB:
|
---|
| 3341 | val = pGa->DALSB;
|
---|
| 3342 | break;
|
---|
| 3343 | case GAR_VPTR2:
|
---|
| 3344 | val = pGa->VPTR2;
|
---|
| 3345 | break;
|
---|
| 3346 | case GAR_VPTR1:
|
---|
| 3347 | val = pGa->VPTR1;
|
---|
| 3348 | break;
|
---|
| 3349 | case GAR_VPTR0:
|
---|
| 3350 | val = pGa->VPTR0;
|
---|
| 3351 | break;
|
---|
| 3352 | case GAR_RFMSB:
|
---|
| 3353 | case GAR_RFLSB:
|
---|
| 3354 | val = elGaDataRead(pThis, pGa);
|
---|
| 3355 | break;
|
---|
| 3356 | default:
|
---|
| 3357 | Assert(0);
|
---|
| 3358 | break;
|
---|
| 3359 | }
|
---|
| 3360 |
|
---|
[93601] | 3361 | Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
|
---|
[93560] | 3362 | return val;
|
---|
| 3363 | }
|
---|
| 3364 |
|
---|
| 3365 |
|
---|
| 3366 | /**
|
---|
| 3367 | * @callback_method_impl{FNIOMIOPORTIN}
|
---|
| 3368 | */
|
---|
[93562] | 3369 | static DECLCALLBACK(VBOXSTRICTRC)
|
---|
| 3370 | neIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
|
---|
[93560] | 3371 | {
|
---|
[93601] | 3372 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
[93560] | 3373 | int rc = VINF_SUCCESS;
|
---|
| 3374 | int reg = Port & 0xf;
|
---|
| 3375 | uint8_t u8Lo, u8Hi = 0;
|
---|
| 3376 | STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3377 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
|
---|
| 3378 | RT_NOREF_PV(pvUser);
|
---|
| 3379 |
|
---|
| 3380 | switch (cb)
|
---|
| 3381 | {
|
---|
| 3382 | case 1:
|
---|
[93601] | 3383 | *pu32 = neIoRead(pDevIns, pThis, reg);
|
---|
[93560] | 3384 | break;
|
---|
| 3385 | case 2:
|
---|
| 3386 | /* Manually split word access if necessary if it's an NE1000. Perhaps overkill. */
|
---|
| 3387 | if (pThis->uDevType == DEV_NE1000)
|
---|
| 3388 | {
|
---|
[93601] | 3389 | u8Lo = neIoRead(pDevIns, pThis, reg);
|
---|
[93560] | 3390 | if (reg < 0xf) // This logic is not entirely accurate (wraparound).
|
---|
[93601] | 3391 | u8Hi = neIoRead(pDevIns, pThis, reg + 1);
|
---|
[93560] | 3392 | *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
|
---|
| 3393 | }
|
---|
| 3394 | else
|
---|
[93601] | 3395 | *pu32 = neIoRead(pDevIns, pThis, reg);
|
---|
[93560] | 3396 | break;
|
---|
| 3397 | default:
|
---|
[93601] | 3398 | rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
|
---|
[93560] | 3399 | "neIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
|
---|
| 3400 | Port, cb);
|
---|
| 3401 | }
|
---|
| 3402 |
|
---|
[93601] | 3403 | Log2Func(("#%d: NE Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
|
---|
[93560] | 3404 | STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3405 | return rc;
|
---|
| 3406 | }
|
---|
| 3407 |
|
---|
| 3408 |
|
---|
| 3409 | /**
|
---|
| 3410 | * @callback_method_impl{FNIOMIOPORTIN}
|
---|
| 3411 | */
|
---|
[93562] | 3412 | static DECLCALLBACK(VBOXSTRICTRC)
|
---|
| 3413 | wdIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
|
---|
[93560] | 3414 | {
|
---|
[93601] | 3415 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
[93560] | 3416 | int rc = VINF_SUCCESS;
|
---|
| 3417 | int reg = Port & 0xf;
|
---|
| 3418 | uint8_t u8Lo, u8Hi = 0;
|
---|
| 3419 | STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3420 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
|
---|
| 3421 | RT_NOREF_PV(pvUser);
|
---|
| 3422 |
|
---|
| 3423 | switch (cb)
|
---|
| 3424 | {
|
---|
| 3425 | case 1:
|
---|
| 3426 | *pu32 = wdIoRead(pThis, reg);
|
---|
| 3427 | break;
|
---|
| 3428 | case 2:
|
---|
| 3429 | /* Manually split word access. */
|
---|
| 3430 | u8Lo = wdIoRead(pThis, reg);
|
---|
| 3431 | if (reg < 0xf) // This logic is not entirely accurate (wraparound).
|
---|
| 3432 | u8Hi = wdIoRead(pThis, reg + 1);
|
---|
| 3433 | *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
|
---|
| 3434 | break;
|
---|
| 3435 | default:
|
---|
[93601] | 3436 | rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
|
---|
[93560] | 3437 | "wdIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
|
---|
| 3438 | Port, cb);
|
---|
| 3439 | }
|
---|
| 3440 |
|
---|
[93601] | 3441 | Log2Func(("#%d: WD Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
|
---|
[93560] | 3442 | STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3443 | return rc;
|
---|
| 3444 | }
|
---|
| 3445 |
|
---|
| 3446 |
|
---|
| 3447 | /**
|
---|
| 3448 | * @callback_method_impl{FNIOMIOPORTIN}
|
---|
| 3449 | */
|
---|
[93562] | 3450 | static DECLCALLBACK(VBOXSTRICTRC)
|
---|
| 3451 | elIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
|
---|
[93560] | 3452 | {
|
---|
[93601] | 3453 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
[93560] | 3454 | int rc = VINF_SUCCESS;
|
---|
| 3455 | int reg = Port & 0xf;
|
---|
| 3456 | uint8_t u8Lo, u8Hi = 0;
|
---|
| 3457 | STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3458 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
|
---|
| 3459 | RT_NOREF_PV(pvUser);
|
---|
| 3460 |
|
---|
| 3461 | switch (cb)
|
---|
| 3462 | {
|
---|
| 3463 | case 1:
|
---|
| 3464 | *pu32 = elGaIoRead(pThis, reg);
|
---|
| 3465 | break;
|
---|
| 3466 | case 2:
|
---|
| 3467 | /* Manually split word access. */
|
---|
| 3468 | u8Lo = elGaIoRead(pThis, reg);
|
---|
| 3469 | if (reg < 0xf) // This logic is not entirely accurate (wraparound).
|
---|
| 3470 | u8Hi = elGaIoRead(pThis, reg + 1);
|
---|
| 3471 | *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
|
---|
| 3472 | break;
|
---|
| 3473 | default:
|
---|
[93601] | 3474 | rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
|
---|
[93560] | 3475 | "elIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
|
---|
| 3476 | Port, cb);
|
---|
| 3477 | }
|
---|
| 3478 |
|
---|
[93601] | 3479 | Log2Func(("#%d: EL Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
|
---|
[93560] | 3480 | STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3481 | return rc;
|
---|
| 3482 | }
|
---|
| 3483 |
|
---|
| 3484 |
|
---|
| 3485 | /**
|
---|
| 3486 | * @callback_method_impl{FNIOMIOPORTIN}
|
---|
| 3487 | */
|
---|
[93562] | 3488 | static DECLCALLBACK(VBOXSTRICTRC)
|
---|
| 3489 | dp8390CoreIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
|
---|
[93560] | 3490 | {
|
---|
[93601] | 3491 | PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
|
---|
[93560] | 3492 | int rc = VINF_SUCCESS;
|
---|
| 3493 | int reg = Port & 0xf;
|
---|
| 3494 | uint8_t u8Lo, u8Hi;
|
---|
| 3495 | STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
|
---|
| 3496 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
|
---|
| 3497 | RT_NOREF_PV(pvUser);
|
---|
| 3498 |
|
---|
| 3499 | switch (cb)
|
---|
| 3500 | {
|
---|
| 3501 | case 1:
|
---|
[93601] | 3502 | *pu32 = dp8390CoreRead(pDevIns, pThis, reg);
|
---|
[93560] | 3503 | break;
|
---|
| 3504 | case 2:
|
---|
| 3505 | /* Manually split word access. */
|
---|
[93601] | 3506 | u8Lo = dp8390CoreRead(pDevIns, pThis, reg + 0);
|
---|
[93560] | 3507 | /* This logic is not entirely accurate. */
|
---|
| 3508 | if (reg < 0xf)
|
---|
|
---|