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 (address@hidden) (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 +#include +#include +#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 +#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);