VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/Dev3C501.cpp@ 97583

Last change on this file since 97583 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.9 KB
Line 
1/* $Id: Dev3C501.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * Dev3C501 - 3Com EtherLink (3C501) Ethernet Adapter Emulation.
4 */
5
6/*
7 * Copyright (C) 2022 Oracle and/or its affiliates.
8 *
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
26 */
27
28/** @page pg_dev_3c501 3Com 3C501 Ethernet Controller Emulation.
29 *
30 * This software was written based on the following documents:
31 *
32 * - 3Com IBM Ethernet (IE) Controller/Transceiver
33 * External Reference Specification, March 15, 1983
34 * - 3Com EtherLink (3C501) Adapter Technical Reference
35 * Manual Part No. 6405-00, November 26, 1988
36 * - SEEQ 8001 EDLC Ethernet Data Link Controller
37 * Preliminary Data Sheet, December 1982
38 *
39 * The emulation is compatible with 3Com 3C501 EtherLink aka IE4. It also
40 * appears to be compatible with the original 1982 3C500 IBM Ethernet aka
41 * IE; the IE and IE4 documentation is nearly identical.
42 *
43 * The EtherLink is a very early design. It has only a single 2K buffer for
44 * both send and receive, and was desgined long before full-duplex Ethernet
45 * was possible (it is capable of simultaneous send and receive, but only in
46 * loopback mode). If it has just received a packet, the EtherLink can't
47 * receive another packet until the first one has been processed by the
48 * host.
49 *
50 * The above problem is greatly alleviated in a VM because incoming packets
51 * can be buffered for a short while and don't have to be immediately
52 * dropped just because the adapter is currently sending or because the
53 * receive status register has not been read yet.
54 *
55 * The first 8 registers (station address, receive and transmit command and
56 * status) are implemented in the SEEQ 8001 EDLC chip. The remaining 8
57 * registers are provided by the 3Com ASIC (0755-02) on the 3C501 or
58 * discrete chips on the 3C500.
59 *
60 * The '16 collisions' bit in the transmit command/status register is nearly
61 * useless. The SEEQ 8001 could retransmit automatically, but the IE/IE4 can
62 * not because the GP Buffer Pointer needs to be reinitialized by software
63 * prior to each transmit attempt. It is unclear if the 16-collision counter
64 * only rolls over modulo 16 or if it is cleared by something other than
65 * reset.
66 *
67 * The 3C501 supports DMA transfers to/from the packet buffer. Many drivers
68 * don't use DMA by default or at all. Due to the overhead of programming
69 * the DMA controller, direct I/O access (rep insb/outsb) is always faster
70 * in a VM. DMA would only be a win for very ancient drivers which don't use
71 * the rep insb/outsb instructions (those didn't exist on the 8086/8088).
72 *
73 * NB: The default DMA channel (channel 1) conflicts with the default Sound
74 * Blaster settings. If both 3C501 and SB16 are used, then one of them
75 * either needs to be reconfigured to use DMA channel other than 1 or the
76 * 3C501 must not use DMA.
77 *
78 * The 3Com documentation implies that writing the low byte of the Receive
79 * Buffer Pointer is enough to clear the pointer. Yet almost all drivers,
80 * including 3Com's sample code, write zeros to both the low and high bytes
81 * of the Receive Buffer Pointer when clearing it. BSD drivers (if_el.c)
82 * notably only write the low byte. It has been verified on a real 3C501
83 * that the documentation is correct. Writing anything to the Receive Buffer
84 * Pointer LSB clears the pointer (writing to the MSB appears to have no
85 * effect whatsoever).
86 *
87 * If the Receive Buffer Pointer is not explicitly cleared prior to
88 * receiving a packet, it will simply keep incrementing from wherever it
89 * was. Once it hits the end of the buffer (wraps around to zero), a
90 * receive overflow will be triggered (because the EDLC's FIFO will no
91 * longer be serviced) but the buffer will contain however much data there
92 * was room for. Note that the SEEQ 8001 datasheet is not explicit, but the
93 * EDLC can probably receive frames with more than 1,500 octets of payload.
94 *
95 * The GP Buffer Pointer behavior is quite curious. It appears to be
96 * internally a 12-bit pointer, and its top bit (that is, bit 11) is ignored
97 * when addressing into the 2K buffer. When writing the MSB, the top 5 bits
98 * are masked (always written as zero), i.e. only a 11-bit value can be
99 * written. Through auto-increment, the GP Buffer Pointer can reach values
100 * that can be read but not written.
101 *
102 * The implementation was tested for correctness using 3Com's diagnostic
103 * utility (3C501.EXE, Version 2.4, 1986 and also DIAGNOSE.COM, Version 2.0,
104 * 1983) and "passes diagnose with flying colors". Note that the interrupt
105 * test does not pass in V2.3 diagnostics by default because it writes an
106 * EOI to port 0F820h instead of 20h, relying on the system board to decode
107 * only the low 10 bits of the address. PCI-based systems decode all address
108 * bits and writes to address 0F820h do not reach the interrupt controller.
109 * The 3C501.EXE utility can be run with the '-i' switch to skip interrupt
110 * tests; the older DIAGNOSE.COM does not have that problem. In both
111 * versions, the preliminary test fails if the MAC address OID is not
112 * 02:60:8C (the utility thinks the PROM is corrupted).
113 *
114 * 3Com's XNS driver (ETH.SYS) likewise requires the OID to be 02:60:8C,
115 * otherwise the driver uses 00:00:00:00:00:00 as its MAC address, which is
116 * not something that produces useful results. Most old drivers (NetWare,
117 * NDIS, XENIX) don't care about the OID, but some (BSDs, Linux, some SCO
118 * UNIX versions) want to see the 3Com OID.
119 *
120 * The MS Networks Client setup also requires the OID to match 3Com's when
121 * detecting the hardware, but the actual NDIS driver does not care. Note
122 * that the setup fails to detect the emulated 3C501 at the default 0x300
123 * base address, but finds it at 0x310 and other addresses.
124 *
125 * Note that especially newer Linux/BSD OSes are a lost cause. Their 3C501
126 * drivers are very hard to configure, broken in various ways, and likely
127 * untested. For example the Linux driver clears the receive buffer pointer
128 * at the end of the interrupt handler, which may easily happen after a
129 * packet was already received. In FreeBSD 6.4, the kernel crashes when the
130 * el0 driver is loaded. In FreeBSD 5.0, the el0 driver sends packets and
131 * reads packets from the card, but the OS never sees any incoming data
132 * (even though the receive packet counter keeps going up).
133 *
134 * The precise receive logic (when a packet is copied to the buffer, when an
135 * interrupt is signaled, when receive goes idle) is difficult to understand
136 * from the 3Com documentation, but is extensively tested by the diagnostic
137 * utility. The SEEQ 8001 datasheet may be easier to understand than the
138 * EtherLink documentation.
139 *
140 * Some drivers (e.g. NetWare DOS IPX shell and ODI drivers) like to reset
141 * the chip more or less after every packet is sent or received. That leads
142 * to a situation where the NIC is briefly unable to receive anything. If we
143 * drop packets in that case, we end up with well over 10% packet loss and
144 * terrible performance. We have to hold off and not drop packets just
145 * because the receiver is disabled for a moment.
146 *
147 * Note that the reset bit in the auxiliary command register does not nearly
148 * reset the entire chip as the documentation suggests. It may only truly
149 * reset the SEEQ 8001 EDLC chip. It is impossible to say how going out of
150 * reset affects the auxiliary command register itself, since it must be
151 * written to exit the reset state. The reset bit clears the EDLC transmit
152 * and command registers, but not the programmed station address. It also
153 * does not disturb the packet buffer, and it does not clear the GP Buffer
154 * Pointer.
155 *
156 * The default EtherLink configuration uses I/O base 300h, IRQ 3, DMA
157 * channel 1. Prior to May 1983, the default IRQ was 5. On old EtherLink
158 * cards, the I/O address was configurable from 200h-3F0h in increments of
159 * 16, DMA 1 or 3, and IRQ 3 or 5. Newer EtherLinks (starting circa in 1984)
160 * in addition allow DMA 2 and IRQ 2, 4, 6, and 7.
161 *
162 */
163
164
165/*********************************************************************************************************************************
166* Header Files *
167*********************************************************************************************************************************/
168#define LOG_GROUP LOG_GROUP_DEV_ELNK
169#include <VBox/vmm/pdmdev.h>
170#include <VBox/vmm/pdmnetifs.h>
171#include <VBox/vmm/pgm.h>
172#include <VBox/version.h>
173#include <iprt/asm.h>
174#include <iprt/assert.h>
175#include <iprt/critsect.h>
176#include <iprt/net.h>
177#include <iprt/string.h>
178#include <iprt/time.h>
179#ifdef IN_RING3
180# include <iprt/mem.h>
181# include <iprt/semaphore.h>
182# include <iprt/uuid.h>
183#endif
184
185#include "VBoxDD.h"
186
187
188/*********************************************************************************************************************************
189* Defined Constants And Macros *
190*********************************************************************************************************************************/
191
192#define ELNK_SAVEDSTATE_VERSION 1
193
194/** Maximum number of times we report a link down to the guest (failure to send frame) */
195#define ELNK_MAX_LINKDOWN_REPORTED 3
196
197/** Maximum number of times we postpone restoring a link that is temporarily down. */
198#define ELNK_MAX_LINKRST_POSTPONED 3
199
200/** Maximum frame size we handle */
201#define MAX_FRAME 1536
202
203/* Size of the packet buffer. */
204#define ELNK_BUF_SIZE 2048u
205
206/* The packet buffer address mask. */
207#define ELNK_BUF_ADR_MASK (ELNK_BUF_SIZE - 1)
208
209/* The GP buffer pointer address within the buffer. */
210#define ELNK_GP(pThis) ((pThis)->uGPBufPtr & ELNK_BUF_ADR_MASK)
211
212/* The GP buffer pointer mask.
213 * NB: The GP buffer pointer is internally a 12-bit counter. When addressing into the
214 * packet buffer, bit 11 is ignored. Required to pass 3C501 diagnostics.
215 */
216#define ELNK_GP_MASK 0xfff
217
218/* The EtherLink is an 8-bit adapter, hence DMA channels up to 3 are available. */
219#define ELNK_MAX_VALID_DMA 3
220
221
222/*********************************************************************************************************************************
223* Structures and Typedefs *
224*********************************************************************************************************************************/
225
226
227/**
228 * EtherLink Transmit Command Register.
229 */
230typedef struct ELNK_XMIT_CMD {
231 uint8_t det_ufl : 1; /* Detect underflow. */
232 uint8_t det_coll : 1; /* Detect collision. */
233 uint8_t det_16col : 1; /* Detect collision 16. */
234 uint8_t det_succ : 1; /* Detect successful xmit. */
235 uint8_t unused : 4;
236} EL_XMT_CMD;
237
238/**
239 * EtherLink Transmit Status Register.
240 *
241 * We will never see any real collisions, although collisions (including 16
242 * successive collisions) may be useful to report when the link is down
243 * (something the 3C501 does not have a concept of).
244 */
245typedef struct ELNK_XMIT_STAT {
246 uint8_t uflow : 1; /* Underflow on transmit. */
247 uint8_t coll : 1; /* Collision on transmit. */
248 uint8_t coll16 : 1; /* 16 collisions on transmit. */
249 uint8_t ready : 1; /* Ready for a new frame. */
250 uint8_t undef : 4;
251} EL_XMT_STAT;
252
253/** Address match (adr_match) modes. */
254typedef enum {
255 EL_ADRM_DISABLED = 0, /* Receiver disabled. */
256 EL_ADRM_PROMISC = 1, /* Receive all addresses. */
257 EL_ADRM_BCAST = 2, /* Receive station + broadcast. */
258 EL_ADRM_MCAST = 3 /* Receive station + multicast. */
259} EL_ADDR_MATCH;
260
261/**
262 * EtherLink Receive Command Register.
263 */
264typedef struct ELNK_RECV_CMD {
265 uint8_t det_ofl : 1; /* Detect overflow errors. */
266 uint8_t det_fcs : 1; /* Detect FCS errors. */
267 uint8_t det_drbl : 1; /* Detect dribble error. */
268 uint8_t det_runt : 1; /* Detect short frames. */
269 uint8_t det_eof : 1; /* Detect EOF (frames without overflow). */
270 uint8_t acpt_good : 1; /* Accept good frames. */
271 uint8_t adr_match : 2; /* Address match mode. */
272} EL_RCV_CMD;
273
274/**
275 * EtherLink Receive Status Register.
276 */
277typedef struct ELNK_RECV_STAT {
278 uint8_t oflow : 1; /* Overflow on receive. */
279 uint8_t fcs : 1; /* FCS error. */
280 uint8_t dribble : 1; /* Dribble error. */
281 uint8_t runt : 1; /* Short frame. */
282 uint8_t no_ovf : 1; /* Received packet w/o overflow. */
283 uint8_t good : 1; /* Received good packet. */
284 uint8_t undef : 1;
285 uint8_t stale : 1; /* Stale receive status. */
286} EL_RCV_STAT;
287
288/** Buffer control (buf_ctl) modes. */
289typedef enum {
290 EL_BCTL_SYSTEM = 0, /* Host has buffer access. */
291 EL_BCTL_XMT_RCV = 1, /* Transmit, then receive. */
292 EL_BCTL_RECEIVE = 2, /* Receive. */
293 EL_BCTL_LOOPBACK = 3 /* Loopback. */
294} EL_BUFFER_CONTROL;
295
296/**
297 * EtherLink Auxiliary Status Register.
298 */
299typedef struct ELNK_AUX_CMD {
300 uint8_t ire : 1; /* Interrupt Request Enable. */
301 uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
302 uint8_t buf_ctl : 2; /* Packet buffer control. */
303 uint8_t unused : 1;
304 uint8_t dma_req : 1; /* DMA request. */
305 uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
306 uint8_t reset : 1; /* Card in reset while set. */
307} EL_AUX_CMD;
308
309/**
310 * EtherLink Auxiliary Status Register.
311 */
312typedef struct ELNK_AUX_STAT {
313 uint8_t recv_bsy : 1; /* Receive busy. */
314 uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
315 uint8_t buf_ctl : 2; /* Packet buffer control. */
316 uint8_t dma_done : 1; /* DMA done. */
317 uint8_t dma_req : 1; /* DMA request. */
318 uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
319 uint8_t xmit_bsy : 1; /* Transmit busy. */
320} EL_AUX_STAT;
321
322/**
323 * Internal interrupt status.
324 */
325typedef struct ELNK_INTR_STAT {
326 uint8_t recv_intr : 1; /* Receive interrupt status. */
327 uint8_t xmit_intr : 1; /* Transmit interrupt status. */
328 uint8_t dma_intr : 1; /* DMA interrupt status. */
329 uint8_t unused : 5;
330} EL_INTR_STAT;
331
332
333/**
334 * EtherLink 3C501 state.
335 */
336typedef struct ELNKSTATE
337{
338 /** Restore timer.
339 * This is used to disconnect and reconnect the link after a restore. */
340 TMTIMERHANDLE hTimerRestore;
341
342 /** Transmit signaller. */
343 PDMTASKHANDLE hXmitTask;
344 /** Receive ready signaller. */
345 PDMTASKHANDLE hCanRxTask;
346
347 /** Internal interrupt flag. */
348 bool fISR;
349 /** Internal DMA active flag. */
350 bool fDMA;
351 /** Internal in-reset flag. */
352 bool fInReset;
353
354 /** The PROM contents. Only 8 bytes addressable, R/O. */
355 uint8_t aPROM[8];
356
357 /** The station address programmed by the guest, W/O. */
358 uint8_t aStationAddr[6];
359 /** General Purpose (GP) Buffer Pointer, R/W. */
360 uint16_t uGPBufPtr;
361
362 /** Receive (RCV) Buffer Pointer, R/WC. */
363 uint16_t uRCVBufPtr;
364 /** Transmit Command Register, W/O. */
365 union {
366 uint8_t XmitCmdReg;
367 EL_XMT_CMD XmitCmd;
368 };
369 /** Transmit Status Register, R/O. */
370 union {
371 uint8_t XmitStatReg;
372 EL_XMT_STAT XmitStat;
373 };
374 /** Receive Command Register, W/O. */
375 union {
376 uint8_t RcvCmdReg;
377 EL_RCV_CMD RcvCmd;
378 };
379 /** Receive Status Register, R/O. */
380 union {
381 uint8_t RcvStatReg;
382 EL_RCV_STAT RcvStat;
383 };
384 /** Auxiliary Command Register, W/O. */
385 union {
386 uint8_t AuxCmdReg;
387 EL_AUX_CMD AuxCmd;
388 };
389 /** Auxiliary Status Register, R/O. */
390 union {
391 uint8_t AuxStatReg;
392 EL_AUX_STAT AuxStat;
393 };
394
395 /** Base port of the I/O space region. */
396 RTIOPORT IOPortBase;
397 /** The configured ISA IRQ. */
398 uint8_t uIsaIrq;
399 /** The configured ISA DMA channel. */
400 uint8_t uIsaDma;
401 /** If set the link is currently up. */
402 bool fLinkUp;
403 /** If set the link is temporarily down because of a saved state load. */
404 bool fLinkTempDown;
405 /** Number of times we've reported the link down. */
406 uint16_t cLinkDownReported;
407 /** Number of times we've postponed the link restore. */
408 uint16_t cLinkRestorePostponed;
409
410 /** The "hardware" MAC address. */
411 RTMAC MacConfigured;
412 /** Internal interrupt state. */
413 union {
414 uint8_t IntrStateReg;
415 EL_INTR_STAT IntrState;
416 };
417
418 /** Set if ELNKSTATER3::pDrv is not NULL. */
419 bool fDriverAttached;
420 /** The LED. */
421 PDMLED Led;
422 /** Status LUN: The LED ports. */
423 PDMILEDPORTS ILeds;
424 /** Partner of ILeds. */
425 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
426
427 /** Access critical section. */
428 PDMCRITSECT CritSect;
429 /** Event semaphore for blocking on receive. */
430 RTSEMEVENT hEventOutOfRxSpace;
431 /** We are waiting/about to start waiting for more receive buffers. */
432 bool volatile fMaybeOutOfSpace;
433
434 /* MS to wait before we enable the link. */
435 uint32_t cMsLinkUpDelay;
436 /** The device instance number (for logging). */
437 uint32_t iInstance;
438
439 STAMCOUNTER StatReceiveBytes;
440 STAMCOUNTER StatTransmitBytes;
441 STAMCOUNTER StatPktsLostReset;
442#ifdef VBOX_WITH_STATISTICS
443 STAMPROFILEADV StatIOReadRZ;
444 STAMPROFILEADV StatIOReadR3;
445 STAMPROFILEADV StatIOWriteRZ;
446 STAMPROFILEADV StatIOWriteR3;
447 STAMPROFILEADV StatReceive;
448 STAMPROFILEADV StatTransmitR3;
449 STAMPROFILEADV StatTransmitRZ;
450 STAMPROFILE StatTransmitSendR3;
451 STAMPROFILE StatTransmitSendRZ;
452 STAMPROFILE StatRxOverflow;
453 STAMCOUNTER StatRxOverflowWakeup;
454 STAMPROFILEADV StatInterrupt;
455 STAMCOUNTER StatResets;
456 STAMCOUNTER StatDropPktAdrmDis;
457 STAMCOUNTER StatDropPktZeroLen;
458 STAMCOUNTER StatDropPktVMNotRunning;
459 STAMCOUNTER StatDropPktNoLink;
460 STAMCOUNTER StatDropPktStaleRcv;
461#endif /* VBOX_WITH_STATISTICS */
462
463 /** ISA I/O ports. */
464 IOMIOPORTHANDLE hIoPortsIsa;
465
466 /** The loopback transmit buffer (avoid stack allocations). */
467 uint8_t abLoopBuf[ELNK_BUF_SIZE];
468
469 /** The runt pad buffer (only really needs 60 bytes). */
470 uint8_t abRuntBuf[64];
471
472 /** The packet buffer. */
473 uint8_t abPacketBuf[ELNK_BUF_SIZE];
474} ELNKSTATE, *PELNKSTATE;
475
476
477/**
478 * EtherLink state for ring-3.
479 *
480 * @implements PDMIBASE
481 * @implements PDMINETWORKDOWN
482 * @implements PDMINETWORKCONFIG
483 * @implements PDMILEDPORTS
484 */
485typedef struct ELNKSTATER3
486{
487 /** Pointer to the device instance. */
488 PPDMDEVINSR3 pDevIns;
489 /** Pointer to the connector of the attached network driver. */
490 PPDMINETWORKUPR3 pDrv;
491 /** Pointer to the attached network driver. */
492 R3PTRTYPE(PPDMIBASE) pDrvBase;
493 /** LUN\#0 + status LUN: The base interface. */
494 PDMIBASE IBase;
495 /** LUN\#0: The network port interface. */
496 PDMINETWORKDOWN INetworkDown;
497 /** LUN\#0: The network config port interface. */
498 PDMINETWORKCONFIG INetworkConfig;
499
500 /** Status LUN: The LED ports. */
501 PDMILEDPORTS ILeds;
502 /** Partner of ILeds. */
503 R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
504} ELNKSTATER3;
505/** Pointer to an EtherLink state structure for ring-3. */
506typedef ELNKSTATER3 *PELNKSTATER3;
507
508
509/**
510 * EtherLink state for ring-0.
511 */
512typedef struct ELNKSTATER0
513{
514 /** Pointer to the connector of the attached network driver. */
515 PPDMINETWORKUPR0 pDrv;
516} ELNKSTATER0;
517/** Pointer to an EtherLink state structure for ring-0. */
518typedef ELNKSTATER0 *PELNKSTATER0;
519
520
521/**
522 * EtherLink state for raw-mode.
523 */
524typedef struct ELNKSTATERC
525{
526 /** Pointer to the connector of the attached network driver. */
527 PPDMINETWORKUPRC pDrv;
528} ELNKSTATERC;
529/** Pointer to an EtherLink state structure for raw-mode. */
530typedef ELNKSTATERC *PELNKSTATERC;
531
532
533/** The EtherLink state structure for the current context. */
534typedef CTX_SUFF(ELNKSTATE) ELNKSTATECC;
535/** Pointer to an EtherLink state structure for the current
536 * context. */
537typedef CTX_SUFF(PELNKSTATE) PELNKSTATECC;
538
539
540#ifndef VBOX_DEVICE_STRUCT_TESTCASE
541
542
543/*********************************************************************************************************************************
544* Internal Functions *
545*********************************************************************************************************************************/
546
547static int elnkAsyncTransmit(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fOnWorkerThread);
548
549/**
550 * Checks if the link is up.
551 * @returns true if the link is up.
552 * @returns false if the link is down.
553 */
554DECLINLINE(bool) elnkIsLinkUp(PELNKSTATE pThis)
555{
556 return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
557}
558
559
560#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
561#define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
562#endif
563
564#define ETHER_ADDR_LEN ETH_ALEN
565#define ETH_ALEN 6
566#pragma pack(1)
567struct ether_header /** @todo Use RTNETETHERHDR? */
568{
569 uint8_t ether_dhost[ETH_ALEN]; /**< destination ethernet address */
570 uint8_t ether_shost[ETH_ALEN]; /**< source ethernet address */
571 uint16_t ether_type; /**< packet type ID field */
572};
573#pragma pack()
574
575
576/**
577 * Check if incoming frame matches the station address.
578 */
579DECLINLINE(int) padr_match(PELNKSTATE pThis, const uint8_t *buf)
580{
581 struct ether_header *hdr = (struct ether_header *)buf;
582 int result;
583
584 /* Checks own + broadcast as well as own + multicast. */
585 result = (pThis->RcvCmd.adr_match >= EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, pThis->aStationAddr, 6);
586
587 return result;
588}
589
590
591/**
592 * Check if incoming frame is an accepted broadcast frame.
593 */
594DECLINLINE(int) padr_bcast(PELNKSTATE pThis, const uint8_t *buf)
595{
596 static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
597 struct ether_header *hdr = (struct ether_header *)buf;
598 int result = (pThis->RcvCmd.adr_match == EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, aBCAST, 6);
599 return result;
600}
601
602
603/**
604 * Check if incoming frame is an accepted multicast frame.
605 */
606DECLINLINE(int) padr_mcast(PELNKSTATE pThis, const uint8_t *buf)
607{
608 struct ether_header *hdr = (struct ether_header *)buf;
609 int result = (pThis->RcvCmd.adr_match == EL_ADRM_MCAST) && ETHER_IS_MULTICAST(hdr->ether_dhost);
610 return result;
611}
612
613
614/**
615 * Update the device IRQ line based on internal state.
616 */
617static void elnkUpdateIrq(PPDMDEVINS pDevIns, PELNKSTATE pThis)
618{
619 bool fISR = false;
620
621 STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
622
623 /* IRQ is active if any interrupt source is active and interrupts
624 * are enabled via RIDE or IRE.
625 */
626 if (pThis->IntrStateReg && (pThis->AuxCmd.ride || pThis->AuxCmd.ire))
627 fISR = true;
628
629 Log2(("#%d set irq fISR=%d\n", pThis->iInstance, fISR));
630
631 /* The IRQ line typically does not change. */
632 if (RT_UNLIKELY(fISR != pThis->fISR))
633 {
634 Log(("#%d IRQ=%d, state=%d\n", pThis->iInstance, pThis->uIsaIrq, fISR));
635 PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, fISR);
636 pThis->fISR = fISR;
637 }
638 STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
639}
640
641
642/**
643 * Perform a software reset of the NIC.
644 */
645static void elnkSoftReset(PPDMDEVINS pDevIns, PELNKSTATE pThis)
646{
647 LogFlowFunc(("#%d:\n", pThis->iInstance));
648
649 /* Clear some of the user-visible register state. */
650 pThis->XmitCmdReg = 0;
651 pThis->XmitStatReg = 0;
652 pThis->RcvCmdReg = 0;
653 pThis->RcvStatReg = 0;
654 pThis->AuxCmdReg = 0;
655 pThis->AuxStatReg = 0;
656
657 /* The "stale receive status" is cleared by receiving an "interesting" packet. */
658 pThis->RcvStat.stale = 1;
659
660 /* By virtue of setting the buffer control to system, transmit is set to busy. */
661 pThis->AuxStat.xmit_bsy = 1;
662
663 /* Clear internal interrupt state. */
664 pThis->IntrStateReg = 0;
665 elnkUpdateIrq(pDevIns, pThis);
666
667 /* Note that a soft reset does not clear the packet buffer; software often
668 * assumes that it survives soft reset. The programmed station address is
669 * likewise not reset, and the buffer pointers are not reset either.
670 * Verified on a real 3C501.
671 */
672
673 /* No longer in reset state. */
674 pThis->fInReset = false;
675}
676
677#ifdef IN_RING3
678
679static DECLCALLBACK(void) elnkR3WakeupReceive(PPDMDEVINS pDevIns)
680{
681 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
682 STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
683 if (pThis->hEventOutOfRxSpace != NIL_RTSEMEVENT)
684 RTSemEventSignal(pThis->hEventOutOfRxSpace);
685}
686
687/**
688 * @callback_method_impl{FNPDMTASKDEV,
689 * Signal to R3 that NIC is ready to receive a packet.
690 */
691static DECLCALLBACK(void) elnkR3CanRxTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
692{
693 RT_NOREF(pvUser);
694 elnkR3WakeupReceive(pDevIns);
695}
696
697#endif /* IN_RING3 */
698
699
700/**
701 * Write incoming data into the packet buffer.
702 */
703static void elnkReceiveLocked(PPDMDEVINS pDevIns, PELNKSTATE pThis, const uint8_t *src, size_t cbToRecv, bool fLoopback)
704{
705 int is_padr = 0, is_bcast = 0, is_mcast = 0;
706 union {
707 uint8_t RcvStatNewReg;
708 EL_RCV_STAT RcvStatNew;
709 };
710
711 /*
712 * Drop all packets if the VM is not running yet/anymore.
713 */
714 VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
715 if ( enmVMState != VMSTATE_RUNNING
716 && enmVMState != VMSTATE_RUNNING_LS)
717 {
718 STAM_COUNTER_INC(&pThis->StatDropPktVMNotRunning);
719 return;
720 }
721
722 /* Drop everything if address matching is disabled. */
723 if (RT_UNLIKELY(pThis->RcvCmd.adr_match == EL_ADRM_DISABLED))
724 {
725 STAM_COUNTER_INC(&pThis->StatDropPktAdrmDis);
726 return;
727 }
728
729 /* Drop zero-length packets (how does that even happen?). */
730 if (RT_UNLIKELY(!cbToRecv))
731 {
732 STAM_COUNTER_INC(&pThis->StatDropPktZeroLen);
733 return;
734 }
735
736 /*
737 * Drop all packets if the cable is not connected (and not in loopback).
738 */
739 if (RT_UNLIKELY(!elnkIsLinkUp(pThis) && !fLoopback))
740 {
741 STAM_COUNTER_INC(&pThis->StatDropPktNoLink);
742 return;
743 }
744
745 /*
746 * Do not receive further packets until receive status was read.
747 */
748 if (RT_UNLIKELY(pThis->RcvStat.stale == 0))
749 {
750 STAM_COUNTER_INC(&pThis->StatDropPktStaleRcv);
751 return;
752 }
753
754 LogFlowFunc(("#%d: size on wire=%d, RCV ptr=%u\n", pThis->iInstance, cbToRecv, pThis->uRCVBufPtr));
755
756 /*
757 * Perform address matching. Packets which do not pass the address
758 * filter are always ignored.
759 */
760 /// @todo cbToRecv must be 6 or more (complete address)
761 if ( pThis->RcvCmd.adr_match == EL_ADRM_PROMISC /* promiscuous enabled */
762 || (is_padr = padr_match(pThis, src))
763 || (is_bcast = padr_bcast(pThis, src))
764 || (is_mcast = padr_mcast(pThis, src)))
765 {
766 uint8_t *dst = pThis->abPacketBuf + pThis->uRCVBufPtr;
767
768 Log2Func(("#%d Packet passed address filter (is_padr=%d, is_bcast=%d, is_mcast=%d), size=%d\n", pThis->iInstance, cbToRecv, is_padr, is_bcast, is_mcast));
769
770 /* Receive status is evaluated from scratch. The stale bit must remain set until we know better. */
771 RcvStatNewReg = 0;
772 RcvStatNew.stale = 1;
773 pThis->RcvStatReg = 0x80;
774
775 /* Detect errors: Runts, overflow, and FCS errors.
776 * NB: Dribble errors can not happen because we can only receive an
777 * integral number of bytes. FCS errors are only possible in loopback
778 * mode in case the FCS is deliberately corrupted.
779 */
780
781 /* See if we need to pad, and how much. Have to be careful because the
782 * Receive Buffer Pointer might be near the end of the buffer.
783 */
784 if (RT_UNLIKELY(cbToRecv < 60))
785 {
786 /* In loopback mode only, short packets are flagged as errors because
787 * diagnostic tools want to see the errors. Otherwise they're padded to
788 * minimum length (if packet came over the wire, it should have been
789 * properly padded).
790 */
791 /// @todo This really is kind of wrong. We shouldn't be doing any
792 /// padding here, it should be done by the sending side!
793 if (!fLoopback)
794 {
795 memset(pThis->abRuntBuf, 0, sizeof(pThis->abRuntBuf));
796 memcpy(pThis->abRuntBuf, src, cbToRecv);
797 cbToRecv = 60;
798 src = pThis->abRuntBuf;
799 }
800 else
801 {
802 LogFunc(("#%d runt, size=%d\n", pThis->iInstance, cbToRecv));
803 RcvStatNew.runt = 1;
804 }
805 }
806
807 /* We don't care how big the frame is; if it fits into the buffer, all is
808 * good. But conversely if the Receive Buffer Pointer is initially near the
809 * end of the buffer, a small frame can trigger an overflow.
810 */
811 if (pThis->uRCVBufPtr + cbToRecv <= ELNK_BUF_SIZE)
812 {
813 RcvStatNew.no_ovf = 1;
814 }
815 else
816 {
817 LogFunc(("#%d overflow, size=%d\n", pThis->iInstance, cbToRecv));
818 RcvStatNew.oflow = 1;
819 }
820
821 if (fLoopback && pThis->AuxCmd.xmit_bf)
822 {
823 LogFunc(("#%d bad FCS\n", pThis->iInstance));
824 RcvStatNew.fcs = 1;
825 }
826
827 /* Error-free packets are considered good. */
828 if (RcvStatNew.no_ovf && !RcvStatNew.fcs && !RcvStatNew.runt)
829 RcvStatNew.good = 1;
830
831 uint16_t cbCopy = (uint16_t)RT_MIN(ELNK_BUF_SIZE - pThis->uRCVBufPtr, cbToRecv);
832
833 /* All packets that passed the address filter are copied to the buffer. */
834
835 STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbCopy);
836
837 /* Copy incoming data to the packet buffer. NB: Starts at the current
838 * Receive Buffer Pointer position.
839 */
840 memcpy(dst, src, cbCopy);
841
842 /* Packet length is indicated via the receive buffer pointer. */
843 pThis->uRCVBufPtr = (pThis->uRCVBufPtr + cbCopy) & ELNK_GP_MASK;
844
845 Log2Func(("Received packet, size=%d, RP=%u\n", cbCopy, pThis->uRCVBufPtr));
846
847 /*
848 * If one of the "interesting" conditions was hit, stop receiving until
849 * the status register is read (mark it not stale).
850 * NB: The precise receive logic is not very well described in the EtherLink
851 * documentation. It was refined using the 3C501.EXE diagnostic utility.
852 */
853 if ( (RcvStatNew.good && pThis->RcvCmd.acpt_good)
854 || (RcvStatNew.no_ovf && pThis->RcvCmd.det_eof)
855 || (RcvStatNew.runt && pThis->RcvCmd.det_runt)
856 || (RcvStatNew.dribble && pThis->RcvCmd.det_drbl)
857 || (RcvStatNew.fcs && pThis->RcvCmd.det_fcs)
858 || (RcvStatNew.oflow && pThis->RcvCmd.det_ofl))
859 {
860 pThis->AuxStat.recv_bsy = 0;
861 pThis->IntrState.recv_intr = 1;
862 RcvStatNew.stale = 0; /* Prevents further receive until set again. */
863 }
864 /* Finally update the receive status. */
865 pThis->RcvStat = RcvStatNew;
866
867 LogFlowFunc(("#%d: RcvCmd=%02X, RcvStat=%02X, RCVBufPtr=%u\n", pThis->iInstance, pThis->RcvCmdReg, pThis->RcvStatReg, pThis->uRCVBufPtr));
868 elnkUpdateIrq(pDevIns, pThis);
869 }
870}
871
872
873/**
874 * Transmit data from the packet buffer.
875 *
876 * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
877 *
878 * @param pDevIns The device instance.
879 * @param pThis The EtherLink shared instance
880 * data.
881 * @param pThisCC The EtherLink state data for the
882 * current context.
883 * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
884 */
885static int elnkXmitBuffer(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fOnWorkerThread)
886{
887 RT_NOREF_PV(fOnWorkerThread);
888 int rc;
889
890 /*
891 * Grab the xmit lock of the driver as well as the 3C501 device state.
892 */
893 PPDMINETWORKUP pDrv = pThisCC->pDrv;
894 if (pDrv)
895 {
896 rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
897 if (RT_FAILURE(rc))
898 return rc;
899 }
900 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
901 if (RT_SUCCESS(rc))
902 {
903 /** @todo check if we're supposed to suspend now. */
904 /*
905 * Do the transmitting.
906 */
907 int rc2 = elnkAsyncTransmit(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
908 AssertReleaseRC(rc2);
909
910 /*
911 * Release the locks.
912 */
913 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
914 }
915 else
916 AssertLogRelRC(rc);
917 if (pDrv)
918 pDrv->pfnEndXmit(pDrv);
919
920 return rc;
921}
922
923
924#ifdef IN_RING3
925
926/**
927 * @callback_method_impl{FNPDMTASKDEV,
928 * This is just a very simple way of delaying sending to R3.
929 */
930static DECLCALLBACK(void) elnkR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
931{
932 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
933 PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
934 NOREF(pvUser);
935
936 /*
937 * Transmit if we can.
938 */
939 elnkXmitBuffer(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
940}
941#endif /* IN_RING3 */
942
943
944/**
945 * Allocates a scatter/gather buffer for a transfer.
946 *
947 * @returns See PPDMINETWORKUP::pfnAllocBuf.
948 * @param pThis The shared state data.
949 * @param pThisCC The current context state data.
950 * @param cbMin The minimum buffer size.
951 * @param fLoopback Set if we're in loopback mode.
952 * @param pSgLoop Pointer to stack storage for the loopback SG.
953 * @param ppSgBuf Where to return the SG buffer descriptor on success.
954 * Always set.
955 */
956DECLINLINE(int) elnkXmitAllocBuf(PELNKSTATE pThis, PELNKSTATECC pThisCC, size_t cbMin, bool fLoopback,
957 PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
958{
959 int rc;
960
961 if (!fLoopback)
962 {
963 PPDMINETWORKUP pDrv = pThisCC->pDrv;
964 if (RT_LIKELY(pDrv))
965 {
966 rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
967 AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
968 if (RT_FAILURE(rc))
969 *ppSgBuf = NULL;
970 }
971 else
972 {
973 rc = VERR_NET_DOWN;
974 *ppSgBuf = NULL;
975 }
976 }
977 else
978 {
979 /* Fake loopback allocator. */
980 pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
981 pSgLoop->cbUsed = 0;
982 pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
983 pSgLoop->pvAllocator = pThis;
984 pSgLoop->pvUser = NULL;
985 pSgLoop->cSegs = 1;
986 pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
987 pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
988 *ppSgBuf = pSgLoop;
989 rc = VINF_SUCCESS;
990 }
991 return rc;
992}
993
994
995/**
996 * Sends the scatter/gather buffer.
997 *
998 * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
999 *
1000 * @returns See PDMINETWORKUP::pfnSendBuf.
1001 * @param pDevIns The device instance.
1002 * @param pThis The shared EtherLink state data.
1003 * @param pThisCC The current context state data.
1004 * @param fLoopback Set if we're in loopback mode.
1005 * @param pSgBuf The SG to send.
1006 * @param fOnWorkerThread Set if we're being called on a work thread. Clear
1007 * if an EMT.
1008 */
1009DECLINLINE(int) elnkXmitSendBuf(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
1010{
1011 int rc;
1012 STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
1013 if (!fLoopback)
1014 {
1015 STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
1016 if (pSgBuf->cbUsed > 70) /* unqualified guess */
1017 pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
1018
1019 PPDMINETWORKUP pDrv = pThisCC->pDrv;
1020 if (RT_LIKELY(pDrv))
1021 {
1022 rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
1023 AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
1024 }
1025 else
1026 rc = VERR_NET_DOWN;
1027
1028 pThis->Led.Actual.s.fWriting = 0;
1029 STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
1030 }
1031 else
1032 {
1033 /* Loopback, immediately send buffer to the receive path. */
1034 Assert(pSgBuf->pvAllocator == (void *)pThis);
1035 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
1036
1037 LogFlowFunc(("#%d: loopback (%u bytes)\n", pThis->iInstance, pSgBuf->cbUsed));
1038 elnkReceiveLocked(pDevIns, pThis, pThis->abLoopBuf, pSgBuf->cbUsed, fLoopback);
1039 pThis->Led.Actual.s.fReading = 0;
1040 rc = VINF_SUCCESS;
1041 }
1042 return rc;
1043}
1044
1045
1046/**
1047 * Reads the entire frame into the scatter gather buffer.
1048 */
1049DECLINLINE(void) elnkXmitRead(PPDMDEVINS pDevIns, PELNKSTATE pThis, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
1050{
1051 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); RT_NOREF(pDevIns);
1052 Assert(pSgBuf->cbAvailable >= cbFrame);
1053
1054 pSgBuf->cbUsed = cbFrame;
1055 memcpy(pSgBuf->aSegs[0].pvSeg, &pThis->abPacketBuf[ELNK_GP(pThis)], cbFrame);
1056}
1057
1058/**
1059 * Try to transmit a frame.
1060 */
1061static void elnkTransmit(PPDMDEVINS pDevIns, PELNKSTATE pThis)
1062{
1063 PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
1064
1065 /*
1066 * Transmit the packet if possible, defer it if we cannot do it
1067 * in the current context.
1068 */
1069#if defined(IN_RING0) || defined(IN_RC)
1070 if (!pThisCC->pDrv)
1071 {
1072 int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
1073 AssertRC(rc);
1074 }
1075 else
1076#endif
1077 {
1078 int rc = elnkXmitBuffer(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
1079 if (rc == VERR_TRY_AGAIN)
1080 rc = VINF_SUCCESS;
1081 AssertRC(rc);
1082 }
1083}
1084
1085
1086/**
1087 * If a packet is waiting, poke the receiving machinery.
1088 *
1089 * @threads EMT.
1090 */
1091static void elnkKickReceive(PPDMDEVINS pDevIns, PELNKSTATE pThis)
1092{
1093 /* Some drivers (e.g. NetWare IPX shell/ODI drivers) first go to receive mode through
1094 * the aux command register and only then enable address matching.
1095 */
1096 if ((pThis->AuxStat.recv_bsy == 1) && (pThis->RcvCmd.adr_match != EL_ADRM_DISABLED))
1097 {
1098 if (pThis->fMaybeOutOfSpace)
1099 {
1100#ifdef IN_RING3
1101 elnkR3WakeupReceive(pDevIns);
1102#else
1103 int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hCanRxTask);
1104 AssertRC(rc);
1105#endif
1106 }
1107 }
1108
1109}
1110
1111/**
1112 * Try transmitting a frame.
1113 *
1114 * @threads TX or EMT.
1115 */
1116static int elnkAsyncTransmit(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fOnWorkerThread)
1117{
1118 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1119
1120 /*
1121 * Just drop it if not transmitting. Can happen with delayed transmits
1122 * if transmit was disabled in the meantime.
1123 */
1124 if (RT_UNLIKELY(!pThis->AuxStat.xmit_bsy))
1125 {
1126 LogFunc(("#%d: Nope, xmit disabled (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
1127 return VINF_SUCCESS;
1128 }
1129
1130 if (RT_UNLIKELY((pThis->AuxCmd.buf_ctl != EL_BCTL_XMT_RCV) && (pThis->AuxCmd.buf_ctl != EL_BCTL_LOOPBACK)))
1131 {
1132 LogFunc(("#%d: Nope, not in xmit-then-receive or loopback state (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
1133 return VINF_SUCCESS;
1134 }
1135
1136 /*
1137 * Blast out data from the packet buffer.
1138 */
1139 int rc;
1140 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
1141 do
1142 {
1143 /* Don't send anything when the link is down. */
1144 if (RT_UNLIKELY( !elnkIsLinkUp(pThis)
1145 && pThis->cLinkDownReported > ELNK_MAX_LINKDOWN_REPORTED)
1146 )
1147 break;
1148
1149 bool const fLoopback = pThis->AuxCmd.buf_ctl == EL_BCTL_LOOPBACK;
1150 PDMSCATTERGATHER SgLoop;
1151 PPDMSCATTERGATHER pSgBuf;
1152
1153 /*
1154 * Sending is easy peasy, there is by definition always
1155 * a complete packet on hand.
1156 */
1157 const unsigned cb = ELNK_BUF_SIZE - ELNK_GP(pThis); /* Packet size. */
1158 LogFunc(("#%d: cb=%d\n", pThis->iInstance, cb));
1159
1160 pThis->XmitStatReg = 0; /* Clear transmit status before filling it out. */
1161
1162 if (RT_LIKELY(elnkIsLinkUp(pThis) || fLoopback))
1163 {
1164 if (RT_LIKELY(cb <= MAX_FRAME))
1165 {
1166 rc = elnkXmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
1167 if (RT_SUCCESS(rc))
1168 {
1169 elnkXmitRead(pDevIns, pThis, cb, pSgBuf);
1170 rc = elnkXmitSendBuf(pDevIns, pThis, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
1171 Log2Func(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
1172 }
1173 else if (rc == VERR_TRY_AGAIN)
1174 {
1175 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
1176 LogFunc(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
1177 return VINF_SUCCESS;
1178 }
1179 if (RT_SUCCESS(rc))
1180 pThis->XmitStat.ready = 1;
1181 else
1182 pThis->XmitStat.coll = 1; /* Pretend there was a collision. */
1183 }
1184 else
1185 {
1186 /* Signal error, as this violates the Ethernet specs. */
1187 /** @todo check if the correct error is generated. */
1188 LogRel(("3C501#%d: illegal giant frame (%u bytes) -> signalling error\n", pThis->iInstance, cb));
1189 }
1190 }
1191 else
1192 {
1193 /* Signal a transmit error pretending there was a collision. */
1194 pThis->cLinkDownReported++;
1195 pThis->XmitStat.coll = 1;
1196 }
1197 /* Transmit officially done, update register state. */
1198 pThis->AuxStat.xmit_bsy = 0;
1199 pThis->IntrState.xmit_intr = !!(pThis->XmitCmdReg & pThis->XmitStatReg);
1200 LogFlowFunc(("#%d: XmitCmd=%02X, XmitStat=%02X\n", pThis->iInstance, pThis->XmitCmdReg, pThis->XmitStatReg));
1201
1202 /* NB: After a transmit, the GP Buffer Pointer points just past
1203 * the end of the packet buffer (3C501 diagnostics).
1204 */
1205 pThis->uGPBufPtr = ELNK_BUF_SIZE;
1206
1207 /* NB: The buffer control does *not* change to Receive and stays the way it was. */
1208 if (RT_UNLIKELY(!fLoopback))
1209 {
1210 pThis->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
1211 elnkKickReceive(pDevIns, pThis);
1212 }
1213 } while (0); /* No loop, because there isn't ever more than one packet to transmit. */
1214
1215 elnkUpdateIrq(pDevIns, pThis);
1216
1217 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
1218
1219 return VINF_SUCCESS;
1220}
1221
1222/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
1223
1224
1225static int elnkCsrWrite(PPDMDEVINS pDevIns, PELNKSTATE pThis, uint8_t data)
1226{
1227 int rc = VINF_SUCCESS;
1228 bool fTransmit = false;
1229 bool fReceive = false;
1230 bool fDMAR;
1231 union {
1232 uint8_t reg;
1233 EL_AUX_CMD val;
1234 };
1235
1236 reg = data;
1237
1238 /* Handle reset first. */
1239 if (pThis->AuxCmd.reset != val.reset)
1240 {
1241 if (val.reset)
1242 {
1243 /* Card is placed into reset. Just set the flag. NB: When in reset
1244 * state, we permit writes to other registers, but those have no
1245 * effect and will be overwritten when the card is taken out of reset.
1246 */
1247 LogFunc(("#%d: Card going into reset\n", pThis->iInstance));
1248 pThis->fInReset = true;
1249
1250 /* Many EtherLink drivers like to reset the card a lot. That can lead to
1251 * packet loss if a packet was already received before the card was reset.
1252 */
1253 if (RT_UNLIKELY(!pThis->RcvStat.stale))
1254 STAM_REL_COUNTER_INC(&pThis->StatPktsLostReset);
1255 }
1256 else
1257 {
1258 /* Card is being taken out of reset. */
1259 LogFunc(("#%d: Card going out of reset\n", pThis->iInstance));
1260 STAM_COUNTER_INC(&pThis->StatResets);
1261 elnkSoftReset(pDevIns, pThis);
1262 }
1263 pThis->AuxCmd.reset = val.reset; /* Update the reset bit, if nothing else. */
1264 }
1265
1266 /* If the card is in reset, stop right here. */
1267 if (pThis->fInReset)
1268 return rc;
1269
1270 /* Evaluate DMA state. If it changed, we'll have to go back to R3. */
1271 fDMAR = val.dma_req && val.ride;
1272 if (fDMAR != pThis->fDMA)
1273#ifdef IN_RING3
1274 {
1275 /* Start/stop DMA as requested. */
1276 pThis->fDMA = fDMAR;
1277 PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, fDMAR);
1278 if (fDMAR)
1279 PDMDevHlpDMASchedule(pDevIns);
1280 Log(("3C501#%d: DMARQ for channel %u set to %u\n", pThis->iInstance, pThis->uIsaDma, fDMAR));
1281 }
1282#else
1283 return VINF_IOM_R3_IOPORT_WRITE;
1284#endif
1285
1286 /* Interrupt enable changes. */
1287 if ((pThis->AuxCmd.ire != val.ire) || (pThis->AuxCmd.ride != val.ride))
1288 {
1289 pThis->AuxStat.ride = pThis->AuxCmd.ride = val.ride;
1290 pThis->AuxCmd.ire = val.ire; /* NB: IRE is not visible in the aux status register. */
1291 }
1292
1293 /* DMA Request changes. */
1294 if (pThis->AuxCmd.dma_req != val.dma_req)
1295 {
1296 pThis->AuxStat.dma_req = pThis->AuxCmd.dma_req = val.dma_req;
1297 if (!val.dma_req)
1298 {
1299 /* Clearing the DMA Request bit also clears the DMA Done status bit and any DMA interrupt. */
1300 pThis->IntrState.dma_intr = 0;
1301 pThis->AuxStat.dma_done = 0;
1302 }
1303 }
1304
1305 /* Packet buffer control changes. */
1306 if (pThis->AuxCmd.buf_ctl != val.buf_ctl)
1307 {
1308#ifdef LOG_ENABLED
1309 static const char *apszBuffCntrl[4] = { "System", "Xmit then Recv", "Receive", "Loopback" };
1310 Log(("3C501#%d: Packet buffer control `%s' -> `%s'\n", pThis->iInstance, apszBuffCntrl[pThis->AuxCmd.buf_ctl], apszBuffCntrl[val.buf_ctl]));
1311#endif
1312 if (val.buf_ctl == EL_BCTL_XMT_RCV)
1313 {
1314 /* Transmit, then receive. */
1315 Log2(("3C501#%d: Transmit %u bytes\n%Rhxs\nxmit_bsy=%u\n", pThis->iInstance, ELNK_BUF_SIZE - pThis->uGPBufPtr, &pThis->abPacketBuf[pThis->uGPBufPtr], pThis->AuxStat.xmit_bsy));
1316 fTransmit = true;
1317 pThis->AuxStat.recv_bsy = 0;
1318 }
1319 else if (val.buf_ctl == EL_BCTL_SYSTEM)
1320 {
1321 pThis->AuxStat.xmit_bsy = 1; /* Transmit Busy is set here and cleared once actual transmit completes. */
1322 pThis->AuxStat.recv_bsy = 0;
1323 }
1324 else if (val.buf_ctl == EL_BCTL_RECEIVE)
1325 {
1326 /* Special case: If going from xmit-then-receive mode to receive mode, and we received
1327 * a packet already (right after the receive), don't restart receive and lose the already
1328 * received packet.
1329 */
1330 if (!pThis->uRCVBufPtr)
1331 fReceive = true;
1332 }
1333 else
1334 {
1335 /* For loopback, we go through the regular transmit and receive path. That may be an
1336 * overkill but the receive path is too complex for a special loopback-only case.
1337 */
1338 fTransmit = true;
1339 pThis->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
1340 }
1341 pThis->AuxStat.buf_ctl = pThis->AuxCmd.buf_ctl = val.buf_ctl;
1342 }
1343
1344 /* NB: Bit 1 (xmit_bf, transmit packets with bad FCS) is a simple control
1345 * bit which does not require special handling here. Just copy it over.
1346 */
1347 pThis->AuxStat.xmit_bf = pThis->AuxCmd.xmit_bf = val.xmit_bf;
1348
1349 /* There are multiple bits that affect interrupt state. Handle them now. */
1350 elnkUpdateIrq(pDevIns, pThis);
1351
1352 /* After fully updating register state, do a transmit (including loopback) or receive. */
1353 if (fTransmit)
1354 elnkTransmit(pDevIns, pThis);
1355 else if (fReceive)
1356 {
1357 pThis->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
1358 elnkKickReceive(pDevIns, pThis);
1359 }
1360
1361 return rc;
1362}
1363
1364static int elIoWrite(PPDMDEVINS pDevIns, PELNKSTATE pThis, uint32_t addr, uint32_t val)
1365{
1366 int reg = addr & 0xf;
1367 int rc = VINF_SUCCESS;
1368
1369 Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
1370
1371 switch (reg)
1372 {
1373 case 0x00: /* Six bytes of station address. */
1374 case 0x01:
1375 case 0x02:
1376 case 0x03:
1377 case 0x04:
1378 case 0x05:
1379 pThis->aStationAddr[reg] = val;
1380 break;
1381
1382 case 0x06: /* Receive command. */
1383 {
1384 EL_RCV_CMD OldRcvCmd = pThis->RcvCmd;
1385
1386 pThis->RcvCmdReg = val;
1387 /* If address filter just got enabled, receive may need a kick. */
1388 if (OldRcvCmd.adr_match == EL_ADRM_DISABLED && pThis->RcvCmd.adr_match != EL_ADRM_DISABLED)
1389 elnkKickReceive(pDevIns, pThis);
1390 Log2(("Receive Command register set to %02X\n", pThis->RcvCmdReg));
1391 break;
1392 }
1393
1394 case 0x07: /* Transmit command. */
1395 pThis->XmitCmdReg = val;
1396 Log2(("Transmit Command register set to %02X\n", pThis->XmitCmdReg));
1397 break;
1398
1399 case 0x08: /* GP Buffer pointer LSB. */
1400 pThis->uGPBufPtr = (pThis->uGPBufPtr & 0xff00) | (uint8_t)val;
1401 Log2(("GP Buffer Pointer LSB write, now %u\n", pThis->uGPBufPtr));
1402 break;
1403
1404 case 0x09: /* GP Buffer pointer MSB. */
1405 pThis->uGPBufPtr = ((uint8_t)val << 8) | RT_LOBYTE(pThis->uGPBufPtr);
1406 Log2(("GP Buffer Pointer MSB write, now %u\n", pThis->uGPBufPtr));
1407 break;
1408
1409 case 0x0a: /* RCV Buffer pointer clear. */
1410 pThis->uRCVBufPtr = 0;
1411 Log2(("RCV Buffer Pointer cleared (%02X)\n", val));
1412 break;
1413
1414 case 0x0b: /* RCV buffer pointer MSB. */
1415 case 0x0c: /* Ethernet address PROM window. */
1416 case 0x0d: /* Undocumented. */
1417 Log(("Writing read-only register %02X!\n", reg));
1418 break;
1419
1420 case 0x0e: /* Auxiliary Command (CSR). */
1421 rc = elnkCsrWrite(pDevIns, pThis, val);
1422 break;
1423
1424 case 0x0f: /* Buffer window. */
1425 /* Writes use low 11 bits of GP buffer pointer, auto-increment. */
1426 if (pThis->AuxCmd.buf_ctl != EL_BCTL_SYSTEM)
1427 {
1428 Log(("Packet buffer write ignored, buf_ctl=%u!\n", pThis->AuxCmd.buf_ctl));
1429 /// @todo Does this still increment GPBufPtr?
1430 break;
1431 }
1432 pThis->abPacketBuf[ELNK_GP(pThis)] = val;
1433 pThis->uGPBufPtr = (pThis->uGPBufPtr + 1) & ELNK_GP_MASK;
1434 break;
1435 }
1436
1437 return rc;
1438}
1439
1440static uint32_t elIoRead(PPDMDEVINS pDevIns, PELNKSTATE pThis, uint32_t addr, int *pRC)
1441{
1442 uint32_t val = UINT32_MAX;
1443
1444 *pRC = VINF_SUCCESS;
1445
1446 switch (addr & 0x0f)
1447 {
1448 case 0x00: /* Receive status register aliases. The SEEQ 8001 */
1449 case 0x02: /* EDLC clearly only decodes one bit for reads. */
1450 case 0x04:
1451 case 0x06: /* Receive status register. */
1452 val = pThis->RcvStatReg;
1453 pThis->RcvStat.stale = 1; /* Allows further reception. */
1454 pThis->IntrState.recv_intr = 0; /* Reading clears receive interrupt. */
1455 elnkUpdateIrq(pDevIns, pThis);
1456 break;
1457
1458 case 0x01: /* Transmit status register aliases. */
1459 case 0x03:
1460 case 0x05:
1461 case 0x07: /* Transmit status register. */
1462 val = pThis->XmitStatReg;
1463 pThis->IntrState.xmit_intr = 0; /* Reading clears transmit interrupt. */
1464 elnkUpdateIrq(pDevIns, pThis);
1465 break;
1466
1467 case 0x08: /* GP Buffer pointer LSB. */
1468 val = RT_LOBYTE(pThis->uGPBufPtr);
1469 break;
1470
1471 case 0x09: /* GP Buffer pointer MSB. */
1472 val = RT_HIBYTE(pThis->uGPBufPtr);
1473 break;
1474
1475 case 0x0a: /* RCV Buffer pointer LSB. */
1476 val = RT_LOBYTE(pThis->uRCVBufPtr);
1477 break;
1478
1479 case 0x0b: /* RCV Buffer pointer MSB. */
1480 val = RT_HIBYTE(pThis->uRCVBufPtr);
1481 break;
1482
1483 case 0x0c: /* Ethernet address PROM window. */
1484 case 0x0d: /* Alias. */
1485 /* Reads use low 3 bits of GP buffer pointer, no auto-increment. */
1486 val = pThis->aPROM[pThis->uGPBufPtr & 7];
1487 break;
1488
1489 case 0x0e: /* Auxiliary status register. */
1490 val = pThis->AuxStatReg;
1491 break;
1492
1493 case 0x0f: /* Buffer window. */
1494 /* Reads use low 11 bits of GP buffer pointer, auto-increment. */
1495 val = pThis->abPacketBuf[ELNK_GP(pThis)];
1496 pThis->uGPBufPtr = (pThis->uGPBufPtr + 1) & ELNK_GP_MASK;
1497 break;
1498 }
1499
1500 elnkUpdateIrq(pDevIns, pThis);
1501
1502 Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
1503 return val;
1504}
1505
1506
1507/**
1508 * @callback_method_impl{FNIOMIOPORTIN}
1509 */
1510static DECLCALLBACK(VBOXSTRICTRC)
1511elnkIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1512{
1513 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1514 int rc = VINF_SUCCESS;
1515 uint8_t u8Lo, u8Hi;
1516 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
1517 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1518 RT_NOREF_PV(pvUser);
1519
1520 switch (cb)
1521 {
1522 case 1:
1523 *pu32 = elIoRead(pDevIns, pThis, Port, &rc);
1524 break;
1525 case 2:
1526 /* Manually split word access. */
1527 u8Lo = elIoRead(pDevIns, pThis, Port + 0, &rc);
1528 Assert(RT_SUCCESS(rc));
1529 u8Hi = elIoRead(pDevIns, pThis, Port + 1, &rc);
1530 Assert(RT_SUCCESS(rc));
1531 *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
1532 break;
1533 default:
1534 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
1535 "elnkIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
1536 Port, cb);
1537 }
1538
1539 Log2Func(("#%d: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
1540 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
1541 return rc;
1542}
1543
1544
1545/**
1546 * @callback_method_impl{FNIOMIOPORTOUT}
1547 */
1548static DECLCALLBACK(VBOXSTRICTRC)
1549elnkIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1550{
1551 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1552 int rc = VINF_SUCCESS;
1553 STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
1554 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
1555 RT_NOREF_PV(pvUser);
1556
1557 switch (cb)
1558 {
1559 case 1:
1560 rc = elIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
1561 break;
1562 case 2:
1563 /* Manually split word access. */
1564 rc = elIoWrite(pDevIns, pThis, Port + 0, RT_LOBYTE(u32));
1565 if (!RT_SUCCESS(rc))
1566 break;
1567 rc = elIoWrite(pDevIns, pThis, Port + 1, RT_HIBYTE(u32));
1568 break;
1569 default:
1570 rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
1571 "elnkIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
1572 Port, cb);
1573 }
1574
1575 Log2Func(("#%d: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
1576 STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
1577 return rc;
1578}
1579
1580
1581#ifdef IN_RING3
1582
1583/* Shamelessly stolen from DevDMA.cpp */
1584
1585/* Test the decrement bit of mode register. */
1586#define IS_MODE_DEC(c) ((c) & 0x20)
1587/* Test the auto-init bit of mode register. */
1588#define IS_MODE_AI(c) ((c) & 0x10)
1589/* Extract the transfer type bits of mode register. */
1590#define GET_MODE_XTYP(c) (((c) & 0x0c) >> 2)
1591
1592/* DMA transfer modes. */
1593enum {
1594 DMODE_DEMAND, /* Demand transfer mode. */
1595 DMODE_SINGLE, /* Single transfer mode. */
1596 DMODE_BLOCK, /* Block transfer mode. */
1597 DMODE_CASCADE /* Cascade mode. */
1598};
1599
1600/* DMA transfer types. */
1601enum {
1602 DTYPE_VERIFY, /* Verify transfer type. */
1603 DTYPE_WRITE, /* Write transfer type. */
1604 DTYPE_READ, /* Read transfer type. */
1605 DTYPE_ILLEGAL /* Undefined. */
1606};
1607
1608static DECLCALLBACK(uint32_t) elnkR3DMAXferHandler(PPDMDEVINS pDevIns, void *opaque,
1609 unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
1610{
1611 RT_NOREF(pDevIns);
1612 PELNKSTATE pThis = (PELNKSTATE)opaque;
1613 int dma_mode;
1614 int dma_type;
1615 uint32_t cbToXfer;
1616 uint32_t cbXferred;
1617 uint16_t uLastPos;
1618 int rc;
1619
1620 /*
1621 * The 3C501 EtherLink uses DMA as an alternative to accessing
1622 * the buffer window register. The GP Buffer Pointer controls
1623 * the address into the packet buffer for both writing to and
1624 * reading from the buffer.
1625 */
1626 dma_mode = PDMDevHlpDMAGetChannelMode(pDevIns, pThis->uIsaDma);
1627 dma_type = GET_MODE_XTYP(dma_mode);
1628 LogFlowFunc(("dma_mode=%d, dma_type=%d, dma_pos=%u, dma_len=%u, GPBP=%u\n", dma_mode, dma_type, dma_pos, dma_len, pThis->uGPBufPtr));
1629
1630 cbToXfer = dma_len;
1631
1632 if (dma_type == DTYPE_WRITE)
1633 {
1634 /* Write transfer type. Reading from device, writing to memory. */
1635 rc = PDMDevHlpDMAWriteMemory(pDevIns, nchan,
1636 &pThis->abPacketBuf[ELNK_GP(pThis)],
1637 dma_pos, cbToXfer, &cbXferred);
1638 AssertMsgRC(rc, ("DMAWriteMemory -> %Rrc\n", rc));
1639 uLastPos = pThis->uRCVBufPtr;
1640 }
1641 else
1642 {
1643 /* Read of Verify transfer type. Reading from memory, writing to device. */
1644 rc = PDMDevHlpDMAReadMemory(pDevIns, nchan,
1645 &pThis->abPacketBuf[ELNK_GP(pThis)],
1646 dma_pos, cbToXfer, &cbXferred);
1647 AssertMsgRC(rc, ("DMAReadMemory -> %Rrc\n", rc));
1648 uLastPos = 0; /* Stop when buffer address wraps back to zero. */
1649 }
1650 Log2Func(("After DMA transfer: GPBufPtr=%u, lastpos=%u, cbXferred=%u\n", pThis->uGPBufPtr, uLastPos, cbXferred));
1651
1652 /* Advance the GP buffer pointer and see if transfer completed (it almost certainly did). */
1653 pThis->uGPBufPtr = (pThis->uGPBufPtr + cbXferred) & ELNK_GP_MASK;
1654 if (ELNK_GP(pThis) == uLastPos || 1)
1655 {
1656 Log2(("DMA completed\n"));
1657 PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, 0);
1658 pThis->IntrState.dma_intr = 1;
1659 pThis->AuxStat.dma_done = 1;
1660 elnkUpdateIrq(pDevIns, pThis);
1661 }
1662 else
1663 {
1664 Log(("DMA continuing: GPBufPtr=%u, lastpos=%u, cbXferred=%u\n", pThis->uGPBufPtr, uLastPos, cbXferred));
1665 PDMDevHlpDMASchedule(pDevIns);
1666 }
1667
1668 /* Returns the updated transfer count. */
1669 return dma_pos + cbXferred;
1670}
1671
1672
1673/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
1674
1675/**
1676 * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
1677 *
1678 * This is only called when we restore a saved state and temporarily
1679 * disconnected the network link to inform the guest that network connections
1680 * should be considered lost.
1681 */
1682static DECLCALLBACK(void) elnkR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1683{
1684 RT_NOREF(pvUser);
1685 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1686 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1687 AssertReleaseRC(rc);
1688
1689 rc = VERR_GENERAL_FAILURE;
1690
1691 /* The EhterLink cards have no concept of a link state, and cables were assumed to be
1692 * permanently attached (AUI or BNC). We can simulate a disconnected cable by reporting
1693 * collisions on transmit, but a guest that waits to receive something will never know.
1694 * For that reason, the link is temporarily down, we will only postpone restoring it
1695 * a couple of times, and then reconnect regardless of whether the guest noticed
1696 * anything or not.
1697 */
1698 if ( (pThis->cLinkDownReported <= ELNK_MAX_LINKDOWN_REPORTED)
1699 && (pThis->cLinkRestorePostponed <= ELNK_MAX_LINKRST_POSTPONED))
1700 rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
1701 if (RT_FAILURE(rc))
1702 {
1703 pThis->fLinkTempDown = false;
1704 if (pThis->fLinkUp)
1705 {
1706 LogRel(("3C501#%d: The link is back up again after the restore.\n",
1707 pThis->iInstance));
1708 LogFunc(("#%d: cLinkDownReported=%d\n",
1709 pThis->iInstance, pThis->cLinkDownReported));
1710 pThis->Led.Actual.s.fError = 0;
1711 }
1712 }
1713 else
1714 {
1715 LogFunc(("#%d: cLinkDownReported=%d, cLinkRestorePostponed=%d, wait another 1500ms...\n",
1716 pThis->iInstance, pThis->cLinkDownReported, pThis->cLinkRestorePostponed));
1717 pThis->cLinkRestorePostponed++;
1718 }
1719
1720 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1721}
1722
1723
1724/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
1725
1726/**
1727 * @callback_method_impl{FNDBGFHANDLERDEV}
1728 */
1729static DECLCALLBACK(void) elnkR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1730{
1731 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1732 bool fStationAddr = false;
1733 bool fRecvBuffer = false;
1734 bool fSendBuffer = false;
1735 static const char *apszAddrMatch[4] = { "Disabled", "Promiscuous", "Broadcast", "Multicast" };
1736 static const char *apszBuffCntrl[4] = { "System", "Xmit then Recv", "Receive", "Loopback" };
1737 /*
1738 * Parse args.
1739 */
1740 if (pszArgs)
1741 {
1742 fStationAddr = strstr(pszArgs, "verbose") || strstr(pszArgs, "addr");
1743 fRecvBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "recvbuf");
1744 fSendBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "sendbuf");
1745 }
1746
1747 /*
1748 * Show info.
1749 */
1750 pHlp->pfnPrintf(pHlp,
1751 "3C501 #%d: port=%RTiop IRQ=%u DMA=%u mac-cfg=%RTmac%s%s %s\n",
1752 pThis->iInstance,
1753 pThis->IOPortBase, pThis->uIsaIrq, pThis->uIsaDma, &pThis->MacConfigured,
1754 pDevIns->fRCEnabled ? " RC" : "", pDevIns->fR0Enabled ? " RZ" : "",
1755 pThis->fDriverAttached ? "attached" : "unattached!");
1756
1757 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
1758 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
1759
1760 pHlp->pfnPrintf(pHlp, " GP Buf Ptr : %u (masked %u)\n", pThis->uGPBufPtr, ELNK_GP(pThis));
1761 pHlp->pfnPrintf(pHlp, " RCV Buf Ptr: %u\n", pThis->uRCVBufPtr);
1762 pHlp->pfnPrintf(pHlp, " Recv Command: %02X Recv Status: %02X\n", pThis->RcvCmdReg, pThis->RcvStatReg);
1763 pHlp->pfnPrintf(pHlp, " Xmit Command: %02X Xmit Status: %02X\n", pThis->XmitCmdReg, pThis->XmitStatReg);
1764 pHlp->pfnPrintf(pHlp, " Aux Command: %02X Aux Status: %02X\n", pThis->AuxCmdReg, pThis->AuxStatReg);
1765
1766 pHlp->pfnPrintf(pHlp, " Address matching: %s\n", apszAddrMatch[pThis->RcvCmd.adr_match]);
1767 pHlp->pfnPrintf(pHlp, " Buffer control : %s\n", apszBuffCntrl[pThis->AuxCmd.buf_ctl]);
1768 pHlp->pfnPrintf(pHlp, " Interrupt state : xmit=%u recv=%u dma=%u\n", pThis->IntrState.xmit_intr, pThis->IntrState.recv_intr, pThis->IntrState.dma_intr);
1769 if (pThis->fLinkTempDown)
1770 {
1771 pHlp->pfnPrintf(pHlp, " Link down count : %d\n", pThis->cLinkDownReported);
1772 pHlp->pfnPrintf(pHlp, " Postpone count : %d\n", pThis->cLinkRestorePostponed);
1773 }
1774
1775 /* Dump the station address. */
1776 if (fStationAddr)
1777 {
1778 pHlp->pfnPrintf(pHlp, " Station address : %RTmac\n", &pThis->aStationAddr);
1779 }
1780
1781 /* Dump the beginning of the send buffer. */
1782 if (fSendBuffer)
1783 {
1784 pHlp->pfnPrintf(pHlp, "Send buffer (start at %u):\n", ELNK_GP(pThis));
1785 unsigned dump_end = RT_MIN((ELNK_GP(pThis)) + 64, sizeof(pThis->abPacketBuf) - 16);
1786 for (unsigned ofs = ELNK_GP(pThis); ofs < dump_end; ofs += 16)
1787 pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, &pThis->abPacketBuf[ofs]);
1788 pHlp->pfnPrintf(pHlp, "pktbuf at %p, end at %p\n", &pThis->abPacketBuf[ELNK_GP(pThis)], &pThis->abPacketBuf[ELNK_BUF_SIZE]);
1789 }
1790
1791 /* Dump the beginning of the receive buffer. */
1792 if (fRecvBuffer)
1793 {
1794 pHlp->pfnPrintf(pHlp, "Receive buffer (start at 0):\n");
1795 unsigned dump_end = RT_MIN(pThis->uRCVBufPtr, 64);
1796 for (unsigned ofs = 0; ofs < dump_end; ofs += 16)
1797 pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, &pThis->abPacketBuf[ofs]);
1798 pHlp->pfnPrintf(pHlp, "pktbuf at %p, end at %p\n", pThis->abPacketBuf, &pThis->abPacketBuf[pThis->uRCVBufPtr]);
1799 }
1800
1801 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1802}
1803
1804
1805/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
1806
1807
1808static void elnkR3HardReset(PPDMDEVINS pDevIns, PELNKSTATE pThis)
1809{
1810 LogFlowFunc(("#%d:\n", pThis->iInstance));
1811
1812 /* Initialize the PROM */
1813 Assert(sizeof(pThis->MacConfigured) == 6);
1814 memcpy(pThis->aPROM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
1815 pThis->aPROM[6] = pThis->aPROM[7] = 0; /* The two padding bytes. */
1816
1817 /* Clear the packet buffer and station address. */
1818 memset(pThis->abPacketBuf, 0, sizeof(pThis->abPacketBuf));
1819 memset(pThis->aStationAddr, 0, sizeof(pThis->aStationAddr));
1820
1821 /* Reset the buffer pointers. */
1822 pThis->uGPBufPtr = 0;
1823 pThis->uRCVBufPtr = 0;
1824
1825 elnkSoftReset(pDevIns, pThis);
1826}
1827
1828/**
1829 * Takes down the link temporarily if it's current status is up.
1830 *
1831 * This is used during restore and when replumbing the network link.
1832 *
1833 * The temporary link outage is supposed to indicate to the OS that all network
1834 * connections have been lost and that it for instance is appropriate to
1835 * renegotiate any DHCP lease.
1836 *
1837 * @param pDevIns The device instance.
1838 * @param pThis The device instance data.
1839 */
1840static void elnkTempLinkDown(PPDMDEVINS pDevIns, PELNKSTATE pThis)
1841{
1842 if (pThis->fLinkUp)
1843 {
1844 pThis->fLinkTempDown = true;
1845 pThis->cLinkDownReported = 0;
1846 pThis->cLinkRestorePostponed = 0;
1847 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
1848 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
1849 AssertRC(rc);
1850 }
1851}
1852
1853
1854/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
1855
1856/**
1857 * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
1858 */
1859static DECLCALLBACK(int) elnkLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1860{
1861 RT_NOREF(uPass);
1862 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1863 pDevIns->pHlpR3->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
1864 return VINF_SSM_DONT_CALL_AGAIN;
1865}
1866
1867
1868/**
1869 * @callback_method_impl{FNSSMDEVSAVEPREP,
1870 * Serializes the receive thread, it may be working inside the critsect.}
1871 */
1872static DECLCALLBACK(int) elnkSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1873{
1874 RT_NOREF(pSSM);
1875 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1876
1877 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1878 AssertRC(rc);
1879 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1880
1881 return VINF_SUCCESS;
1882}
1883
1884
1885/**
1886 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1887 */
1888static DECLCALLBACK(int) elnkSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1889{
1890 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1891 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1892
1893 pHlp->pfnSSMPutU16(pSSM, pThis->uGPBufPtr);
1894 pHlp->pfnSSMPutU16(pSSM, pThis->uRCVBufPtr);
1895 pHlp->pfnSSMPutU8(pSSM, pThis->XmitCmdReg);
1896 pHlp->pfnSSMPutU8(pSSM, pThis->XmitStatReg);
1897 pHlp->pfnSSMPutU8(pSSM, pThis->RcvCmdReg);
1898 pHlp->pfnSSMPutU8(pSSM, pThis->RcvStatReg);
1899 pHlp->pfnSSMPutU8(pSSM, pThis->AuxCmdReg);
1900 pHlp->pfnSSMPutU8(pSSM, pThis->AuxStatReg);
1901
1902 pHlp->pfnSSMPutU8(pSSM, pThis->IntrStateReg);
1903 pHlp->pfnSSMPutBool(pSSM, pThis->fInReset);
1904 pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
1905 pHlp->pfnSSMPutBool(pSSM, pThis->fISR);
1906 pHlp->pfnSSMPutMem(pSSM, pThis->aStationAddr, sizeof(pThis->aStationAddr));
1907
1908 /* Save the configured MAC address. */
1909 pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
1910
1911 return VINF_SUCCESS;
1912}
1913
1914
1915/**
1916 * @callback_method_impl{FNSSMDEVLOADPREP},
1917 * Serializes the receive thread, it may be working inside the critsect.}
1918 */
1919static DECLCALLBACK(int) elnkLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1920{
1921 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1922 RT_NOREF(pSSM);
1923
1924 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
1925 AssertRC(rc);
1926
1927 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1928
1929 return rc;
1930}
1931
1932
1933/**
1934 * @callback_method_impl{FNSSMDEVLOADEXEC}
1935 */
1936static DECLCALLBACK(int) elnkLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1937{
1938 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
1939 PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
1940 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1941
1942 if (SSM_VERSION_MAJOR_CHANGED(uVersion, ELNK_SAVEDSTATE_VERSION))
1943 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1944
1945 if (uPass == SSM_PASS_FINAL)
1946 {
1947 /* restore data */
1948 pHlp->pfnSSMGetU16(pSSM, &pThis->uGPBufPtr);
1949 pHlp->pfnSSMGetU16(pSSM, &pThis->uRCVBufPtr);
1950 pHlp->pfnSSMGetU8(pSSM, &pThis->XmitCmdReg);
1951 pHlp->pfnSSMGetU8(pSSM, &pThis->XmitStatReg);
1952 pHlp->pfnSSMGetU8(pSSM, &pThis->RcvCmdReg);
1953 pHlp->pfnSSMGetU8(pSSM, &pThis->RcvStatReg);
1954 pHlp->pfnSSMGetU8(pSSM, &pThis->AuxCmdReg);
1955 pHlp->pfnSSMGetU8(pSSM, &pThis->AuxStatReg);
1956
1957 pHlp->pfnSSMGetU8(pSSM, &pThis->IntrStateReg);
1958 pHlp->pfnSSMGetBool(pSSM, &pThis->fInReset);
1959 pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
1960 pHlp->pfnSSMGetBool(pSSM, &pThis->fISR);
1961 pHlp->pfnSSMGetMem(pSSM, &pThis->aStationAddr, sizeof(pThis->aStationAddr));
1962 }
1963
1964 /* check config */
1965 RTMAC Mac;
1966 int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
1967 AssertRCReturn(rc, rc);
1968 if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
1969 && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
1970 LogRel(("3C501#%u: The mac address differs: config=%RTmac saved=%RTmac\n", pThis->iInstance, &pThis->MacConfigured, &Mac));
1971
1972 if (uPass == SSM_PASS_FINAL)
1973 {
1974 /* update promiscuous mode. */
1975 if (pThisCC->pDrv)
1976 pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, 0 /* promiscuous enabled */);
1977
1978 /* Indicate link down to the guest OS that all network connections have
1979 been lost, unless we've been teleported here. */
1980 if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
1981 elnkTempLinkDown(pDevIns, pThis);
1982 }
1983
1984 return VINF_SUCCESS;
1985}
1986
1987
1988/* -=-=-=-=-=- ELNKSTATE::INetworkDown -=-=-=-=-=- */
1989
1990/**
1991 * Check if the device/driver can receive data now.
1992 *
1993 * Worker for elnkNet_WaitReceiveAvail(). This must be called before
1994 * the pfnRecieve() method is called.
1995 *
1996 * @returns VBox status code.
1997 * @param pDevIns The device instance data.
1998 * @param pThis The shared instance data.
1999 */
2000static int elnkCanReceive(PPDMDEVINS pDevIns, PELNKSTATE pThis)
2001{
2002 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2003 AssertReleaseRC(rc);
2004
2005 rc = VINF_SUCCESS;
2006
2007 /*
2008 * The real 3C501 is very limited in that the packet buffer can only hold one
2009 * frame and and it is shared between transmit and receive, which means the card
2010 * frequently drops packets on a busy network. We cheat a bit and try to hold
2011 * off when it looks like receive is only temporarily unavailable.
2012 *
2013 * If the receiver is disabled, accept packet and drop it to avoid
2014 * packet pile-ups. If it's enabled, take a closer look.
2015 */
2016#if 0
2017 if (pThis->RcvCmd.adr_match != EL_ADRM_DISABLED) {
2018 /* The 3C501 is only prepared to accept a packet if the receiver is busy.
2019 * When not busy, try to delay packets.
2020 */
2021 if (!pThis->AuxStat.recv_bsy)
2022 {
2023 rc = VERR_NET_NO_BUFFER_SPACE;
2024 }
2025 }
2026#else
2027 if (pThis->RcvCmd.adr_match == EL_ADRM_DISABLED || !pThis->AuxStat.recv_bsy)
2028 {
2029 rc = VERR_NET_NO_BUFFER_SPACE;
2030 }
2031#endif
2032 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2033 return rc;
2034}
2035
2036
2037/**
2038 * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
2039 */
2040static DECLCALLBACK(int) elnkNet_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
2041{
2042 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkDown);
2043 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2044 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2045
2046 int rc = elnkCanReceive(pDevIns, pThis);
2047 if (RT_SUCCESS(rc))
2048 return VINF_SUCCESS;
2049 if (RT_UNLIKELY(cMillies == 0))
2050 return VERR_NET_NO_BUFFER_SPACE;
2051
2052 rc = VERR_INTERRUPTED;
2053 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
2054 STAM_PROFILE_START(&pThis->StatRxOverflow, a);
2055 VMSTATE enmVMState;
2056 while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
2057 || enmVMState == VMSTATE_RUNNING_LS))
2058 {
2059 int rc2 = elnkCanReceive(pDevIns, pThis);
2060 if (RT_SUCCESS(rc2))
2061 {
2062 rc = VINF_SUCCESS;
2063 break;
2064 }
2065 LogFlowFunc(("waiting cMillies=%u...\n", cMillies));
2066
2067 /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
2068 * is true -- even if (transmit) polling is disabled. */
2069 rc2 = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2070 AssertReleaseRC(rc2);
2071 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2072 RTSemEventWait(pThis->hEventOutOfRxSpace, cMillies);
2073 }
2074 STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
2075 ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
2076
2077 return rc;
2078}
2079
2080
2081/**
2082 * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
2083 */
2084static DECLCALLBACK(int) elnkNet_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
2085{
2086 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkDown);
2087 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2088 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2089 int rc;
2090
2091 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
2092 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2093 AssertReleaseRC(rc);
2094
2095 if (cb > 50) /* unqualified guess */
2096 pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
2097 elnkReceiveLocked(pDevIns, pThis, (const uint8_t *)pvBuf, cb, false);
2098 pThis->Led.Actual.s.fReading = 0;
2099
2100 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2101 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
2102
2103 return VINF_SUCCESS;
2104}
2105
2106
2107/**
2108 * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
2109 */
2110static DECLCALLBACK(void) elnkNet_XmitPending(PPDMINETWORKDOWN pInterface)
2111{
2112 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkDown);
2113 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2114 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2115
2116 elnkXmitBuffer(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
2117}
2118
2119
2120/* -=-=-=-=-=- ELNKSTATE::INetworkConfig -=-=-=-=-=- */
2121
2122/**
2123 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
2124 */
2125static DECLCALLBACK(int) elnkGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
2126{
2127 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkConfig);
2128 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2129 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2130
2131 LogFlowFunc(("#%d\n", pThis->iInstance));
2132 /// @todo This is broken!! We can't properly get the MAC address set by the guest
2133#if 0
2134 memcpy(pMac, pThis->aStationAddr, sizeof(*pMac));
2135#else
2136 memcpy(pMac, pThis->aPROM, sizeof(*pMac));
2137#endif
2138 return VINF_SUCCESS;
2139}
2140
2141
2142/**
2143 * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
2144 */
2145static DECLCALLBACK(PDMNETWORKLINKSTATE) elnkGetLinkState(PPDMINETWORKCONFIG pInterface)
2146{
2147 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkConfig);
2148 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2149 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2150
2151 if (pThis->fLinkUp && !pThis->fLinkTempDown)
2152 return PDMNETWORKLINKSTATE_UP;
2153 if (!pThis->fLinkUp)
2154 return PDMNETWORKLINKSTATE_DOWN;
2155 if (pThis->fLinkTempDown)
2156 return PDMNETWORKLINKSTATE_DOWN_RESUME;
2157 AssertMsgFailed(("Invalid link state!\n"));
2158 return PDMNETWORKLINKSTATE_INVALID;
2159}
2160
2161
2162/**
2163 * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
2164 */
2165static DECLCALLBACK(int) elnkSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
2166{
2167 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkConfig);
2168 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2169 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2170 bool fLinkUp;
2171
2172 AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
2173 ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
2174
2175 if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
2176 {
2177 elnkTempLinkDown(pDevIns, pThis);
2178 /*
2179 * Note that we do not notify the driver about the link state change because
2180 * the change is only temporary and can be disregarded from the driver's
2181 * point of view (see @bugref{7057}).
2182 */
2183 return VINF_SUCCESS;
2184 }
2185 /* has the state changed? */
2186 fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
2187 if (pThis->fLinkUp != fLinkUp)
2188 {
2189 pThis->fLinkUp = fLinkUp;
2190 if (fLinkUp)
2191 {
2192 /* Connect with a configured delay. */
2193 pThis->fLinkTempDown = true;
2194 pThis->cLinkDownReported = 0;
2195 pThis->cLinkRestorePostponed = 0;
2196 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
2197 int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
2198 AssertRC(rc);
2199 }
2200 else
2201 {
2202 /* Disconnect. */
2203 pThis->cLinkDownReported = 0;
2204 pThis->cLinkRestorePostponed = 0;
2205 pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
2206 }
2207 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
2208 if (pThisCC->pDrv)
2209 pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
2210 }
2211 return VINF_SUCCESS;
2212}
2213
2214
2215/* -=-=-=-=-=- ELNKSTATE::ILeds (LUN#0) -=-=-=-=-=- */
2216
2217/**
2218 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
2219 */
2220static DECLCALLBACK(int) elnkQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
2221{
2222 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, ILeds);
2223 PPDMDEVINS pDevIns = pThisCC->pDevIns;
2224 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2225 if (iLUN == 0)
2226 {
2227 *ppLed = &pThis->Led;
2228 return VINF_SUCCESS;
2229 }
2230 return VERR_PDM_LUN_NOT_FOUND;
2231}
2232
2233
2234/* -=-=-=-=-=- ELNKSTATE::IBase (LUN#0) -=-=-=-=-=- */
2235
2236/**
2237 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2238 */
2239static DECLCALLBACK(void *) elnkQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
2240{
2241 PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, IBase);
2242 Assert(&pThisCC->IBase == pInterface);
2243 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
2244 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
2245 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
2246 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
2247 return NULL;
2248}
2249
2250
2251/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
2252
2253/**
2254 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
2255 */
2256static DECLCALLBACK(void) elnkR3PowerOff(PPDMDEVINS pDevIns)
2257{
2258 /* Poke thread waiting for buffer space. */
2259 elnkR3WakeupReceive(pDevIns);
2260}
2261
2262
2263/**
2264 * @interface_method_impl{PDMDEVREG,pfnDetach}
2265 *
2266 * One port on the network card has been disconnected from the network.
2267 */
2268static DECLCALLBACK(void) elnkR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2269{
2270 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2271 PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
2272 RT_NOREF(fFlags);
2273 LogFlowFunc(("#%d:\n", pThis->iInstance));
2274
2275 AssertLogRelReturnVoid(iLUN == 0);
2276
2277 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2278 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
2279
2280 /*
2281 * Zero some important members.
2282 */
2283 pThis->fDriverAttached = false;
2284 pThisCC->pDrvBase = NULL;
2285 pThisCC->pDrv = NULL;
2286
2287 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2288}
2289
2290
2291/**
2292 * @interface_method_impl{PDMDEVREG,pfnAttach}
2293 * One port on the network card has been connected to a network.
2294 */
2295static DECLCALLBACK(int) elnkR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
2296{
2297 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2298 PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
2299 RT_NOREF(fFlags);
2300 LogFlowFunc(("#%d:\n", pThis->iInstance));
2301
2302 AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
2303
2304 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
2305 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
2306
2307 /*
2308 * Attach the driver.
2309 */
2310 int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
2311 if (RT_SUCCESS(rc))
2312 {
2313 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2314 AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2315 rc = VERR_PDM_MISSING_INTERFACE_BELOW);
2316 pThis->fDriverAttached = true;
2317 }
2318 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2319 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2320 {
2321 /* This should never happen because this function is not called
2322 * if there is no driver to attach! */
2323 Log(("#%d: No attached driver!\n", pThis->iInstance));
2324 }
2325
2326 /*
2327 * Temporary set the link down if it was up so that the guest
2328 * will know that we have change the configuration of the
2329 * network card
2330 */
2331 if (RT_SUCCESS(rc))
2332 elnkTempLinkDown(pDevIns, pThis);
2333
2334 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
2335 return rc;
2336}
2337
2338
2339/**
2340 * @interface_method_impl{PDMDEVREG,pfnSuspend}
2341 */
2342static DECLCALLBACK(void) elnkR3Suspend(PPDMDEVINS pDevIns)
2343{
2344 /* Poke thread waiting for buffer space. */
2345 elnkR3WakeupReceive(pDevIns);
2346}
2347
2348
2349/**
2350 * @interface_method_impl{PDMDEVREG,pfnReset}
2351 */
2352static DECLCALLBACK(void) elnkR3Reset(PPDMDEVINS pDevIns)
2353{
2354 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2355 if (pThis->fLinkTempDown)
2356 {
2357 pThis->cLinkDownReported = 0x1000;
2358 pThis->cLinkRestorePostponed = 0x1000;
2359 PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
2360 elnkR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
2361 }
2362
2363 /** @todo How to flush the queues? */
2364 elnkR3HardReset(pDevIns, pThis);
2365}
2366
2367
2368/**
2369 * @interface_method_impl{PDMDEVREG,pfnRelocate}
2370 */
2371static DECLCALLBACK(void) elnkR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
2372{
2373 PELNKSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PELNKSTATERC);
2374 pThisRC->pDrv += offDelta;
2375}
2376
2377
2378/**
2379 * @interface_method_impl{PDMDEVREG,pfnDestruct}
2380 */
2381static DECLCALLBACK(int) elnkR3Destruct(PPDMDEVINS pDevIns)
2382{
2383 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
2384 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2385
2386 if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
2387 {
2388 RTSemEventSignal(pThis->hEventOutOfRxSpace);
2389 RTSemEventDestroy(pThis->hEventOutOfRxSpace);
2390 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
2391 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
2392 }
2393 return VINF_SUCCESS;
2394}
2395
2396
2397/**
2398 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2399 */
2400static DECLCALLBACK(int) elnkR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
2401{
2402 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2403 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2404 PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
2405 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2406 PPDMIBASE pBase;
2407 char szTmp[128];
2408 int rc;
2409
2410 /*
2411 * Init what's required to make the destructor safe.
2412 */
2413 pThis->iInstance = iInstance;
2414 pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
2415 pThis->hIoPortsIsa = NIL_IOMIOPORTHANDLE;
2416 pThisCC->pDevIns = pDevIns;
2417
2418 /*
2419 * Validate configuration.
2420 */
2421 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|Port|IRQ|DMA|LinkUpDelay|LineSpeed", "");
2422
2423 /*
2424 * Read the configuration.
2425 */
2426 rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
2427 if (RT_FAILURE(rc))
2428 return PDMDEV_SET_ERROR(pDevIns, rc,
2429 N_("Configuration error: Failed to get the \"MAC\" value"));
2430 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
2431 if (RT_FAILURE(rc))
2432 return PDMDEV_SET_ERROR(pDevIns, rc,
2433 N_("Configuration error: Failed to get the \"CableConnected\" value"));
2434
2435 /*
2436 * Process ISA configuration options.
2437 */
2438 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
2439 if (RT_FAILURE(rc))
2440 return PDMDEV_SET_ERROR(pDevIns, rc,
2441 N_("Configuration error: Failed to get the \"Port\" value"));
2442
2443 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
2444 if (RT_FAILURE(rc))
2445 return PDMDEV_SET_ERROR(pDevIns, rc,
2446 N_("Configuration error: Failed to get the \"IRQ\" value"));
2447
2448 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pThis->uIsaDma, 1);
2449 if (RT_FAILURE(rc))
2450 return PDMDEV_SET_ERROR(pDevIns, rc,
2451 N_("Configuration error: Failed to get the \"DMA\" value"));
2452
2453 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
2454 if (RT_FAILURE(rc))
2455 return PDMDEV_SET_ERROR(pDevIns, rc,
2456 N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
2457 Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
2458 if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
2459 {
2460 LogRel(("3C501#%d WARNING! Link up delay is set to %u seconds!\n",
2461 iInstance, pThis->cMsLinkUpDelay / 1000));
2462 }
2463 Log(("#%d Link up delay is set to %u seconds\n",
2464 iInstance, pThis->cMsLinkUpDelay / 1000));
2465
2466
2467 /*
2468 * Initialize data (most of it anyway).
2469 */
2470 pThis->Led.u32Magic = PDMLED_MAGIC;
2471 /* IBase */
2472 pThisCC->IBase.pfnQueryInterface = elnkQueryInterface;
2473 /* INetworkPort */
2474 pThisCC->INetworkDown.pfnWaitReceiveAvail = elnkNet_WaitReceiveAvail;
2475 pThisCC->INetworkDown.pfnReceive = elnkNet_Receive;
2476 pThisCC->INetworkDown.pfnXmitPending = elnkNet_XmitPending;
2477 /* INetworkConfig */
2478 pThisCC->INetworkConfig.pfnGetMac = elnkGetMac;
2479 pThisCC->INetworkConfig.pfnGetLinkState = elnkGetLinkState;
2480 pThisCC->INetworkConfig.pfnSetLinkState = elnkSetLinkState;
2481 /* ILeds */
2482 pThisCC->ILeds.pfnQueryStatusLed = elnkQueryStatusLed;
2483
2484 /*
2485 * We use our own critical section (historical reasons).
2486 */
2487 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "3C501#%u", iInstance);
2488 AssertRCReturn(rc, rc);
2489 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
2490 AssertRCReturn(rc, rc);
2491
2492 rc = RTSemEventCreate(&pThis->hEventOutOfRxSpace);
2493 AssertRCReturn(rc, rc);
2494
2495 /*
2496 * Register ISA I/O ranges for the EtherLink 3C501.
2497 */
2498 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, elnkIOPortWrite, elnkIOPortRead,
2499 "3C501", NULL /*paExtDesc*/, &pThis->hIoPortsIsa);
2500 if (RT_FAILURE(rc))
2501 return rc;
2502
2503 /*
2504 * Register DMA channel.
2505 */
2506 if (pThis->uIsaDma <= ELNK_MAX_VALID_DMA)
2507 {
2508 rc = PDMDevHlpDMARegister(pDevIns, pThis->uIsaDma, elnkR3DMAXferHandler, pThis);
2509 if (RT_FAILURE(rc))
2510 return rc;
2511 LogRel(("3C501#%d: Enabling DMA channel %u\n", iInstance, pThis->uIsaDma));
2512 }
2513 else
2514 LogRel(("3C501#%d: Disabling DMA\n", iInstance));
2515
2516 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, elnkR3TimerRestore, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
2517 "3C501 Restore Timer", &pThis->hTimerRestore);
2518 if (RT_FAILURE(rc))
2519 return rc;
2520
2521 rc = PDMDevHlpSSMRegisterEx(pDevIns, ELNK_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
2522 NULL, elnkLiveExec, NULL,
2523 elnkSavePrep, elnkSaveExec, NULL,
2524 elnkLoadPrep, elnkLoadExec, NULL);
2525 if (RT_FAILURE(rc))
2526 return rc;
2527
2528 /*
2529 * Create the transmit queue.
2530 */
2531 rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "3C501-Xmit", elnkR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
2532 if (RT_FAILURE(rc))
2533 return rc;
2534
2535 /*
2536 * Create the RX notifier signaller.
2537 */
2538 rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "3C501-Rcv", elnkR3CanRxTaskCallback, NULL /*pvUser*/, &pThis->hCanRxTask);
2539 if (RT_FAILURE(rc))
2540 return rc;
2541
2542 /*
2543 * Register the info item.
2544 */
2545 RTStrPrintf(szTmp, sizeof(szTmp), "elnk%d", pThis->iInstance);
2546 PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "3C501 info", elnkR3Info);
2547
2548 /*
2549 * Attach status driver (optional).
2550 */
2551 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
2552 if (RT_SUCCESS(rc))
2553 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
2554 else if ( rc != VERR_PDM_NO_ATTACHED_DRIVER
2555 && rc != VERR_PDM_CFG_MISSING_DRIVER_NAME)
2556 {
2557 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
2558 return rc;
2559 }
2560
2561 /*
2562 * Attach driver.
2563 */
2564 rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
2565 if (RT_SUCCESS(rc))
2566 {
2567 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
2568 AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
2569 VERR_PDM_MISSING_INTERFACE_BELOW);
2570 pThis->fDriverAttached = true;
2571 }
2572 else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
2573 || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
2574 {
2575 /* No error! */
2576 Log(("No attached driver!\n"));
2577 }
2578 else
2579 return rc;
2580
2581 /*
2582 * Reset the device state. (Do after attaching.)
2583 */
2584 elnkR3HardReset(pDevIns, pThis);
2585
2586 /*
2587 * Register statistics counters.
2588 */
2589 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/EtherLink%u/BytesReceived", iInstance);
2590 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/EtherLink%u/BytesTransmitted", iInstance);
2591
2592 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/EtherLink%d/ReceiveBytes", iInstance);
2593 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/EtherLink%d/TransmitBytes", iInstance);
2594
2595#ifdef VBOX_WITH_STATISTICS
2596 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ", "/Devices/EtherLink%d/IO/ReadRZ", iInstance);
2597 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3", "/Devices/EtherLink%d/IO/ReadR3", iInstance);
2598 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ", "/Devices/EtherLink%d/IO/WriteRZ", iInstance);
2599 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3", "/Devices/EtherLink%d/IO/WriteR3", iInstance);
2600 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/EtherLink%d/Receive", iInstance);
2601 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/EtherLink%d/RxOverflow", iInstance);
2602 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups", "/Devices/EtherLink%d/RxOverflowWakeup", iInstance);
2603 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ", "/Devices/EtherLink%d/Transmit/TotalRZ", iInstance);
2604 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3", "/Devices/EtherLink%d/Transmit/TotalR3", iInstance);
2605 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in RZ", "/Devices/EtherLink%d/Transmit/SendRZ", iInstance);
2606 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in R3", "/Devices/EtherLink%d/Transmit/SendR3", iInstance);
2607
2608 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks", "/Devices/EtherLink%d/UpdateIRQ", iInstance);
2609
2610 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatResets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of soft resets", "/Devices/EtherLink%d/SoftResets", iInstance);
2611 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktAdrmDis, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, disabled address match", "/Devices/EtherLink%d/DropPktAdrmDis", iInstance);
2612 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktZeroLen, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped zero length packet", "/Devices/EtherLink%d/DropPktZeroLen", iInstance);
2613 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktVMNotRunning,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, VM not running", "/Devices/EtherLink%d/DropPktVMNotRunning", iInstance);
2614 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoLink, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, no link", "/Devices/EtherLink%d/DropPktNoLink", iInstance);
2615 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktStaleRcv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, status register unread", "/Devices/EtherLink%d/DropPktStaleRcv", iInstance);
2616#endif /* VBOX_WITH_STATISTICS */
2617 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatPktsLostReset, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of packets lost due to resets", "/Devices/EtherLink%d/PktsLostByReset", iInstance);
2618
2619 return VINF_SUCCESS;
2620}
2621
2622#else
2623
2624/**
2625 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
2626 */
2627static DECLCALLBACK(int) elnkRZConstruct(PPDMDEVINS pDevIns)
2628{
2629 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2630 PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
2631
2632 /* Critical section setup: */
2633 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
2634 AssertRCReturn(rc, rc);
2635
2636 /* ISA I/O ports: */
2637 if (pThis->hIoPortsIsa != NIL_IOMIOPORTHANDLE)
2638 {
2639 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsa, elnkIOPortWrite, elnkIOPortRead, NULL /*pvUser*/);
2640 AssertRCReturn(rc, rc);
2641 }
2642
2643 return VINF_SUCCESS;
2644}
2645
2646#endif /* IN_RING3 */
2647
2648/**
2649 * The device registration structure.
2650 */
2651const PDMDEVREG g_Device3C501 =
2652{
2653 /* .u32Version = */ PDM_DEVREG_VERSION,
2654 /* .uReserved0 = */ 0,
2655 /* .szName = */ "3c501",
2656 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
2657 /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
2658 /* .cMaxInstances = */ ~0U,
2659 /* .uSharedVersion = */ 42,
2660 /* .cbInstanceShared = */ sizeof(ELNKSTATE),
2661 /* .cbInstanceCC = */ sizeof(ELNKSTATECC),
2662 /* .cbInstanceRC = */ sizeof(ELNKSTATERC),
2663 /* .cMaxPciDevices = */ 0,
2664 /* .cMaxMsixVectors = */ 0,
2665 /* .pszDescription = */ "3Com EtherLink 3C501 adapter.\n",
2666#if defined(IN_RING3)
2667 /* .pszRCMod = */ "VBoxDDRC.rc",
2668 /* .pszR0Mod = */ "VBoxDDR0.r0",
2669 /* .pfnConstruct = */ elnkR3Construct,
2670 /* .pfnDestruct = */ elnkR3Destruct,
2671 /* .pfnRelocate = */ elnkR3Relocate,
2672 /* .pfnMemSetup = */ NULL,
2673 /* .pfnPowerOn = */ NULL,
2674 /* .pfnReset = */ elnkR3Reset,
2675 /* .pfnSuspend = */ elnkR3Suspend,
2676 /* .pfnResume = */ NULL,
2677 /* .pfnAttach = */ elnkR3Attach,
2678 /* .pfnDetach = */ elnkR3Detach,
2679 /* .pfnQueryInterface = */ NULL,
2680 /* .pfnInitComplete = */ NULL,
2681 /* .pfnPowerOff = */ elnkR3PowerOff,
2682 /* .pfnSoftReset = */ NULL,
2683 /* .pfnReserved0 = */ NULL,
2684 /* .pfnReserved1 = */ NULL,
2685 /* .pfnReserved2 = */ NULL,
2686 /* .pfnReserved3 = */ NULL,
2687 /* .pfnReserved4 = */ NULL,
2688 /* .pfnReserved5 = */ NULL,
2689 /* .pfnReserved6 = */ NULL,
2690 /* .pfnReserved7 = */ NULL,
2691#elif defined(IN_RING0)
2692 /* .pfnEarlyConstruct = */ NULL,
2693 /* .pfnConstruct = */ elnkRZConstruct,
2694 /* .pfnDestruct = */ NULL,
2695 /* .pfnFinalDestruct = */ NULL,
2696 /* .pfnRequest = */ NULL,
2697 /* .pfnReserved0 = */ NULL,
2698 /* .pfnReserved1 = */ NULL,
2699 /* .pfnReserved2 = */ NULL,
2700 /* .pfnReserved3 = */ NULL,
2701 /* .pfnReserved4 = */ NULL,
2702 /* .pfnReserved5 = */ NULL,
2703 /* .pfnReserved6 = */ NULL,
2704 /* .pfnReserved7 = */ NULL,
2705#elif defined(IN_RC)
2706 /* .pfnConstruct = */ NULL,
2707 /* .pfnReserved0 = */ NULL,
2708 /* .pfnReserved1 = */ NULL,
2709 /* .pfnReserved2 = */ NULL,
2710 /* .pfnReserved3 = */ NULL,
2711 /* .pfnReserved4 = */ NULL,
2712 /* .pfnReserved5 = */ NULL,
2713 /* .pfnReserved6 = */ NULL,
2714 /* .pfnReserved7 = */ NULL,
2715#else
2716# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2717#endif
2718 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2719};
2720
2721#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette