📄 rtl8139.c
字号:
/** * QEMU RTL8139 emulation * * Copyright (c) 2006 Igor Kovalenko * * 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: * 2006-Jan-28 Mark Malakanov : TSAD and CSCR implementation (for Windows driver) * */#include "vl.h"/* debug RTL8139 card *///#define DEBUG_RTL8139 1/* debug RTL8139 card C+ mode only *///#define DEBUG_RTL8139CP 1/* RTL8139 provides frame CRC with received packet, this feature seems to be ignored by most drivers, disabled by default *///#define RTL8139_CALCULATE_RXCRC 1#if defined(RTL8139_CALCULATE_RXCRC)/* For crc32 */#include <zlib.h>#endif#define SET_MASKED(input, mask, curr) \ ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )/* arg % size for size which is a power of 2 */#define MOD2(input, size) \ ( ( input ) & ( size - 1 ) )/* Symbolic offsets to registers. */enum RTL8139_registers { MAC0 = 0, /* Ethernet hardware address. */ MAR0 = 8, /* Multicast filter. */ TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ RxBuf = 0x30, ChipCmd = 0x37, RxBufPtr = 0x38, RxBufAddr = 0x3A, IntrMask = 0x3C, IntrStatus = 0x3E, TxConfig = 0x40, RxConfig = 0x44, Timer = 0x48, /* A general-purpose counter. */ RxMissed = 0x4C, /* 24 bits valid, write clears. */ Cfg9346 = 0x50, Config0 = 0x51, Config1 = 0x52, FlashReg = 0x54, MediaStatus = 0x58, Config3 = 0x59, Config4 = 0x5A, /* absent on RTL-8139A */ HltClk = 0x5B, MultiIntr = 0x5C, PCIRevisionID = 0x5E, TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/ BasicModeCtrl = 0x62, BasicModeStatus = 0x64, NWayAdvert = 0x66, NWayLPAR = 0x68, NWayExpansion = 0x6A, /* Undocumented registers, but required for proper operation. */ FIFOTMS = 0x70, /* FIFO Control and test. */ CSCR = 0x74, /* Chip Status and Configuration Register. */ PARA78 = 0x78, PARA7c = 0x7c, /* Magic transceiver parameter register. */ Config5 = 0xD8, /* absent on RTL-8139A */ /* C+ mode */ TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */ RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */ TxThresh = 0xEC, /* Early Tx threshold */};enum ClearBitMasks { MultiIntrClear = 0xF000, ChipCmdClear = 0xE2, Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),};enum ChipCmdBits { CmdReset = 0x10, CmdRxEnb = 0x08, CmdTxEnb = 0x04, RxBufEmpty = 0x01,};/* C+ mode */enum CplusCmdBits { CPlusRxEnb = 0x0002, CPlusTxEnb = 0x0001,};/* Interrupt register bits, using my own meaningful names. */enum IntrStatusBits { PCIErr = 0x8000, PCSTimeout = 0x4000, RxFIFOOver = 0x40, RxUnderrun = 0x20, RxOverflow = 0x10, TxErr = 0x08, TxOK = 0x04, RxErr = 0x02, RxOK = 0x01, RxAckBits = RxFIFOOver | RxOverflow | RxOK,};enum TxStatusBits { TxHostOwns = 0x2000, TxUnderrun = 0x4000, TxStatOK = 0x8000, TxOutOfWindow = 0x20000000, TxAborted = 0x40000000, TxCarrierLost = 0x80000000,};enum RxStatusBits { RxMulticast = 0x8000, RxPhysical = 0x4000, RxBroadcast = 0x2000, RxBadSymbol = 0x0020, RxRunt = 0x0010, RxTooLong = 0x0008, RxCRCErr = 0x0004, RxBadAlign = 0x0002, RxStatusOK = 0x0001,};/* Bits in RxConfig. */enum rx_mode_bits { AcceptErr = 0x20, AcceptRunt = 0x10, AcceptBroadcast = 0x08, AcceptMulticast = 0x04, AcceptMyPhys = 0x02, AcceptAllPhys = 0x01,};/* Bits in TxConfig. */enum tx_config_bits { /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ TxIFGShift = 24, TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ TxClearAbt = (1 << 0), /* Clear abort (WO) */ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */};/* Transmit Status of All Descriptors (TSAD) Register */enum TSAD_bits { TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3 TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2 TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1 TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0 TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3 TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2 TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1 TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0 TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3 TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2 TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1 TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0 TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3 TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2 TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1 TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0};/* Bits in Config1 */enum Config1Bits { Cfg1_PM_Enable = 0x01, Cfg1_VPD_Enable = 0x02, Cfg1_PIO = 0x04, Cfg1_MMIO = 0x08, LWAKE = 0x10, /* not on 8139, 8139A */ Cfg1_Driver_Load = 0x20, Cfg1_LED0 = 0x40, Cfg1_LED1 = 0x80, SLEEP = (1 << 1), /* only on 8139, 8139A */ PWRDN = (1 << 0), /* only on 8139, 8139A */};/* Bits in Config3 */enum Config3Bits { Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */};/* Bits in Config4 */enum Config4Bits { LWPTN = (1 << 2), /* not on 8139, 8139A */};/* Bits in Config5 */enum Config5Bits { Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */};enum RxConfigBits { /* rx fifo threshold */ RxCfgFIFOShift = 13, RxCfgFIFONone = (7 << RxCfgFIFOShift), /* Max DMA burst */ RxCfgDMAShift = 8, RxCfgDMAUnlimited = (7 << RxCfgDMAShift), /* rx ring buffer length */ RxCfgRcv8K = 0, RxCfgRcv16K = (1 << 11), RxCfgRcv32K = (1 << 12), RxCfgRcv64K = (1 << 11) | (1 << 12), /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ RxNoWrap = (1 << 7),};/* Twister tuning parameters from RealTek. Completely undocumented, but required to tune bad links on some boards. *//*enum CSCRBits { CSCR_LinkOKBit = 0x0400, CSCR_LinkChangeBit = 0x0800, CSCR_LinkStatusBits = 0x0f000, CSCR_LinkDownOffCmd = 0x003c0, CSCR_LinkDownCmd = 0x0f3c0,*/enum CSCRBits { CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */ CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/ CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/ CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/ CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/ CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/ CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/ CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/ CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/};enum Cfg9346Bits { Cfg9346_Lock = 0x00, Cfg9346_Unlock = 0xC0,};typedef enum { CH_8139 = 0, CH_8139_K, CH_8139A, CH_8139A_G, CH_8139B, CH_8130, CH_8139C, CH_8100, CH_8100B_8139D, CH_8101,} chip_t;enum chip_flags { HasHltClk = (1 << 0), HasLWake = (1 << 1),};#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)/* Size is 64 * 16bit words */#define EEPROM_9346_ADDR_BITS 6#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS)#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)enum Chip9346Operation{ Chip9346_op_mask = 0xc0, /* 10 zzzzzz */ Chip9346_op_read = 0x80, /* 10 AAAAAA */ Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */ Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */ Chip9346_op_write_enable = 0x30, /* 00 11zzzz */ Chip9346_op_write_all = 0x10, /* 00 01zzzz */ Chip9346_op_write_disable = 0x00, /* 00 00zzzz */};enum Chip9346Mode{ Chip9346_none = 0, Chip9346_enter_command_mode, Chip9346_read_command, Chip9346_data_read, /* from output register */ Chip9346_data_write, /* to input register, then to contents at specified address */ Chip9346_data_write_all, /* to input register, then filling contents */};typedef struct EEprom9346{ uint16_t contents[EEPROM_9346_SIZE]; int mode; uint32_t tick; uint8_t address; uint16_t input; uint16_t output; uint8_t eecs; uint8_t eesk; uint8_t eedi; uint8_t eedo;} EEprom9346;typedef struct RTL8139State { uint8_t phys[8]; /* mac address */ uint8_t mult[8]; /* multicast mask array */ uint32_t TxStatus[4]; /* TxStatus0 */ uint32_t TxAddr[4]; /* TxAddr0 */ uint32_t RxBuf; /* Receive buffer */ uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */ uint32_t RxBufPtr; uint32_t RxBufAddr; uint16_t IntrStatus; uint16_t IntrMask; uint32_t TxConfig; uint32_t RxConfig; uint32_t RxMissed; uint16_t CSCR; uint8_t Cfg9346; uint8_t Config0; uint8_t Config1; uint8_t Config3; uint8_t Config4; uint8_t Config5; uint8_t clock_enabled; uint8_t bChipCmdState; uint16_t MultiIntr; uint16_t BasicModeCtrl; uint16_t BasicModeStatus; uint16_t NWayAdvert; uint16_t NWayLPAR; uint16_t NWayExpansion; uint16_t CpCmd; uint8_t TxThresh; int irq; PCIDevice *pci_dev; VLANClientState *vc; uint8_t macaddr[6]; int rtl8139_mmio_io_addr; /* C ring mode */ uint32_t currTxDesc; /* C+ mode */ uint32_t currCPlusRxDesc; uint32_t currCPlusTxDesc; uint32_t RxRingAddrLO; uint32_t RxRingAddrHI; EEprom9346 eeprom; } RTL8139State;void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command){#if defined(DEBUG_RTL8139) printf("RTL8139: eeprom command 0x%02x\n", command);#endif switch (command & Chip9346_op_mask) { case Chip9346_op_read: { eeprom->address = command & EEPROM_9346_ADDR_MASK; eeprom->output = eeprom->contents[eeprom->address]; eeprom->eedo = 0; eeprom->tick = 0; eeprom->mode = Chip9346_data_read;#if defined(DEBUG_RTL8139) printf("RTL8139: eeprom read from address 0x%02x data=0x%04x\n", eeprom->address, eeprom->output);#endif } break; case Chip9346_op_write: { eeprom->address = command & EEPROM_9346_ADDR_MASK; eeprom->input = 0; eeprom->tick = 0; eeprom->mode = Chip9346_none; /* Chip9346_data_write */#if defined(DEBUG_RTL8139) printf("RTL8139: eeprom begin write to address 0x%02x\n", eeprom->address);#endif } break; default: eeprom->mode = Chip9346_none; switch (command & Chip9346_op_ext_mask) { case Chip9346_op_write_enable:#if defined(DEBUG_RTL8139) printf("RTL8139: eeprom write enabled\n");#endif break; case Chip9346_op_write_all:#if defined(DEBUG_RTL8139) printf("RTL8139: eeprom begin write all\n");#endif break; case Chip9346_op_write_disable:#if defined(DEBUG_RTL8139) printf("RTL8139: eeprom write disabled\n");#endif break; } break; }}void prom9346_shift_clock(EEprom9346 *eeprom){ int bit = eeprom->eedi?1:0; ++ eeprom->tick;#if defined(DEBUG_RTL8139) printf("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, eeprom->eedo);#endif switch (eeprom->mode) { case Chip9346_enter_command_mode: if (bit) { eeprom->mode = Chip9346_read_command; eeprom->tick = 0; eeprom->input = 0;#if defined(DEBUG_RTL8139) printf("eeprom: +++ synchronized, begin command read\n");#endif } break; case Chip9346_read_command: eeprom->input = (eeprom->input << 1) | (bit & 1); if (eeprom->tick == 8)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -