* [Qemu-devel] PATCH: Add 3C90X emulation
@ 2009-05-05 7:56 Matthew Iselin
2009-06-01 21:18 ` [Qemu-devel] " Sebastian Herbszt
0 siblings, 1 reply; 7+ messages in thread
From: Matthew Iselin @ 2009-05-05 7:56 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 420 bytes --]
Hi,
This patch adds the 3Com 3C90x series to the set of NICs that QEMU can
emulate. It is still a work in progress, however
at this stage it has the base required functionality and works with
Ubuntu and Damn Small Linux.
Part 1 modifies the Makefile.target file to add the object file for
the emulation implementation.
Part 2 includes the hw directory modifications, including the new 3c90x.c file.
Thanks,
Matthew
[-- Attachment #2: 3c90x_1.patch --]
[-- Type: text/x-patch, Size: 291 bytes --]
--- ./qemu-0.10.3-clean/Makefile.target 2009-05-02 03:02:44.000000000 +1000
+++ ./qemu-0.10.3/Makefile.target 2009-05-05 17:16:41.000000000 +1000
@@ -572,6 +572,7 @@ OBJS += eepro100.o
OBJS += ne2000.o
OBJS += pcnet.o
OBJS += rtl8139.o
+OBJS += 3c90x.o
OBJS += e1000.o
# Serial mouse
[-- Attachment #3: 3c90x_2.patch --]
[-- Type: text/x-patch, Size: 73297 bytes --]
diff -ruNp -- ./qemu-0.10.3-clean/hw/3c90x.c ./qemu-0.10.3/hw/3c90x.c
--- ./qemu-0.10.3-clean/hw/3c90x.c 1970-01-01 10:00:00.000000000 +1000
+++ ./qemu-0.10.3/hw/3c90x.c 2009-05-05 17:25:23.000000000 +1000
@@ -0,0 +1,2405 @@
+/**
+ * QEMU 3C90X Emulation
+ *
+ * Copyright (c) 2009 Matthew Iselin (QEMU VERSION)
+ * Copyright (C) 2004 John Kelley (pearpc@kelley.ca) (PEARPC VERSION)
+ * Copyright (C) 2003 Stefan Weyergraf (PEARPC VERSION)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+ * Modifications:
+ * (none)
+ */
+
+/**
+ * TODO:
+ * - Still a lot of unimplemented functionality
+ * - On-chip TCP checksum emulation
+ * - savevm functions
+ * TESTED ON:
+ * - Damn Small Linux (4.4.10)
+ * - Ubuntu (8.10, 5.10)
+ * - Pedigree
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "qemu-timer.h"
+#include "net.h"
+
+// Should we inspect incoming frames and print extra debugging information about them?
+//#define DEBUG_3C90X_ANALYSE_FRAMES 0
+
+#ifdef DEBUG_3C90X_ANALYSE_FRAMES
+#define __FAVOR_BSD
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#endif
+
+#define PACKED __attribute__((packed));
+#define ALIGNED __attribute__((aligned));
+#define PACKED8 __attribute__((aligned(8)));
+#define PACKED16 __attribute__((aligned(16)));
+#define PACKED32 __attribute__((aligned(32)));
+
+/* debug 3C90X card */
+// #define DEBUG_3C90X 1
+
+/* define this to output TX and RX events */
+// #define DEBUG_3C90X_AUX 1
+
+#define PCI_FREQUENCY 33000000L
+
+/* Calculate CRCs properly on Tx packets */
+#define A3C90X_CALCULATE_TXCRC 1
+
+#if defined(A3C90x_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#if defined (DEBUG_3C90X)
+# define DEBUG_PRINT(x) do { printf(x) ; } while (0)
+# define DEBUG_PRINT_FORMAT(x) do { printf x ; } while (0)
+#else
+# define DEBUG_PRINT(x)
+# define DEBUG_PRINT_FORMAT(x)
+#endif
+
+#if defined (DEBUG_3C90X_AUX)
+# define AUX_DEBUG_PRINT(x) do { printf(x) ; } while (0)
+# define AUX_DEBUG_PRINT_FORMAT(x) do { printf x ; } while (0)
+#else
+# define AUX_DEBUG_PRINT(x) DEBUG_PRINT(x)
+# define AUX_DEBUG_PRINT_FORMAT(x) DEBUG_PRINT_FORMAT(x)
+#endif
+
+#define MAX_PACKET_SIZE 16384
+
+enum Command
+{
+ CmdTotalReset = 0<<11,
+ CmdSelectWindow = 1<<11,
+ CmdEnableDC = 2<<11, // CmdStartCoax
+ CmdRxDisable = 3<<11,
+ CmdRxEnable = 4<<11,
+ CmdRxReset = 5<<11,
+ CmdStall = 6<<11,
+ CmdTxDone = 7<<11,
+ CmdRxDiscard = 8<<11,
+ CmdTxEnable = 9<<11,
+ CmdTxDisable = 10<<11,
+ CmdTxReset = 11<<11,
+ CmdReqIntr = 12<<11, // CmdFakeIntr
+ CmdAckIntr = 13<<11,
+ CmdSetIntrEnb = 14<<11,
+ CmdSetIndicationEnable = 15<<11, // CmdSetStatusEnb
+ CmdSetRxFilter = 16<<11,
+ CmdSetRxEarlyThresh = 17<<11,
+ CmdSetTxThreshold = 18<<11, // aka TxAgain ?
+ CmdSetTxStartThresh = 19<<11, // set TxStartTresh
+ // CmdStartDMAUp = 20<<11,
+ // CmdStartDMADown = (20<<11)+1,
+ CmdStatsEnable = 21<<11,
+ CmdStatsDisable = 22<<11,
+ CmdDisableDC = 23<<11, // CmdStopCoax
+ CmdSetTxReclaimThresh = 24<<11,
+ CmdSetHashFilterBit = 25<<11
+};
+
+/*
+ * IntStatusBits
+ */
+enum IntStatusBits
+{
+ IS_interruptLatch = 1<<0,
+ IS_hostError = 1<<1,
+ IS_txComplete = 1<<2,
+ /* bit 3 is unspecified */
+ IS_rxComplete = 1<<4,
+ IS_rxEarly = 1<<5,
+ IS_intRequested = 1<<6,
+ IS_updateStats = 1<<7,
+ IS_linkEvent = 1<<8,
+ IS_dnComplete = 1<<9,
+ IS_upComplete = 1<<10,
+ IS_cmdInProgress = 1<<11,
+ /* bit 12 is unspecified */
+ /* [15:13] is currently selected window */
+};
+
+/*
+ * DmaCtrlBits ([1] p.96)
+ */
+enum DmaCtrlBits
+{
+ /* bit 0 unspecified */
+ DC_dnCmplReq = 1<<1,
+ DC_dnStalled = 1<<2,
+ DC_upComplete = 1<<3, // FIXME: same as in IntStatus, but always visible
+ DC_dnComplete = 1<<4, // same as above ^^^
+ DC_upRxEarlyEnable = 1<<5,
+ DC_armCountdown = 1<<6,
+ DC_dnInProg = 1<<7,
+ DC_counterSpeed = 1<<8,
+ DC_countdownMode = 1<<9,
+ /* bits 10-15 unspecified */
+ DC_upAltSeqDisable = 1<<16,
+ DC_dnAltSeqDisable = 1<<17,
+ DC_defeatMWI = 1<<20,
+ DC_defeatMRL = 1<<21,
+ DC_upOverDiscEnable = 1<<22,
+ DC_targetAbort = 1<<30,
+ DC_masterAbort = 1<<31
+};
+
+/*
+ * MII Registers
+ * TODO: Implement MII properly
+ */
+/*enum MIIControlBits {
+ MIIC_collision = 1<<7,
+ MIIC_fullDuplex = 1<<8,
+ MIIC_restartNegote = 1<<9,
+ MIIC_collision = 1<<7,
+ rest missing
+};*/
+
+struct MIIRegisters
+{
+ uint16_t control;
+ uint16_t status;
+ uint16_t id0;
+ uint16_t id1;
+ uint16_t advert;
+ uint16_t linkPartner;
+ uint16_t expansion;
+ uint16_t nextPage;
+} PACKED;
+typedef struct MIIRegisters MIIRegisters;
+
+/*
+ * Registers
+ */
+union RegWindow
+{
+ uint8_t b[16];
+ uint16_t u16[8];
+} PACKED;
+typedef union RegWindow RegWindow;
+
+struct Registers
+{
+ // 0x10 uint8_ts missing (current window)
+ uint32_t r0;
+ uint32_t r1;
+ uint8_t TxPktId;
+ uint8_t r2;
+ uint8_t Timer;
+ uint8_t TxStatus;
+ uint16_t r3;
+ uint16_t __dontUseMe;// really: uint16_t IntStatusAuto;
+ uint32_t DmaCtrl; // [1] p.95 (dn), p.100 (up)
+ uint32_t DnListPtr; // [1] p.98
+ uint16_t r4;
+ uint8_t DnBurstThresh; // [1] p.97
+ uint8_t r5;
+ uint8_t DnPriorityThresh;
+ uint8_t DnPoll; // [1] p.100
+ uint16_t r6;
+ uint32_t UpPktStatus;
+ uint16_t FreeTimer;
+ uint16_t Countdown;
+ uint32_t UpListPtr; // [1] p.115
+ uint8_t UpPriorityThresh;
+ uint8_t UpPoll;
+ uint8_t UpBurstThresh;
+ uint8_t r7;
+ uint32_t RealTimeCount;
+ uint8_t ConfigAddress;
+ uint8_t r8;
+ uint8_t r9;
+ uint8_t r10;
+ uint8_t ConfigData;
+ uint8_t r11;
+ uint8_t r12;
+ uint8_t r13;
+ uint32_t r14[9];
+ uint32_t DebugData;
+ uint16_t DebugControl;
+ uint16_t r15;
+ uint16_t DnMaxBurst;
+ uint16_t UpMaxBurst;
+ uint16_t PowerMgmtCtrl;
+ uint16_t r16;
+} PACKED;
+typedef struct Registers Registers;
+
+#define RA_INV 0
+
+static uint8_t gRegAccess[0x70] =
+{
+ /* 0x10 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x14 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x18 */
+ 1, /* TxPktId */
+ RA_INV,
+ 1, /* Timer */
+ 1, /* TxStatus */
+ /* 0x1c */
+ RA_INV, RA_INV,
+ RA_INV, RA_INV, /* IntStatusAuto */
+ /* 0x20 */
+ 4, RA_INV, RA_INV, RA_INV, /* DmaCtrl */
+ /* 0x24 */
+ 4, RA_INV, RA_INV, RA_INV, /* DnListPtr */
+ /* 0x28 */
+ RA_INV, RA_INV,
+ 1, /* DnBurstThresh */
+ RA_INV,
+ /* 0x2c */
+ 1, /* DnPriorityThresh */
+ 1, /* DnPoll */
+ RA_INV,
+ 1,
+ /* 0x30 */
+ 4, RA_INV, RA_INV, RA_INV, /* UpPktStatus */
+ /* 0x34 */
+ 2, RA_INV, /* FreeTimer */
+ 2, RA_INV, /* Countdown */
+ /* 0x38 */
+ 4, RA_INV, RA_INV, RA_INV, /* UpListPtr */
+ /* 0x3c */
+ 1, /* UpPriorityThresh */
+ 1, /* UpPoll */
+ 1, /* UpBurstThresh */
+ RA_INV,
+ /* 0x40 */
+ 4, RA_INV, RA_INV, RA_INV, /* RealTimeCount */
+ /* 0x44 */
+ 1, /* ConfigAddress */
+ RA_INV,
+ RA_INV,
+ RA_INV,
+ /* 0x48 */
+ 1, /* ConfigData */
+ RA_INV,
+ RA_INV,
+ RA_INV,
+ /* 0x4c */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x50 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x70 */
+ 4, RA_INV, RA_INV, RA_INV, /* DebugData */
+ /* 0x74 */
+ 2, RA_INV, /* DebugControl */
+ RA_INV, RA_INV,
+ /* 0x78 */
+ 2, RA_INV, /* DnMaxBurst */
+ 2, RA_INV, /* UpMaxBurst */
+ /* 0x7c */
+ 2, RA_INV, /* PowerMgmtCtrl */
+ RA_INV, RA_INV
+};
+
+/*
+ * Window 0
+ */
+struct RegWindow0
+{
+ uint32_t r0;
+ uint32_t BiosRomAddr;
+ uint8_t BiosRomData;
+ uint8_t r1;
+ uint16_t EepromCommand;
+ uint16_t EepromData;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow0 RegWindow0;
+
+enum W0_Offsets
+{
+ W0_EEPROMCmd = 0xa,
+ W0_EEPROMData = 0xc
+};
+
+enum W0_EEPROMOpcode
+{
+ EEOP_SubCmd = 0<<6,
+ EEOP_WriteReg = 1<<6,
+ EEOP_ReadReg = 2<<6,
+ EEOP_EraseReg = 3<<6
+};
+
+enum W0_EEPROMSubCmd
+{
+ EESC_WriteDisable = 0<<4,
+ EESC_WriteAll = 1<<4,
+ EESC_EraseAll = 2<<4,
+ EESC_WriteEnable = 3<<4
+};
+
+/*
+ * Window 2
+ */
+struct RegWindow2
+{
+ uint16_t StationAddress[6];
+ uint16_t StationMask[6];
+ uint16_t ResetOptions;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow2 RegWindow2;
+
+/*
+ * Window 3
+ */
+struct RegWindow3
+{
+ uint32_t InternalConfig; // [1] p.58,76
+ uint16_t MaxPktSize;
+ uint16_t MacControl; // [1] p.179
+ uint16_t MediaOptions; // [1] p.78 (EE), p.181
+ uint16_t RxFree;
+ uint16_t TxFree; // [1] p.101
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow3 RegWindow3;
+
+/*
+ * Window 4
+ */
+enum W4_PhysMgmtBits
+{
+ PM_mgmtClk = 1<<0,
+ PM_mgmtData = 1<<1,
+ PM_mgmtDir = 1<<2
+};
+
+struct RegWindow4
+{
+ uint16_t r0; /* offset 0x0 */
+ uint16_t r1; /* offset 0x2 */
+ uint16_t FifoDiagnostic; /* offset 0x4 */
+ uint16_t NetDiagnostic; // [1] p.184 /* offset 0x6 */
+ uint16_t PhysMgmt; // [1] p.186 /* offset 0x8 */
+ uint16_t MediaStatus; // [1] p.182 /* offset 0xa */
+ uint8_t BadSSD;
+ uint8_t Upperuint8sOK;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow4 RegWindow4;
+
+/*
+ * Window 5
+ */
+enum RxFilterBits // [1] p.112
+{
+ RXFILT_receiveIndividual = 1,
+ RXFILT_receiveMulticast = 2,
+ RXFILT_receiveBroadcast = 4,
+ RXFILT_receiveAllFrames = 8,
+ RXFILT_receiveMulticastHash = 16
+};
+
+struct RegWindow5
+{
+ uint16_t TxStartThresh;
+ uint16_t r0;
+ uint16_t r1;
+ uint16_t RxEarlyThresh;
+ uint8_t RxFilter; // [1] p.112
+ uint8_t TxReclaimThresh;
+ uint16_t InterruptEnable; // [1] p.120
+ uint16_t IndicationEnable; // [1] p.120
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow5 RegWindow5;
+
+/*
+ * Window 6
+ */
+struct RegWindow6
+{
+ uint8_t CarrierLost;
+ uint8_t SqeErrors;
+ uint8_t MultipleCollisions;
+ uint8_t SingleCollisions;
+ uint8_t LateCollisions;
+ uint8_t RxOverruns;
+ uint8_t FramesXmittedOk;
+ uint8_t FramesRcvdOk;
+ uint8_t FramesDeferred;
+ uint8_t UpperFramesOk;
+ uint16_t BytesRcvdOk;
+ uint16_t BytesXmittedOk;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow6 RegWindow6;
+
+/*
+ * EEPROM
+ */
+enum EEPROMField
+{
+ EEPROM_NodeAddress0 = 0x00,
+ EEPROM_NodeAddress1 = 0x01,
+ EEPROM_NodeAddress2 = 0x02,
+ EEPROM_DeviceID = 0x03,
+ EEPROM_ManifacturerID = 0x07,
+ EEPROM_PCIParam = 0x08,
+ EEPROM_RomInfo = 0x09,
+ EEPROM_OEMNodeAddress0 = 0x0a,
+ EEPROM_OEMNodeAddress1 = 0x0b,
+ EEPROM_OEMNodeAddress2 = 0x0c,
+ EEPROM_SoftwareInfo = 0x0d,
+ EEPROM_CompWord = 0x0e,
+ EEPROM_SoftwareInfo2 = 0x0f,
+ EEPROM_Caps = 0x10,
+ EEPROM_InternalConfig0 = 0x12,
+ EEPROM_InternalConfig1 = 0x13,
+ EEPROM_SubsystemVendorID = 0x17,
+ EEPROM_SubsystemID = 0x18,
+ EEPROM_MediaOptions = 0x19,
+ EEPROM_SmbAddress = 0x1b,
+ EEPROM_PCIParam2 = 0x1c,
+ EEPROM_PCIParam3 = 0x1d,
+ EEPROM_Checksum = 0x20
+};
+
+/*
+ * Up/Downloading
+ */
+
+// must be on 8-uint8_t physical address boundary
+struct DPD0
+{
+ uint32_t DnNextPtr;
+ uint32_t FrameStartHeader;
+ /* DPDFragDesc Frags[n] */
+} PACKED8;
+typedef struct DPD0 DPD0;
+
+enum FrameStartHeaderBits
+{
+ FSH_rndupBndry = 3<<0,
+ FSH_pktId = 15<<2,
+ /* 12:10 unspecified */
+ FSH_crcAppendDisable = 1<<13,
+ FSH_txIndicate = 1<<15,
+ FSH_dnComplete = 1<<16,
+ FSH_reArmDisable = 1<<23,
+ FSH_lastKap = 1<<24,
+ FSH_addIpChecksum = 1<<25, /** TODO: write support for these */
+ FSH_addTcpChecksum = 1<<26,
+ FSH_addUdpChecksum = 1<<27,
+ FSH_rndupDefeat = 1<<28,
+ FSH_dpdEmpty = 1<<29,
+ /* 30 unspecified */
+ FSH_dnIndicate = 1<<31
+};
+
+// must be on 16-uint8_t physical address boundary
+struct DPD1
+{
+ uint32_t DnNextPtr;
+ uint32_t ScheduleTime;
+ uint32_t FrameStartHeader;
+ uint32_t res;
+ /* DPDFragDesc Frags[n] */
+} PACKED16;
+typedef struct DPD1 DPD1;
+
+struct DPDFragDesc
+{
+ uint32_t DnFragAddr;
+ uint32_t DnFragLen; // [12:0] fragLen, [31] lastFrag
+} PACKED;
+typedef struct DPDFragDesc DPDFragDesc;
+
+// must be on 8-uint8_t physical address boundary
+struct UPD
+{
+ uint32_t UpNextPtr;
+ uint32_t UpPktStatus;
+ /* UPDFragDesc Frags[n] */
+} PACKED8;
+typedef struct UPD UPD;
+
+struct UPDFragDesc
+{
+ uint32_t UpFragAddr;
+ uint32_t UpFragLen; // [12:0] fragLen, [31] lastFrag
+} PACKED;
+typedef struct UPDFragDesc UPDFragDesc;
+
+#define MAX_DPD_FRAGS 63
+#define MAX_UPD_FRAGS 63
+#define MAX_UPD_SIZE (sizeof(UPD) + sizeof(UPDFragDesc)*MAX_UPD_FRAGS) // 512
+
+enum UpPktStatusBits
+{
+ UPS_upPktLen = 0x1fff,
+ /* 13 unspecified */
+ UPS_upError = 1<<14,
+ UPS_upComplete = 1<<15,
+ UPS_upOverrun = 1<<16,
+ UPS_runtFrame = 1<<17,
+ UPS_alignmentError = 1<<18,
+ UPS_crcError = 1<<19,
+ UPS_oversizedFrame = 1<<20,
+ /* 22:21 unspecified */
+ UPS_dribbleBits = 1<<23,
+ UPS_upOverflow = 1<<24,
+ UPS_ipChecksumError = 1<<25,
+ UPS_tcpChecksumError = 1<<26,
+ UPS_udpChecksumError = 1<<27,
+ UPD_impliedBufferEnable = 1<<28,
+ UPS_ipChecksumChecked = 1<<29,
+ UPS_tcpChecksumChecked = 1<<30,
+ UPS_udpChecksumChecked = 1<<31
+};
+
+// IEEE 802.3 MAC, Ethernet-II
+struct EthFrameII
+{
+ uint8_t destMAC[6];
+ uint8_t srcMAC[6];
+ uint8_t type[2];
+} PACKED;
+typedef struct EthFrameII EthFrameII;
+
+struct A3C90XState
+{
+
+ uint16_t mEEPROM[0x40];
+ char mEEPROMWritable;
+ Registers mRegisters;
+ RegWindow mWindows[8];
+ uint16_t mIntStatus;
+ char mRxEnabled;
+ char mTxEnabled;
+ char mUpStalled;
+ char mDnStalled;
+ uint8_t mRxPacket[MAX_PACKET_SIZE];
+ uint32_t mRxPacketSize;
+ uint16_t mMIIRegs[8];
+ uint32_t mMIIReadWord;
+ uint64_t mMIIWriteWord;
+ uint32_t mMIIWrittenBits;
+ uint16_t mLastHiClkPhysMgmt;
+ uint8_t mMAC[6];
+
+
+ PCIDevice *pci_dev;
+ int a3c90x_mmio_io_addr;
+
+ VLANClientState *vc;
+
+};
+typedef struct A3C90XState A3C90XState;
+
+static void a3c90x_reset(A3C90XState *s);
+
+static void setCR(uint16_t cr, void *opaque);
+
+static uint32_t readRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size);
+static void writeRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size);
+
+static void checkDnWork(void *opaque);
+static void checkUpWork(void *opaque);
+
+static void txDPD0(void *opaque, DPD0 *dpd);
+static void rxUPD(void *opaque, UPD *upd);
+
+static void indicate(uint32_t indications, void *opaque);
+static void acknowledge(uint32_t indications, void *opaque);
+static void maybeRaiseIntr(void *opaque);
+
+static char passesRxFilter(uint8_t *pbuf, uint32_t psize, void *opaque);
+
+static void handle_rx(void *opaque, const uint8_t *buf, int size);
+
+static void a3c90x_cleanup(VLANClientState *vc);
+
+static int compareMACs(uint8_t a[6], uint8_t b[6]);
+
+/*
+ * misc
+ */
+static int compareMACs(uint8_t a[6], uint8_t b[6])
+{
+ uint32_t i;
+ for (i = 0; i < 6; i++)
+ {
+ if (a[i] != b[i]) return a[i] - b[i];
+ }
+ return 0;
+}
+
+static void a3c90x_reset(A3C90XState *s)
+{
+ /* FIXME: resetting can be done more fine-grained (see TotalReset cmd).
+ * this is reset ALL regs.
+ */
+ if (sizeof(Registers) != 0x70)
+ {
+ DEBUG_PRINT("sizeof Registers != 0x70\n");
+ }
+
+ RegWindow3 *w3 = (RegWindow3*) &(s->mWindows[3]);
+ RegWindow4 *w4 = (RegWindow4*) &(s->mWindows[4]);
+ RegWindow5 *w5 = (RegWindow5*) &(s->mWindows[5]);
+
+ // internals
+ s->mEEPROMWritable = 0;
+ memset(&(s->mWindows), 0, sizeof s->mWindows);
+ memset(&(s->mRegisters), 0, sizeof s->mRegisters);
+ s->mIntStatus = 0;
+ s->mRxEnabled = 0;
+ s->mTxEnabled = 0;
+ s->mUpStalled = 0;
+ s->mDnStalled = 0;
+ w3->MaxPktSize = 1514 /* FIXME: should depend on sizeof mRxPacket*/;
+ w3->RxFree = 16*1024;
+ w3->TxFree = 16*1024;
+ s->mRxPacketSize = 0;
+ w5->TxStartThresh = 8188;
+ memset(s->mEEPROM, 0, sizeof s->mEEPROM);
+ s->mEEPROM[EEPROM_NodeAddress0] = (s->mMAC[0]<<8) | s->mMAC[1];
+ s->mEEPROM[EEPROM_NodeAddress1] = (s->mMAC[2]<<8) | s->mMAC[3];
+ s->mEEPROM[EEPROM_NodeAddress2] = (s->mMAC[4]<<8) | s->mMAC[5];
+ s->mEEPROM[EEPROM_DeviceID] = 0x9200;
+ s->mEEPROM[EEPROM_ManifacturerID] = 0x6d50;
+ s->mEEPROM[EEPROM_PCIParam] = 0x2940;
+ s->mEEPROM[EEPROM_RomInfo] = 0; // no ROM
+ s->mEEPROM[EEPROM_OEMNodeAddress0] = s->mEEPROM[EEPROM_NodeAddress0];
+ s->mEEPROM[EEPROM_OEMNodeAddress1] = s->mEEPROM[EEPROM_NodeAddress1];
+ s->mEEPROM[EEPROM_OEMNodeAddress2] = s->mEEPROM[EEPROM_NodeAddress2];
+ s->mEEPROM[EEPROM_SoftwareInfo] = 0x4010;
+ s->mEEPROM[EEPROM_CompWord] = 0;
+ s->mEEPROM[EEPROM_SoftwareInfo2] = 0x00aa;
+ s->mEEPROM[EEPROM_Caps] = 0x72a2;
+ s->mEEPROM[EEPROM_InternalConfig0] = 0;
+ s->mEEPROM[EEPROM_InternalConfig1] = 0x0050; // default is 0x0180
+ s->mEEPROM[EEPROM_SubsystemVendorID] = 0x10b7;
+ s->mEEPROM[EEPROM_SubsystemID] = 0x9200;
+ s->mEEPROM[EEPROM_MediaOptions] = 0x000a;
+ s->mEEPROM[EEPROM_SmbAddress] = 0x6300;
+ s->mEEPROM[EEPROM_PCIParam2] = 0xffb7;
+ s->mEEPROM[EEPROM_PCIParam3] = 0xb7b7;
+ s->mEEPROM[EEPROM_Checksum] = 0;
+
+ // MII
+ memset(s->mMIIRegs, 0, sizeof s->mMIIRegs);
+ MIIRegisters *miiregs = (MIIRegisters*) s->mMIIRegs;
+ miiregs->status = (1<<14) | (1<<13) | (1<<12) | (1<<11) | (1<<5) | (1<<3) | (1<<2) | 1;
+ miiregs->linkPartner = (1<<14) | (1<<7) | 1;
+ miiregs->advert = (1<<14) | (1 << 10) | (1<<7) | 1;
+ s->mMIIReadWord = 0;
+ s->mMIIWriteWord = 0;
+ s->mMIIWrittenBits = 0;
+ s->mLastHiClkPhysMgmt = 0;
+
+ // Register follow-ups
+ w3->MediaOptions = s->mEEPROM[EEPROM_MediaOptions];
+ w3->InternalConfig = s->mEEPROM[EEPROM_InternalConfig0] |
+ (s->mEEPROM[EEPROM_InternalConfig1] << 16);
+
+ // A valid link is established on the NIC
+ w4->MediaStatus = (1 << 11) | 0x8000;
+
+ // And clean out the RX buffer
+ memset(s->mRxPacket, 0xab, MAX_PACKET_SIZE);
+}
+
+static uint32_t readRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size)
+{
+ DEBUG_PRINT("readRegWindow\n");
+ switch (window)
+ {
+ /* window 0 */
+ case 0:
+ {
+ RegWindow0 *w0 = (RegWindow0*) &s->mWindows[0];
+ switch (port)
+ {
+ case W0_EEPROMCmd:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("WARN: EepromCommand, size != 2\n");
+ return 0;
+ }
+ return w0->EepromCommand;
+ break;
+ }
+ case W0_EEPROMData:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("WARN: EepromData, size != 2\n");
+ return 0;
+ }
+ return w0->EepromData;
+ break;
+ }
+ default:
+ DEBUG_PRINT("WARN: reading here unimpl\n");
+ return 0;
+ break;
+ }
+ break;
+ }
+ /* window 1 */
+ case 1:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[1].b[port], size);
+ return data;
+ break;
+ }
+ /* window 2 */
+ case 2:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[2].b[port], size);
+ return data;
+ break;
+ }
+ /* window 3 */
+ case 3:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[3].b[port], size);
+ return data;
+ break;
+ }
+ /* window 4 */
+ case 4:
+ {
+ DEBUG_PRINT("Read from window 4\n");
+ RegWindow4 *w4 = (RegWindow4*) &s->mWindows[4];
+ data = 0;
+ switch (port)
+ {
+ case 8:
+ {
+ // MII-interface
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.8.read\n");
+ return 0;
+ }
+ char mgmtData = (s->mMIIReadWord & 0x80000000) ? 1 : 0;
+ if (mgmtData)
+ {
+ data = w4->PhysMgmt | PM_mgmtData;
+ }
+ else
+ {
+ data = w4->PhysMgmt & (~PM_mgmtData);
+ }
+ break;
+ }
+ case 0xc:
+ {
+ if (size != 1)
+ {
+ DEBUG_PRINT("alignment.4.c.read\n");
+ return 0;
+ }
+ // reading clears
+ w4->BadSSD = 0;
+ memcpy(&data, &s->mWindows[4].b[port], size);
+ return data;
+ break;
+ }
+ default:
+ memcpy(&data, &(s->mWindows[4].b[port]), size);
+ return data;
+ }
+ break;
+ }
+ /* Window 5 */
+ case 5:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[5].b[port], size);
+ return data;
+ break;
+ }
+ /* Window 6 */
+ case 6:
+ {
+ RegWindow6 *w6 = (RegWindow6*) &s->mWindows[6];
+ // reading clears
+ if ((port == 0xa) && (size == 2))
+ {
+ // FIXME: BytesRcvdOk really is 20 bits !
+ // when reading here, write upper 4 bits
+ // in w4.UpperBytesOk[3:0]. no clearing.
+ w6->BytesRcvdOk = 0;
+ }
+ else if ((port == 0xc) && (size == 2))
+ {
+ // FIXME: BytesXmittedOk really is 20 bits !
+ // when reading here, write upper 4 bits
+ // in w4.UpperBytesOk[7:4]. no clearing.
+ w6->BytesXmittedOk = 0;
+ }
+ else if ((port == 0) && (size == 1))
+ {
+ w6->CarrierLost = 0;
+ }
+ else if ((port == 8) && (size == 1))
+ {
+ w6->FramesDeferred = 0;
+ }
+ else if ((port == 7) && (size == 1))
+ {
+ // FIXME: FramesRcvdOk really is 10 bits !
+ // when reading here, write upper 2 bits
+ // in w6.UpperFramesOk[1:0]. no clearing.
+ }
+ else if ((port == 6) && (size == 1))
+ {
+ // FIXME: FramesXmittedOk really is 10 bits !
+ // when reading here, write upper 2 bits
+ // in w6.UpperFramesOk[5:4]. no clearing.
+ }
+ else if ((port == 4) && (size == 1))
+ {
+ w6->LateCollisions = 0;
+ }
+ else if ((port == 2) && (size == 1))
+ {
+ w6->MultipleCollisions = 0;
+ }
+ else if ((port == 5) && (size == 1))
+ {
+ w6->RxOverruns = 0;
+ }
+ else if ((port == 3) && (size == 1))
+ {
+ w6->SingleCollisions = 0;
+ }
+ else if ((port == 1) && (size == 1))
+ {
+ w6->SqeErrors = 0;
+ }
+ data = 0;
+ memcpy(&data, &s->mWindows[6].b[port], size);
+ return data;
+ break;
+ }
+ /* Window 7 */
+ case 7:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[7].b[port], size);
+ return data;
+ break;
+ }
+ default:
+ DEBUG_PRINT("reading here unimpl.\n");
+ }
+
+ return 0;
+}
+
+static void writeRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size)
+{
+ DEBUG_PRINT("writeRegWindow\n");
+ switch (window)
+ {
+ /* Window 0 */
+ case 0:
+ {
+ RegWindow0 *w0 = (RegWindow0*) &s->mWindows[0];
+ switch (port)
+ {
+ case W0_EEPROMCmd:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("EepromCommand, size != 2\n");
+ return;
+ }
+ w0->EepromCommand = data & 0xff7f; // clear eepromBusy
+ uint32_t eeprom_addr = ((data >> 2) & 0xffc0) | (data & 0x3f);
+ switch (data & 0xc0)
+ {
+ case EEOP_SubCmd:
+ switch (data & 0x30)
+ {
+ case EESC_WriteDisable:
+ DEBUG_PRINT("EESC_WriteDisable\n");
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_WriteAll:
+ // FIXME: this needs fixing :)
+ DEBUG_PRINT("WriteAll not impl.\n");
+ memset(s->mEEPROM, 0xff, sizeof s->mEEPROM);
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_EraseAll:
+ DEBUG_PRINT("EraseAll not impl.\n");
+ // SINGLESTEP("");
+ memset(s->mEEPROM, 0, sizeof s->mEEPROM);
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_WriteEnable:
+ DEBUG_PRINT("EESC_WriteEnable\n");
+ s->mEEPROMWritable = 0;
+ break;
+ default:
+ DEBUG_PRINT("impossible\n");
+ // SINGLESTEP("");
+ }
+ break;
+ case EEOP_WriteReg:
+ if (s->mEEPROMWritable)
+ {
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ // disabled
+ DEBUG_PRINT("EEOP_WriteReg\n");
+ // SINGLESTEP("");
+ s->mEEPROM[eeprom_addr] = w0->EepromData;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_WriteReg\n");
+ }
+ s->mEEPROMWritable = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(not writable): EEOP_WriteReg\n");
+ }
+ break;
+ case EEOP_ReadReg:
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ w0->EepromData = s->mEEPROM[eeprom_addr];
+ DEBUG_PRINT("EEOP_ReadReg\n");
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_ReadReg\n");
+ }
+ break;
+ case EEOP_EraseReg:
+ if (s->mEEPROMWritable)
+ {
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ // disabled
+ DEBUG_PRINT("EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ s->mEEPROM[eeprom_addr] = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ }
+ s->mEEPROMWritable = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(not writable): EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ }
+ break;
+ default:
+ DEBUG_PRINT("impossible\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ case W0_EEPROMData:
+ if (size != 2)
+ {
+ DEBUG_PRINT("EepromData, size != 2\n");
+ // SINGLESTEP("");
+ }
+ w0->EepromData = data;
+ break;
+ default:
+ DEBUG_PRINT("writing here unimpl.0\n");
+ // SINGLESTEP("");
+ break;
+ }
+ break;
+ }
+ /* Window 2 */
+ case 2:
+ {
+ if (port+size<=0xc)
+ {
+ DEBUG_PRINT("StationAddress or StationMask\n");
+ /* StationAddress or StationMask */
+ memcpy(&s->mWindows[2].b[port], &data, size);
+ }
+ else
+ {
+ DEBUG_PRINT("writing here unimpl.2\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ /* Window 3 */
+ case 3:
+ {
+ RegWindow3 *w3 = (RegWindow3*) &s->mWindows[3];
+ switch (port)
+ {
+ case 0:
+ if (size != 4)
+ {
+ DEBUG_PRINT("alignment.3.0\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("InternalConfig\n");
+ w3->InternalConfig = data;
+ break;
+ case 4:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.4\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("ERR: MaxPktSize\n");
+ w3->MaxPktSize = data;
+ break;
+ case 6:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.6\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("MacControl\n");
+ if (data != 0)
+ {
+ DEBUG_PRINT("setting MacControl != 0\n");
+ // SINGLESTEP("");
+ }
+ w3->MacControl = data;
+ break;
+ case 8:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.8\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("MediaOptions\n");
+ w3->MediaOptions = data;
+ break;
+ case 10:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.10\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("RxFree\n");
+ // SINGLESTEP("");
+ w3->RxFree = data;
+ break;
+ case 12:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.12\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("TxFree\n");
+ // SINGLESTEP("");
+ w3->TxFree = data;
+ break;
+ default:
+ DEBUG_PRINT("writing here unimpl.3\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ /* Window 4 */
+ case 4:
+ {
+ RegWindow4 *w4 = (RegWindow4*) &s->mWindows[4];
+ switch (port)
+ {
+ case 6:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.6\n");
+ // SINGLESTEP("");
+ }
+ uint32_t mask = 0xf341;
+ DEBUG_PRINT("NetDiagnostic");
+ w4->NetDiagnostic &= ~mask;
+ w4->NetDiagnostic |= data & mask;
+ break;
+ }
+ case 8:
+ {
+ // MII-interface
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.8\n");
+ // SINGLESTEP("");
+ }
+ char hiClk = (!((w4->PhysMgmt & PM_mgmtClk) && (data & PM_mgmtClk))) ? 1 : 0;
+ if (hiClk)
+ {
+ // Z means lo edge of mgmtDir
+ char Z = ((s->mLastHiClkPhysMgmt & PM_mgmtDir) && !(data & PM_mgmtDir)) ? 1 : 0;
+ if (Z)
+ {
+ // check if the 5 frames have been sent
+ if (((s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2)) & 0x3ffffffffULL) == 0x3fffffffdULL)
+ {
+ uint32_t opcode = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2)) & 3;
+ uint32_t PHYaddr = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2-5)) & 0x1f;
+ uint32_t REGaddr = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2-5-5)) & 0x1f;
+ if ((PHYaddr == 0x18 /* hardcoded address [1] p.196 */)
+ && (REGaddr < 0x10))
+ {
+ switch (opcode)
+ {
+ case 1:
+ {
+ // Opcode Write
+ DEBUG_PRINT("Opcode Write\n");
+ if (s->mMIIWrittenBits == 64)
+ {
+ uint32_t value = s->mMIIWriteWord & 0xffff;
+ s->mMIIRegs[REGaddr] = value;
+ }
+ else
+ {
+ DEBUG_PRINT("But invalid write count\n");
+ }
+ s->mMIIWriteWord = 0;
+ break;
+ }
+ case 2:
+ {
+ // Opcode Read
+ DEBUG_PRINT("Opcode Read\n");
+ if (s->mMIIWrittenBits == 32+2+2+5+5)
+ {
+ // msb gets sent first and is zero to indicated success
+ // the register to be sent follows msb to lsb
+ s->mMIIReadWord = s->mMIIRegs[REGaddr] << 15;
+ }
+ else
+ {
+ DEBUG_PRINT("But invalid write count\n");
+ }
+ s->mMIIWriteWord = 0;
+ break;
+ }
+ default:
+ // error
+ DEBUG_PRINT("Invalid opcode\n");
+ s->mMIIReadWord = 0xffffffff;
+ }
+ }
+ else
+ {
+ // error
+ DEBUG_PRINT("Invalid PHY or REG\n");
+ s->mMIIReadWord = 0xffffffff;
+ }
+ }
+ s->mMIIWrittenBits = 0;
+ w4->PhysMgmt = data;
+ }
+ else if (data & PM_mgmtDir)
+ {
+ // write
+ char mgmtData = (data & PM_mgmtData) ? 1 : 0;
+ w4->PhysMgmt = data;
+ s->mMIIWriteWord <<= 1;
+ s->mMIIWriteWord |= mgmtData ? 1 : 0;
+ s->mMIIWrittenBits++;
+ }
+ else
+ {
+ // read
+ char mgmtData = (s->mMIIReadWord & 0x80000000) ? 1 : 0;
+ w4->PhysMgmt = data;
+ if (mgmtData)
+ {
+ w4->PhysMgmt = w4->PhysMgmt | PM_mgmtData;
+ }
+ else
+ {
+ w4->PhysMgmt = w4->PhysMgmt & (~PM_mgmtData);
+ }
+ s->mMIIReadWord <<= 1;
+ }
+ s->mLastHiClkPhysMgmt = w4->PhysMgmt;
+ }
+ else
+ {
+ w4->PhysMgmt = data;
+ }
+ break;
+ }
+ case 10:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.10\n");
+ // SINGLESTEP("");
+ }
+ uint32_t mask = 0x10cc;
+ DEBUG_PRINT("MediaStatus\n");
+ w4->MediaStatus &= ~mask;
+ w4->MediaStatus |= data & mask;
+ w4->MediaStatus |= 0x8000; // auiDisable always on
+ break;
+ }
+ default:
+ DEBUG_PRINT("generic to window 4\n");
+ // SINGLESTEP("");
+ memcpy(&s->mWindows[4].b[port], &data, size);
+ }
+ break;
+ }
+ /**/
+ default:
+ DEBUG_PRINT("writing here unimpl.\n");
+ // SINGLESTEP("");
+ }
+}
+
+static void setCR(uint16_t cr, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT("setCR\n");
+ switch (cr & (31<<11))
+ {
+ case CmdTotalReset:
+ // FIXME: care about params
+ DEBUG_PRINT("TotalReset\n");
+ a3c90x_reset(opaque);
+ break;
+ case CmdSelectWindow:
+ {
+ DEBUG_PRINT("SelectWindow\n");
+ s->mIntStatus &= 0x1fff;
+ s->mIntStatus |= (cr & 7)<<13;
+ break;
+ }
+ case CmdTxReset:
+ DEBUG_PRINT("TxReset\n");
+ break;
+ case CmdRxReset:
+ DEBUG_PRINT("RxReset\n");
+ break;
+ case CmdSetIndicationEnable:
+ {
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ DEBUG_PRINT("SetIndicationEnable\n");
+ w5->IndicationEnable = cr & 0x7fe;
+ break;
+ }
+ case CmdSetIntrEnb:
+ {
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ DEBUG_PRINT("SetIntrEnab\n");
+ w5->InterruptEnable = cr & 0x7fe;
+ break;
+ }
+ case CmdStatsEnable:
+ /* implement me */
+ DEBUG_PRINT("StatsEnable\n");
+ break;
+ case CmdStatsDisable:
+ /* implement me */
+ DEBUG_PRINT("StatsDisable\n");
+ break;
+ case CmdEnableDC:
+ /* implement me */
+ DEBUG_PRINT("EnableDC\n");
+ break;
+ case CmdDisableDC:
+ /* implement me */
+ DEBUG_PRINT("DisableDC\n");
+ break;
+ case CmdStall:
+ {
+ /* FIXME: threading */
+ switch (cr & 3)
+ {
+ case 0: /* UpStall */
+ case 1: /* UpUnstall */
+ {
+ DEBUG_PRINT("Stall\n");
+ char stall = (!(cr & 1)) ? 1 : 0;
+ s->mUpStalled = stall;
+ checkUpWork(opaque);
+ break;
+ }
+ case 2: /* DnStall */
+ case 3: /* DnUnstall */
+ {
+ DEBUG_PRINT("Stall\n");
+ char stall = (!(cr & 1)) ? 1 : 0;
+ s->mDnStalled = stall;
+ s->mRegisters.DmaCtrl &= ~DC_dnStalled;
+ if (stall) s->mRegisters.DmaCtrl |= DC_dnStalled;
+ checkDnWork(opaque);
+ break;
+ }
+ }
+ break;
+ }
+ case CmdSetRxFilter:
+ {
+ DEBUG_PRINT("SetRxFilter\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->RxFilter = cr & 31;
+ break;
+ }
+ case CmdSetTxReclaimThresh:
+ {
+ DEBUG_PRINT("SetTxReclaimHash\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->TxReclaimThresh = cr & 255;
+ break;
+ }
+ case CmdSetTxStartThresh:
+ {
+ DEBUG_PRINT("SetTxStartTresh\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->TxStartThresh = (cr & 0x7ff) << 2;
+ break;
+ }
+ case CmdSetHashFilterBit:
+ {
+ /** TODO: implement */
+ // char value = (cr & 0x400) ? 1 : 0;
+ // uint32_t which = cr & 0x3f;
+ DEBUG_PRINT("SetHashFilterBit\n");
+ break;
+ }
+ case CmdSetRxEarlyThresh:
+ {
+ DEBUG_PRINT("SetTxStartTresh\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->RxEarlyThresh = (cr & 0x7ff) << 2;
+ break;
+ }
+ case CmdRxEnable:
+ {
+ DEBUG_PRINT("RxEnable\n");
+ s->mRxEnabled = 1;
+ break;
+ }
+ case CmdRxDisable:
+ {
+ DEBUG_PRINT("RxDisable\n");
+ s->mRxEnabled = 0;
+ break;
+ }
+ case CmdTxEnable:
+ {
+ DEBUG_PRINT("TxEnable\n");
+ s->mTxEnabled = 1;
+ break;
+ }
+ case CmdTxDisable:
+ {
+ DEBUG_PRINT("TxDisable\n");
+ s->mTxEnabled = 0;
+ break;
+ }
+ case CmdAckIntr:
+ {
+ /*
+ 0x1 interruptLatchAck
+ 0x2 linkEventAck
+ 0x20 rxEarlyAck
+ 0x40 intRequestedAck
+ 0x200 dnCompleteAck
+ 0x400 upCompleteAck
+
+ 0x5
+ */
+ DEBUG_PRINT("AckIntr\n");
+ // ack/clear corresponding bits in IntStatus
+ uint32_t ISack = 0;
+ if (cr & 0x01) ISack |= IS_interruptLatch;
+ if (cr & 0x02) ISack |= IS_linkEvent;
+ if (cr & 0x20) ISack |= IS_rxEarly;
+ if (cr & 0x40) ISack |= IS_intRequested;
+ if (cr & 0x200) ISack |= IS_dnComplete;
+ if (cr & 0x400) ISack |= IS_upComplete;
+ acknowledge(ISack, opaque);
+ break;
+ }
+ /* case CmdReqIntr: {
+ RegWindow5 &w5 = (RegWindow5&)mWindows[5];
+ // set intRequested in IntStatus
+ mIntStatus |= IS_intRequested;
+
+ // FIXME: generate Interrupt (if enabled)
+ break;
+ }*/
+
+ /*
+ case CmdTxDone:
+ case CmdRxDiscard:
+ case CmdSetTxThreshold:
+ */
+ default:
+ DEBUG_PRINT("command not implemented\n");
+ }
+}
+
+static void txDPD0(void *opaque, DPD0 *dpd)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT("txDPD0\n");
+
+ // FIXME: createHostStruct()
+ uint32_t fsh = dpd->FrameStartHeader;
+ DEBUG_PRINT_FORMAT(("fsh = %08x\n", fsh));
+ if (fsh & FSH_dpdEmpty)
+ {
+ // modify FrameStartHeader in DPD (!)
+ dpd->FrameStartHeader |= FSH_dnComplete;
+ // set next DnListPtr
+ s->mRegisters.DnListPtr = dpd->DnNextPtr;
+ DEBUG_PRINT("dpd empty\n");
+ return;
+ }
+ DPDFragDesc *frags = (DPDFragDesc*)(dpd+1);
+ uint8_t pbuf[MAX_PACKET_SIZE];
+ uint8_t *p = pbuf;
+
+ // some packet drivers need padding
+ // uint framePrefix = mEthTun->getWriteFramePrefix();
+ // memset(p, 0, framePrefix);
+ // p += framePrefix;
+
+ DEBUG_PRINT_FORMAT(("DPD: NextPtr = %x, FSH = %x, FragAddr = %x, FragLen = %x\n",
+ dpd->DnNextPtr, dpd->FrameStartHeader, frags->DnFragAddr, frags->DnFragLen));
+
+ //
+ uint32_t i = 0;
+ // assemble packet from fragments (up to MAX_DPD_FRAGS fragments)
+ while (i < MAX_DPD_FRAGS)
+ {
+ uint32_t addr = frags->DnFragAddr;
+ uint32_t len = frags->DnFragLen & 0x1fff;
+ if (p-pbuf+len >= sizeof pbuf)
+ {
+ DEBUG_PRINT("packet too big!\n");
+ // SINGLESTEP("");
+ return;
+ }
+ DEBUG_PRINT("dma_read\n");
+
+ if (len == 0 || addr == 0)
+ {
+ DEBUG_PRINT("No data! Bail!\n");
+ return;
+ }
+
+ cpu_physical_memory_read(addr, p, len);
+
+ DEBUG_PRINT_FORMAT((" - DnAddr = %x, DnFragLen = %x\n", frags->DnFragAddr, frags->DnFragLen));
+
+ p += len;
+ // last fragment ?
+ if (frags->DnFragLen & 0x80000000) break;
+ frags++;
+ i++;
+ }
+ uint32_t psize = p-pbuf;
+ if (!(fsh & FSH_rndupDefeat))
+ {
+ // round packet length
+ switch (fsh & FSH_rndupBndry)
+ {
+ case 0:
+ {
+ // 4 bytes
+ uint32_t gap = ((psize+3) & ~3) -psize;
+ memset(pbuf+psize, 0, gap);
+ psize += gap;
+ break;
+ }
+ case 2:
+ {
+ // 2 bytes
+ uint32_t gap = ((psize+1) & ~1) -psize;
+ memset(pbuf+psize, 0, gap);
+ psize += gap;
+ break;
+ }
+ }
+ }
+ //FSH_reArmDisable = 1<<23,
+ //FSH_lastKap = 1<<24,
+ //FSH_addIpChecksum = 1<<25,
+ //FSH_addTcpChecksum = 1<<26,
+ //FSH_addUdpChecksum = 1<<27,
+ if (fsh & (0x1f << 23))
+ {
+ DEBUG_PRINT("unsupported flags in fsh\n");
+ }
+
+ if (psize<60)
+ {
+ // pad packet to at least 60 bytes (+4 bytes crc = 64 bytes)
+ memset(pbuf+psize, 0, (60-psize));
+ psize = 60;
+ }
+ // append crc
+ if (!(fsh & FSH_crcAppendDisable))
+ {
+#ifdef A3C90x_CALCULATE_TXCRC
+ uint32_t crc = crc32(0, pbuf, psize);
+#else
+ uint32_t crc = 0;
+#endif
+ pbuf[psize+0] = crc;
+ pbuf[psize+1] = crc>>8;
+ pbuf[psize+2] = crc>>16;
+ pbuf[psize+3] = crc>>24;
+ psize += 4;
+ DEBUG_PRINT("crc complete\n");
+ }
+
+ AUX_DEBUG_PRINT("3C90X: Packet sent\n");
+
+ qemu_send_packet(s->vc, pbuf, psize);
+
+ // indications
+ s->mRegisters.DmaCtrl |= DC_dnComplete;
+ uint8_t txStatus = 0;
+ uint32_t inds = 0;
+ if (fsh & FSH_dnIndicate) inds |= IS_dnComplete;
+ if (fsh & FSH_txIndicate)
+ {
+ inds |= IS_txComplete;
+ txStatus |= (1 << 6);
+ }
+
+ // transmit complete
+ txStatus |= (1 << 7);
+
+ indicate(inds, opaque);
+ // modify FrameStartHeader in DPD (!)
+ dpd->FrameStartHeader |= FSH_dnComplete;
+ // set next DnListPtr, TxPktId
+ s->mRegisters.DnListPtr = dpd->DnNextPtr;
+ uint32_t pktId = (fsh & FSH_pktId) >> 2;
+ s->mRegisters.TxPktId = pktId;
+ s->mRegisters.TxStatus = txStatus;
+ // maybe generate interrupt
+ maybeRaiseIntr(opaque);
+}
+
+static char passesRxFilter(uint8_t *pbuf, uint32_t psize, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ EthFrameII *f = (EthFrameII*) pbuf;
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ if (w5->RxFilter & RXFILT_receiveAllFrames) return 1;
+ // FIXME: Multicast hashing not implemented
+ if (w5->RxFilter & RXFILT_receiveMulticastHash) return 1;
+ // FIXME: Multicasting not understood
+ if (w5->RxFilter & RXFILT_receiveMulticast) return 1;
+ if (w5->RxFilter & RXFILT_receiveBroadcast)
+ {
+ uint8_t broadcastMAC[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+ if (compareMACs(f->destMAC, broadcastMAC) == 0) return 1;
+ }
+ if (w5->RxFilter & RXFILT_receiveIndividual)
+ {
+ uint8_t destMAC[6];
+ uint8_t thisMAC[6];
+ RegWindow2 *w2 = (RegWindow2*) &s->mWindows[2];
+ uint32_t i;
+ for (i = 0; i < 6; i++)
+ {
+ destMAC[i] = f->destMAC[i] & ~w2->StationMask[i];
+ thisMAC[i] = w2->StationAddress[i] & ~w2->StationMask[i];
+ }
+ return (compareMACs(destMAC, thisMAC) == 0) ? 1 : 0;
+ }
+ return 0;
+}
+
+static void rxUPD(void *opaque, UPD *upd)
+{
+ A3C90XState *s = opaque;
+
+ // FIXME: threading to care about (mRegisters.DmaCtrl & DC_upAltSeqDisable)
+ DEBUG_PRINT("rxUPD()\n");
+
+ char error = 0;
+
+ if (upd->UpPktStatus & UPS_upComplete)
+ {
+ // IO_3C90X_WARN("UPD already upComplete!\n");
+
+ // the top of the ring buffer is already used,
+ // stall the upload and throw away the packet.
+ // the ring buffers are filled.
+
+ s->mUpStalled = 1;
+ return;
+ }
+
+ uint32_t upPktStatus = 0;
+
+ if (s->mRegisters.UpPoll)
+ {
+ DEBUG_PRINT("UpPoll unsupported\n");
+ // SINGLESTEP("");
+ return;
+ }
+ // FIXME:
+// if (mRegisters.DmaCtrl & DC_upRxEarlyEnable)
+// IO_3C90X_ERR("DC_upRxEarlyEnable unsupported\n");
+
+ if ((s->mRxPacketSize > 0x1fff) || (s->mRxPacketSize > sizeof s->mRxPacket))
+ {
+ DEBUG_PRINT("oversized frame\n");
+ upd->UpPktStatus = UPS_upError | UPS_oversizedFrame;
+ error = 1;
+ }
+
+ if (s->mRxPacketSize < 60)
+ {
+ // pad packet to at least 60 bytes (+4 bytes crc = 64 bytes)
+ memset(s->mRxPacket+s->mRxPacketSize, 0, (60-s->mRxPacketSize));
+ s->mRxPacketSize = 60;
+ }
+
+ /* RegWindow5 &w5 = (RegWindow5&)mWindows[5];
+ if ((mRxPacketSize < 60) && (w5.RxEarlyThresh >= 60)) {
+ IO_3C90X_TRACE("runt frame\n");
+ upPktStatus |= UPS_upError | UPS_runtFrame;
+ upd->UpPktStatus = upPktStatus;
+ error = true;
+ }*/
+ if (upd->UpPktStatus & UPD_impliedBufferEnable)
+ {
+ DEBUG_PRINT("UPD_impliedBufferEnable unsupported\n");
+ // SINGLESTEP("");
+ return;
+ }
+ UPDFragDesc *frags = (UPDFragDesc*)(upd+1);
+
+ uint8_t *p = s->mRxPacket;
+ uint32_t i = 0;
+ while (!error && i < MAX_UPD_FRAGS) // (up to MAX_UPD_FRAGS fragments)
+ {
+ uint32_t addr = frags->UpFragAddr;
+ uint32_t len = frags->UpFragLen & 0x1fff;
+ if (p-s->mRxPacket+len > sizeof s->mRxPacket)
+ {
+ upPktStatus |= UPS_upError | UPS_upOverflow;
+ upd->UpPktStatus = upPktStatus;
+ DEBUG_PRINT("UPD overflow!\n");
+ // SINGLESTEP("");
+ error = 1;
+ break;
+ }
+
+ cpu_physical_memory_write(addr, p, len);
+
+ p += len;
+ // last fragment ?
+ if (frags->UpFragLen & 0x80000000) break;
+ frags++;
+ i++;
+ }
+
+ if (!error)
+ {
+ DEBUG_PRINT("successfully uploaded packet\n");
+ }
+ upPktStatus |= s->mRxPacketSize & 0x1fff;
+ upPktStatus |= UPS_upComplete;
+ upd->UpPktStatus = upPktStatus;
+
+ s->mRxPacketSize = 0;
+
+ /* The client OS is waiting for a change in status, but won't see it
+ * until we dma our local copy upd->UpPktStatus back to the client address space
+ */
+ cpu_physical_memory_write(s->mRegisters.UpListPtr + 4, (const uint8_t*) &(upd->UpPktStatus), sizeof(upd->UpPktStatus));
+
+ s->mRegisters.UpListPtr = upd->UpNextPtr;
+
+ // Indications
+ s->mRegisters.DmaCtrl |= DC_upComplete;
+ indicate(IS_upComplete, opaque);
+ maybeRaiseIntr(opaque);
+}
+
+static void indicate(uint32_t indications, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ if ((w5->IndicationEnable & indications) != indications)
+ {
+ DEBUG_PRINT("some masked\n");
+ }
+ s->mIntStatus |= w5->IndicationEnable & indications;
+ if (indications & IS_upComplete)
+ {
+ s->mRegisters.DmaCtrl |= DC_upComplete;
+ }
+ if (indications & IS_dnComplete)
+ {
+ s->mRegisters.DmaCtrl |= DC_dnComplete;
+ }
+}
+
+static void acknowledge(uint32_t indications, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT_FORMAT(("intStatus was %x [indications=%x]\n", s->mIntStatus, indications));
+ s->mIntStatus &= ~indications;
+ DEBUG_PRINT_FORMAT(("intStatus is now %x\n", s->mIntStatus));
+ if (indications & IS_upComplete)
+ {
+ s->mRegisters.DmaCtrl &= ~DC_upComplete;
+ }
+ if (indications & IS_dnComplete)
+ {
+ s->mRegisters.DmaCtrl &= ~DC_dnComplete;
+ }
+
+ // lower the irq line now that the IRQ is ack'd
+ qemu_set_irq(s->pci_dev->irq[0], 0);
+}
+
+static void maybeRaiseIntr(void *opaque)
+{
+ A3C90XState *s = opaque;
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+
+ DEBUG_PRINT("maybeRaiseIntr\n");
+ DEBUG_PRINT_FORMAT(("IndEnable = %x, IntEnable = %x, IntStatus = %x\n", w5->IndicationEnable, w5->InterruptEnable, s->mIntStatus));
+
+ if (w5->IndicationEnable & w5->InterruptEnable & s->mIntStatus)
+ {
+ s->mIntStatus |= IS_interruptLatch;
+
+ DEBUG_PRINT("Generating interrupt!\n");
+
+ // raise the IRQ line
+ qemu_set_irq(s->pci_dev->irq[0], 1);
+ }
+ else
+ {
+ // lower the IRQ line
+ qemu_set_irq(s->pci_dev->irq[0], 0);
+ }
+}
+
+static void checkDnWork(void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ while (!s->mDnStalled && (s->mRegisters.DnListPtr != 0))
+ {
+ uint8_t dpd[512];
+
+ cpu_physical_memory_read(s->mRegisters.DnListPtr, dpd, sizeof dpd);
+
+ uint8_t type = dpd[7] >> 6;
+ switch (type)
+ {
+ case 0:
+ case 2:
+ {
+ DPD0 *p = (DPD0*) dpd;
+ DEBUG_PRINT("Got a type 0 DPD!\n");
+ txDPD0(opaque, p);
+ break;
+ }
+ case 1:
+ {
+ DEBUG_PRINT("Got a type 1 DPD! Not implemented!\n");
+ s->mRegisters.DnListPtr = 0;
+ break;
+ }
+ default:
+ DEBUG_PRINT("Unsupported packet type\n");
+ s->mRegisters.DnListPtr = 0;
+ break;
+ };
+
+ break;
+ }
+}
+
+static void checkUpWork(void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ if (s->mRxEnabled && !s->mUpStalled && s->mRxPacketSize && (s->mRegisters.UpListPtr != 0))
+ {
+ uint8_t upd[MAX_UPD_SIZE];
+
+ cpu_physical_memory_read(s->mRegisters.UpListPtr, upd, sizeof upd);
+ UPD *p = (UPD*) upd;
+ rxUPD(opaque, p);
+
+ }
+ else
+ {
+ DEBUG_PRINT("Not uploading\n");
+ DEBUG_PRINT_FORMAT(("rxEnabled = %x, upStalled = %x, RX Packet size = %x, Up list ptr = %x\n",
+ s->mRxEnabled, s->mUpStalled, s->mRxPacketSize, s->mRegisters.UpListPtr));
+ }
+}
+
+static void handle_rx(void *opaque, const uint8_t *buf, int size)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT_FORMAT(("3c90x: handle_rx (%d bytes)\n", size));
+ if (s->mRxPacketSize)
+ {
+ DEBUG_PRINT("Old packet not yet uploaded!\n");
+ }
+ else
+ {
+ s->mRxPacketSize = size;
+ memcpy(s->mRxPacket, buf, size);
+ if (s->mRxEnabled && (s->mRxPacketSize > sizeof(EthFrameII)))
+ {
+ indicate(IS_rxComplete, opaque);
+ maybeRaiseIntr(opaque);
+ acknowledge(IS_rxComplete, opaque);
+ if (!passesRxFilter(s->mRxPacket, s->mRxPacketSize, opaque))
+ {
+ DEBUG_PRINT_FORMAT(("Received %d bytes, but they don't pass the filter!\n", s->mRxPacketSize));
+ s->mRxPacketSize = 0;
+ }
+ else
+ {
+ // and now, we do some extra debugging output...
+#ifdef DEBUG_3C90X_ANALYSE_FRAMES
+ EthFrameII *ethFrame = (EthFrameII*) s->mRxPacket;
+ AUX_DEBUG_PRINT_FORMAT(("Incoming packet information [%d bytes]:\n", s->mRxPacketSize));
+ if (ethFrame->type[0] == 8)
+ {
+ if (ethFrame->type[1] == 0x06)
+ AUX_DEBUG_PRINT("ARP\n");
+ else if (ethFrame->type[1] == 0)
+ {
+ struct ip *ipHeader = (struct ip*) (s->mRxPacket + sizeof(EthFrameII));
+ if (ipHeader->ip_p == 0x11)
+ {
+ struct udphdr *udpHeader = (struct udphdr*) (s->mRxPacket + sizeof(EthFrameII) + (ipHeader->ip_hl * 4));
+ AUX_DEBUG_PRINT_FORMAT(("UDP: src=%d dest=%d\n", ntohs(udpHeader->uh_sport), ntohs(udpHeader->uh_dport)));
+ }
+ else if (ipHeader->ip_p == 0x06)
+ {
+ struct tcphdr *tcpHeader = (struct tcphdr*) (s->mRxPacket + sizeof(EthFrameII) + (ipHeader->ip_hl * 4));
+ AUX_DEBUG_PRINT_FORMAT(("TCP: src=%d dest=%d ", ntohs(tcpHeader->th_sport), ntohs(tcpHeader->th_dport)));
+ AUX_DEBUG_PRINT("flags =");
+ if (tcpHeader->th_flags & TH_FIN)
+ AUX_DEBUG_PRINT(" FIN");
+ if (tcpHeader->th_flags & TH_SYN)
+ AUX_DEBUG_PRINT(" SYN");
+ if (tcpHeader->th_flags & TH_RST)
+ AUX_DEBUG_PRINT(" RST");
+ if (tcpHeader->th_flags & TH_PUSH)
+ AUX_DEBUG_PRINT(" PSH");
+ if (tcpHeader->th_flags & TH_ACK)
+ AUX_DEBUG_PRINT(" ACK");
+ if (tcpHeader->th_flags & TH_URG)
+ AUX_DEBUG_PRINT(" URG");
+ AUX_DEBUG_PRINT_FORMAT((" seq=%d ack=%d", ntohl(tcpHeader->th_seq), ntohl(tcpHeader->th_ack)));
+ AUX_DEBUG_PRINT("\n");
+ }
+ }
+ }
+ else
+ AUX_DEBUG_PRINT("(can't inspect)\n");
+#endif
+ DEBUG_PRINT_FORMAT(("Received %d bytes!\n", s->mRxPacketSize));
+ }
+ }
+ else
+ {
+ DEBUG_PRINT_FORMAT(("Oops - RxEnabled = %x, packetSize = %d [eth=%d]\n", s->mRxEnabled, s->mRxPacketSize, sizeof(EthFrameII)));
+ s->mRxPacketSize = 0;
+ }
+ }
+ checkUpWork(opaque);
+}
+
+static int a3c90x_can_receive(void *opaque)
+{
+ A3C90XState *s = opaque;
+ if (s->mRxEnabled)
+ {
+ if (s->mRxPacketSize)
+ {
+ // If there's already a packet there, try and upload it again
+ DEBUG_PRINT("Old packet not yet uploaded!\n");
+ checkUpWork(opaque);
+ return 0;
+ }
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void a3c90x_receive(void *opaque, const uint8_t *buf, int size)
+{
+ AUX_DEBUG_PRINT("3C90X: Packet received\n");
+ handle_rx(opaque, buf, size);
+}
+
+static uint32_t a3c90x_io_readx(void *opaque, uint8_t port, int size)
+{
+ A3C90XState *s = opaque;
+ uint32_t data = 0;
+
+ if (port == 0xe)
+ {
+ // IntStatus (no matter which window)
+ if (size != 2)
+ {
+ DEBUG_PRINT("unaligned read from IntStatus\n");
+ }
+ DEBUG_PRINT("read IntStatus\n");
+ return s->mIntStatus;
+ }
+ else if (port >= 0 && (port+size <= 0x0e))
+ {
+ // read from window
+ uint32_t curwindow = s->mIntStatus >> 13;
+ return readRegWindow(opaque, curwindow, port, data, size);
+ }
+ else if ((port+size > 0x1e) && (port <= 0x1f))
+ {
+ if ((port != 0x1e) || (size != 2))
+ {
+ DEBUG_PRINT("unaligned read from IntStatusAuto\n");
+ }
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ // side-effects of reading IntStatusAuto:
+ // 1.clear InterruptEnable
+ w5->InterruptEnable = 0;
+ // 2.clear some flags
+ acknowledge(IS_dnComplete | IS_upComplete
+ | IS_rxEarly | IS_intRequested
+ | IS_interruptLatch | IS_linkEvent, opaque);
+ DEBUG_PRINT("read IntStatusAuto\n");
+ return s->mIntStatus;
+ }
+ else if ((port >= 0x10) && (port+size <= 0x10 + sizeof(Registers)))
+ {
+ uint8_t l = gRegAccess[port-0x10];
+ if (l != size)
+ {
+ DEBUG_PRINT("invalid/unaligned read\n");
+ }
+ // read from (standard) register
+ memcpy(&data, ((uint8_t*)&s->mRegisters)+port-0x10, size);
+ switch (port)
+ {
+ case 0x1a:
+ DEBUG_PRINT("read Timer\n");
+ break;
+ case 0x20:
+ DEBUG_PRINT("read DmaCtrl\n");
+ break;
+ case 0x24:
+ DEBUG_PRINT("read DownListPtr\n");
+ break;
+ case 0x38:
+ DEBUG_PRINT("read UpListPtr\n");
+ break;
+ default:
+ DEBUG_PRINT("read reg\n");
+ break;
+ }
+ return data;
+ }
+ return 0;
+}
+
+static void a3c90x_io_writex(void *opaque, uint8_t port, uint32_t data, int size)
+{
+ A3C90XState *s = opaque;
+
+ if (port == 0xe)
+ {
+ // CommandReg (no matter which window)
+ if (size != 2)
+ {
+ DEBUG_PRINT("unaligned write to CommandReg\n");
+ }
+ setCR(data, opaque);
+ }
+ else if (port >= 0 && (port+size <= 0x0e))
+ {
+ // write to window
+ uint32_t curwindow = s->mIntStatus >> 13;
+ writeRegWindow(opaque, curwindow, port, data, size);
+ }
+ else if (port >= 0x10 && (port + size <= 0x10 + sizeof(Registers)))
+ {
+ uint8_t l = gRegAccess[port-0x10];
+ if (l != size)
+ {
+ DEBUG_PRINT("invalid/unaligned write to register\n");
+ }
+ switch (port)
+ {
+ case 0x20:
+ {
+ uint32_t DmaCtrlRWMask = DC_upRxEarlyEnable | DC_counterSpeed |
+ DC_countdownMode | DC_defeatMWI | DC_defeatMRL |
+ DC_upOverDiscEnable;
+ s->mRegisters.DmaCtrl &= ~DmaCtrlRWMask;
+ s->mRegisters.DmaCtrl |= data & DmaCtrlRWMask;
+ DEBUG_PRINT("write DmaCtrl\n");
+ break;
+ }
+ case 0x24:
+ {
+ if (!s->mRegisters.DnListPtr)
+ {
+ s->mRegisters.DnListPtr = data;
+ DEBUG_PRINT("write DnListPtr\n");
+ }
+ else
+ {
+ DEBUG_PRINT("didn't write DnListPtr cause it's not 0\n");
+ }
+ checkDnWork(opaque);
+ break;
+ }
+ case 0x38:
+ {
+ s->mRegisters.UpListPtr = data;
+ DEBUG_PRINT("write UpListPtr\n");
+ checkUpWork(opaque);
+ break;
+ }
+ case 0x2d:
+ DEBUG_PRINT("DnPoll\n");
+ // SINGLESTEP("");
+ break;
+ case 0x2a:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write DnBurstThresh\n");
+ break;
+ case 0x2c:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write DnPriorityThresh\n");
+ break;
+ case 0x2f:
+ // used by Darwin as TxFreeThresh. Not documented in [1].
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write TxFreeThresh\n");
+ break;
+ case 0x3c:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write UpPriorityThresh\n");
+ break;
+ case 0x3e:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write UpBurstThresh\n");
+ break;
+ case 0x1b:
+ if (size != 1)
+ {
+ DEBUG_PRINT("wrong size for write to TxStatus\n");
+ return;
+ }
+ DEBUG_PRINT_FORMAT(("Writing %x to TxStatus\n", data));
+ s->mRegisters.TxStatus = data;
+
+ // acknowledge the relevant bits
+ acknowledge(IS_txComplete, opaque);
+ // | IS_dnComplete | IS_cmdInProgress
+
+ // NOTE: "An I/O write of an arbitrary value to TxStatus advances the queue to the next transmit status byte."
+ // We also need to keep the queue of TX Status bytes!
+
+ // maybeRaiseIntr(opaque);
+ break;
+ default:
+ DEBUG_PRINT("write to register\n");
+ // SINGLESTEP("");
+ // write to (standard) register
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ }
+ }
+}
+
+static void a3c90x_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 1);
+}
+
+static void a3c90x_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 2);
+}
+
+static void a3c90x_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 4);
+}
+
+static uint32_t a3c90x_io_readb(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 1);
+}
+
+static uint32_t a3c90x_io_readw(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 2);
+}
+
+static uint32_t a3c90x_io_readl(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 4);
+}
+
+/* */
+
+static void a3c90x_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t a3c90x_ioport_readb(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_ioport_readw(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readw(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_ioport_readl(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readl(opaque, addr & 0xFF);
+}
+
+/* */
+
+static void a3c90x_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ a3c90x_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ a3c90x_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ a3c90x_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t a3c90x_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return a3c90x_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = a3c90x_io_readw(opaque, addr & 0xFF);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t a3c90x_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = a3c90x_io_readl(opaque, addr & 0xFF);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
+/***********************************************************/
+/* PCI 3C90x definitions */
+
+typedef struct PCI3C90XState
+{
+ PCIDevice dev;
+ A3C90XState a3c90x;
+} PCI3C90XState;
+
+static void a3c90x_mmio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCI3C90XState *d = (PCI3C90XState *)pci_dev;
+ A3C90XState *s = &d->a3c90x;
+
+ cpu_register_physical_memory(addr + 0, 0x100, s->a3c90x_mmio_io_addr);
+}
+
+static void a3c90x_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCI3C90XState *d = (PCI3C90XState *)pci_dev;
+ A3C90XState *s = &d->a3c90x;
+
+ register_ioport_write(addr, 0x100, 1, a3c90x_ioport_writeb, s);
+ register_ioport_read( addr, 0x100, 1, a3c90x_ioport_readb, s);
+
+ register_ioport_write(addr, 0x100, 2, a3c90x_ioport_writew, s);
+ register_ioport_read( addr, 0x100, 2, a3c90x_ioport_readw, s);
+
+ register_ioport_write(addr, 0x100, 4, a3c90x_ioport_writel, s);
+ register_ioport_read( addr, 0x100, 4, a3c90x_ioport_readl, s);
+}
+
+static CPUReadMemoryFunc *a3c90x_mmio_read[3] =
+{
+ a3c90x_mmio_readb,
+ a3c90x_mmio_readw,
+ a3c90x_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *a3c90x_mmio_write[3] =
+{
+ a3c90x_mmio_writeb,
+ a3c90x_mmio_writew,
+ a3c90x_mmio_writel,
+};
+
+static inline int64_t a3c90x_get_next_tctr_time(A3C90XState *s, int64_t current_time)
+{
+ int64_t next_time = current_time +
+ muldiv64(1, ticks_per_sec, PCI_FREQUENCY);
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+void a3c90x_cleanup(VLANClientState *vc)
+{
+ DEBUG_PRINT("3C90X: Cleanup not implemented\n");
+}
+
+PCIDevice *pci_a3c90x_init(PCIBus *bus, NICInfo *nd, int devfn)
+{
+ PCI3C90XState *d;
+ A3C90XState *s;
+ uint8_t *pci_conf;
+
+ d = (PCI3C90XState *)pci_register_device(bus,
+ "3C90x", sizeof(PCI3C90XState),
+ devfn,
+ NULL, NULL);
+ pci_conf = d->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_3COM);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_3C90X);
+ pci_conf[0x04] = 0x07; /* command = I/O space, Bus Master */
+ pci_conf[0x08] = 0;
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ pci_conf[0x0e] = 0x00; /* header_type */
+ pci_conf[0x3d] = 1; /* interrupt pin 0 */
+ pci_conf[0x34] = 0xdc;
+
+ pci_conf[0x3e] = 5;
+ pci_conf[0x3f] = 48;
+
+ s = &d->a3c90x;
+
+ /* I/O handler for memory-mapped I/O */
+ s->a3c90x_mmio_io_addr =
+ cpu_register_io_memory(0, a3c90x_mmio_read, a3c90x_mmio_write, s);
+
+ pci_register_io_region(&d->dev, 0, 0x100,
+ PCI_ADDRESS_SPACE_IO, a3c90x_ioport_map);
+
+ pci_register_io_region(&d->dev, 1, 0x100,
+ PCI_ADDRESS_SPACE_MEM, a3c90x_mmio_map);
+
+ s->pci_dev = (PCIDevice *)d;
+ memcpy(s->mMAC, nd->macaddr, 6);
+ a3c90x_reset(s);
+ s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
+ a3c90x_receive, a3c90x_can_receive, a3c90x_cleanup, s);
+
+ qemu_format_nic_info_str(s->vc, s->mMAC);
+
+ //register_savevm("3c90x", -1, 4, a3c90x_save, a3c90x_load, s);
+
+ return (PCIDevice *)d;
+}
+
diff -ruNp -- ./qemu-0.10.3-clean/hw/pci.c ./qemu-0.10.3/hw/pci.c
--- ./qemu-0.10.3-clean/hw/pci.c 2009-05-02 03:02:44.000000000 +1000
+++ ./qemu-0.10.3/hw/pci.c 2009-05-05 17:14:51.000000000 +1000
@@ -781,6 +781,7 @@ static const char * const pci_nic_models
"i82557b",
"i82559er",
"rtl8139",
+ "3c90x",
"e1000",
"pcnet",
"virtio",
@@ -795,6 +796,7 @@ static PCINICInitFn pci_nic_init_fns[] =
pci_i82557b_init,
pci_i82559er_init,
pci_rtl8139_init,
+ pci_a3c90x_init,
pci_e1000_init,
pci_pcnet_init,
virtio_net_init,
diff -ruNp -- ./qemu-0.10.3-clean/hw/pci.h ./qemu-0.10.3/hw/pci.h
--- ./qemu-0.10.3-clean/hw/pci.h 2009-05-02 03:02:44.000000000 +1000
+++ ./qemu-0.10.3/hw/pci.h 2009-05-05 17:16:21.000000000 +1000
@@ -85,6 +85,9 @@ extern target_phys_addr_t pci_mem_base;
#define PCI_DEVICE_ID_REALTEK_RTL8029 0x8029
#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_DEVICE_ID_3C90X 0x9200
+
#define PCI_VENDOR_ID_XILINX 0x10ee
#define PCI_VENDOR_ID_MARVELL 0x11ab
@@ -294,6 +297,9 @@ PCIDevice *pci_ne2000_init(PCIBus *bus,
PCIDevice *pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn);
+/* 3c90x.c */
+PCIDevice *pci_a3c90x_init(PCIBus *bus, NICInfo *nd, int devfn);
+
/* e1000.c */
PCIDevice *pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn);
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] Re: PATCH: Add 3C90X emulation
2009-05-05 7:56 [Qemu-devel] PATCH: Add 3C90X emulation Matthew Iselin
@ 2009-06-01 21:18 ` Sebastian Herbszt
[not found] ` <f88ae150906020410k270280d8k1d1c77f168e06ce8@mail.gmail.com>
0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Herbszt @ 2009-06-01 21:18 UTC (permalink / raw)
To: Matthew Iselin, qemu-devel
Matthew Iselin wrote:
> Hi,
>
> This patch adds the 3Com 3C90x series to the set of NICs that QEMU can
> emulate. It is still a work in progress, however
> at this stage it has the base required functionality and works with
> Ubuntu and Damn Small Linux.
>
> Part 1 modifies the Makefile.target file to add the object file for
> the emulation implementation.
>
> Part 2 includes the hw directory modifications, including the new 3c90x.c file.
>
> Thanks,
>
> Matthew
The patch unfortunatelly no longer applies. Can you please rebase on top of git master
and resend?
- Sebastian
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] PATCH: Add 3C90X emulation
[not found] ` <f88ae150906020410k270280d8k1d1c77f168e06ce8@mail.gmail.com>
@ 2009-06-02 11:10 ` Matthew Iselin
2009-06-02 22:19 ` [Qemu-devel] " Sebastian Herbszt
0 siblings, 1 reply; 7+ messages in thread
From: Matthew Iselin @ 2009-06-02 11:10 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 203 bytes --]
Sebastian wrote:
> The patch unfortunatelly no longer applies. Can you please rebase on top of
> git master
> and resend?
Hi,
The attached patch now applies to the latest git master.
Thanks,
Matthew
[-- Attachment #2: 3c90x.patch --]
[-- Type: text/x-patch, Size: 73378 bytes --]
diff --git a/Makefile.target b/Makefile.target
index 82ada5a..d43355c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -580,6 +580,7 @@ OBJS += eepro100.o
OBJS += ne2000.o
OBJS += pcnet.o
OBJS += rtl8139.o
+OBJS += 3c90x.o
OBJS += e1000.o
ifeq ($(TARGET_BASE_ARCH), i386)
diff --git a/hw/3c90x.c b/hw/3c90x.c
new file mode 100644
index 0000000..232f719
--- /dev/null
+++ b/hw/3c90x.c
@@ -0,0 +1,2405 @@
+/**
+ * QEMU 3C90X Emulation
+ *
+ * Copyright (c) 2009 Matthew Iselin (QEMU VERSION)
+ * Copyright (C) 2004 John Kelley (pearpc@kelley.ca) (PEARPC VERSION)
+ * Copyright (C) 2003 Stefan Weyergraf (PEARPC VERSION)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+ * Modifications:
+ * (none)
+ */
+
+/**
+ * TODO:
+ * - Still a lot of unimplemented functionality
+ * - On-chip TCP checksum emulation
+ * - savevm functions
+ * TESTED ON:
+ * - Damn Small Linux (4.4.10)
+ * - Ubuntu (8.10, 5.10)
+ * - Pedigree
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "qemu-timer.h"
+#include "net.h"
+
+// Should we inspect incoming frames and print extra debugging information about them?
+//#define DEBUG_3C90X_ANALYSE_FRAMES 0
+
+#ifdef DEBUG_3C90X_ANALYSE_FRAMES
+#define __FAVOR_BSD
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#endif
+
+#define PACKED __attribute__((packed));
+#define ALIGNED __attribute__((aligned));
+#define PACKED8 __attribute__((aligned(8)));
+#define PACKED16 __attribute__((aligned(16)));
+#define PACKED32 __attribute__((aligned(32)));
+
+/* debug 3C90X card */
+// #define DEBUG_3C90X 1
+
+/* define this to output TX and RX events */
+// #define DEBUG_3C90X_AUX 1
+
+#define PCI_FREQUENCY 33000000L
+
+/* Calculate CRCs properly on Tx packets */
+#define A3C90X_CALCULATE_TXCRC 1
+
+#if defined(A3C90x_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#if defined (DEBUG_3C90X)
+# define DEBUG_PRINT(x) do { printf(x) ; } while (0)
+# define DEBUG_PRINT_FORMAT(x) do { printf x ; } while (0)
+#else
+# define DEBUG_PRINT(x)
+# define DEBUG_PRINT_FORMAT(x)
+#endif
+
+#if defined (DEBUG_3C90X_AUX)
+# define AUX_DEBUG_PRINT(x) do { printf(x) ; } while (0)
+# define AUX_DEBUG_PRINT_FORMAT(x) do { printf x ; } while (0)
+#else
+# define AUX_DEBUG_PRINT(x) DEBUG_PRINT(x)
+# define AUX_DEBUG_PRINT_FORMAT(x) DEBUG_PRINT_FORMAT(x)
+#endif
+
+#define MAX_PACKET_SIZE 16384
+
+enum Command
+{
+ CmdTotalReset = 0<<11,
+ CmdSelectWindow = 1<<11,
+ CmdEnableDC = 2<<11, // CmdStartCoax
+ CmdRxDisable = 3<<11,
+ CmdRxEnable = 4<<11,
+ CmdRxReset = 5<<11,
+ CmdStall = 6<<11,
+ CmdTxDone = 7<<11,
+ CmdRxDiscard = 8<<11,
+ CmdTxEnable = 9<<11,
+ CmdTxDisable = 10<<11,
+ CmdTxReset = 11<<11,
+ CmdReqIntr = 12<<11, // CmdFakeIntr
+ CmdAckIntr = 13<<11,
+ CmdSetIntrEnb = 14<<11,
+ CmdSetIndicationEnable = 15<<11, // CmdSetStatusEnb
+ CmdSetRxFilter = 16<<11,
+ CmdSetRxEarlyThresh = 17<<11,
+ CmdSetTxThreshold = 18<<11, // aka TxAgain ?
+ CmdSetTxStartThresh = 19<<11, // set TxStartTresh
+ // CmdStartDMAUp = 20<<11,
+ // CmdStartDMADown = (20<<11)+1,
+ CmdStatsEnable = 21<<11,
+ CmdStatsDisable = 22<<11,
+ CmdDisableDC = 23<<11, // CmdStopCoax
+ CmdSetTxReclaimThresh = 24<<11,
+ CmdSetHashFilterBit = 25<<11
+};
+
+/*
+ * IntStatusBits
+ */
+enum IntStatusBits
+{
+ IS_interruptLatch = 1<<0,
+ IS_hostError = 1<<1,
+ IS_txComplete = 1<<2,
+ /* bit 3 is unspecified */
+ IS_rxComplete = 1<<4,
+ IS_rxEarly = 1<<5,
+ IS_intRequested = 1<<6,
+ IS_updateStats = 1<<7,
+ IS_linkEvent = 1<<8,
+ IS_dnComplete = 1<<9,
+ IS_upComplete = 1<<10,
+ IS_cmdInProgress = 1<<11,
+ /* bit 12 is unspecified */
+ /* [15:13] is currently selected window */
+};
+
+/*
+ * DmaCtrlBits ([1] p.96)
+ */
+enum DmaCtrlBits
+{
+ /* bit 0 unspecified */
+ DC_dnCmplReq = 1<<1,
+ DC_dnStalled = 1<<2,
+ DC_upComplete = 1<<3, // FIXME: same as in IntStatus, but always visible
+ DC_dnComplete = 1<<4, // same as above ^^^
+ DC_upRxEarlyEnable = 1<<5,
+ DC_armCountdown = 1<<6,
+ DC_dnInProg = 1<<7,
+ DC_counterSpeed = 1<<8,
+ DC_countdownMode = 1<<9,
+ /* bits 10-15 unspecified */
+ DC_upAltSeqDisable = 1<<16,
+ DC_dnAltSeqDisable = 1<<17,
+ DC_defeatMWI = 1<<20,
+ DC_defeatMRL = 1<<21,
+ DC_upOverDiscEnable = 1<<22,
+ DC_targetAbort = 1<<30,
+ DC_masterAbort = 1<<31
+};
+
+/*
+ * MII Registers
+ * TODO: Implement MII properly
+ */
+/*enum MIIControlBits {
+ MIIC_collision = 1<<7,
+ MIIC_fullDuplex = 1<<8,
+ MIIC_restartNegote = 1<<9,
+ MIIC_collision = 1<<7,
+ rest missing
+};*/
+
+struct MIIRegisters
+{
+ uint16_t control;
+ uint16_t status;
+ uint16_t id0;
+ uint16_t id1;
+ uint16_t advert;
+ uint16_t linkPartner;
+ uint16_t expansion;
+ uint16_t nextPage;
+} PACKED;
+typedef struct MIIRegisters MIIRegisters;
+
+/*
+ * Registers
+ */
+union RegWindow
+{
+ uint8_t b[16];
+ uint16_t u16[8];
+} PACKED;
+typedef union RegWindow RegWindow;
+
+struct Registers
+{
+ // 0x10 uint8_ts missing (current window)
+ uint32_t r0;
+ uint32_t r1;
+ uint8_t TxPktId;
+ uint8_t r2;
+ uint8_t Timer;
+ uint8_t TxStatus;
+ uint16_t r3;
+ uint16_t __dontUseMe;// really: uint16_t IntStatusAuto;
+ uint32_t DmaCtrl; // [1] p.95 (dn), p.100 (up)
+ uint32_t DnListPtr; // [1] p.98
+ uint16_t r4;
+ uint8_t DnBurstThresh; // [1] p.97
+ uint8_t r5;
+ uint8_t DnPriorityThresh;
+ uint8_t DnPoll; // [1] p.100
+ uint16_t r6;
+ uint32_t UpPktStatus;
+ uint16_t FreeTimer;
+ uint16_t Countdown;
+ uint32_t UpListPtr; // [1] p.115
+ uint8_t UpPriorityThresh;
+ uint8_t UpPoll;
+ uint8_t UpBurstThresh;
+ uint8_t r7;
+ uint32_t RealTimeCount;
+ uint8_t ConfigAddress;
+ uint8_t r8;
+ uint8_t r9;
+ uint8_t r10;
+ uint8_t ConfigData;
+ uint8_t r11;
+ uint8_t r12;
+ uint8_t r13;
+ uint32_t r14[9];
+ uint32_t DebugData;
+ uint16_t DebugControl;
+ uint16_t r15;
+ uint16_t DnMaxBurst;
+ uint16_t UpMaxBurst;
+ uint16_t PowerMgmtCtrl;
+ uint16_t r16;
+} PACKED;
+typedef struct Registers Registers;
+
+#define RA_INV 0
+
+static uint8_t gRegAccess[0x70] =
+{
+ /* 0x10 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x14 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x18 */
+ 1, /* TxPktId */
+ RA_INV,
+ 1, /* Timer */
+ 1, /* TxStatus */
+ /* 0x1c */
+ RA_INV, RA_INV,
+ RA_INV, RA_INV, /* IntStatusAuto */
+ /* 0x20 */
+ 4, RA_INV, RA_INV, RA_INV, /* DmaCtrl */
+ /* 0x24 */
+ 4, RA_INV, RA_INV, RA_INV, /* DnListPtr */
+ /* 0x28 */
+ RA_INV, RA_INV,
+ 1, /* DnBurstThresh */
+ RA_INV,
+ /* 0x2c */
+ 1, /* DnPriorityThresh */
+ 1, /* DnPoll */
+ RA_INV,
+ 1,
+ /* 0x30 */
+ 4, RA_INV, RA_INV, RA_INV, /* UpPktStatus */
+ /* 0x34 */
+ 2, RA_INV, /* FreeTimer */
+ 2, RA_INV, /* Countdown */
+ /* 0x38 */
+ 4, RA_INV, RA_INV, RA_INV, /* UpListPtr */
+ /* 0x3c */
+ 1, /* UpPriorityThresh */
+ 1, /* UpPoll */
+ 1, /* UpBurstThresh */
+ RA_INV,
+ /* 0x40 */
+ 4, RA_INV, RA_INV, RA_INV, /* RealTimeCount */
+ /* 0x44 */
+ 1, /* ConfigAddress */
+ RA_INV,
+ RA_INV,
+ RA_INV,
+ /* 0x48 */
+ 1, /* ConfigData */
+ RA_INV,
+ RA_INV,
+ RA_INV,
+ /* 0x4c */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x50 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x70 */
+ 4, RA_INV, RA_INV, RA_INV, /* DebugData */
+ /* 0x74 */
+ 2, RA_INV, /* DebugControl */
+ RA_INV, RA_INV,
+ /* 0x78 */
+ 2, RA_INV, /* DnMaxBurst */
+ 2, RA_INV, /* UpMaxBurst */
+ /* 0x7c */
+ 2, RA_INV, /* PowerMgmtCtrl */
+ RA_INV, RA_INV
+};
+
+/*
+ * Window 0
+ */
+struct RegWindow0
+{
+ uint32_t r0;
+ uint32_t BiosRomAddr;
+ uint8_t BiosRomData;
+ uint8_t r1;
+ uint16_t EepromCommand;
+ uint16_t EepromData;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow0 RegWindow0;
+
+enum W0_Offsets
+{
+ W0_EEPROMCmd = 0xa,
+ W0_EEPROMData = 0xc
+};
+
+enum W0_EEPROMOpcode
+{
+ EEOP_SubCmd = 0<<6,
+ EEOP_WriteReg = 1<<6,
+ EEOP_ReadReg = 2<<6,
+ EEOP_EraseReg = 3<<6
+};
+
+enum W0_EEPROMSubCmd
+{
+ EESC_WriteDisable = 0<<4,
+ EESC_WriteAll = 1<<4,
+ EESC_EraseAll = 2<<4,
+ EESC_WriteEnable = 3<<4
+};
+
+/*
+ * Window 2
+ */
+struct RegWindow2
+{
+ uint16_t StationAddress[6];
+ uint16_t StationMask[6];
+ uint16_t ResetOptions;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow2 RegWindow2;
+
+/*
+ * Window 3
+ */
+struct RegWindow3
+{
+ uint32_t InternalConfig; // [1] p.58,76
+ uint16_t MaxPktSize;
+ uint16_t MacControl; // [1] p.179
+ uint16_t MediaOptions; // [1] p.78 (EE), p.181
+ uint16_t RxFree;
+ uint16_t TxFree; // [1] p.101
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow3 RegWindow3;
+
+/*
+ * Window 4
+ */
+enum W4_PhysMgmtBits
+{
+ PM_mgmtClk = 1<<0,
+ PM_mgmtData = 1<<1,
+ PM_mgmtDir = 1<<2
+};
+
+struct RegWindow4
+{
+ uint16_t r0; /* offset 0x0 */
+ uint16_t r1; /* offset 0x2 */
+ uint16_t FifoDiagnostic; /* offset 0x4 */
+ uint16_t NetDiagnostic; // [1] p.184 /* offset 0x6 */
+ uint16_t PhysMgmt; // [1] p.186 /* offset 0x8 */
+ uint16_t MediaStatus; // [1] p.182 /* offset 0xa */
+ uint8_t BadSSD;
+ uint8_t Upperuint8sOK;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow4 RegWindow4;
+
+/*
+ * Window 5
+ */
+enum RxFilterBits // [1] p.112
+{
+ RXFILT_receiveIndividual = 1,
+ RXFILT_receiveMulticast = 2,
+ RXFILT_receiveBroadcast = 4,
+ RXFILT_receiveAllFrames = 8,
+ RXFILT_receiveMulticastHash = 16
+};
+
+struct RegWindow5
+{
+ uint16_t TxStartThresh;
+ uint16_t r0;
+ uint16_t r1;
+ uint16_t RxEarlyThresh;
+ uint8_t RxFilter; // [1] p.112
+ uint8_t TxReclaimThresh;
+ uint16_t InterruptEnable; // [1] p.120
+ uint16_t IndicationEnable; // [1] p.120
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow5 RegWindow5;
+
+/*
+ * Window 6
+ */
+struct RegWindow6
+{
+ uint8_t CarrierLost;
+ uint8_t SqeErrors;
+ uint8_t MultipleCollisions;
+ uint8_t SingleCollisions;
+ uint8_t LateCollisions;
+ uint8_t RxOverruns;
+ uint8_t FramesXmittedOk;
+ uint8_t FramesRcvdOk;
+ uint8_t FramesDeferred;
+ uint8_t UpperFramesOk;
+ uint16_t BytesRcvdOk;
+ uint16_t BytesXmittedOk;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow6 RegWindow6;
+
+/*
+ * EEPROM
+ */
+enum EEPROMField
+{
+ EEPROM_NodeAddress0 = 0x00,
+ EEPROM_NodeAddress1 = 0x01,
+ EEPROM_NodeAddress2 = 0x02,
+ EEPROM_DeviceID = 0x03,
+ EEPROM_ManifacturerID = 0x07,
+ EEPROM_PCIParam = 0x08,
+ EEPROM_RomInfo = 0x09,
+ EEPROM_OEMNodeAddress0 = 0x0a,
+ EEPROM_OEMNodeAddress1 = 0x0b,
+ EEPROM_OEMNodeAddress2 = 0x0c,
+ EEPROM_SoftwareInfo = 0x0d,
+ EEPROM_CompWord = 0x0e,
+ EEPROM_SoftwareInfo2 = 0x0f,
+ EEPROM_Caps = 0x10,
+ EEPROM_InternalConfig0 = 0x12,
+ EEPROM_InternalConfig1 = 0x13,
+ EEPROM_SubsystemVendorID = 0x17,
+ EEPROM_SubsystemID = 0x18,
+ EEPROM_MediaOptions = 0x19,
+ EEPROM_SmbAddress = 0x1b,
+ EEPROM_PCIParam2 = 0x1c,
+ EEPROM_PCIParam3 = 0x1d,
+ EEPROM_Checksum = 0x20
+};
+
+/*
+ * Up/Downloading
+ */
+
+// must be on 8-uint8_t physical address boundary
+struct DPD0
+{
+ uint32_t DnNextPtr;
+ uint32_t FrameStartHeader;
+ /* DPDFragDesc Frags[n] */
+} PACKED8;
+typedef struct DPD0 DPD0;
+
+enum FrameStartHeaderBits
+{
+ FSH_rndupBndry = 3<<0,
+ FSH_pktId = 15<<2,
+ /* 12:10 unspecified */
+ FSH_crcAppendDisable = 1<<13,
+ FSH_txIndicate = 1<<15,
+ FSH_dnComplete = 1<<16,
+ FSH_reArmDisable = 1<<23,
+ FSH_lastKap = 1<<24,
+ FSH_addIpChecksum = 1<<25, /** TODO: write support for these */
+ FSH_addTcpChecksum = 1<<26,
+ FSH_addUdpChecksum = 1<<27,
+ FSH_rndupDefeat = 1<<28,
+ FSH_dpdEmpty = 1<<29,
+ /* 30 unspecified */
+ FSH_dnIndicate = 1<<31
+};
+
+// must be on 16-uint8_t physical address boundary
+struct DPD1
+{
+ uint32_t DnNextPtr;
+ uint32_t ScheduleTime;
+ uint32_t FrameStartHeader;
+ uint32_t res;
+ /* DPDFragDesc Frags[n] */
+} PACKED16;
+typedef struct DPD1 DPD1;
+
+struct DPDFragDesc
+{
+ uint32_t DnFragAddr;
+ uint32_t DnFragLen; // [12:0] fragLen, [31] lastFrag
+} PACKED;
+typedef struct DPDFragDesc DPDFragDesc;
+
+// must be on 8-uint8_t physical address boundary
+struct UPD
+{
+ uint32_t UpNextPtr;
+ uint32_t UpPktStatus;
+ /* UPDFragDesc Frags[n] */
+} PACKED8;
+typedef struct UPD UPD;
+
+struct UPDFragDesc
+{
+ uint32_t UpFragAddr;
+ uint32_t UpFragLen; // [12:0] fragLen, [31] lastFrag
+} PACKED;
+typedef struct UPDFragDesc UPDFragDesc;
+
+#define MAX_DPD_FRAGS 63
+#define MAX_UPD_FRAGS 63
+#define MAX_UPD_SIZE (sizeof(UPD) + sizeof(UPDFragDesc)*MAX_UPD_FRAGS) // 512
+
+enum UpPktStatusBits
+{
+ UPS_upPktLen = 0x1fff,
+ /* 13 unspecified */
+ UPS_upError = 1<<14,
+ UPS_upComplete = 1<<15,
+ UPS_upOverrun = 1<<16,
+ UPS_runtFrame = 1<<17,
+ UPS_alignmentError = 1<<18,
+ UPS_crcError = 1<<19,
+ UPS_oversizedFrame = 1<<20,
+ /* 22:21 unspecified */
+ UPS_dribbleBits = 1<<23,
+ UPS_upOverflow = 1<<24,
+ UPS_ipChecksumError = 1<<25,
+ UPS_tcpChecksumError = 1<<26,
+ UPS_udpChecksumError = 1<<27,
+ UPD_impliedBufferEnable = 1<<28,
+ UPS_ipChecksumChecked = 1<<29,
+ UPS_tcpChecksumChecked = 1<<30,
+ UPS_udpChecksumChecked = 1<<31
+};
+
+// IEEE 802.3 MAC, Ethernet-II
+struct EthFrameII
+{
+ uint8_t destMAC[6];
+ uint8_t srcMAC[6];
+ uint8_t type[2];
+} PACKED;
+typedef struct EthFrameII EthFrameII;
+
+struct A3C90XState
+{
+
+ uint16_t mEEPROM[0x40];
+ char mEEPROMWritable;
+ Registers mRegisters;
+ RegWindow mWindows[8];
+ uint16_t mIntStatus;
+ char mRxEnabled;
+ char mTxEnabled;
+ char mUpStalled;
+ char mDnStalled;
+ uint8_t mRxPacket[MAX_PACKET_SIZE];
+ uint32_t mRxPacketSize;
+ uint16_t mMIIRegs[8];
+ uint32_t mMIIReadWord;
+ uint64_t mMIIWriteWord;
+ uint32_t mMIIWrittenBits;
+ uint16_t mLastHiClkPhysMgmt;
+ uint8_t mMAC[6];
+
+
+ PCIDevice *pci_dev;
+ int a3c90x_mmio_io_addr;
+
+ VLANClientState *vc;
+
+};
+typedef struct A3C90XState A3C90XState;
+
+static void a3c90x_reset(A3C90XState *s);
+
+static void setCR(uint16_t cr, void *opaque);
+
+static uint32_t readRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size);
+static void writeRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size);
+
+static void checkDnWork(void *opaque);
+static void checkUpWork(void *opaque);
+
+static void txDPD0(void *opaque, DPD0 *dpd);
+static void rxUPD(void *opaque, UPD *upd);
+
+static void indicate(uint32_t indications, void *opaque);
+static void acknowledge(uint32_t indications, void *opaque);
+static void maybeRaiseIntr(void *opaque);
+
+static char passesRxFilter(uint8_t *pbuf, uint32_t psize, void *opaque);
+
+static void handle_rx(void *opaque, const uint8_t *buf, int size);
+
+static void a3c90x_cleanup(VLANClientState *vc);
+
+static int compareMACs(uint8_t a[6], uint8_t b[6]);
+
+/*
+ * misc
+ */
+static int compareMACs(uint8_t a[6], uint8_t b[6])
+{
+ uint32_t i;
+ for (i = 0; i < 6; i++)
+ {
+ if (a[i] != b[i]) return a[i] - b[i];
+ }
+ return 0;
+}
+
+static void a3c90x_reset(A3C90XState *s)
+{
+ /* FIXME: resetting can be done more fine-grained (see TotalReset cmd).
+ * this is reset ALL regs.
+ */
+ if (sizeof(Registers) != 0x70)
+ {
+ DEBUG_PRINT("sizeof Registers != 0x70\n");
+ }
+
+ RegWindow3 *w3 = (RegWindow3*) &(s->mWindows[3]);
+ RegWindow4 *w4 = (RegWindow4*) &(s->mWindows[4]);
+ RegWindow5 *w5 = (RegWindow5*) &(s->mWindows[5]);
+
+ // internals
+ s->mEEPROMWritable = 0;
+ memset(&(s->mWindows), 0, sizeof s->mWindows);
+ memset(&(s->mRegisters), 0, sizeof s->mRegisters);
+ s->mIntStatus = 0;
+ s->mRxEnabled = 0;
+ s->mTxEnabled = 0;
+ s->mUpStalled = 0;
+ s->mDnStalled = 0;
+ w3->MaxPktSize = 1514 /* FIXME: should depend on sizeof mRxPacket*/;
+ w3->RxFree = 16*1024;
+ w3->TxFree = 16*1024;
+ s->mRxPacketSize = 0;
+ w5->TxStartThresh = 8188;
+ memset(s->mEEPROM, 0, sizeof s->mEEPROM);
+ s->mEEPROM[EEPROM_NodeAddress0] = (s->mMAC[0]<<8) | s->mMAC[1];
+ s->mEEPROM[EEPROM_NodeAddress1] = (s->mMAC[2]<<8) | s->mMAC[3];
+ s->mEEPROM[EEPROM_NodeAddress2] = (s->mMAC[4]<<8) | s->mMAC[5];
+ s->mEEPROM[EEPROM_DeviceID] = 0x9200;
+ s->mEEPROM[EEPROM_ManifacturerID] = 0x6d50;
+ s->mEEPROM[EEPROM_PCIParam] = 0x2940;
+ s->mEEPROM[EEPROM_RomInfo] = 0; // no ROM
+ s->mEEPROM[EEPROM_OEMNodeAddress0] = s->mEEPROM[EEPROM_NodeAddress0];
+ s->mEEPROM[EEPROM_OEMNodeAddress1] = s->mEEPROM[EEPROM_NodeAddress1];
+ s->mEEPROM[EEPROM_OEMNodeAddress2] = s->mEEPROM[EEPROM_NodeAddress2];
+ s->mEEPROM[EEPROM_SoftwareInfo] = 0x4010;
+ s->mEEPROM[EEPROM_CompWord] = 0;
+ s->mEEPROM[EEPROM_SoftwareInfo2] = 0x00aa;
+ s->mEEPROM[EEPROM_Caps] = 0x72a2;
+ s->mEEPROM[EEPROM_InternalConfig0] = 0;
+ s->mEEPROM[EEPROM_InternalConfig1] = 0x0050; // default is 0x0180
+ s->mEEPROM[EEPROM_SubsystemVendorID] = 0x10b7;
+ s->mEEPROM[EEPROM_SubsystemID] = 0x9200;
+ s->mEEPROM[EEPROM_MediaOptions] = 0x000a;
+ s->mEEPROM[EEPROM_SmbAddress] = 0x6300;
+ s->mEEPROM[EEPROM_PCIParam2] = 0xffb7;
+ s->mEEPROM[EEPROM_PCIParam3] = 0xb7b7;
+ s->mEEPROM[EEPROM_Checksum] = 0;
+
+ // MII
+ memset(s->mMIIRegs, 0, sizeof s->mMIIRegs);
+ MIIRegisters *miiregs = (MIIRegisters*) s->mMIIRegs;
+ miiregs->status = (1<<14) | (1<<13) | (1<<12) | (1<<11) | (1<<5) | (1<<3) | (1<<2) | 1;
+ miiregs->linkPartner = (1<<14) | (1<<7) | 1;
+ miiregs->advert = (1<<14) | (1 << 10) | (1<<7) | 1;
+ s->mMIIReadWord = 0;
+ s->mMIIWriteWord = 0;
+ s->mMIIWrittenBits = 0;
+ s->mLastHiClkPhysMgmt = 0;
+
+ // Register follow-ups
+ w3->MediaOptions = s->mEEPROM[EEPROM_MediaOptions];
+ w3->InternalConfig = s->mEEPROM[EEPROM_InternalConfig0] |
+ (s->mEEPROM[EEPROM_InternalConfig1] << 16);
+
+ // A valid link is established on the NIC
+ w4->MediaStatus = (1 << 11) | 0x8000;
+
+ // And clean out the RX buffer
+ memset(s->mRxPacket, 0xab, MAX_PACKET_SIZE);
+}
+
+static uint32_t readRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size)
+{
+ DEBUG_PRINT("readRegWindow\n");
+ switch (window)
+ {
+ /* window 0 */
+ case 0:
+ {
+ RegWindow0 *w0 = (RegWindow0*) &s->mWindows[0];
+ switch (port)
+ {
+ case W0_EEPROMCmd:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("WARN: EepromCommand, size != 2\n");
+ return 0;
+ }
+ return w0->EepromCommand;
+ break;
+ }
+ case W0_EEPROMData:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("WARN: EepromData, size != 2\n");
+ return 0;
+ }
+ return w0->EepromData;
+ break;
+ }
+ default:
+ DEBUG_PRINT("WARN: reading here unimpl\n");
+ return 0;
+ break;
+ }
+ break;
+ }
+ /* window 1 */
+ case 1:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[1].b[port], size);
+ return data;
+ break;
+ }
+ /* window 2 */
+ case 2:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[2].b[port], size);
+ return data;
+ break;
+ }
+ /* window 3 */
+ case 3:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[3].b[port], size);
+ return data;
+ break;
+ }
+ /* window 4 */
+ case 4:
+ {
+ DEBUG_PRINT("Read from window 4\n");
+ RegWindow4 *w4 = (RegWindow4*) &s->mWindows[4];
+ data = 0;
+ switch (port)
+ {
+ case 8:
+ {
+ // MII-interface
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.8.read\n");
+ return 0;
+ }
+ char mgmtData = (s->mMIIReadWord & 0x80000000) ? 1 : 0;
+ if (mgmtData)
+ {
+ data = w4->PhysMgmt | PM_mgmtData;
+ }
+ else
+ {
+ data = w4->PhysMgmt & (~PM_mgmtData);
+ }
+ break;
+ }
+ case 0xc:
+ {
+ if (size != 1)
+ {
+ DEBUG_PRINT("alignment.4.c.read\n");
+ return 0;
+ }
+ // reading clears
+ w4->BadSSD = 0;
+ memcpy(&data, &s->mWindows[4].b[port], size);
+ return data;
+ break;
+ }
+ default:
+ memcpy(&data, &(s->mWindows[4].b[port]), size);
+ return data;
+ }
+ break;
+ }
+ /* Window 5 */
+ case 5:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[5].b[port], size);
+ return data;
+ break;
+ }
+ /* Window 6 */
+ case 6:
+ {
+ RegWindow6 *w6 = (RegWindow6*) &s->mWindows[6];
+ // reading clears
+ if ((port == 0xa) && (size == 2))
+ {
+ // FIXME: BytesRcvdOk really is 20 bits !
+ // when reading here, write upper 4 bits
+ // in w4.UpperBytesOk[3:0]. no clearing.
+ w6->BytesRcvdOk = 0;
+ }
+ else if ((port == 0xc) && (size == 2))
+ {
+ // FIXME: BytesXmittedOk really is 20 bits !
+ // when reading here, write upper 4 bits
+ // in w4.UpperBytesOk[7:4]. no clearing.
+ w6->BytesXmittedOk = 0;
+ }
+ else if ((port == 0) && (size == 1))
+ {
+ w6->CarrierLost = 0;
+ }
+ else if ((port == 8) && (size == 1))
+ {
+ w6->FramesDeferred = 0;
+ }
+ else if ((port == 7) && (size == 1))
+ {
+ // FIXME: FramesRcvdOk really is 10 bits !
+ // when reading here, write upper 2 bits
+ // in w6.UpperFramesOk[1:0]. no clearing.
+ }
+ else if ((port == 6) && (size == 1))
+ {
+ // FIXME: FramesXmittedOk really is 10 bits !
+ // when reading here, write upper 2 bits
+ // in w6.UpperFramesOk[5:4]. no clearing.
+ }
+ else if ((port == 4) && (size == 1))
+ {
+ w6->LateCollisions = 0;
+ }
+ else if ((port == 2) && (size == 1))
+ {
+ w6->MultipleCollisions = 0;
+ }
+ else if ((port == 5) && (size == 1))
+ {
+ w6->RxOverruns = 0;
+ }
+ else if ((port == 3) && (size == 1))
+ {
+ w6->SingleCollisions = 0;
+ }
+ else if ((port == 1) && (size == 1))
+ {
+ w6->SqeErrors = 0;
+ }
+ data = 0;
+ memcpy(&data, &s->mWindows[6].b[port], size);
+ return data;
+ break;
+ }
+ /* Window 7 */
+ case 7:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[7].b[port], size);
+ return data;
+ break;
+ }
+ default:
+ DEBUG_PRINT("reading here unimpl.\n");
+ }
+
+ return 0;
+}
+
+static void writeRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size)
+{
+ DEBUG_PRINT("writeRegWindow\n");
+ switch (window)
+ {
+ /* Window 0 */
+ case 0:
+ {
+ RegWindow0 *w0 = (RegWindow0*) &s->mWindows[0];
+ switch (port)
+ {
+ case W0_EEPROMCmd:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("EepromCommand, size != 2\n");
+ return;
+ }
+ w0->EepromCommand = data & 0xff7f; // clear eepromBusy
+ uint32_t eeprom_addr = ((data >> 2) & 0xffc0) | (data & 0x3f);
+ switch (data & 0xc0)
+ {
+ case EEOP_SubCmd:
+ switch (data & 0x30)
+ {
+ case EESC_WriteDisable:
+ DEBUG_PRINT("EESC_WriteDisable\n");
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_WriteAll:
+ // FIXME: this needs fixing :)
+ DEBUG_PRINT("WriteAll not impl.\n");
+ memset(s->mEEPROM, 0xff, sizeof s->mEEPROM);
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_EraseAll:
+ DEBUG_PRINT("EraseAll not impl.\n");
+ // SINGLESTEP("");
+ memset(s->mEEPROM, 0, sizeof s->mEEPROM);
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_WriteEnable:
+ DEBUG_PRINT("EESC_WriteEnable\n");
+ s->mEEPROMWritable = 0;
+ break;
+ default:
+ DEBUG_PRINT("impossible\n");
+ // SINGLESTEP("");
+ }
+ break;
+ case EEOP_WriteReg:
+ if (s->mEEPROMWritable)
+ {
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ // disabled
+ DEBUG_PRINT("EEOP_WriteReg\n");
+ // SINGLESTEP("");
+ s->mEEPROM[eeprom_addr] = w0->EepromData;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_WriteReg\n");
+ }
+ s->mEEPROMWritable = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(not writable): EEOP_WriteReg\n");
+ }
+ break;
+ case EEOP_ReadReg:
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ w0->EepromData = s->mEEPROM[eeprom_addr];
+ DEBUG_PRINT("EEOP_ReadReg\n");
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_ReadReg\n");
+ }
+ break;
+ case EEOP_EraseReg:
+ if (s->mEEPROMWritable)
+ {
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ // disabled
+ DEBUG_PRINT("EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ s->mEEPROM[eeprom_addr] = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ }
+ s->mEEPROMWritable = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(not writable): EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ }
+ break;
+ default:
+ DEBUG_PRINT("impossible\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ case W0_EEPROMData:
+ if (size != 2)
+ {
+ DEBUG_PRINT("EepromData, size != 2\n");
+ // SINGLESTEP("");
+ }
+ w0->EepromData = data;
+ break;
+ default:
+ DEBUG_PRINT("writing here unimpl.0\n");
+ // SINGLESTEP("");
+ break;
+ }
+ break;
+ }
+ /* Window 2 */
+ case 2:
+ {
+ if (port+size<=0xc)
+ {
+ DEBUG_PRINT("StationAddress or StationMask\n");
+ /* StationAddress or StationMask */
+ memcpy(&s->mWindows[2].b[port], &data, size);
+ }
+ else
+ {
+ DEBUG_PRINT("writing here unimpl.2\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ /* Window 3 */
+ case 3:
+ {
+ RegWindow3 *w3 = (RegWindow3*) &s->mWindows[3];
+ switch (port)
+ {
+ case 0:
+ if (size != 4)
+ {
+ DEBUG_PRINT("alignment.3.0\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("InternalConfig\n");
+ w3->InternalConfig = data;
+ break;
+ case 4:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.4\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("ERR: MaxPktSize\n");
+ w3->MaxPktSize = data;
+ break;
+ case 6:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.6\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("MacControl\n");
+ if (data != 0)
+ {
+ DEBUG_PRINT("setting MacControl != 0\n");
+ // SINGLESTEP("");
+ }
+ w3->MacControl = data;
+ break;
+ case 8:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.8\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("MediaOptions\n");
+ w3->MediaOptions = data;
+ break;
+ case 10:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.10\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("RxFree\n");
+ // SINGLESTEP("");
+ w3->RxFree = data;
+ break;
+ case 12:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.12\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("TxFree\n");
+ // SINGLESTEP("");
+ w3->TxFree = data;
+ break;
+ default:
+ DEBUG_PRINT("writing here unimpl.3\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ /* Window 4 */
+ case 4:
+ {
+ RegWindow4 *w4 = (RegWindow4*) &s->mWindows[4];
+ switch (port)
+ {
+ case 6:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.6\n");
+ // SINGLESTEP("");
+ }
+ uint32_t mask = 0xf341;
+ DEBUG_PRINT("NetDiagnostic");
+ w4->NetDiagnostic &= ~mask;
+ w4->NetDiagnostic |= data & mask;
+ break;
+ }
+ case 8:
+ {
+ // MII-interface
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.8\n");
+ // SINGLESTEP("");
+ }
+ char hiClk = (!((w4->PhysMgmt & PM_mgmtClk) && (data & PM_mgmtClk))) ? 1 : 0;
+ if (hiClk)
+ {
+ // Z means lo edge of mgmtDir
+ char Z = ((s->mLastHiClkPhysMgmt & PM_mgmtDir) && !(data & PM_mgmtDir)) ? 1 : 0;
+ if (Z)
+ {
+ // check if the 5 frames have been sent
+ if (((s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2)) & 0x3ffffffffULL) == 0x3fffffffdULL)
+ {
+ uint32_t opcode = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2)) & 3;
+ uint32_t PHYaddr = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2-5)) & 0x1f;
+ uint32_t REGaddr = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2-5-5)) & 0x1f;
+ if ((PHYaddr == 0x18 /* hardcoded address [1] p.196 */)
+ && (REGaddr < 0x10))
+ {
+ switch (opcode)
+ {
+ case 1:
+ {
+ // Opcode Write
+ DEBUG_PRINT("Opcode Write\n");
+ if (s->mMIIWrittenBits == 64)
+ {
+ uint32_t value = s->mMIIWriteWord & 0xffff;
+ s->mMIIRegs[REGaddr] = value;
+ }
+ else
+ {
+ DEBUG_PRINT("But invalid write count\n");
+ }
+ s->mMIIWriteWord = 0;
+ break;
+ }
+ case 2:
+ {
+ // Opcode Read
+ DEBUG_PRINT("Opcode Read\n");
+ if (s->mMIIWrittenBits == 32+2+2+5+5)
+ {
+ // msb gets sent first and is zero to indicated success
+ // the register to be sent follows msb to lsb
+ s->mMIIReadWord = s->mMIIRegs[REGaddr] << 15;
+ }
+ else
+ {
+ DEBUG_PRINT("But invalid write count\n");
+ }
+ s->mMIIWriteWord = 0;
+ break;
+ }
+ default:
+ // error
+ DEBUG_PRINT("Invalid opcode\n");
+ s->mMIIReadWord = 0xffffffff;
+ }
+ }
+ else
+ {
+ // error
+ DEBUG_PRINT("Invalid PHY or REG\n");
+ s->mMIIReadWord = 0xffffffff;
+ }
+ }
+ s->mMIIWrittenBits = 0;
+ w4->PhysMgmt = data;
+ }
+ else if (data & PM_mgmtDir)
+ {
+ // write
+ char mgmtData = (data & PM_mgmtData) ? 1 : 0;
+ w4->PhysMgmt = data;
+ s->mMIIWriteWord <<= 1;
+ s->mMIIWriteWord |= mgmtData ? 1 : 0;
+ s->mMIIWrittenBits++;
+ }
+ else
+ {
+ // read
+ char mgmtData = (s->mMIIReadWord & 0x80000000) ? 1 : 0;
+ w4->PhysMgmt = data;
+ if (mgmtData)
+ {
+ w4->PhysMgmt = w4->PhysMgmt | PM_mgmtData;
+ }
+ else
+ {
+ w4->PhysMgmt = w4->PhysMgmt & (~PM_mgmtData);
+ }
+ s->mMIIReadWord <<= 1;
+ }
+ s->mLastHiClkPhysMgmt = w4->PhysMgmt;
+ }
+ else
+ {
+ w4->PhysMgmt = data;
+ }
+ break;
+ }
+ case 10:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.10\n");
+ // SINGLESTEP("");
+ }
+ uint32_t mask = 0x10cc;
+ DEBUG_PRINT("MediaStatus\n");
+ w4->MediaStatus &= ~mask;
+ w4->MediaStatus |= data & mask;
+ w4->MediaStatus |= 0x8000; // auiDisable always on
+ break;
+ }
+ default:
+ DEBUG_PRINT("generic to window 4\n");
+ // SINGLESTEP("");
+ memcpy(&s->mWindows[4].b[port], &data, size);
+ }
+ break;
+ }
+ /**/
+ default:
+ DEBUG_PRINT("writing here unimpl.\n");
+ // SINGLESTEP("");
+ }
+}
+
+static void setCR(uint16_t cr, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT("setCR\n");
+ switch (cr & (31<<11))
+ {
+ case CmdTotalReset:
+ // FIXME: care about params
+ DEBUG_PRINT("TotalReset\n");
+ a3c90x_reset(opaque);
+ break;
+ case CmdSelectWindow:
+ {
+ DEBUG_PRINT("SelectWindow\n");
+ s->mIntStatus &= 0x1fff;
+ s->mIntStatus |= (cr & 7)<<13;
+ break;
+ }
+ case CmdTxReset:
+ DEBUG_PRINT("TxReset\n");
+ break;
+ case CmdRxReset:
+ DEBUG_PRINT("RxReset\n");
+ break;
+ case CmdSetIndicationEnable:
+ {
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ DEBUG_PRINT("SetIndicationEnable\n");
+ w5->IndicationEnable = cr & 0x7fe;
+ break;
+ }
+ case CmdSetIntrEnb:
+ {
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ DEBUG_PRINT("SetIntrEnab\n");
+ w5->InterruptEnable = cr & 0x7fe;
+ break;
+ }
+ case CmdStatsEnable:
+ /* implement me */
+ DEBUG_PRINT("StatsEnable\n");
+ break;
+ case CmdStatsDisable:
+ /* implement me */
+ DEBUG_PRINT("StatsDisable\n");
+ break;
+ case CmdEnableDC:
+ /* implement me */
+ DEBUG_PRINT("EnableDC\n");
+ break;
+ case CmdDisableDC:
+ /* implement me */
+ DEBUG_PRINT("DisableDC\n");
+ break;
+ case CmdStall:
+ {
+ /* FIXME: threading */
+ switch (cr & 3)
+ {
+ case 0: /* UpStall */
+ case 1: /* UpUnstall */
+ {
+ DEBUG_PRINT("Stall\n");
+ char stall = (!(cr & 1)) ? 1 : 0;
+ s->mUpStalled = stall;
+ checkUpWork(opaque);
+ break;
+ }
+ case 2: /* DnStall */
+ case 3: /* DnUnstall */
+ {
+ DEBUG_PRINT("Stall\n");
+ char stall = (!(cr & 1)) ? 1 : 0;
+ s->mDnStalled = stall;
+ s->mRegisters.DmaCtrl &= ~DC_dnStalled;
+ if (stall) s->mRegisters.DmaCtrl |= DC_dnStalled;
+ checkDnWork(opaque);
+ break;
+ }
+ }
+ break;
+ }
+ case CmdSetRxFilter:
+ {
+ DEBUG_PRINT("SetRxFilter\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->RxFilter = cr & 31;
+ break;
+ }
+ case CmdSetTxReclaimThresh:
+ {
+ DEBUG_PRINT("SetTxReclaimHash\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->TxReclaimThresh = cr & 255;
+ break;
+ }
+ case CmdSetTxStartThresh:
+ {
+ DEBUG_PRINT("SetTxStartTresh\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->TxStartThresh = (cr & 0x7ff) << 2;
+ break;
+ }
+ case CmdSetHashFilterBit:
+ {
+ /** TODO: implement */
+ // char value = (cr & 0x400) ? 1 : 0;
+ // uint32_t which = cr & 0x3f;
+ DEBUG_PRINT("SetHashFilterBit\n");
+ break;
+ }
+ case CmdSetRxEarlyThresh:
+ {
+ DEBUG_PRINT("SetTxStartTresh\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->RxEarlyThresh = (cr & 0x7ff) << 2;
+ break;
+ }
+ case CmdRxEnable:
+ {
+ DEBUG_PRINT("RxEnable\n");
+ s->mRxEnabled = 1;
+ break;
+ }
+ case CmdRxDisable:
+ {
+ DEBUG_PRINT("RxDisable\n");
+ s->mRxEnabled = 0;
+ break;
+ }
+ case CmdTxEnable:
+ {
+ DEBUG_PRINT("TxEnable\n");
+ s->mTxEnabled = 1;
+ break;
+ }
+ case CmdTxDisable:
+ {
+ DEBUG_PRINT("TxDisable\n");
+ s->mTxEnabled = 0;
+ break;
+ }
+ case CmdAckIntr:
+ {
+ /*
+ 0x1 interruptLatchAck
+ 0x2 linkEventAck
+ 0x20 rxEarlyAck
+ 0x40 intRequestedAck
+ 0x200 dnCompleteAck
+ 0x400 upCompleteAck
+
+ 0x5
+ */
+ DEBUG_PRINT("AckIntr\n");
+ // ack/clear corresponding bits in IntStatus
+ uint32_t ISack = 0;
+ if (cr & 0x01) ISack |= IS_interruptLatch;
+ if (cr & 0x02) ISack |= IS_linkEvent;
+ if (cr & 0x20) ISack |= IS_rxEarly;
+ if (cr & 0x40) ISack |= IS_intRequested;
+ if (cr & 0x200) ISack |= IS_dnComplete;
+ if (cr & 0x400) ISack |= IS_upComplete;
+ acknowledge(ISack, opaque);
+ break;
+ }
+ /* case CmdReqIntr: {
+ RegWindow5 &w5 = (RegWindow5&)mWindows[5];
+ // set intRequested in IntStatus
+ mIntStatus |= IS_intRequested;
+
+ // FIXME: generate Interrupt (if enabled)
+ break;
+ }*/
+
+ /*
+ case CmdTxDone:
+ case CmdRxDiscard:
+ case CmdSetTxThreshold:
+ */
+ default:
+ DEBUG_PRINT("command not implemented\n");
+ }
+}
+
+static void txDPD0(void *opaque, DPD0 *dpd)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT("txDPD0\n");
+
+ // FIXME: createHostStruct()
+ uint32_t fsh = dpd->FrameStartHeader;
+ DEBUG_PRINT_FORMAT(("fsh = %08x\n", fsh));
+ if (fsh & FSH_dpdEmpty)
+ {
+ // modify FrameStartHeader in DPD (!)
+ dpd->FrameStartHeader |= FSH_dnComplete;
+ // set next DnListPtr
+ s->mRegisters.DnListPtr = dpd->DnNextPtr;
+ DEBUG_PRINT("dpd empty\n");
+ return;
+ }
+ DPDFragDesc *frags = (DPDFragDesc*)(dpd+1);
+ uint8_t pbuf[MAX_PACKET_SIZE];
+ uint8_t *p = pbuf;
+
+ // some packet drivers need padding
+ // uint framePrefix = mEthTun->getWriteFramePrefix();
+ // memset(p, 0, framePrefix);
+ // p += framePrefix;
+
+ DEBUG_PRINT_FORMAT(("DPD: NextPtr = %x, FSH = %x, FragAddr = %x, FragLen = %x\n",
+ dpd->DnNextPtr, dpd->FrameStartHeader, frags->DnFragAddr, frags->DnFragLen));
+
+ //
+ uint32_t i = 0;
+ // assemble packet from fragments (up to MAX_DPD_FRAGS fragments)
+ while (i < MAX_DPD_FRAGS)
+ {
+ uint32_t addr = frags->DnFragAddr;
+ uint32_t len = frags->DnFragLen & 0x1fff;
+ if (p-pbuf+len >= sizeof pbuf)
+ {
+ DEBUG_PRINT("packet too big!\n");
+ // SINGLESTEP("");
+ return;
+ }
+ DEBUG_PRINT("dma_read\n");
+
+ if (len == 0 || addr == 0)
+ {
+ DEBUG_PRINT("No data! Bail!\n");
+ return;
+ }
+
+ cpu_physical_memory_read(addr, p, len);
+
+ DEBUG_PRINT_FORMAT((" - DnAddr = %x, DnFragLen = %x\n", frags->DnFragAddr, frags->DnFragLen));
+
+ p += len;
+ // last fragment ?
+ if (frags->DnFragLen & 0x80000000) break;
+ frags++;
+ i++;
+ }
+ uint32_t psize = p-pbuf;
+ if (!(fsh & FSH_rndupDefeat))
+ {
+ // round packet length
+ switch (fsh & FSH_rndupBndry)
+ {
+ case 0:
+ {
+ // 4 bytes
+ uint32_t gap = ((psize+3) & ~3) -psize;
+ memset(pbuf+psize, 0, gap);
+ psize += gap;
+ break;
+ }
+ case 2:
+ {
+ // 2 bytes
+ uint32_t gap = ((psize+1) & ~1) -psize;
+ memset(pbuf+psize, 0, gap);
+ psize += gap;
+ break;
+ }
+ }
+ }
+ //FSH_reArmDisable = 1<<23,
+ //FSH_lastKap = 1<<24,
+ //FSH_addIpChecksum = 1<<25,
+ //FSH_addTcpChecksum = 1<<26,
+ //FSH_addUdpChecksum = 1<<27,
+ if (fsh & (0x1f << 23))
+ {
+ DEBUG_PRINT("unsupported flags in fsh\n");
+ }
+
+ if (psize<60)
+ {
+ // pad packet to at least 60 bytes (+4 bytes crc = 64 bytes)
+ memset(pbuf+psize, 0, (60-psize));
+ psize = 60;
+ }
+ // append crc
+ if (!(fsh & FSH_crcAppendDisable))
+ {
+#ifdef A3C90x_CALCULATE_TXCRC
+ uint32_t crc = crc32(0, pbuf, psize);
+#else
+ uint32_t crc = 0;
+#endif
+ pbuf[psize+0] = crc;
+ pbuf[psize+1] = crc>>8;
+ pbuf[psize+2] = crc>>16;
+ pbuf[psize+3] = crc>>24;
+ psize += 4;
+ DEBUG_PRINT("crc complete\n");
+ }
+
+ AUX_DEBUG_PRINT("3C90X: Packet sent\n");
+
+ qemu_send_packet(s->vc, pbuf, psize);
+
+ // indications
+ s->mRegisters.DmaCtrl |= DC_dnComplete;
+ uint8_t txStatus = 0;
+ uint32_t inds = 0;
+ if (fsh & FSH_dnIndicate) inds |= IS_dnComplete;
+ if (fsh & FSH_txIndicate)
+ {
+ inds |= IS_txComplete;
+ txStatus |= (1 << 6);
+ }
+
+ // transmit complete
+ txStatus |= (1 << 7);
+
+ indicate(inds, opaque);
+ // modify FrameStartHeader in DPD (!)
+ dpd->FrameStartHeader |= FSH_dnComplete;
+ // set next DnListPtr, TxPktId
+ s->mRegisters.DnListPtr = dpd->DnNextPtr;
+ uint32_t pktId = (fsh & FSH_pktId) >> 2;
+ s->mRegisters.TxPktId = pktId;
+ s->mRegisters.TxStatus = txStatus;
+ // maybe generate interrupt
+ maybeRaiseIntr(opaque);
+}
+
+static char passesRxFilter(uint8_t *pbuf, uint32_t psize, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ EthFrameII *f = (EthFrameII*) pbuf;
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ if (w5->RxFilter & RXFILT_receiveAllFrames) return 1;
+ // FIXME: Multicast hashing not implemented
+ if (w5->RxFilter & RXFILT_receiveMulticastHash) return 1;
+ // FIXME: Multicasting not understood
+ if (w5->RxFilter & RXFILT_receiveMulticast) return 1;
+ if (w5->RxFilter & RXFILT_receiveBroadcast)
+ {
+ uint8_t broadcastMAC[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+ if (compareMACs(f->destMAC, broadcastMAC) == 0) return 1;
+ }
+ if (w5->RxFilter & RXFILT_receiveIndividual)
+ {
+ uint8_t destMAC[6];
+ uint8_t thisMAC[6];
+ RegWindow2 *w2 = (RegWindow2*) &s->mWindows[2];
+ uint32_t i;
+ for (i = 0; i < 6; i++)
+ {
+ destMAC[i] = f->destMAC[i] & ~w2->StationMask[i];
+ thisMAC[i] = w2->StationAddress[i] & ~w2->StationMask[i];
+ }
+ return (compareMACs(destMAC, thisMAC) == 0) ? 1 : 0;
+ }
+ return 0;
+}
+
+static void rxUPD(void *opaque, UPD *upd)
+{
+ A3C90XState *s = opaque;
+
+ // FIXME: threading to care about (mRegisters.DmaCtrl & DC_upAltSeqDisable)
+ DEBUG_PRINT("rxUPD()\n");
+
+ char error = 0;
+
+ if (upd->UpPktStatus & UPS_upComplete)
+ {
+ // IO_3C90X_WARN("UPD already upComplete!\n");
+
+ // the top of the ring buffer is already used,
+ // stall the upload and throw away the packet.
+ // the ring buffers are filled.
+
+ s->mUpStalled = 1;
+ return;
+ }
+
+ uint32_t upPktStatus = 0;
+
+ if (s->mRegisters.UpPoll)
+ {
+ DEBUG_PRINT("UpPoll unsupported\n");
+ // SINGLESTEP("");
+ return;
+ }
+ // FIXME:
+// if (mRegisters.DmaCtrl & DC_upRxEarlyEnable)
+// IO_3C90X_ERR("DC_upRxEarlyEnable unsupported\n");
+
+ if ((s->mRxPacketSize > 0x1fff) || (s->mRxPacketSize > sizeof s->mRxPacket))
+ {
+ DEBUG_PRINT("oversized frame\n");
+ upd->UpPktStatus = UPS_upError | UPS_oversizedFrame;
+ error = 1;
+ }
+
+ if (s->mRxPacketSize < 60)
+ {
+ // pad packet to at least 60 bytes (+4 bytes crc = 64 bytes)
+ memset(s->mRxPacket+s->mRxPacketSize, 0, (60-s->mRxPacketSize));
+ s->mRxPacketSize = 60;
+ }
+
+ /* RegWindow5 &w5 = (RegWindow5&)mWindows[5];
+ if ((mRxPacketSize < 60) && (w5.RxEarlyThresh >= 60)) {
+ IO_3C90X_TRACE("runt frame\n");
+ upPktStatus |= UPS_upError | UPS_runtFrame;
+ upd->UpPktStatus = upPktStatus;
+ error = true;
+ }*/
+ if (upd->UpPktStatus & UPD_impliedBufferEnable)
+ {
+ DEBUG_PRINT("UPD_impliedBufferEnable unsupported\n");
+ // SINGLESTEP("");
+ return;
+ }
+ UPDFragDesc *frags = (UPDFragDesc*)(upd+1);
+
+ uint8_t *p = s->mRxPacket;
+ uint32_t i = 0;
+ while (!error && i < MAX_UPD_FRAGS) // (up to MAX_UPD_FRAGS fragments)
+ {
+ uint32_t addr = frags->UpFragAddr;
+ uint32_t len = frags->UpFragLen & 0x1fff;
+ if (p-s->mRxPacket+len > sizeof s->mRxPacket)
+ {
+ upPktStatus |= UPS_upError | UPS_upOverflow;
+ upd->UpPktStatus = upPktStatus;
+ DEBUG_PRINT("UPD overflow!\n");
+ // SINGLESTEP("");
+ error = 1;
+ break;
+ }
+
+ cpu_physical_memory_write(addr, p, len);
+
+ p += len;
+ // last fragment ?
+ if (frags->UpFragLen & 0x80000000) break;
+ frags++;
+ i++;
+ }
+
+ if (!error)
+ {
+ DEBUG_PRINT("successfully uploaded packet\n");
+ }
+ upPktStatus |= s->mRxPacketSize & 0x1fff;
+ upPktStatus |= UPS_upComplete;
+ upd->UpPktStatus = upPktStatus;
+
+ s->mRxPacketSize = 0;
+
+ /* The client OS is waiting for a change in status, but won't see it
+ * until we dma our local copy upd->UpPktStatus back to the client address space
+ */
+ cpu_physical_memory_write(s->mRegisters.UpListPtr + 4, (const uint8_t*) &(upd->UpPktStatus), sizeof(upd->UpPktStatus));
+
+ s->mRegisters.UpListPtr = upd->UpNextPtr;
+
+ // Indications
+ s->mRegisters.DmaCtrl |= DC_upComplete;
+ indicate(IS_upComplete, opaque);
+ maybeRaiseIntr(opaque);
+}
+
+static void indicate(uint32_t indications, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ if ((w5->IndicationEnable & indications) != indications)
+ {
+ DEBUG_PRINT("some masked\n");
+ }
+ s->mIntStatus |= w5->IndicationEnable & indications;
+ if (indications & IS_upComplete)
+ {
+ s->mRegisters.DmaCtrl |= DC_upComplete;
+ }
+ if (indications & IS_dnComplete)
+ {
+ s->mRegisters.DmaCtrl |= DC_dnComplete;
+ }
+}
+
+static void acknowledge(uint32_t indications, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT_FORMAT(("intStatus was %x [indications=%x]\n", s->mIntStatus, indications));
+ s->mIntStatus &= ~indications;
+ DEBUG_PRINT_FORMAT(("intStatus is now %x\n", s->mIntStatus));
+ if (indications & IS_upComplete)
+ {
+ s->mRegisters.DmaCtrl &= ~DC_upComplete;
+ }
+ if (indications & IS_dnComplete)
+ {
+ s->mRegisters.DmaCtrl &= ~DC_dnComplete;
+ }
+
+ // lower the irq line now that the IRQ is ack'd
+ qemu_set_irq(s->pci_dev->irq[0], 0);
+}
+
+static void maybeRaiseIntr(void *opaque)
+{
+ A3C90XState *s = opaque;
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+
+ DEBUG_PRINT("maybeRaiseIntr\n");
+ DEBUG_PRINT_FORMAT(("IndEnable = %x, IntEnable = %x, IntStatus = %x\n", w5->IndicationEnable, w5->InterruptEnable, s->mIntStatus));
+
+ if (w5->IndicationEnable & w5->InterruptEnable & s->mIntStatus)
+ {
+ s->mIntStatus |= IS_interruptLatch;
+
+ DEBUG_PRINT("Generating interrupt!\n");
+
+ // raise the IRQ line
+ qemu_set_irq(s->pci_dev->irq[0], 1);
+ }
+ else
+ {
+ // lower the IRQ line
+ qemu_set_irq(s->pci_dev->irq[0], 0);
+ }
+}
+
+static void checkDnWork(void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ while (!s->mDnStalled && (s->mRegisters.DnListPtr != 0))
+ {
+ uint8_t dpd[512];
+
+ cpu_physical_memory_read(s->mRegisters.DnListPtr, dpd, sizeof dpd);
+
+ uint8_t type = dpd[7] >> 6;
+ switch (type)
+ {
+ case 0:
+ case 2:
+ {
+ DPD0 *p = (DPD0*) dpd;
+ DEBUG_PRINT("Got a type 0 DPD!\n");
+ txDPD0(opaque, p);
+ break;
+ }
+ case 1:
+ {
+ DEBUG_PRINT("Got a type 1 DPD! Not implemented!\n");
+ s->mRegisters.DnListPtr = 0;
+ break;
+ }
+ default:
+ DEBUG_PRINT("Unsupported packet type\n");
+ s->mRegisters.DnListPtr = 0;
+ break;
+ };
+
+ break;
+ }
+}
+
+static void checkUpWork(void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ if (s->mRxEnabled && !s->mUpStalled && s->mRxPacketSize && (s->mRegisters.UpListPtr != 0))
+ {
+ uint8_t upd[MAX_UPD_SIZE];
+
+ cpu_physical_memory_read(s->mRegisters.UpListPtr, upd, sizeof upd);
+ UPD *p = (UPD*) upd;
+ rxUPD(opaque, p);
+
+ }
+ else
+ {
+ DEBUG_PRINT("Not uploading\n");
+ DEBUG_PRINT_FORMAT(("rxEnabled = %x, upStalled = %x, RX Packet size = %x, Up list ptr = %x\n",
+ s->mRxEnabled, s->mUpStalled, s->mRxPacketSize, s->mRegisters.UpListPtr));
+ }
+}
+
+static void handle_rx(void *opaque, const uint8_t *buf, int size)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT_FORMAT(("3c90x: handle_rx (%d bytes)\n", size));
+ if (s->mRxPacketSize)
+ {
+ DEBUG_PRINT("Old packet not yet uploaded!\n");
+ }
+ else
+ {
+ s->mRxPacketSize = size;
+ memcpy(s->mRxPacket, buf, size);
+ if (s->mRxEnabled && (s->mRxPacketSize > sizeof(EthFrameII)))
+ {
+ indicate(IS_rxComplete, opaque);
+ maybeRaiseIntr(opaque);
+ acknowledge(IS_rxComplete, opaque);
+ if (!passesRxFilter(s->mRxPacket, s->mRxPacketSize, opaque))
+ {
+ DEBUG_PRINT_FORMAT(("Received %d bytes, but they don't pass the filter!\n", s->mRxPacketSize));
+ s->mRxPacketSize = 0;
+ }
+ else
+ {
+ // and now, we do some extra debugging output...
+#ifdef DEBUG_3C90X_ANALYSE_FRAMES
+ EthFrameII *ethFrame = (EthFrameII*) s->mRxPacket;
+ AUX_DEBUG_PRINT_FORMAT(("Incoming packet information [%d bytes]:\n", s->mRxPacketSize));
+ if (ethFrame->type[0] == 8)
+ {
+ if (ethFrame->type[1] == 0x06)
+ AUX_DEBUG_PRINT("ARP\n");
+ else if (ethFrame->type[1] == 0)
+ {
+ struct ip *ipHeader = (struct ip*) (s->mRxPacket + sizeof(EthFrameII));
+ if (ipHeader->ip_p == 0x11)
+ {
+ struct udphdr *udpHeader = (struct udphdr*) (s->mRxPacket + sizeof(EthFrameII) + (ipHeader->ip_hl * 4));
+ AUX_DEBUG_PRINT_FORMAT(("UDP: src=%d dest=%d\n", ntohs(udpHeader->uh_sport), ntohs(udpHeader->uh_dport)));
+ }
+ else if (ipHeader->ip_p == 0x06)
+ {
+ struct tcphdr *tcpHeader = (struct tcphdr*) (s->mRxPacket + sizeof(EthFrameII) + (ipHeader->ip_hl * 4));
+ AUX_DEBUG_PRINT_FORMAT(("TCP: src=%d dest=%d ", ntohs(tcpHeader->th_sport), ntohs(tcpHeader->th_dport)));
+ AUX_DEBUG_PRINT("flags =");
+ if (tcpHeader->th_flags & TH_FIN)
+ AUX_DEBUG_PRINT(" FIN");
+ if (tcpHeader->th_flags & TH_SYN)
+ AUX_DEBUG_PRINT(" SYN");
+ if (tcpHeader->th_flags & TH_RST)
+ AUX_DEBUG_PRINT(" RST");
+ if (tcpHeader->th_flags & TH_PUSH)
+ AUX_DEBUG_PRINT(" PSH");
+ if (tcpHeader->th_flags & TH_ACK)
+ AUX_DEBUG_PRINT(" ACK");
+ if (tcpHeader->th_flags & TH_URG)
+ AUX_DEBUG_PRINT(" URG");
+ AUX_DEBUG_PRINT_FORMAT((" seq=%d ack=%d", ntohl(tcpHeader->th_seq), ntohl(tcpHeader->th_ack)));
+ AUX_DEBUG_PRINT("\n");
+ }
+ }
+ }
+ else
+ AUX_DEBUG_PRINT("(can't inspect)\n");
+#endif
+ DEBUG_PRINT_FORMAT(("Received %d bytes!\n", s->mRxPacketSize));
+ }
+ }
+ else
+ {
+ DEBUG_PRINT_FORMAT(("Oops - RxEnabled = %x, packetSize = %d [eth=%d]\n", s->mRxEnabled, s->mRxPacketSize, sizeof(EthFrameII)));
+ s->mRxPacketSize = 0;
+ }
+ }
+ checkUpWork(opaque);
+}
+
+static int a3c90x_can_receive(void *opaque)
+{
+ A3C90XState *s = opaque;
+ if (s->mRxEnabled)
+ {
+ if (s->mRxPacketSize)
+ {
+ // If there's already a packet there, try and upload it again
+ DEBUG_PRINT("Old packet not yet uploaded!\n");
+ checkUpWork(opaque);
+ return 0;
+ }
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void a3c90x_receive(void *opaque, const uint8_t *buf, int size)
+{
+ AUX_DEBUG_PRINT("3C90X: Packet received\n");
+ handle_rx(opaque, buf, size);
+}
+
+static uint32_t a3c90x_io_readx(void *opaque, uint8_t port, int size)
+{
+ A3C90XState *s = opaque;
+ uint32_t data = 0;
+
+ if (port == 0xe)
+ {
+ // IntStatus (no matter which window)
+ if (size != 2)
+ {
+ DEBUG_PRINT("unaligned read from IntStatus\n");
+ }
+ DEBUG_PRINT("read IntStatus\n");
+ return s->mIntStatus;
+ }
+ else if (port >= 0 && (port+size <= 0x0e))
+ {
+ // read from window
+ uint32_t curwindow = s->mIntStatus >> 13;
+ return readRegWindow(opaque, curwindow, port, data, size);
+ }
+ else if ((port+size > 0x1e) && (port <= 0x1f))
+ {
+ if ((port != 0x1e) || (size != 2))
+ {
+ DEBUG_PRINT("unaligned read from IntStatusAuto\n");
+ }
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ // side-effects of reading IntStatusAuto:
+ // 1.clear InterruptEnable
+ w5->InterruptEnable = 0;
+ // 2.clear some flags
+ acknowledge(IS_dnComplete | IS_upComplete
+ | IS_rxEarly | IS_intRequested
+ | IS_interruptLatch | IS_linkEvent, opaque);
+ DEBUG_PRINT("read IntStatusAuto\n");
+ return s->mIntStatus;
+ }
+ else if ((port >= 0x10) && (port+size <= 0x10 + sizeof(Registers)))
+ {
+ uint8_t l = gRegAccess[port-0x10];
+ if (l != size)
+ {
+ DEBUG_PRINT("invalid/unaligned read\n");
+ }
+ // read from (standard) register
+ memcpy(&data, ((uint8_t*)&s->mRegisters)+port-0x10, size);
+ switch (port)
+ {
+ case 0x1a:
+ DEBUG_PRINT("read Timer\n");
+ break;
+ case 0x20:
+ DEBUG_PRINT("read DmaCtrl\n");
+ break;
+ case 0x24:
+ DEBUG_PRINT("read DownListPtr\n");
+ break;
+ case 0x38:
+ DEBUG_PRINT("read UpListPtr\n");
+ break;
+ default:
+ DEBUG_PRINT("read reg\n");
+ break;
+ }
+ return data;
+ }
+ return 0;
+}
+
+static void a3c90x_io_writex(void *opaque, uint8_t port, uint32_t data, int size)
+{
+ A3C90XState *s = opaque;
+
+ if (port == 0xe)
+ {
+ // CommandReg (no matter which window)
+ if (size != 2)
+ {
+ DEBUG_PRINT("unaligned write to CommandReg\n");
+ }
+ setCR(data, opaque);
+ }
+ else if (port >= 0 && (port+size <= 0x0e))
+ {
+ // write to window
+ uint32_t curwindow = s->mIntStatus >> 13;
+ writeRegWindow(opaque, curwindow, port, data, size);
+ }
+ else if (port >= 0x10 && (port + size <= 0x10 + sizeof(Registers)))
+ {
+ uint8_t l = gRegAccess[port-0x10];
+ if (l != size)
+ {
+ DEBUG_PRINT("invalid/unaligned write to register\n");
+ }
+ switch (port)
+ {
+ case 0x20:
+ {
+ uint32_t DmaCtrlRWMask = DC_upRxEarlyEnable | DC_counterSpeed |
+ DC_countdownMode | DC_defeatMWI | DC_defeatMRL |
+ DC_upOverDiscEnable;
+ s->mRegisters.DmaCtrl &= ~DmaCtrlRWMask;
+ s->mRegisters.DmaCtrl |= data & DmaCtrlRWMask;
+ DEBUG_PRINT("write DmaCtrl\n");
+ break;
+ }
+ case 0x24:
+ {
+ if (!s->mRegisters.DnListPtr)
+ {
+ s->mRegisters.DnListPtr = data;
+ DEBUG_PRINT("write DnListPtr\n");
+ }
+ else
+ {
+ DEBUG_PRINT("didn't write DnListPtr cause it's not 0\n");
+ }
+ checkDnWork(opaque);
+ break;
+ }
+ case 0x38:
+ {
+ s->mRegisters.UpListPtr = data;
+ DEBUG_PRINT("write UpListPtr\n");
+ checkUpWork(opaque);
+ break;
+ }
+ case 0x2d:
+ DEBUG_PRINT("DnPoll\n");
+ // SINGLESTEP("");
+ break;
+ case 0x2a:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write DnBurstThresh\n");
+ break;
+ case 0x2c:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write DnPriorityThresh\n");
+ break;
+ case 0x2f:
+ // used by Darwin as TxFreeThresh. Not documented in [1].
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write TxFreeThresh\n");
+ break;
+ case 0x3c:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write UpPriorityThresh\n");
+ break;
+ case 0x3e:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write UpBurstThresh\n");
+ break;
+ case 0x1b:
+ if (size != 1)
+ {
+ DEBUG_PRINT("wrong size for write to TxStatus\n");
+ return;
+ }
+ DEBUG_PRINT_FORMAT(("Writing %x to TxStatus\n", data));
+ s->mRegisters.TxStatus = data;
+
+ // acknowledge the relevant bits
+ acknowledge(IS_txComplete, opaque);
+ // | IS_dnComplete | IS_cmdInProgress
+
+ // NOTE: "An I/O write of an arbitrary value to TxStatus advances the queue to the next transmit status byte."
+ // We also need to keep the queue of TX Status bytes!
+
+ // maybeRaiseIntr(opaque);
+ break;
+ default:
+ DEBUG_PRINT("write to register\n");
+ // SINGLESTEP("");
+ // write to (standard) register
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ }
+ }
+}
+
+static void a3c90x_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 1);
+}
+
+static void a3c90x_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 2);
+}
+
+static void a3c90x_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 4);
+}
+
+static uint32_t a3c90x_io_readb(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 1);
+}
+
+static uint32_t a3c90x_io_readw(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 2);
+}
+
+static uint32_t a3c90x_io_readl(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 4);
+}
+
+/* */
+
+static void a3c90x_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t a3c90x_ioport_readb(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_ioport_readw(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readw(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_ioport_readl(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readl(opaque, addr & 0xFF);
+}
+
+/* */
+
+static void a3c90x_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ a3c90x_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ a3c90x_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ a3c90x_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t a3c90x_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return a3c90x_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = a3c90x_io_readw(opaque, addr & 0xFF);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t a3c90x_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = a3c90x_io_readl(opaque, addr & 0xFF);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
+/***********************************************************/
+/* PCI 3C90x definitions */
+
+typedef struct PCI3C90XState
+{
+ PCIDevice dev;
+ A3C90XState a3c90x;
+} PCI3C90XState;
+
+static void a3c90x_mmio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCI3C90XState *d = (PCI3C90XState *)pci_dev;
+ A3C90XState *s = &d->a3c90x;
+
+ cpu_register_physical_memory(addr + 0, 0x100, s->a3c90x_mmio_io_addr);
+}
+
+static void a3c90x_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCI3C90XState *d = (PCI3C90XState *)pci_dev;
+ A3C90XState *s = &d->a3c90x;
+
+ register_ioport_write(addr, 0x100, 1, a3c90x_ioport_writeb, s);
+ register_ioport_read( addr, 0x100, 1, a3c90x_ioport_readb, s);
+
+ register_ioport_write(addr, 0x100, 2, a3c90x_ioport_writew, s);
+ register_ioport_read( addr, 0x100, 2, a3c90x_ioport_readw, s);
+
+ register_ioport_write(addr, 0x100, 4, a3c90x_ioport_writel, s);
+ register_ioport_read( addr, 0x100, 4, a3c90x_ioport_readl, s);
+}
+
+static CPUReadMemoryFunc *a3c90x_mmio_read[3] =
+{
+ a3c90x_mmio_readb,
+ a3c90x_mmio_readw,
+ a3c90x_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *a3c90x_mmio_write[3] =
+{
+ a3c90x_mmio_writeb,
+ a3c90x_mmio_writew,
+ a3c90x_mmio_writel,
+};
+
+static inline int64_t a3c90x_get_next_tctr_time(A3C90XState *s, int64_t current_time)
+{
+ int64_t next_time = current_time +
+ muldiv64(1, ticks_per_sec, PCI_FREQUENCY);
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+void a3c90x_cleanup(VLANClientState *vc)
+{
+ DEBUG_PRINT("3C90X: Cleanup not implemented\n");
+}
+
+PCIDevice *pci_a3c90x_init(PCIBus *bus, NICInfo *nd, int devfn)
+{
+ PCI3C90XState *d;
+ A3C90XState *s;
+ uint8_t *pci_conf;
+
+ d = (PCI3C90XState *)pci_register_device(bus,
+ "3C90x", sizeof(PCI3C90XState),
+ devfn,
+ NULL, NULL);
+ pci_conf = d->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_3COM);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_3C90X);
+ pci_conf[0x04] = 0x07; /* command = I/O space, Bus Master */
+ pci_conf[0x08] = 0;
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ pci_conf[0x0e] = 0x00; /* header_type */
+ pci_conf[0x3d] = 1; /* interrupt pin 0 */
+ pci_conf[0x34] = 0xdc;
+
+ pci_conf[0x3e] = 5;
+ pci_conf[0x3f] = 48;
+
+ s = &d->a3c90x;
+
+ /* I/O handler for memory-mapped I/O */
+ s->a3c90x_mmio_io_addr =
+ cpu_register_io_memory(0, a3c90x_mmio_read, a3c90x_mmio_write, s);
+
+ pci_register_io_region(&d->dev, 0, 0x100,
+ PCI_ADDRESS_SPACE_IO, a3c90x_ioport_map);
+
+ pci_register_io_region(&d->dev, 1, 0x100,
+ PCI_ADDRESS_SPACE_MEM, a3c90x_mmio_map);
+
+ s->pci_dev = (PCIDevice *)d;
+ memcpy(s->mMAC, nd->macaddr, 6);
+ a3c90x_reset(s);
+ s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
+ a3c90x_receive, a3c90x_can_receive, a3c90x_cleanup, s);
+
+ qemu_format_nic_info_str(s->vc, s->mMAC);
+
+ //register_savevm("3c90x", -1, 4, a3c90x_save, a3c90x_load, s);
+
+ return (PCIDevice *)d;
+}
+
diff --git a/hw/pci.c b/hw/pci.c
index bfd3942..e8603df 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -783,6 +783,7 @@ static const char * const pci_nic_models[] = {
"i82557b",
"i82559er",
"rtl8139",
+ "3c90x",
"e1000",
"pcnet",
"virtio",
@@ -797,6 +798,7 @@ static PCINICInitFn pci_nic_init_fns[] = {
pci_i82557b_init,
pci_i82559er_init,
pci_rtl8139_init,
+ pci_a3c90x_init,
pci_e1000_init,
pci_pcnet_init,
virtio_net_init,
diff --git a/hw/pci.h b/hw/pci.h
index 4a30d98..18bedfe 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -242,6 +242,9 @@ PCIDevice *pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn);
PCIDevice *pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn);
+/* 3c90x.c */
+PCIDevice *pci_a3c90x_init(PCIBus *bus, NICInfo *nd, int devfn);
+
/* e1000.c */
PCIDevice *pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn);
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 427fcd5..4f7b788 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -73,6 +73,9 @@
#define PCI_VENDOR_ID_REALTEK 0x10ec
#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_DEVICE_ID_3C90X 0x9200
+
#define PCI_VENDOR_ID_XILINX 0x10ee
#define PCI_VENDOR_ID_MARVELL 0x11ab
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] Re: PATCH: Add 3C90X emulation
2009-06-02 11:10 ` [Qemu-devel] " Matthew Iselin
@ 2009-06-02 22:19 ` Sebastian Herbszt
[not found] ` <f88ae150906021635k34fcbeebx5e533c49d6f1875a@mail.gmail.com>
0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Herbszt @ 2009-06-02 22:19 UTC (permalink / raw)
To: Matthew Iselin, qemu-devel
Matthew Iselin wrote:
> Sebastian wrote:
>> The patch unfortunatelly no longer applies. Can you please rebase on top of
>> git master
>> and resend?
>
> Hi,
>
> The attached patch now applies to the latest git master.
Still no go here. Can you please state on top of which commit it does apply?
- Sebastian
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] Re: PATCH: Add 3C90X emulation
[not found] ` <f88ae150906031520nf8a724ck3f42162a36b3a1ff@mail.gmail.com>
@ 2009-07-02 10:01 ` Matthew Iselin
2009-07-02 10:21 ` Kevin Wolf
0 siblings, 1 reply; 7+ messages in thread
From: Matthew Iselin @ 2009-07-02 10:01 UTC (permalink / raw)
To: qemu-devel
Hi,
After a long break in which I wasn't able to actually get access to a
system to create the patch on, I present this patch, which applies
cleanly to commit 3da6abd472200bc30b88d5a900ad316d9517d163 ("Use
pstrcpy and pstrcat to avoid OpenBSD linker warning").
Thanks,
Matthew
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] Re: PATCH: Add 3C90X emulation
2009-07-02 10:01 ` Matthew Iselin
@ 2009-07-02 10:21 ` Kevin Wolf
2009-07-02 10:39 ` Matthew Iselin
0 siblings, 1 reply; 7+ messages in thread
From: Kevin Wolf @ 2009-07-02 10:21 UTC (permalink / raw)
To: Matthew Iselin; +Cc: qemu-devel
Matthew Iselin schrieb:
> Hi,
>
> After a long break in which I wasn't able to actually get access to a
> system to create the patch on, I present this patch, which applies
> cleanly to commit 3da6abd472200bc30b88d5a900ad316d9517d163 ("Use
> pstrcpy and pstrcat to avoid OpenBSD linker warning").
I think you forgot something... ;-)
Also, if you want the patch to merged you should send a proper patch
description that can serve as a commit message, including a
Signed-off-by line. You don't seem to have included this in the previous
attempts.
Kevin
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] Re: PATCH: Add 3C90X emulation
2009-07-02 10:21 ` Kevin Wolf
@ 2009-07-02 10:39 ` Matthew Iselin
0 siblings, 0 replies; 7+ messages in thread
From: Matthew Iselin @ 2009-07-02 10:39 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 390 bytes --]
On Thu, Jul 2, 2009 at 8:21 PM, Kevin Wolf<kwolf@redhat.com> wrote:
> I think you forgot something... ;-)
> Also, if you want the patch to merged you should send a proper patch
> description that can serve as a commit message, including a
> Signed-off-by line. You don't seem to have included this in the previous
> attempts.
>
> Kevin
>
Hi,
Whoops! New patch attached.
Cheers,
Matthew
[-- Attachment #2: 3C90x-emulation.patch --]
[-- Type: application/octet-stream, Size: 73396 bytes --]
This patch adds an emulation of the 3Com 3C90x series of NICs to QEMU.
Signed-off-by: Matthew Iselin <matthew@theiselins.net>
---
Makefile.target | 1 +
hw/3c90x.c | 2408 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pci.c | 2 +
hw/pci_ids.h | 3 +
4 files changed, 2414 insertions(+), 0 deletions(-)
create mode 100644 hw/3c90x.c
diff --git a/Makefile.target b/Makefile.target
index a593503..eb45199 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -555,6 +555,7 @@ obj-y += eepro100.o
obj-y += ne2000.o
obj-y += pcnet.o
obj-y += rtl8139.o
+obj-y += 3c90x.o
obj-y += e1000.o
# Generic watchdog support and some watchdog devices
diff --git a/hw/3c90x.c b/hw/3c90x.c
new file mode 100644
index 0000000..bcbe5a4
--- /dev/null
+++ b/hw/3c90x.c
@@ -0,0 +1,2408 @@
+/**
+ * QEMU 3C90X Emulation
+ *
+ * Copyright (c) 2009 Matthew Iselin (QEMU VERSION)
+ * Copyright (C) 2004 John Kelley (pearpc@kelley.ca) (PEARPC VERSION)
+ * Copyright (C) 2003 Stefan Weyergraf (PEARPC VERSION)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+ * Modifications:
+ * (none)
+ */
+
+/**
+ * TODO:
+ * - Still a lot of unimplemented functionality
+ * - On-chip TCP checksum emulation
+ * - savevm functions
+ * TESTED ON:
+ * - Damn Small Linux (4.4.10)
+ * - Ubuntu (8.10, 5.10)
+ * - Pedigree
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "net.h"
+
+// Should we inspect incoming frames and print extra debugging information about them?
+//#define DEBUG_3C90X_ANALYSE_FRAMES 0
+
+#ifdef DEBUG_3C90X_ANALYSE_FRAMES
+#define __FAVOR_BSD
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#endif
+
+#define PACKED __attribute__((packed));
+#define ALIGNED __attribute__((aligned));
+#define PACKED8 __attribute__((aligned(8)));
+#define PACKED16 __attribute__((aligned(16)));
+#define PACKED32 __attribute__((aligned(32)));
+
+/* debug 3C90X card */
+// #define DEBUG_3C90X 1
+
+/* define this to output TX and RX events */
+// #define DEBUG_3C90X_AUX 1
+
+#define PCI_FREQUENCY 33000000L
+
+/* Calculate CRCs properly on Tx packets */
+#define A3C90X_CALCULATE_TXCRC 1
+
+#if defined(A3C90x_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#if defined (DEBUG_3C90X)
+# define DEBUG_PRINT(x) do { printf(x) ; } while (0)
+# define DEBUG_PRINT_FORMAT(x) do { printf x ; } while (0)
+#else
+# define DEBUG_PRINT(x)
+# define DEBUG_PRINT_FORMAT(x)
+#endif
+
+#if defined (DEBUG_3C90X_AUX)
+# define AUX_DEBUG_PRINT(x) do { printf(x) ; } while (0)
+# define AUX_DEBUG_PRINT_FORMAT(x) do { printf x ; } while (0)
+#else
+# define AUX_DEBUG_PRINT(x) DEBUG_PRINT(x)
+# define AUX_DEBUG_PRINT_FORMAT(x) DEBUG_PRINT_FORMAT(x)
+#endif
+
+#define MAX_PACKET_SIZE 16384
+
+enum Command
+{
+ CmdTotalReset = 0<<11,
+ CmdSelectWindow = 1<<11,
+ CmdEnableDC = 2<<11, // CmdStartCoax
+ CmdRxDisable = 3<<11,
+ CmdRxEnable = 4<<11,
+ CmdRxReset = 5<<11,
+ CmdStall = 6<<11,
+ CmdTxDone = 7<<11,
+ CmdRxDiscard = 8<<11,
+ CmdTxEnable = 9<<11,
+ CmdTxDisable = 10<<11,
+ CmdTxReset = 11<<11,
+ CmdReqIntr = 12<<11, // CmdFakeIntr
+ CmdAckIntr = 13<<11,
+ CmdSetIntrEnb = 14<<11,
+ CmdSetIndicationEnable = 15<<11, // CmdSetStatusEnb
+ CmdSetRxFilter = 16<<11,
+ CmdSetRxEarlyThresh = 17<<11,
+ CmdSetTxThreshold = 18<<11, // aka TxAgain ?
+ CmdSetTxStartThresh = 19<<11, // set TxStartTresh
+ // CmdStartDMAUp = 20<<11,
+ // CmdStartDMADown = (20<<11)+1,
+ CmdStatsEnable = 21<<11,
+ CmdStatsDisable = 22<<11,
+ CmdDisableDC = 23<<11, // CmdStopCoax
+ CmdSetTxReclaimThresh = 24<<11,
+ CmdSetHashFilterBit = 25<<11
+};
+
+/*
+ * IntStatusBits
+ */
+enum IntStatusBits
+{
+ IS_interruptLatch = 1<<0,
+ IS_hostError = 1<<1,
+ IS_txComplete = 1<<2,
+ /* bit 3 is unspecified */
+ IS_rxComplete = 1<<4,
+ IS_rxEarly = 1<<5,
+ IS_intRequested = 1<<6,
+ IS_updateStats = 1<<7,
+ IS_linkEvent = 1<<8,
+ IS_dnComplete = 1<<9,
+ IS_upComplete = 1<<10,
+ IS_cmdInProgress = 1<<11,
+ /* bit 12 is unspecified */
+ /* [15:13] is currently selected window */
+};
+
+/*
+ * DmaCtrlBits ([1] p.96)
+ */
+enum DmaCtrlBits
+{
+ /* bit 0 unspecified */
+ DC_dnCmplReq = 1<<1,
+ DC_dnStalled = 1<<2,
+ DC_upComplete = 1<<3, // FIXME: same as in IntStatus, but always visible
+ DC_dnComplete = 1<<4, // same as above ^^^
+ DC_upRxEarlyEnable = 1<<5,
+ DC_armCountdown = 1<<6,
+ DC_dnInProg = 1<<7,
+ DC_counterSpeed = 1<<8,
+ DC_countdownMode = 1<<9,
+ /* bits 10-15 unspecified */
+ DC_upAltSeqDisable = 1<<16,
+ DC_dnAltSeqDisable = 1<<17,
+ DC_defeatMWI = 1<<20,
+ DC_defeatMRL = 1<<21,
+ DC_upOverDiscEnable = 1<<22,
+ DC_targetAbort = 1<<30,
+ DC_masterAbort = 1<<31
+};
+
+/*
+ * MII Registers
+ * TODO: Implement MII properly
+ */
+/*enum MIIControlBits {
+ MIIC_collision = 1<<7,
+ MIIC_fullDuplex = 1<<8,
+ MIIC_restartNegote = 1<<9,
+ MIIC_collision = 1<<7,
+ rest missing
+};*/
+
+struct MIIRegisters
+{
+ uint16_t control;
+ uint16_t status;
+ uint16_t id0;
+ uint16_t id1;
+ uint16_t advert;
+ uint16_t linkPartner;
+ uint16_t expansion;
+ uint16_t nextPage;
+} PACKED;
+typedef struct MIIRegisters MIIRegisters;
+
+/*
+ * Registers
+ */
+union RegWindow
+{
+ uint8_t b[16];
+ uint16_t u16[8];
+} PACKED;
+typedef union RegWindow RegWindow;
+
+struct Registers
+{
+ // 0x10 uint8_ts missing (current window)
+ uint32_t r0;
+ uint32_t r1;
+ uint8_t TxPktId;
+ uint8_t r2;
+ uint8_t Timer;
+ uint8_t TxStatus;
+ uint16_t r3;
+ uint16_t __dontUseMe;// really: uint16_t IntStatusAuto;
+ uint32_t DmaCtrl; // [1] p.95 (dn), p.100 (up)
+ uint32_t DnListPtr; // [1] p.98
+ uint16_t r4;
+ uint8_t DnBurstThresh; // [1] p.97
+ uint8_t r5;
+ uint8_t DnPriorityThresh;
+ uint8_t DnPoll; // [1] p.100
+ uint16_t r6;
+ uint32_t UpPktStatus;
+ uint16_t FreeTimer;
+ uint16_t Countdown;
+ uint32_t UpListPtr; // [1] p.115
+ uint8_t UpPriorityThresh;
+ uint8_t UpPoll;
+ uint8_t UpBurstThresh;
+ uint8_t r7;
+ uint32_t RealTimeCount;
+ uint8_t ConfigAddress;
+ uint8_t r8;
+ uint8_t r9;
+ uint8_t r10;
+ uint8_t ConfigData;
+ uint8_t r11;
+ uint8_t r12;
+ uint8_t r13;
+ uint32_t r14[9];
+ uint32_t DebugData;
+ uint16_t DebugControl;
+ uint16_t r15;
+ uint16_t DnMaxBurst;
+ uint16_t UpMaxBurst;
+ uint16_t PowerMgmtCtrl;
+ uint16_t r16;
+} PACKED;
+typedef struct Registers Registers;
+
+#define RA_INV 0
+
+static uint8_t gRegAccess[0x70] =
+{
+ /* 0x10 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x14 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x18 */
+ 1, /* TxPktId */
+ RA_INV,
+ 1, /* Timer */
+ 1, /* TxStatus */
+ /* 0x1c */
+ RA_INV, RA_INV,
+ RA_INV, RA_INV, /* IntStatusAuto */
+ /* 0x20 */
+ 4, RA_INV, RA_INV, RA_INV, /* DmaCtrl */
+ /* 0x24 */
+ 4, RA_INV, RA_INV, RA_INV, /* DnListPtr */
+ /* 0x28 */
+ RA_INV, RA_INV,
+ 1, /* DnBurstThresh */
+ RA_INV,
+ /* 0x2c */
+ 1, /* DnPriorityThresh */
+ 1, /* DnPoll */
+ RA_INV,
+ 1,
+ /* 0x30 */
+ 4, RA_INV, RA_INV, RA_INV, /* UpPktStatus */
+ /* 0x34 */
+ 2, RA_INV, /* FreeTimer */
+ 2, RA_INV, /* Countdown */
+ /* 0x38 */
+ 4, RA_INV, RA_INV, RA_INV, /* UpListPtr */
+ /* 0x3c */
+ 1, /* UpPriorityThresh */
+ 1, /* UpPoll */
+ 1, /* UpBurstThresh */
+ RA_INV,
+ /* 0x40 */
+ 4, RA_INV, RA_INV, RA_INV, /* RealTimeCount */
+ /* 0x44 */
+ 1, /* ConfigAddress */
+ RA_INV,
+ RA_INV,
+ RA_INV,
+ /* 0x48 */
+ 1, /* ConfigData */
+ RA_INV,
+ RA_INV,
+ RA_INV,
+ /* 0x4c */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x50 */
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ RA_INV, RA_INV, RA_INV, RA_INV,
+ /* 0x70 */
+ 4, RA_INV, RA_INV, RA_INV, /* DebugData */
+ /* 0x74 */
+ 2, RA_INV, /* DebugControl */
+ RA_INV, RA_INV,
+ /* 0x78 */
+ 2, RA_INV, /* DnMaxBurst */
+ 2, RA_INV, /* UpMaxBurst */
+ /* 0x7c */
+ 2, RA_INV, /* PowerMgmtCtrl */
+ RA_INV, RA_INV
+};
+
+/*
+ * Window 0
+ */
+struct RegWindow0
+{
+ uint32_t r0;
+ uint32_t BiosRomAddr;
+ uint8_t BiosRomData;
+ uint8_t r1;
+ uint16_t EepromCommand;
+ uint16_t EepromData;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow0 RegWindow0;
+
+enum W0_Offsets
+{
+ W0_EEPROMCmd = 0xa,
+ W0_EEPROMData = 0xc
+};
+
+enum W0_EEPROMOpcode
+{
+ EEOP_SubCmd = 0<<6,
+ EEOP_WriteReg = 1<<6,
+ EEOP_ReadReg = 2<<6,
+ EEOP_EraseReg = 3<<6
+};
+
+enum W0_EEPROMSubCmd
+{
+ EESC_WriteDisable = 0<<4,
+ EESC_WriteAll = 1<<4,
+ EESC_EraseAll = 2<<4,
+ EESC_WriteEnable = 3<<4
+};
+
+/*
+ * Window 2
+ */
+struct RegWindow2
+{
+ uint16_t StationAddress[6];
+ uint16_t StationMask[6];
+ uint16_t ResetOptions;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow2 RegWindow2;
+
+/*
+ * Window 3
+ */
+struct RegWindow3
+{
+ uint32_t InternalConfig; // [1] p.58,76
+ uint16_t MaxPktSize;
+ uint16_t MacControl; // [1] p.179
+ uint16_t MediaOptions; // [1] p.78 (EE), p.181
+ uint16_t RxFree;
+ uint16_t TxFree; // [1] p.101
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow3 RegWindow3;
+
+/*
+ * Window 4
+ */
+enum W4_PhysMgmtBits
+{
+ PM_mgmtClk = 1<<0,
+ PM_mgmtData = 1<<1,
+ PM_mgmtDir = 1<<2
+};
+
+struct RegWindow4
+{
+ uint16_t r0; /* offset 0x0 */
+ uint16_t r1; /* offset 0x2 */
+ uint16_t FifoDiagnostic; /* offset 0x4 */
+ uint16_t NetDiagnostic; // [1] p.184 /* offset 0x6 */
+ uint16_t PhysMgmt; // [1] p.186 /* offset 0x8 */
+ uint16_t MediaStatus; // [1] p.182 /* offset 0xa */
+ uint8_t BadSSD;
+ uint8_t Upperuint8sOK;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow4 RegWindow4;
+
+/*
+ * Window 5
+ */
+enum RxFilterBits // [1] p.112
+{
+ RXFILT_receiveIndividual = 1,
+ RXFILT_receiveMulticast = 2,
+ RXFILT_receiveBroadcast = 4,
+ RXFILT_receiveAllFrames = 8,
+ RXFILT_receiveMulticastHash = 16
+};
+
+struct RegWindow5
+{
+ uint16_t TxStartThresh;
+ uint16_t r0;
+ uint16_t r1;
+ uint16_t RxEarlyThresh;
+ uint8_t RxFilter; // [1] p.112
+ uint8_t TxReclaimThresh;
+ uint16_t InterruptEnable; // [1] p.120
+ uint16_t IndicationEnable; // [1] p.120
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow5 RegWindow5;
+
+/*
+ * Window 6
+ */
+struct RegWindow6
+{
+ uint8_t CarrierLost;
+ uint8_t SqeErrors;
+ uint8_t MultipleCollisions;
+ uint8_t SingleCollisions;
+ uint8_t LateCollisions;
+ uint8_t RxOverruns;
+ uint8_t FramesXmittedOk;
+ uint8_t FramesRcvdOk;
+ uint8_t FramesDeferred;
+ uint8_t UpperFramesOk;
+ uint16_t BytesRcvdOk;
+ uint16_t BytesXmittedOk;
+ uint16_t XXX; // IntStatus/CommandRegister
+} PACKED;
+typedef struct RegWindow6 RegWindow6;
+
+/*
+ * EEPROM
+ */
+enum EEPROMField
+{
+ EEPROM_NodeAddress0 = 0x00,
+ EEPROM_NodeAddress1 = 0x01,
+ EEPROM_NodeAddress2 = 0x02,
+ EEPROM_DeviceID = 0x03,
+ EEPROM_ManifacturerID = 0x07,
+ EEPROM_PCIParam = 0x08,
+ EEPROM_RomInfo = 0x09,
+ EEPROM_OEMNodeAddress0 = 0x0a,
+ EEPROM_OEMNodeAddress1 = 0x0b,
+ EEPROM_OEMNodeAddress2 = 0x0c,
+ EEPROM_SoftwareInfo = 0x0d,
+ EEPROM_CompWord = 0x0e,
+ EEPROM_SoftwareInfo2 = 0x0f,
+ EEPROM_Caps = 0x10,
+ EEPROM_InternalConfig0 = 0x12,
+ EEPROM_InternalConfig1 = 0x13,
+ EEPROM_SubsystemVendorID = 0x17,
+ EEPROM_SubsystemID = 0x18,
+ EEPROM_MediaOptions = 0x19,
+ EEPROM_SmbAddress = 0x1b,
+ EEPROM_PCIParam2 = 0x1c,
+ EEPROM_PCIParam3 = 0x1d,
+ EEPROM_Checksum = 0x20
+};
+
+/*
+ * Up/Downloading
+ */
+
+// must be on 8-uint8_t physical address boundary
+struct DPD0
+{
+ uint32_t DnNextPtr;
+ uint32_t FrameStartHeader;
+ /* DPDFragDesc Frags[n] */
+} PACKED8;
+typedef struct DPD0 DPD0;
+
+enum FrameStartHeaderBits
+{
+ FSH_rndupBndry = 3<<0,
+ FSH_pktId = 15<<2,
+ /* 12:10 unspecified */
+ FSH_crcAppendDisable = 1<<13,
+ FSH_txIndicate = 1<<15,
+ FSH_dnComplete = 1<<16,
+ FSH_reArmDisable = 1<<23,
+ FSH_lastKap = 1<<24,
+ FSH_addIpChecksum = 1<<25, /** TODO: write support for these */
+ FSH_addTcpChecksum = 1<<26,
+ FSH_addUdpChecksum = 1<<27,
+ FSH_rndupDefeat = 1<<28,
+ FSH_dpdEmpty = 1<<29,
+ /* 30 unspecified */
+ FSH_dnIndicate = 1<<31
+};
+
+// must be on 16-uint8_t physical address boundary
+struct DPD1
+{
+ uint32_t DnNextPtr;
+ uint32_t ScheduleTime;
+ uint32_t FrameStartHeader;
+ uint32_t res;
+ /* DPDFragDesc Frags[n] */
+} PACKED16;
+typedef struct DPD1 DPD1;
+
+struct DPDFragDesc
+{
+ uint32_t DnFragAddr;
+ uint32_t DnFragLen; // [12:0] fragLen, [31] lastFrag
+} PACKED;
+typedef struct DPDFragDesc DPDFragDesc;
+
+// must be on 8-uint8_t physical address boundary
+struct UPD
+{
+ uint32_t UpNextPtr;
+ uint32_t UpPktStatus;
+ /* UPDFragDesc Frags[n] */
+} PACKED8;
+typedef struct UPD UPD;
+
+struct UPDFragDesc
+{
+ uint32_t UpFragAddr;
+ uint32_t UpFragLen; // [12:0] fragLen, [31] lastFrag
+} PACKED;
+typedef struct UPDFragDesc UPDFragDesc;
+
+#define MAX_DPD_FRAGS 63
+#define MAX_UPD_FRAGS 63
+#define MAX_UPD_SIZE (sizeof(UPD) + sizeof(UPDFragDesc)*MAX_UPD_FRAGS) // 512
+
+enum UpPktStatusBits
+{
+ UPS_upPktLen = 0x1fff,
+ /* 13 unspecified */
+ UPS_upError = 1<<14,
+ UPS_upComplete = 1<<15,
+ UPS_upOverrun = 1<<16,
+ UPS_runtFrame = 1<<17,
+ UPS_alignmentError = 1<<18,
+ UPS_crcError = 1<<19,
+ UPS_oversizedFrame = 1<<20,
+ /* 22:21 unspecified */
+ UPS_dribbleBits = 1<<23,
+ UPS_upOverflow = 1<<24,
+ UPS_ipChecksumError = 1<<25,
+ UPS_tcpChecksumError = 1<<26,
+ UPS_udpChecksumError = 1<<27,
+ UPD_impliedBufferEnable = 1<<28,
+ UPS_ipChecksumChecked = 1<<29,
+ UPS_tcpChecksumChecked = 1<<30,
+ UPS_udpChecksumChecked = 1<<31
+};
+
+// IEEE 802.3 MAC, Ethernet-II
+struct EthFrameII
+{
+ uint8_t destMAC[6];
+ uint8_t srcMAC[6];
+ uint8_t type[2];
+} PACKED;
+typedef struct EthFrameII EthFrameII;
+
+struct A3C90XState
+{
+
+ uint16_t mEEPROM[0x40];
+ char mEEPROMWritable;
+ Registers mRegisters;
+ RegWindow mWindows[8];
+ uint16_t mIntStatus;
+ char mRxEnabled;
+ char mTxEnabled;
+ char mUpStalled;
+ char mDnStalled;
+ uint8_t mRxPacket[MAX_PACKET_SIZE];
+ uint32_t mRxPacketSize;
+ uint16_t mMIIRegs[8];
+ uint32_t mMIIReadWord;
+ uint64_t mMIIWriteWord;
+ uint32_t mMIIWrittenBits;
+ uint16_t mLastHiClkPhysMgmt;
+ uint8_t mMAC[6];
+
+
+ PCIDevice *pci_dev;
+ int a3c90x_mmio_io_addr;
+
+ VLANClientState *vc;
+
+};
+typedef struct A3C90XState A3C90XState;
+
+static void a3c90x_reset(A3C90XState *s);
+
+static void setCR(uint16_t cr, void *opaque);
+
+static uint32_t readRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size);
+static void writeRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size);
+
+static void checkDnWork(void *opaque);
+static void checkUpWork(void *opaque);
+
+static void txDPD0(void *opaque, DPD0 *dpd);
+static void rxUPD(void *opaque, UPD *upd);
+
+static void indicate(uint32_t indications, void *opaque);
+static void acknowledge(uint32_t indications, void *opaque);
+static void maybeRaiseIntr(void *opaque);
+
+static char passesRxFilter(uint8_t *pbuf, uint32_t psize, void *opaque);
+
+static void handle_rx(void *opaque, const uint8_t *buf, int size);
+
+static void a3c90x_cleanup(VLANClientState *vc);
+
+static int compareMACs(uint8_t a[6], uint8_t b[6]);
+
+/*
+ * misc
+ */
+static int compareMACs(uint8_t a[6], uint8_t b[6])
+{
+ uint32_t i;
+ for (i = 0; i < 6; i++)
+ {
+ if (a[i] != b[i]) return a[i] - b[i];
+ }
+ return 0;
+}
+
+static void a3c90x_reset(A3C90XState *s)
+{
+ /* FIXME: resetting can be done more fine-grained (see TotalReset cmd).
+ * this is reset ALL regs.
+ */
+ if (sizeof(Registers) != 0x70)
+ {
+ DEBUG_PRINT("sizeof Registers != 0x70\n");
+ }
+
+ RegWindow3 *w3 = (RegWindow3*) &(s->mWindows[3]);
+ RegWindow4 *w4 = (RegWindow4*) &(s->mWindows[4]);
+ RegWindow5 *w5 = (RegWindow5*) &(s->mWindows[5]);
+
+ // internals
+ s->mEEPROMWritable = 0;
+ memset(&(s->mWindows), 0, sizeof s->mWindows);
+ memset(&(s->mRegisters), 0, sizeof s->mRegisters);
+ s->mIntStatus = 0;
+ s->mRxEnabled = 0;
+ s->mTxEnabled = 0;
+ s->mUpStalled = 0;
+ s->mDnStalled = 0;
+ w3->MaxPktSize = 1514 /* FIXME: should depend on sizeof mRxPacket*/;
+ w3->RxFree = 16*1024;
+ w3->TxFree = 16*1024;
+ s->mRxPacketSize = 0;
+ w5->TxStartThresh = 8188;
+ memset(s->mEEPROM, 0, sizeof s->mEEPROM);
+ s->mEEPROM[EEPROM_NodeAddress0] = (s->mMAC[0]<<8) | s->mMAC[1];
+ s->mEEPROM[EEPROM_NodeAddress1] = (s->mMAC[2]<<8) | s->mMAC[3];
+ s->mEEPROM[EEPROM_NodeAddress2] = (s->mMAC[4]<<8) | s->mMAC[5];
+ s->mEEPROM[EEPROM_DeviceID] = 0x9200;
+ s->mEEPROM[EEPROM_ManifacturerID] = 0x6d50;
+ s->mEEPROM[EEPROM_PCIParam] = 0x2940;
+ s->mEEPROM[EEPROM_RomInfo] = 0; // no ROM
+ s->mEEPROM[EEPROM_OEMNodeAddress0] = s->mEEPROM[EEPROM_NodeAddress0];
+ s->mEEPROM[EEPROM_OEMNodeAddress1] = s->mEEPROM[EEPROM_NodeAddress1];
+ s->mEEPROM[EEPROM_OEMNodeAddress2] = s->mEEPROM[EEPROM_NodeAddress2];
+ s->mEEPROM[EEPROM_SoftwareInfo] = 0x4010;
+ s->mEEPROM[EEPROM_CompWord] = 0;
+ s->mEEPROM[EEPROM_SoftwareInfo2] = 0x00aa;
+ s->mEEPROM[EEPROM_Caps] = 0x72a2;
+ s->mEEPROM[EEPROM_InternalConfig0] = 0;
+ s->mEEPROM[EEPROM_InternalConfig1] = 0x0050; // default is 0x0180
+ s->mEEPROM[EEPROM_SubsystemVendorID] = 0x10b7;
+ s->mEEPROM[EEPROM_SubsystemID] = 0x9200;
+ s->mEEPROM[EEPROM_MediaOptions] = 0x000a;
+ s->mEEPROM[EEPROM_SmbAddress] = 0x6300;
+ s->mEEPROM[EEPROM_PCIParam2] = 0xffb7;
+ s->mEEPROM[EEPROM_PCIParam3] = 0xb7b7;
+ s->mEEPROM[EEPROM_Checksum] = 0;
+
+ // MII
+ memset(s->mMIIRegs, 0, sizeof s->mMIIRegs);
+ MIIRegisters *miiregs = (MIIRegisters*) s->mMIIRegs;
+ miiregs->status = (1<<14) | (1<<13) | (1<<12) | (1<<11) | (1<<5) | (1<<3) | (1<<2) | 1;
+ miiregs->linkPartner = (1<<14) | (1<<7) | 1;
+ miiregs->advert = (1<<14) | (1 << 10) | (1<<7) | 1;
+ s->mMIIReadWord = 0;
+ s->mMIIWriteWord = 0;
+ s->mMIIWrittenBits = 0;
+ s->mLastHiClkPhysMgmt = 0;
+
+ // Register follow-ups
+ w3->MediaOptions = s->mEEPROM[EEPROM_MediaOptions];
+ w3->InternalConfig = s->mEEPROM[EEPROM_InternalConfig0] |
+ (s->mEEPROM[EEPROM_InternalConfig1] << 16);
+
+ // A valid link is established on the NIC
+ w4->MediaStatus = (1 << 11) | 0x8000;
+
+ // And clean out the RX buffer
+ memset(s->mRxPacket, 0xab, MAX_PACKET_SIZE);
+}
+
+static uint32_t readRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size)
+{
+ DEBUG_PRINT("readRegWindow\n");
+ switch (window)
+ {
+ /* window 0 */
+ case 0:
+ {
+ RegWindow0 *w0 = (RegWindow0*) &s->mWindows[0];
+ switch (port)
+ {
+ case W0_EEPROMCmd:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("WARN: EepromCommand, size != 2\n");
+ return 0;
+ }
+ return w0->EepromCommand;
+ break;
+ }
+ case W0_EEPROMData:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("WARN: EepromData, size != 2\n");
+ return 0;
+ }
+ return w0->EepromData;
+ break;
+ }
+ default:
+ DEBUG_PRINT("WARN: reading here unimpl\n");
+ return 0;
+ break;
+ }
+ break;
+ }
+ /* window 1 */
+ case 1:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[1].b[port], size);
+ return data;
+ break;
+ }
+ /* window 2 */
+ case 2:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[2].b[port], size);
+ return data;
+ break;
+ }
+ /* window 3 */
+ case 3:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[3].b[port], size);
+ return data;
+ break;
+ }
+ /* window 4 */
+ case 4:
+ {
+ DEBUG_PRINT("Read from window 4\n");
+ RegWindow4 *w4 = (RegWindow4*) &s->mWindows[4];
+ data = 0;
+ switch (port)
+ {
+ case 8:
+ {
+ // MII-interface
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.8.read\n");
+ return 0;
+ }
+ char mgmtData = (s->mMIIReadWord & 0x80000000) ? 1 : 0;
+ if (mgmtData)
+ {
+ data = w4->PhysMgmt | PM_mgmtData;
+ }
+ else
+ {
+ data = w4->PhysMgmt & (~PM_mgmtData);
+ }
+ break;
+ }
+ case 0xc:
+ {
+ if (size != 1)
+ {
+ DEBUG_PRINT("alignment.4.c.read\n");
+ return 0;
+ }
+ // reading clears
+ w4->BadSSD = 0;
+ memcpy(&data, &s->mWindows[4].b[port], size);
+ return data;
+ break;
+ }
+ default:
+ memcpy(&data, &(s->mWindows[4].b[port]), size);
+ return data;
+ }
+ break;
+ }
+ /* Window 5 */
+ case 5:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[5].b[port], size);
+ return data;
+ break;
+ }
+ /* Window 6 */
+ case 6:
+ {
+ RegWindow6 *w6 = (RegWindow6*) &s->mWindows[6];
+ // reading clears
+ if ((port == 0xa) && (size == 2))
+ {
+ // FIXME: BytesRcvdOk really is 20 bits !
+ // when reading here, write upper 4 bits
+ // in w4.UpperBytesOk[3:0]. no clearing.
+ w6->BytesRcvdOk = 0;
+ }
+ else if ((port == 0xc) && (size == 2))
+ {
+ // FIXME: BytesXmittedOk really is 20 bits !
+ // when reading here, write upper 4 bits
+ // in w4.UpperBytesOk[7:4]. no clearing.
+ w6->BytesXmittedOk = 0;
+ }
+ else if ((port == 0) && (size == 1))
+ {
+ w6->CarrierLost = 0;
+ }
+ else if ((port == 8) && (size == 1))
+ {
+ w6->FramesDeferred = 0;
+ }
+ else if ((port == 7) && (size == 1))
+ {
+ // FIXME: FramesRcvdOk really is 10 bits !
+ // when reading here, write upper 2 bits
+ // in w6.UpperFramesOk[1:0]. no clearing.
+ }
+ else if ((port == 6) && (size == 1))
+ {
+ // FIXME: FramesXmittedOk really is 10 bits !
+ // when reading here, write upper 2 bits
+ // in w6.UpperFramesOk[5:4]. no clearing.
+ }
+ else if ((port == 4) && (size == 1))
+ {
+ w6->LateCollisions = 0;
+ }
+ else if ((port == 2) && (size == 1))
+ {
+ w6->MultipleCollisions = 0;
+ }
+ else if ((port == 5) && (size == 1))
+ {
+ w6->RxOverruns = 0;
+ }
+ else if ((port == 3) && (size == 1))
+ {
+ w6->SingleCollisions = 0;
+ }
+ else if ((port == 1) && (size == 1))
+ {
+ w6->SqeErrors = 0;
+ }
+ data = 0;
+ memcpy(&data, &s->mWindows[6].b[port], size);
+ return data;
+ break;
+ }
+ /* Window 7 */
+ case 7:
+ {
+ data = 0;
+ memcpy(&data, &s->mWindows[7].b[port], size);
+ return data;
+ break;
+ }
+ default:
+ DEBUG_PRINT("reading here unimpl.\n");
+ }
+
+ return 0;
+}
+
+static void writeRegWindow(A3C90XState *s, uint32_t window, uint32_t port, uint32_t data, uint32_t size)
+{
+ DEBUG_PRINT("writeRegWindow\n");
+ switch (window)
+ {
+ /* Window 0 */
+ case 0:
+ {
+ RegWindow0 *w0 = (RegWindow0*) &s->mWindows[0];
+ switch (port)
+ {
+ case W0_EEPROMCmd:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("EepromCommand, size != 2\n");
+ return;
+ }
+ w0->EepromCommand = data & 0xff7f; // clear eepromBusy
+ uint32_t eeprom_addr = ((data >> 2) & 0xffc0) | (data & 0x3f);
+ switch (data & 0xc0)
+ {
+ case EEOP_SubCmd:
+ switch (data & 0x30)
+ {
+ case EESC_WriteDisable:
+ DEBUG_PRINT("EESC_WriteDisable\n");
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_WriteAll:
+ // FIXME: this needs fixing :)
+ DEBUG_PRINT("WriteAll not impl.\n");
+ memset(s->mEEPROM, 0xff, sizeof s->mEEPROM);
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_EraseAll:
+ DEBUG_PRINT("EraseAll not impl.\n");
+ // SINGLESTEP("");
+ memset(s->mEEPROM, 0, sizeof s->mEEPROM);
+ s->mEEPROMWritable = 0;
+ break;
+ case EESC_WriteEnable:
+ DEBUG_PRINT("EESC_WriteEnable\n");
+ s->mEEPROMWritable = 0;
+ break;
+ default:
+ DEBUG_PRINT("impossible\n");
+ // SINGLESTEP("");
+ }
+ break;
+ case EEOP_WriteReg:
+ if (s->mEEPROMWritable)
+ {
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ // disabled
+ DEBUG_PRINT("EEOP_WriteReg\n");
+ // SINGLESTEP("");
+ s->mEEPROM[eeprom_addr] = w0->EepromData;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_WriteReg\n");
+ }
+ s->mEEPROMWritable = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(not writable): EEOP_WriteReg\n");
+ }
+ break;
+ case EEOP_ReadReg:
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ w0->EepromData = s->mEEPROM[eeprom_addr];
+ DEBUG_PRINT("EEOP_ReadReg\n");
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_ReadReg\n");
+ }
+ break;
+ case EEOP_EraseReg:
+ if (s->mEEPROMWritable)
+ {
+ if (eeprom_addr*2 < sizeof s->mEEPROM)
+ {
+ // disabled
+ DEBUG_PRINT("EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ s->mEEPROM[eeprom_addr] = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(out of bounds): EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ }
+ s->mEEPROMWritable = 0;
+ }
+ else
+ {
+ DEBUG_PRINT("FAILED(not writable): EEOP_EraseReg\n");
+ // SINGLESTEP("");
+ }
+ break;
+ default:
+ DEBUG_PRINT("impossible\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ case W0_EEPROMData:
+ if (size != 2)
+ {
+ DEBUG_PRINT("EepromData, size != 2\n");
+ // SINGLESTEP("");
+ }
+ w0->EepromData = data;
+ break;
+ default:
+ DEBUG_PRINT("writing here unimpl.0\n");
+ // SINGLESTEP("");
+ break;
+ }
+ break;
+ }
+ /* Window 2 */
+ case 2:
+ {
+ if (port+size<=0xc)
+ {
+ DEBUG_PRINT("StationAddress or StationMask\n");
+ /* StationAddress or StationMask */
+ memcpy(&s->mWindows[2].b[port], &data, size);
+ }
+ else
+ {
+ DEBUG_PRINT("writing here unimpl.2\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ /* Window 3 */
+ case 3:
+ {
+ RegWindow3 *w3 = (RegWindow3*) &s->mWindows[3];
+ switch (port)
+ {
+ case 0:
+ if (size != 4)
+ {
+ DEBUG_PRINT("alignment.3.0\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("InternalConfig\n");
+ w3->InternalConfig = data;
+ break;
+ case 4:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.4\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("ERR: MaxPktSize\n");
+ w3->MaxPktSize = data;
+ break;
+ case 6:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.6\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("MacControl\n");
+ if (data != 0)
+ {
+ DEBUG_PRINT("setting MacControl != 0\n");
+ // SINGLESTEP("");
+ }
+ w3->MacControl = data;
+ break;
+ case 8:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.8\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("MediaOptions\n");
+ w3->MediaOptions = data;
+ break;
+ case 10:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.10\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("RxFree\n");
+ // SINGLESTEP("");
+ w3->RxFree = data;
+ break;
+ case 12:
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.3.12\n");
+ // SINGLESTEP("");
+ }
+ DEBUG_PRINT("TxFree\n");
+ // SINGLESTEP("");
+ w3->TxFree = data;
+ break;
+ default:
+ DEBUG_PRINT("writing here unimpl.3\n");
+ // SINGLESTEP("");
+ }
+ break;
+ }
+ /* Window 4 */
+ case 4:
+ {
+ RegWindow4 *w4 = (RegWindow4*) &s->mWindows[4];
+ switch (port)
+ {
+ case 6:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.6\n");
+ // SINGLESTEP("");
+ }
+ uint32_t mask = 0xf341;
+ DEBUG_PRINT("NetDiagnostic");
+ w4->NetDiagnostic &= ~mask;
+ w4->NetDiagnostic |= data & mask;
+ break;
+ }
+ case 8:
+ {
+ // MII-interface
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.8\n");
+ // SINGLESTEP("");
+ }
+ char hiClk = (!((w4->PhysMgmt & PM_mgmtClk) && (data & PM_mgmtClk))) ? 1 : 0;
+ if (hiClk)
+ {
+ // Z means lo edge of mgmtDir
+ char Z = ((s->mLastHiClkPhysMgmt & PM_mgmtDir) && !(data & PM_mgmtDir)) ? 1 : 0;
+ if (Z)
+ {
+ // check if the 5 frames have been sent
+ if (((s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2)) & 0x3ffffffffULL) == 0x3fffffffdULL)
+ {
+ uint32_t opcode = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2)) & 3;
+ uint32_t PHYaddr = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2-5)) & 0x1f;
+ uint32_t REGaddr = (s->mMIIWriteWord >> (s->mMIIWrittenBits-32-2-2-5-5)) & 0x1f;
+ if ((PHYaddr == 0x18 /* hardcoded address [1] p.196 */)
+ && (REGaddr < 0x10))
+ {
+ switch (opcode)
+ {
+ case 1:
+ {
+ // Opcode Write
+ DEBUG_PRINT("Opcode Write\n");
+ if (s->mMIIWrittenBits == 64)
+ {
+ uint32_t value = s->mMIIWriteWord & 0xffff;
+ s->mMIIRegs[REGaddr] = value;
+ }
+ else
+ {
+ DEBUG_PRINT("But invalid write count\n");
+ }
+ s->mMIIWriteWord = 0;
+ break;
+ }
+ case 2:
+ {
+ // Opcode Read
+ DEBUG_PRINT("Opcode Read\n");
+ if (s->mMIIWrittenBits == 32+2+2+5+5)
+ {
+ // msb gets sent first and is zero to indicated success
+ // the register to be sent follows msb to lsb
+ s->mMIIReadWord = s->mMIIRegs[REGaddr] << 15;
+ }
+ else
+ {
+ DEBUG_PRINT("But invalid write count\n");
+ }
+ s->mMIIWriteWord = 0;
+ break;
+ }
+ default:
+ // error
+ DEBUG_PRINT("Invalid opcode\n");
+ s->mMIIReadWord = 0xffffffff;
+ }
+ }
+ else
+ {
+ // error
+ DEBUG_PRINT("Invalid PHY or REG\n");
+ s->mMIIReadWord = 0xffffffff;
+ }
+ }
+ s->mMIIWrittenBits = 0;
+ w4->PhysMgmt = data;
+ }
+ else if (data & PM_mgmtDir)
+ {
+ // write
+ char mgmtData = (data & PM_mgmtData) ? 1 : 0;
+ w4->PhysMgmt = data;
+ s->mMIIWriteWord <<= 1;
+ s->mMIIWriteWord |= mgmtData ? 1 : 0;
+ s->mMIIWrittenBits++;
+ }
+ else
+ {
+ // read
+ char mgmtData = (s->mMIIReadWord & 0x80000000) ? 1 : 0;
+ w4->PhysMgmt = data;
+ if (mgmtData)
+ {
+ w4->PhysMgmt = w4->PhysMgmt | PM_mgmtData;
+ }
+ else
+ {
+ w4->PhysMgmt = w4->PhysMgmt & (~PM_mgmtData);
+ }
+ s->mMIIReadWord <<= 1;
+ }
+ s->mLastHiClkPhysMgmt = w4->PhysMgmt;
+ }
+ else
+ {
+ w4->PhysMgmt = data;
+ }
+ break;
+ }
+ case 10:
+ {
+ if (size != 2)
+ {
+ DEBUG_PRINT("alignment.4.10\n");
+ // SINGLESTEP("");
+ }
+ uint32_t mask = 0x10cc;
+ DEBUG_PRINT("MediaStatus\n");
+ w4->MediaStatus &= ~mask;
+ w4->MediaStatus |= data & mask;
+ w4->MediaStatus |= 0x8000; // auiDisable always on
+ break;
+ }
+ default:
+ DEBUG_PRINT("generic to window 4\n");
+ // SINGLESTEP("");
+ memcpy(&s->mWindows[4].b[port], &data, size);
+ }
+ break;
+ }
+ /**/
+ default:
+ DEBUG_PRINT("writing here unimpl.\n");
+ // SINGLESTEP("");
+ }
+}
+
+static void setCR(uint16_t cr, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT("setCR\n");
+ switch (cr & (31<<11))
+ {
+ case CmdTotalReset:
+ // FIXME: care about params
+ DEBUG_PRINT("TotalReset\n");
+ a3c90x_reset(opaque);
+ break;
+ case CmdSelectWindow:
+ {
+ DEBUG_PRINT("SelectWindow\n");
+ s->mIntStatus &= 0x1fff;
+ s->mIntStatus |= (cr & 7)<<13;
+ break;
+ }
+ case CmdTxReset:
+ DEBUG_PRINT("TxReset\n");
+ break;
+ case CmdRxReset:
+ DEBUG_PRINT("RxReset\n");
+ break;
+ case CmdSetIndicationEnable:
+ {
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ DEBUG_PRINT("SetIndicationEnable\n");
+ w5->IndicationEnable = cr & 0x7fe;
+ break;
+ }
+ case CmdSetIntrEnb:
+ {
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ DEBUG_PRINT("SetIntrEnab\n");
+ w5->InterruptEnable = cr & 0x7fe;
+ break;
+ }
+ case CmdStatsEnable:
+ /* implement me */
+ DEBUG_PRINT("StatsEnable\n");
+ break;
+ case CmdStatsDisable:
+ /* implement me */
+ DEBUG_PRINT("StatsDisable\n");
+ break;
+ case CmdEnableDC:
+ /* implement me */
+ DEBUG_PRINT("EnableDC\n");
+ break;
+ case CmdDisableDC:
+ /* implement me */
+ DEBUG_PRINT("DisableDC\n");
+ break;
+ case CmdStall:
+ {
+ /* FIXME: threading */
+ switch (cr & 3)
+ {
+ case 0: /* UpStall */
+ case 1: /* UpUnstall */
+ {
+ DEBUG_PRINT("Stall\n");
+ char stall = (!(cr & 1)) ? 1 : 0;
+ s->mUpStalled = stall;
+ checkUpWork(opaque);
+ break;
+ }
+ case 2: /* DnStall */
+ case 3: /* DnUnstall */
+ {
+ DEBUG_PRINT("Stall\n");
+ char stall = (!(cr & 1)) ? 1 : 0;
+ s->mDnStalled = stall;
+ s->mRegisters.DmaCtrl &= ~DC_dnStalled;
+ if (stall) s->mRegisters.DmaCtrl |= DC_dnStalled;
+ checkDnWork(opaque);
+ break;
+ }
+ }
+ break;
+ }
+ case CmdSetRxFilter:
+ {
+ DEBUG_PRINT("SetRxFilter\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->RxFilter = cr & 31;
+ break;
+ }
+ case CmdSetTxReclaimThresh:
+ {
+ DEBUG_PRINT("SetTxReclaimHash\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->TxReclaimThresh = cr & 255;
+ break;
+ }
+ case CmdSetTxStartThresh:
+ {
+ DEBUG_PRINT("SetTxStartTresh\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->TxStartThresh = (cr & 0x7ff) << 2;
+ break;
+ }
+ case CmdSetHashFilterBit:
+ {
+ /** TODO: implement */
+ // char value = (cr & 0x400) ? 1 : 0;
+ // uint32_t which = cr & 0x3f;
+ DEBUG_PRINT("SetHashFilterBit\n");
+ break;
+ }
+ case CmdSetRxEarlyThresh:
+ {
+ DEBUG_PRINT("SetTxStartTresh\n");
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ w5->RxEarlyThresh = (cr & 0x7ff) << 2;
+ break;
+ }
+ case CmdRxEnable:
+ {
+ DEBUG_PRINT("RxEnable\n");
+ s->mRxEnabled = 1;
+ break;
+ }
+ case CmdRxDisable:
+ {
+ DEBUG_PRINT("RxDisable\n");
+ s->mRxEnabled = 0;
+ break;
+ }
+ case CmdTxEnable:
+ {
+ DEBUG_PRINT("TxEnable\n");
+ s->mTxEnabled = 1;
+ break;
+ }
+ case CmdTxDisable:
+ {
+ DEBUG_PRINT("TxDisable\n");
+ s->mTxEnabled = 0;
+ break;
+ }
+ case CmdAckIntr:
+ {
+ /*
+ 0x1 interruptLatchAck
+ 0x2 linkEventAck
+ 0x20 rxEarlyAck
+ 0x40 intRequestedAck
+ 0x200 dnCompleteAck
+ 0x400 upCompleteAck
+
+ 0x5
+ */
+ DEBUG_PRINT("AckIntr\n");
+ // ack/clear corresponding bits in IntStatus
+ uint32_t ISack = 0;
+ if (cr & 0x01) ISack |= IS_interruptLatch;
+ if (cr & 0x02) ISack |= IS_linkEvent;
+ if (cr & 0x20) ISack |= IS_rxEarly;
+ if (cr & 0x40) ISack |= IS_intRequested;
+ if (cr & 0x200) ISack |= IS_dnComplete;
+ if (cr & 0x400) ISack |= IS_upComplete;
+ acknowledge(ISack, opaque);
+ break;
+ }
+ /* case CmdReqIntr: {
+ RegWindow5 &w5 = (RegWindow5&)mWindows[5];
+ // set intRequested in IntStatus
+ mIntStatus |= IS_intRequested;
+
+ // FIXME: generate Interrupt (if enabled)
+ break;
+ }*/
+
+ /*
+ case CmdTxDone:
+ case CmdRxDiscard:
+ case CmdSetTxThreshold:
+ */
+ default:
+ DEBUG_PRINT("command not implemented\n");
+ }
+}
+
+static void txDPD0(void *opaque, DPD0 *dpd)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT("txDPD0\n");
+
+ // FIXME: createHostStruct()
+ uint32_t fsh = dpd->FrameStartHeader;
+ DEBUG_PRINT_FORMAT(("fsh = %08x\n", fsh));
+ if (fsh & FSH_dpdEmpty)
+ {
+ // modify FrameStartHeader in DPD (!)
+ dpd->FrameStartHeader |= FSH_dnComplete;
+ // set next DnListPtr
+ s->mRegisters.DnListPtr = dpd->DnNextPtr;
+ DEBUG_PRINT("dpd empty\n");
+ return;
+ }
+ DPDFragDesc *frags = (DPDFragDesc*)(dpd+1);
+ uint8_t pbuf[MAX_PACKET_SIZE];
+ uint8_t *p = pbuf;
+
+ // some packet drivers need padding
+ // uint framePrefix = mEthTun->getWriteFramePrefix();
+ // memset(p, 0, framePrefix);
+ // p += framePrefix;
+
+ DEBUG_PRINT_FORMAT(("DPD: NextPtr = %x, FSH = %x, FragAddr = %x, FragLen = %x\n",
+ dpd->DnNextPtr, dpd->FrameStartHeader, frags->DnFragAddr, frags->DnFragLen));
+
+ //
+ uint32_t i = 0;
+ // assemble packet from fragments (up to MAX_DPD_FRAGS fragments)
+ while (i < MAX_DPD_FRAGS)
+ {
+ uint32_t addr = frags->DnFragAddr;
+ uint32_t len = frags->DnFragLen & 0x1fff;
+ if (p-pbuf+len >= sizeof pbuf)
+ {
+ DEBUG_PRINT("packet too big!\n");
+ // SINGLESTEP("");
+ return;
+ }
+ DEBUG_PRINT("dma_read\n");
+
+ if (len == 0 || addr == 0)
+ {
+ DEBUG_PRINT("No data! Bail!\n");
+ return;
+ }
+
+ cpu_physical_memory_read(addr, p, len);
+
+ DEBUG_PRINT_FORMAT((" - DnAddr = %x, DnFragLen = %x\n", frags->DnFragAddr, frags->DnFragLen));
+
+ p += len;
+ // last fragment ?
+ if (frags->DnFragLen & 0x80000000) break;
+ frags++;
+ i++;
+ }
+ uint32_t psize = p-pbuf;
+ if (!(fsh & FSH_rndupDefeat))
+ {
+ // round packet length
+ switch (fsh & FSH_rndupBndry)
+ {
+ case 0:
+ {
+ // 4 bytes
+ uint32_t gap = ((psize+3) & ~3) -psize;
+ memset(pbuf+psize, 0, gap);
+ psize += gap;
+ break;
+ }
+ case 2:
+ {
+ // 2 bytes
+ uint32_t gap = ((psize+1) & ~1) -psize;
+ memset(pbuf+psize, 0, gap);
+ psize += gap;
+ break;
+ }
+ }
+ }
+ //FSH_reArmDisable = 1<<23,
+ //FSH_lastKap = 1<<24,
+ //FSH_addIpChecksum = 1<<25,
+ //FSH_addTcpChecksum = 1<<26,
+ //FSH_addUdpChecksum = 1<<27,
+ if (fsh & (0x1f << 23))
+ {
+ DEBUG_PRINT("unsupported flags in fsh\n");
+ }
+
+ if (psize<60)
+ {
+ // pad packet to at least 60 bytes (+4 bytes crc = 64 bytes)
+ memset(pbuf+psize, 0, (60-psize));
+ psize = 60;
+ }
+ // append crc
+ if (!(fsh & FSH_crcAppendDisable))
+ {
+#ifdef A3C90x_CALCULATE_TXCRC
+ uint32_t crc = crc32(0, pbuf, psize);
+#else
+ uint32_t crc = 0;
+#endif
+ pbuf[psize+0] = crc;
+ pbuf[psize+1] = crc>>8;
+ pbuf[psize+2] = crc>>16;
+ pbuf[psize+3] = crc>>24;
+ psize += 4;
+ DEBUG_PRINT("crc complete\n");
+ }
+
+ AUX_DEBUG_PRINT("3C90X: Packet sent\n");
+
+ qemu_send_packet(s->vc, pbuf, psize);
+
+ // indications
+ s->mRegisters.DmaCtrl |= DC_dnComplete;
+ uint8_t txStatus = 0;
+ uint32_t inds = 0;
+ if (fsh & FSH_dnIndicate) inds |= IS_dnComplete;
+ if (fsh & FSH_txIndicate)
+ {
+ inds |= IS_txComplete;
+ txStatus |= (1 << 6);
+ }
+
+ // transmit complete
+ txStatus |= (1 << 7);
+
+ indicate(inds, opaque);
+ // modify FrameStartHeader in DPD (!)
+ dpd->FrameStartHeader |= FSH_dnComplete;
+ // set next DnListPtr, TxPktId
+ s->mRegisters.DnListPtr = dpd->DnNextPtr;
+ uint32_t pktId = (fsh & FSH_pktId) >> 2;
+ s->mRegisters.TxPktId = pktId;
+ s->mRegisters.TxStatus = txStatus;
+ // maybe generate interrupt
+ maybeRaiseIntr(opaque);
+}
+
+static char passesRxFilter(uint8_t *pbuf, uint32_t psize, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ EthFrameII *f = (EthFrameII*) pbuf;
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ if (w5->RxFilter & RXFILT_receiveAllFrames) return 1;
+ // FIXME: Multicast hashing not implemented
+ if (w5->RxFilter & RXFILT_receiveMulticastHash) return 1;
+ // FIXME: Multicasting not understood
+ if (w5->RxFilter & RXFILT_receiveMulticast) return 1;
+ if (w5->RxFilter & RXFILT_receiveBroadcast)
+ {
+ uint8_t broadcastMAC[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+ if (compareMACs(f->destMAC, broadcastMAC) == 0) return 1;
+ }
+ if (w5->RxFilter & RXFILT_receiveIndividual)
+ {
+ uint8_t destMAC[6];
+ uint8_t thisMAC[6];
+ RegWindow2 *w2 = (RegWindow2*) &s->mWindows[2];
+ uint32_t i;
+ for (i = 0; i < 6; i++)
+ {
+ destMAC[i] = f->destMAC[i] & ~w2->StationMask[i];
+ thisMAC[i] = w2->StationAddress[i] & ~w2->StationMask[i];
+ }
+ return (compareMACs(destMAC, thisMAC) == 0) ? 1 : 0;
+ }
+ return 0;
+}
+
+static void rxUPD(void *opaque, UPD *upd)
+{
+ A3C90XState *s = opaque;
+
+ // FIXME: threading to care about (mRegisters.DmaCtrl & DC_upAltSeqDisable)
+ DEBUG_PRINT("rxUPD()\n");
+
+ char error = 0;
+
+ if (upd->UpPktStatus & UPS_upComplete)
+ {
+ // IO_3C90X_WARN("UPD already upComplete!\n");
+
+ // the top of the ring buffer is already used,
+ // stall the upload and throw away the packet.
+ // the ring buffers are filled.
+
+ s->mUpStalled = 1;
+ return;
+ }
+
+ uint32_t upPktStatus = 0;
+
+ if (s->mRegisters.UpPoll)
+ {
+ DEBUG_PRINT("UpPoll unsupported\n");
+ // SINGLESTEP("");
+ return;
+ }
+ // FIXME:
+// if (mRegisters.DmaCtrl & DC_upRxEarlyEnable)
+// IO_3C90X_ERR("DC_upRxEarlyEnable unsupported\n");
+
+ if ((s->mRxPacketSize > 0x1fff) || (s->mRxPacketSize > sizeof s->mRxPacket))
+ {
+ DEBUG_PRINT("oversized frame\n");
+ upd->UpPktStatus = UPS_upError | UPS_oversizedFrame;
+ error = 1;
+ }
+
+ if (s->mRxPacketSize < 60)
+ {
+ // pad packet to at least 60 bytes (+4 bytes crc = 64 bytes)
+ memset(s->mRxPacket+s->mRxPacketSize, 0, (60-s->mRxPacketSize));
+ s->mRxPacketSize = 60;
+ }
+
+ /* RegWindow5 &w5 = (RegWindow5&)mWindows[5];
+ if ((mRxPacketSize < 60) && (w5.RxEarlyThresh >= 60)) {
+ IO_3C90X_TRACE("runt frame\n");
+ upPktStatus |= UPS_upError | UPS_runtFrame;
+ upd->UpPktStatus = upPktStatus;
+ error = true;
+ }*/
+ if (upd->UpPktStatus & UPD_impliedBufferEnable)
+ {
+ DEBUG_PRINT("UPD_impliedBufferEnable unsupported\n");
+ // SINGLESTEP("");
+ return;
+ }
+ UPDFragDesc *frags = (UPDFragDesc*)(upd+1);
+
+ uint8_t *p = s->mRxPacket;
+ uint32_t i = 0;
+ while (!error && i < MAX_UPD_FRAGS) // (up to MAX_UPD_FRAGS fragments)
+ {
+ uint32_t addr = frags->UpFragAddr;
+ uint32_t len = frags->UpFragLen & 0x1fff;
+ if (p-s->mRxPacket+len > sizeof s->mRxPacket)
+ {
+ upPktStatus |= UPS_upError | UPS_upOverflow;
+ upd->UpPktStatus = upPktStatus;
+ DEBUG_PRINT("UPD overflow!\n");
+ // SINGLESTEP("");
+ error = 1;
+ break;
+ }
+
+ cpu_physical_memory_write(addr, p, len);
+
+ p += len;
+ // last fragment ?
+ if (frags->UpFragLen & 0x80000000) break;
+ frags++;
+ i++;
+ }
+
+ if (!error)
+ {
+ DEBUG_PRINT("successfully uploaded packet\n");
+ }
+ upPktStatus |= s->mRxPacketSize & 0x1fff;
+ upPktStatus |= UPS_upComplete;
+ upd->UpPktStatus = upPktStatus;
+
+ s->mRxPacketSize = 0;
+
+ /* The client OS is waiting for a change in status, but won't see it
+ * until we dma our local copy upd->UpPktStatus back to the client address space
+ */
+ cpu_physical_memory_write(s->mRegisters.UpListPtr + 4, (const uint8_t*) &(upd->UpPktStatus), sizeof(upd->UpPktStatus));
+
+ s->mRegisters.UpListPtr = upd->UpNextPtr;
+
+ // Indications
+ s->mRegisters.DmaCtrl |= DC_upComplete;
+ indicate(IS_upComplete, opaque);
+ maybeRaiseIntr(opaque);
+}
+
+static void indicate(uint32_t indications, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ if ((w5->IndicationEnable & indications) != indications)
+ {
+ DEBUG_PRINT("some masked\n");
+ }
+ s->mIntStatus |= w5->IndicationEnable & indications;
+ if (indications & IS_upComplete)
+ {
+ s->mRegisters.DmaCtrl |= DC_upComplete;
+ }
+ if (indications & IS_dnComplete)
+ {
+ s->mRegisters.DmaCtrl |= DC_dnComplete;
+ }
+}
+
+static void acknowledge(uint32_t indications, void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT_FORMAT(("intStatus was %x [indications=%x]\n", s->mIntStatus, indications));
+ s->mIntStatus &= ~indications;
+ DEBUG_PRINT_FORMAT(("intStatus is now %x\n", s->mIntStatus));
+ if (indications & IS_upComplete)
+ {
+ s->mRegisters.DmaCtrl &= ~DC_upComplete;
+ }
+ if (indications & IS_dnComplete)
+ {
+ s->mRegisters.DmaCtrl &= ~DC_dnComplete;
+ }
+
+ // lower the irq line now that the IRQ is ack'd
+ qemu_set_irq(s->pci_dev->irq[0], 0);
+}
+
+static void maybeRaiseIntr(void *opaque)
+{
+ A3C90XState *s = opaque;
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+
+ DEBUG_PRINT("maybeRaiseIntr\n");
+ DEBUG_PRINT_FORMAT(("IndEnable = %x, IntEnable = %x, IntStatus = %x\n", w5->IndicationEnable, w5->InterruptEnable, s->mIntStatus));
+
+ if (w5->IndicationEnable & w5->InterruptEnable & s->mIntStatus)
+ {
+ s->mIntStatus |= IS_interruptLatch;
+
+ DEBUG_PRINT("Generating interrupt!\n");
+
+ // raise the IRQ line
+ qemu_set_irq(s->pci_dev->irq[0], 1);
+ }
+ else
+ {
+ // lower the IRQ line
+ qemu_set_irq(s->pci_dev->irq[0], 0);
+ }
+}
+
+static void checkDnWork(void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ while (!s->mDnStalled && (s->mRegisters.DnListPtr != 0))
+ {
+ uint8_t dpd[512];
+
+ cpu_physical_memory_read(s->mRegisters.DnListPtr, dpd, sizeof dpd);
+
+ uint8_t type = dpd[7] >> 6;
+ switch (type)
+ {
+ case 0:
+ case 2:
+ {
+ DPD0 *p = (DPD0*) dpd;
+ DEBUG_PRINT("Got a type 0 DPD!\n");
+ txDPD0(opaque, p);
+ break;
+ }
+ case 1:
+ {
+ DEBUG_PRINT("Got a type 1 DPD! Not implemented!\n");
+ s->mRegisters.DnListPtr = 0;
+ break;
+ }
+ default:
+ DEBUG_PRINT("Unsupported packet type\n");
+ s->mRegisters.DnListPtr = 0;
+ break;
+ };
+
+ break;
+ }
+}
+
+static void checkUpWork(void *opaque)
+{
+ A3C90XState *s = opaque;
+
+ if (s->mRxEnabled && !s->mUpStalled && s->mRxPacketSize && (s->mRegisters.UpListPtr != 0))
+ {
+ uint8_t upd[MAX_UPD_SIZE];
+
+ cpu_physical_memory_read(s->mRegisters.UpListPtr, upd, sizeof upd);
+ UPD *p = (UPD*) upd;
+ rxUPD(opaque, p);
+
+ }
+ else
+ {
+ DEBUG_PRINT("Not uploading\n");
+ DEBUG_PRINT_FORMAT(("rxEnabled = %x, upStalled = %x, RX Packet size = %x, Up list ptr = %x\n",
+ s->mRxEnabled, s->mUpStalled, s->mRxPacketSize, s->mRegisters.UpListPtr));
+ }
+}
+
+static void handle_rx(void *opaque, const uint8_t *buf, int size)
+{
+ A3C90XState *s = opaque;
+
+ DEBUG_PRINT_FORMAT(("3c90x: handle_rx (%d bytes)\n", size));
+ if (s->mRxPacketSize)
+ {
+ DEBUG_PRINT("Old packet not yet uploaded!\n");
+ }
+ else
+ {
+ s->mRxPacketSize = size;
+ memcpy(s->mRxPacket, buf, size);
+ if (s->mRxEnabled && (s->mRxPacketSize > sizeof(EthFrameII)))
+ {
+ indicate(IS_rxComplete, opaque);
+ maybeRaiseIntr(opaque);
+ acknowledge(IS_rxComplete, opaque);
+ if (!passesRxFilter(s->mRxPacket, s->mRxPacketSize, opaque))
+ {
+ DEBUG_PRINT_FORMAT(("Received %d bytes, but they don't pass the filter!\n", s->mRxPacketSize));
+ s->mRxPacketSize = 0;
+ }
+ else
+ {
+ // and now, we do some extra debugging output...
+#ifdef DEBUG_3C90X_ANALYSE_FRAMES
+ EthFrameII *ethFrame = (EthFrameII*) s->mRxPacket;
+ AUX_DEBUG_PRINT_FORMAT(("Incoming packet information [%d bytes]:\n", s->mRxPacketSize));
+ if (ethFrame->type[0] == 8)
+ {
+ if (ethFrame->type[1] == 0x06)
+ AUX_DEBUG_PRINT("ARP\n");
+ else if (ethFrame->type[1] == 0)
+ {
+ struct ip *ipHeader = (struct ip*) (s->mRxPacket + sizeof(EthFrameII));
+ if (ipHeader->ip_p == 0x11)
+ {
+ struct udphdr *udpHeader = (struct udphdr*) (s->mRxPacket + sizeof(EthFrameII) + (ipHeader->ip_hl * 4));
+ AUX_DEBUG_PRINT_FORMAT(("UDP: src=%d dest=%d\n", ntohs(udpHeader->uh_sport), ntohs(udpHeader->uh_dport)));
+ }
+ else if (ipHeader->ip_p == 0x06)
+ {
+ struct tcphdr *tcpHeader = (struct tcphdr*) (s->mRxPacket + sizeof(EthFrameII) + (ipHeader->ip_hl * 4));
+ AUX_DEBUG_PRINT_FORMAT(("TCP: src=%d dest=%d ", ntohs(tcpHeader->th_sport), ntohs(tcpHeader->th_dport)));
+ AUX_DEBUG_PRINT("flags =");
+ if (tcpHeader->th_flags & TH_FIN)
+ AUX_DEBUG_PRINT(" FIN");
+ if (tcpHeader->th_flags & TH_SYN)
+ AUX_DEBUG_PRINT(" SYN");
+ if (tcpHeader->th_flags & TH_RST)
+ AUX_DEBUG_PRINT(" RST");
+ if (tcpHeader->th_flags & TH_PUSH)
+ AUX_DEBUG_PRINT(" PSH");
+ if (tcpHeader->th_flags & TH_ACK)
+ AUX_DEBUG_PRINT(" ACK");
+ if (tcpHeader->th_flags & TH_URG)
+ AUX_DEBUG_PRINT(" URG");
+ AUX_DEBUG_PRINT_FORMAT((" seq=%d ack=%d", ntohl(tcpHeader->th_seq), ntohl(tcpHeader->th_ack)));
+ AUX_DEBUG_PRINT("\n");
+ }
+ }
+ }
+ else
+ AUX_DEBUG_PRINT("(can't inspect)\n");
+#endif
+ DEBUG_PRINT_FORMAT(("Received %d bytes!\n", s->mRxPacketSize));
+ }
+ }
+ else
+ {
+ DEBUG_PRINT_FORMAT(("Oops - RxEnabled = %x, packetSize = %d [eth=%d]\n", s->mRxEnabled, s->mRxPacketSize, sizeof(EthFrameII)));
+ s->mRxPacketSize = 0;
+ }
+ }
+ checkUpWork(opaque);
+}
+
+static int a3c90x_can_receive(void *opaque)
+{
+ A3C90XState *s = opaque;
+ if (s->mRxEnabled)
+ {
+ if (s->mRxPacketSize)
+ {
+ // If there's already a packet there, try and upload it again
+ DEBUG_PRINT("Old packet not yet uploaded!\n");
+ checkUpWork(opaque);
+ return 0;
+ }
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void a3c90x_receive(void *opaque, const uint8_t *buf, int size)
+{
+ AUX_DEBUG_PRINT("3C90X: Packet received\n");
+ handle_rx(opaque, buf, size);
+}
+
+static uint32_t a3c90x_io_readx(void *opaque, uint8_t port, int size)
+{
+ A3C90XState *s = opaque;
+ uint32_t data = 0;
+
+ if (port == 0xe)
+ {
+ // IntStatus (no matter which window)
+ if (size != 2)
+ {
+ DEBUG_PRINT("unaligned read from IntStatus\n");
+ }
+ DEBUG_PRINT("read IntStatus\n");
+ return s->mIntStatus;
+ }
+ else if (port >= 0 && (port+size <= 0x0e))
+ {
+ // read from window
+ uint32_t curwindow = s->mIntStatus >> 13;
+ return readRegWindow(opaque, curwindow, port, data, size);
+ }
+ else if ((port+size > 0x1e) && (port <= 0x1f))
+ {
+ if ((port != 0x1e) || (size != 2))
+ {
+ DEBUG_PRINT("unaligned read from IntStatusAuto\n");
+ }
+ RegWindow5 *w5 = (RegWindow5*) &s->mWindows[5];
+ // side-effects of reading IntStatusAuto:
+ // 1.clear InterruptEnable
+ w5->InterruptEnable = 0;
+ // 2.clear some flags
+ acknowledge(IS_dnComplete | IS_upComplete
+ | IS_rxEarly | IS_intRequested
+ | IS_interruptLatch | IS_linkEvent, opaque);
+ DEBUG_PRINT("read IntStatusAuto\n");
+ return s->mIntStatus;
+ }
+ else if ((port >= 0x10) && (port+size <= 0x10 + sizeof(Registers)))
+ {
+ uint8_t l = gRegAccess[port-0x10];
+ if (l != size)
+ {
+ DEBUG_PRINT("invalid/unaligned read\n");
+ }
+ // read from (standard) register
+ memcpy(&data, ((uint8_t*)&s->mRegisters)+port-0x10, size);
+ switch (port)
+ {
+ case 0x1a:
+ DEBUG_PRINT("read Timer\n");
+ break;
+ case 0x20:
+ DEBUG_PRINT("read DmaCtrl\n");
+ break;
+ case 0x24:
+ DEBUG_PRINT("read DownListPtr\n");
+ break;
+ case 0x38:
+ DEBUG_PRINT("read UpListPtr\n");
+ break;
+ default:
+ DEBUG_PRINT("read reg\n");
+ break;
+ }
+ return data;
+ }
+ return 0;
+}
+
+static void a3c90x_io_writex(void *opaque, uint8_t port, uint32_t data, int size)
+{
+ A3C90XState *s = opaque;
+
+ if (port == 0xe)
+ {
+ // CommandReg (no matter which window)
+ if (size != 2)
+ {
+ DEBUG_PRINT("unaligned write to CommandReg\n");
+ }
+ setCR(data, opaque);
+ }
+ else if (port >= 0 && (port+size <= 0x0e))
+ {
+ // write to window
+ uint32_t curwindow = s->mIntStatus >> 13;
+ writeRegWindow(opaque, curwindow, port, data, size);
+ }
+ else if (port >= 0x10 && (port + size <= 0x10 + sizeof(Registers)))
+ {
+ uint8_t l = gRegAccess[port-0x10];
+ if (l != size)
+ {
+ DEBUG_PRINT("invalid/unaligned write to register\n");
+ }
+ switch (port)
+ {
+ case 0x20:
+ {
+ uint32_t DmaCtrlRWMask = DC_upRxEarlyEnable | DC_counterSpeed |
+ DC_countdownMode | DC_defeatMWI | DC_defeatMRL |
+ DC_upOverDiscEnable;
+ s->mRegisters.DmaCtrl &= ~DmaCtrlRWMask;
+ s->mRegisters.DmaCtrl |= data & DmaCtrlRWMask;
+ DEBUG_PRINT("write DmaCtrl\n");
+ break;
+ }
+ case 0x24:
+ {
+ if (!s->mRegisters.DnListPtr)
+ {
+ s->mRegisters.DnListPtr = data;
+ DEBUG_PRINT("write DnListPtr\n");
+ }
+ else
+ {
+ DEBUG_PRINT("didn't write DnListPtr cause it's not 0\n");
+ }
+ checkDnWork(opaque);
+ break;
+ }
+ case 0x38:
+ {
+ s->mRegisters.UpListPtr = data;
+ DEBUG_PRINT("write UpListPtr\n");
+ checkUpWork(opaque);
+ break;
+ }
+ case 0x2d:
+ DEBUG_PRINT("DnPoll\n");
+ // SINGLESTEP("");
+ break;
+ case 0x2a:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write DnBurstThresh\n");
+ break;
+ case 0x2c:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write DnPriorityThresh\n");
+ break;
+ case 0x2f:
+ // used by Darwin as TxFreeThresh. Not documented in [1].
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write TxFreeThresh\n");
+ break;
+ case 0x3c:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write UpPriorityThresh\n");
+ break;
+ case 0x3e:
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ DEBUG_PRINT("write UpBurstThresh\n");
+ break;
+ case 0x1b:
+ if (size != 1)
+ {
+ DEBUG_PRINT("wrong size for write to TxStatus\n");
+ return;
+ }
+ DEBUG_PRINT_FORMAT(("Writing %x to TxStatus\n", data));
+ s->mRegisters.TxStatus = data;
+
+ // acknowledge the relevant bits
+ acknowledge(IS_txComplete, opaque);
+ // | IS_dnComplete | IS_cmdInProgress
+
+ // NOTE: "An I/O write of an arbitrary value to TxStatus advances the queue to the next transmit status byte."
+ // We also need to keep the queue of TX Status bytes!
+
+ // maybeRaiseIntr(opaque);
+ break;
+ default:
+ DEBUG_PRINT("write to register\n");
+ // SINGLESTEP("");
+ // write to (standard) register
+ memcpy(((uint8_t*)&s->mRegisters)+port-0x10, &data, size);
+ }
+ }
+}
+
+static void a3c90x_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 1);
+}
+
+static void a3c90x_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 2);
+}
+
+static void a3c90x_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+ a3c90x_io_writex(opaque, addr, val, 4);
+}
+
+static uint32_t a3c90x_io_readb(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 1);
+}
+
+static uint32_t a3c90x_io_readw(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 2);
+}
+
+static uint32_t a3c90x_io_readl(void *opaque, uint8_t addr)
+{
+ return a3c90x_io_readx(opaque, addr, 4);
+}
+
+/* */
+
+static void a3c90x_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ a3c90x_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t a3c90x_ioport_readb(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_ioport_readw(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readw(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_ioport_readl(void *opaque, uint32_t addr)
+{
+ return a3c90x_io_readl(opaque, addr & 0xFF);
+}
+
+/* */
+
+static void a3c90x_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ a3c90x_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ a3c90x_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void a3c90x_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ a3c90x_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t a3c90x_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return a3c90x_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t a3c90x_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = a3c90x_io_readw(opaque, addr & 0xFF);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ return val;
+}
+
+static uint32_t a3c90x_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = a3c90x_io_readl(opaque, addr & 0xFF);
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ return val;
+}
+
+/***********************************************************/
+/* PCI 3C90x definitions */
+
+typedef struct PCI3C90XState
+{
+ PCIDevice dev;
+ A3C90XState a3c90x;
+} PCI3C90XState;
+
+static void a3c90x_mmio_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCI3C90XState *d = (PCI3C90XState *)pci_dev;
+ A3C90XState *s = &d->a3c90x;
+
+ cpu_register_physical_memory(addr + 0, 0x100, s->a3c90x_mmio_io_addr);
+}
+
+static void a3c90x_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCI3C90XState *d = (PCI3C90XState *)pci_dev;
+ A3C90XState *s = &d->a3c90x;
+
+ register_ioport_write(addr, 0x100, 1, a3c90x_ioport_writeb, s);
+ register_ioport_read( addr, 0x100, 1, a3c90x_ioport_readb, s);
+
+ register_ioport_write(addr, 0x100, 2, a3c90x_ioport_writew, s);
+ register_ioport_read( addr, 0x100, 2, a3c90x_ioport_readw, s);
+
+ register_ioport_write(addr, 0x100, 4, a3c90x_ioport_writel, s);
+ register_ioport_read( addr, 0x100, 4, a3c90x_ioport_readl, s);
+}
+
+static CPUReadMemoryFunc *a3c90x_mmio_read[3] =
+{
+ a3c90x_mmio_readb,
+ a3c90x_mmio_readw,
+ a3c90x_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *a3c90x_mmio_write[3] =
+{
+ a3c90x_mmio_writeb,
+ a3c90x_mmio_writew,
+ a3c90x_mmio_writel,
+};
+
+static inline int64_t a3c90x_get_next_tctr_time(A3C90XState *s, int64_t current_time)
+{
+ int64_t next_time = current_time +
+ muldiv64(1, ticks_per_sec, PCI_FREQUENCY);
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+void a3c90x_cleanup(VLANClientState *vc)
+{
+ DEBUG_PRINT("3C90X: Cleanup not implemented\n");
+}
+
+static void pci_a3c90x_init(PCIDevice *pci_dev)
+{
+ PCI3C90XState *d;
+ A3C90XState *s;
+ uint8_t *pci_conf;
+
+ d = (PCI3C90XState *) pci_dev;
+ pci_conf = d->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_3COM);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_3C90X);
+ pci_conf[0x04] = 0x07; /* command = I/O space, Bus Master */
+ pci_conf[0x08] = 0;
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ pci_conf[0x0e] = 0x00; /* header_type */
+ pci_conf[0x3d] = 1; /* interrupt pin 0 */
+ pci_conf[0x34] = 0xdc;
+
+ pci_conf[0x3e] = 5;
+ pci_conf[0x3f] = 48;
+
+ s = &d->a3c90x;
+
+ /* I/O handler for memory-mapped I/O */
+ s->a3c90x_mmio_io_addr =
+ cpu_register_io_memory(0, a3c90x_mmio_read, a3c90x_mmio_write, s);
+
+ pci_register_io_region(&d->dev, 0, 0x100,
+ PCI_ADDRESS_SPACE_IO, a3c90x_ioport_map);
+
+ pci_register_io_region(&d->dev, 1, 0x100,
+ PCI_ADDRESS_SPACE_MEM, a3c90x_mmio_map);
+
+ s->pci_dev = (PCIDevice *)d;
+ qdev_get_macaddr(&d->dev.qdev, s->mMAC);
+ a3c90x_reset(s);
+ s->vc = qdev_get_vlan_client(&d->dev.qdev,
+ a3c90x_receive, a3c90x_can_receive,
+ a3c90x_cleanup, s);
+
+ qemu_format_nic_info_str(s->vc, s->mMAC);
+
+ //register_savevm("3c90x", -1, 4, a3c90x_save, a3c90x_load, s);
+}
+
+static void a3c90x_register_devices(void)
+{
+ pci_qdev_register("3c90x", sizeof(PCI3C90XState), pci_a3c90x_init);
+}
+
+device_init(a3c90x_register_devices)
diff --git a/hw/pci.c b/hw/pci.c
index 4458079..e11dda6 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -771,6 +771,7 @@ static const char * const pci_nic_models[] = {
"i82557b",
"i82559er",
"rtl8139",
+ "3c90x",
"e1000",
"pcnet",
"virtio",
@@ -783,6 +784,7 @@ static const char * const pci_nic_names[] = {
"i82557b",
"i82559er",
"rtl8139",
+ "3c90x",
"e1000",
"pcnet",
"virtio-net-pci",
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index 3afe674..edbbcb7 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -75,6 +75,9 @@
#define PCI_VENDOR_ID_REALTEK 0x10ec
#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_VENDOR_ID_3COM_3C90X 0x9200
+
#define PCI_VENDOR_ID_XILINX 0x10ee
#define PCI_VENDOR_ID_MARVELL 0x11ab
--
1.6.3.2.1299.gee46c
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2009-07-02 10:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-05 7:56 [Qemu-devel] PATCH: Add 3C90X emulation Matthew Iselin
2009-06-01 21:18 ` [Qemu-devel] " Sebastian Herbszt
[not found] ` <f88ae150906020410k270280d8k1d1c77f168e06ce8@mail.gmail.com>
2009-06-02 11:10 ` [Qemu-devel] " Matthew Iselin
2009-06-02 22:19 ` [Qemu-devel] " Sebastian Herbszt
[not found] ` <f88ae150906021635k34fcbeebx5e533c49d6f1875a@mail.gmail.com>
[not found] ` <f88ae150906030219s7102599h80749b4653fcb2e4@mail.gmail.com>
[not found] ` <EE2BA4D82E644425BA529D5A9D43F756@FSCPC>
[not found] ` <f88ae150906031520nf8a724ck3f42162a36b3a1ff@mail.gmail.com>
2009-07-02 10:01 ` Matthew Iselin
2009-07-02 10:21 ` Kevin Wolf
2009-07-02 10:39 ` Matthew Iselin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.