VirtualBox

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

Last change on this file since 95433 was 93675, checked in by vboxsync, 3 years ago

Dev3C501/DevDP8390: Fixed saved state loading; force link restore after about 6 seconds because old drivers may not expect cable disconnects at all and will never notice.

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