xref: /openbmc/qemu/hw/net/rtl8139.c (revision a53b931645183bd0c15dd19ae0708fc3c81ecf1d)
149ab747fSPaolo Bonzini /**
249ab747fSPaolo Bonzini  * QEMU RTL8139 emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2006 Igor Kovalenko
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini 
2449ab747fSPaolo Bonzini  * Modifications:
2549ab747fSPaolo Bonzini  *  2006-Jan-28  Mark Malakanov :   TSAD and CSCR implementation (for Windows driver)
2649ab747fSPaolo Bonzini  *
2749ab747fSPaolo Bonzini  *  2006-Apr-28  Juergen Lock   :   EEPROM emulation changes for FreeBSD driver
2849ab747fSPaolo Bonzini  *                                  HW revision ID changes for FreeBSD driver
2949ab747fSPaolo Bonzini  *
3049ab747fSPaolo Bonzini  *  2006-Jul-01  Igor Kovalenko :   Implemented loopback mode for FreeBSD driver
3149ab747fSPaolo Bonzini  *                                  Corrected packet transfer reassembly routine for 8139C+ mode
3249ab747fSPaolo Bonzini  *                                  Rearranged debugging print statements
3349ab747fSPaolo Bonzini  *                                  Implemented PCI timer interrupt (disabled by default)
3449ab747fSPaolo Bonzini  *                                  Implemented Tally Counters, increased VM load/save version
3549ab747fSPaolo Bonzini  *                                  Implemented IP/TCP/UDP checksum task offloading
3649ab747fSPaolo Bonzini  *
3749ab747fSPaolo Bonzini  *  2006-Jul-04  Igor Kovalenko :   Implemented TCP segmentation offloading
3849ab747fSPaolo Bonzini  *                                  Fixed MTU=1500 for produced ethernet frames
3949ab747fSPaolo Bonzini  *
4049ab747fSPaolo Bonzini  *  2006-Jul-09  Igor Kovalenko :   Fixed TCP header length calculation while processing
4149ab747fSPaolo Bonzini  *                                  segmentation offloading
4249ab747fSPaolo Bonzini  *                                  Removed slirp.h dependency
4349ab747fSPaolo Bonzini  *                                  Added rx/tx buffer reset when enabling rx/tx operation
4449ab747fSPaolo Bonzini  *
4549ab747fSPaolo Bonzini  *  2010-Feb-04  Frediano Ziglio:   Rewrote timer support using QEMU timer only
46b6af0975SDaniel P. Berrange  *                                  when strictly needed (required for
4749ab747fSPaolo Bonzini  *                                  Darwin)
4849ab747fSPaolo Bonzini  *  2011-Mar-22  Benjamin Poirier:  Implemented VLAN offloading
4949ab747fSPaolo Bonzini  */
5049ab747fSPaolo Bonzini 
51e8d40465SPeter Maydell #include "qemu/osdep.h"
52*5691f477SMichael Tokarev #include <zlib.h> /* for crc32 */
5349ab747fSPaolo Bonzini 
54edf5ca5dSMarkus Armbruster #include "hw/pci/pci_device.h"
55a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
56d6454270SMarkus Armbruster #include "migration/vmstate.h"
5749ab747fSPaolo Bonzini #include "sysemu/dma.h"
580b8fa32fSMarkus Armbruster #include "qemu/module.h"
5949ab747fSPaolo Bonzini #include "qemu/timer.h"
6049ab747fSPaolo Bonzini #include "net/net.h"
615d61721aSStefan Hajnoczi #include "net/eth.h"
6249ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
63db1015e9SEduardo Habkost #include "qom/object.h"
6449ab747fSPaolo Bonzini 
6549ab747fSPaolo Bonzini /* debug RTL8139 card */
6649ab747fSPaolo Bonzini //#define DEBUG_RTL8139 1
6749ab747fSPaolo Bonzini 
6837b9ab92SLaurent Vivier #define PCI_PERIOD 30    /* 30 ns period = 33.333333 Mhz frequency */
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini #define SET_MASKED(input, mask, curr) \
7149ab747fSPaolo Bonzini     ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
7249ab747fSPaolo Bonzini 
7349ab747fSPaolo Bonzini /* arg % size for size which is a power of 2 */
7449ab747fSPaolo Bonzini #define MOD2(input, size) \
7549ab747fSPaolo Bonzini     ( ( input ) & ( size - 1 )  )
7649ab747fSPaolo Bonzini 
7749ab747fSPaolo Bonzini #define ETHER_TYPE_LEN 2
7849ab747fSPaolo Bonzini 
7949ab747fSPaolo Bonzini #define VLAN_TCI_LEN 2
8049ab747fSPaolo Bonzini #define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN)
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini #if defined (DEBUG_RTL8139)
8349ab747fSPaolo Bonzini #  define DPRINTF(fmt, ...) \
8449ab747fSPaolo Bonzini     do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0)
8549ab747fSPaolo Bonzini #else
DPRINTF(const char * fmt,...)869edc6313SMarc-André Lureau static inline G_GNUC_PRINTF(1, 2) int DPRINTF(const char *fmt, ...)
8749ab747fSPaolo Bonzini {
8849ab747fSPaolo Bonzini     return 0;
8949ab747fSPaolo Bonzini }
9049ab747fSPaolo Bonzini #endif
9149ab747fSPaolo Bonzini 
9239257515SPeter Crosthwaite #define TYPE_RTL8139 "rtl8139"
9339257515SPeter Crosthwaite 
948063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(RTL8139State, RTL8139)
9539257515SPeter Crosthwaite 
9649ab747fSPaolo Bonzini /* Symbolic offsets to registers. */
9749ab747fSPaolo Bonzini enum RTL8139_registers {
9849ab747fSPaolo Bonzini     MAC0 = 0,        /* Ethernet hardware address. */
9949ab747fSPaolo Bonzini     MAR0 = 8,        /* Multicast filter. */
10049ab747fSPaolo Bonzini     TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */
1012431f4f1SMichael Tokarev                      /* Dump Tally Counter control register(64bit). C+ mode only */
10249ab747fSPaolo Bonzini     TxAddr0 = 0x20,  /* Tx descriptors (also four 32bit). */
10349ab747fSPaolo Bonzini     RxBuf = 0x30,
10449ab747fSPaolo Bonzini     ChipCmd = 0x37,
10549ab747fSPaolo Bonzini     RxBufPtr = 0x38,
10649ab747fSPaolo Bonzini     RxBufAddr = 0x3A,
10749ab747fSPaolo Bonzini     IntrMask = 0x3C,
10849ab747fSPaolo Bonzini     IntrStatus = 0x3E,
10949ab747fSPaolo Bonzini     TxConfig = 0x40,
11049ab747fSPaolo Bonzini     RxConfig = 0x44,
11149ab747fSPaolo Bonzini     Timer = 0x48,        /* A general-purpose counter. */
11249ab747fSPaolo Bonzini     RxMissed = 0x4C,    /* 24 bits valid, write clears. */
11349ab747fSPaolo Bonzini     Cfg9346 = 0x50,
11449ab747fSPaolo Bonzini     Config0 = 0x51,
11549ab747fSPaolo Bonzini     Config1 = 0x52,
11649ab747fSPaolo Bonzini     FlashReg = 0x54,
11749ab747fSPaolo Bonzini     MediaStatus = 0x58,
11849ab747fSPaolo Bonzini     Config3 = 0x59,
11949ab747fSPaolo Bonzini     Config4 = 0x5A,        /* absent on RTL-8139A */
12049ab747fSPaolo Bonzini     HltClk = 0x5B,
12149ab747fSPaolo Bonzini     MultiIntr = 0x5C,
12249ab747fSPaolo Bonzini     PCIRevisionID = 0x5E,
12349ab747fSPaolo Bonzini     TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/
12449ab747fSPaolo Bonzini     BasicModeCtrl = 0x62,
12549ab747fSPaolo Bonzini     BasicModeStatus = 0x64,
12649ab747fSPaolo Bonzini     NWayAdvert = 0x66,
12749ab747fSPaolo Bonzini     NWayLPAR = 0x68,
12849ab747fSPaolo Bonzini     NWayExpansion = 0x6A,
12949ab747fSPaolo Bonzini     /* Undocumented registers, but required for proper operation. */
13049ab747fSPaolo Bonzini     FIFOTMS = 0x70,        /* FIFO Control and test. */
13149ab747fSPaolo Bonzini     CSCR = 0x74,        /* Chip Status and Configuration Register. */
13249ab747fSPaolo Bonzini     PARA78 = 0x78,
13349ab747fSPaolo Bonzini     PARA7c = 0x7c,        /* Magic transceiver parameter register. */
13449ab747fSPaolo Bonzini     Config5 = 0xD8,        /* absent on RTL-8139A */
13549ab747fSPaolo Bonzini     /* C+ mode */
13649ab747fSPaolo Bonzini     TxPoll        = 0xD9,    /* Tell chip to check Tx descriptors for work */
13749ab747fSPaolo Bonzini     RxMaxSize    = 0xDA, /* Max size of an Rx packet (8169 only) */
13849ab747fSPaolo Bonzini     CpCmd        = 0xE0, /* C+ Command register (C+ mode only) */
13949ab747fSPaolo Bonzini     IntrMitigate    = 0xE2,    /* rx/tx interrupt mitigation control */
14049ab747fSPaolo Bonzini     RxRingAddrLO    = 0xE4, /* 64-bit start addr of Rx ring */
14149ab747fSPaolo Bonzini     RxRingAddrHI    = 0xE8, /* 64-bit start addr of Rx ring */
14249ab747fSPaolo Bonzini     TxThresh    = 0xEC, /* Early Tx threshold */
14349ab747fSPaolo Bonzini };
14449ab747fSPaolo Bonzini 
14549ab747fSPaolo Bonzini enum ClearBitMasks {
14649ab747fSPaolo Bonzini     MultiIntrClear = 0xF000,
14749ab747fSPaolo Bonzini     ChipCmdClear = 0xE2,
14849ab747fSPaolo Bonzini     Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
14949ab747fSPaolo Bonzini };
15049ab747fSPaolo Bonzini 
15149ab747fSPaolo Bonzini enum ChipCmdBits {
15249ab747fSPaolo Bonzini     CmdReset = 0x10,
15349ab747fSPaolo Bonzini     CmdRxEnb = 0x08,
15449ab747fSPaolo Bonzini     CmdTxEnb = 0x04,
15549ab747fSPaolo Bonzini     RxBufEmpty = 0x01,
15649ab747fSPaolo Bonzini };
15749ab747fSPaolo Bonzini 
15849ab747fSPaolo Bonzini /* C+ mode */
15949ab747fSPaolo Bonzini enum CplusCmdBits {
16049ab747fSPaolo Bonzini     CPlusRxVLAN   = 0x0040, /* enable receive VLAN detagging */
16149ab747fSPaolo Bonzini     CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */
16249ab747fSPaolo Bonzini     CPlusRxEnb    = 0x0002,
16349ab747fSPaolo Bonzini     CPlusTxEnb    = 0x0001,
16449ab747fSPaolo Bonzini };
16549ab747fSPaolo Bonzini 
16649ab747fSPaolo Bonzini /* Interrupt register bits, using my own meaningful names. */
16749ab747fSPaolo Bonzini enum IntrStatusBits {
16849ab747fSPaolo Bonzini     PCIErr = 0x8000,
16949ab747fSPaolo Bonzini     PCSTimeout = 0x4000,
17049ab747fSPaolo Bonzini     RxFIFOOver = 0x40,
17149ab747fSPaolo Bonzini     RxUnderrun = 0x20, /* Packet Underrun / Link Change */
17249ab747fSPaolo Bonzini     RxOverflow = 0x10,
17349ab747fSPaolo Bonzini     TxErr = 0x08,
17449ab747fSPaolo Bonzini     TxOK = 0x04,
17549ab747fSPaolo Bonzini     RxErr = 0x02,
17649ab747fSPaolo Bonzini     RxOK = 0x01,
17749ab747fSPaolo Bonzini 
17849ab747fSPaolo Bonzini     RxAckBits = RxFIFOOver | RxOverflow | RxOK,
17949ab747fSPaolo Bonzini };
18049ab747fSPaolo Bonzini 
18149ab747fSPaolo Bonzini enum TxStatusBits {
18249ab747fSPaolo Bonzini     TxHostOwns = 0x2000,
18349ab747fSPaolo Bonzini     TxUnderrun = 0x4000,
18449ab747fSPaolo Bonzini     TxStatOK = 0x8000,
18549ab747fSPaolo Bonzini     TxOutOfWindow = 0x20000000,
18649ab747fSPaolo Bonzini     TxAborted = 0x40000000,
18749ab747fSPaolo Bonzini     TxCarrierLost = 0x80000000,
18849ab747fSPaolo Bonzini };
18949ab747fSPaolo Bonzini enum RxStatusBits {
19049ab747fSPaolo Bonzini     RxMulticast = 0x8000,
19149ab747fSPaolo Bonzini     RxPhysical = 0x4000,
19249ab747fSPaolo Bonzini     RxBroadcast = 0x2000,
19349ab747fSPaolo Bonzini     RxBadSymbol = 0x0020,
19449ab747fSPaolo Bonzini     RxRunt = 0x0010,
19549ab747fSPaolo Bonzini     RxTooLong = 0x0008,
19649ab747fSPaolo Bonzini     RxCRCErr = 0x0004,
19749ab747fSPaolo Bonzini     RxBadAlign = 0x0002,
19849ab747fSPaolo Bonzini     RxStatusOK = 0x0001,
19949ab747fSPaolo Bonzini };
20049ab747fSPaolo Bonzini 
20149ab747fSPaolo Bonzini /* Bits in RxConfig. */
20249ab747fSPaolo Bonzini enum rx_mode_bits {
20349ab747fSPaolo Bonzini     AcceptErr = 0x20,
20449ab747fSPaolo Bonzini     AcceptRunt = 0x10,
20549ab747fSPaolo Bonzini     AcceptBroadcast = 0x08,
20649ab747fSPaolo Bonzini     AcceptMulticast = 0x04,
20749ab747fSPaolo Bonzini     AcceptMyPhys = 0x02,
20849ab747fSPaolo Bonzini     AcceptAllPhys = 0x01,
20949ab747fSPaolo Bonzini };
21049ab747fSPaolo Bonzini 
21149ab747fSPaolo Bonzini /* Bits in TxConfig. */
21249ab747fSPaolo Bonzini enum tx_config_bits {
21349ab747fSPaolo Bonzini 
21449ab747fSPaolo Bonzini         /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
21549ab747fSPaolo Bonzini         TxIFGShift = 24,
21649ab747fSPaolo Bonzini         TxIFG84 = (0 << TxIFGShift),    /* 8.4us / 840ns (10 / 100Mbps) */
21749ab747fSPaolo Bonzini         TxIFG88 = (1 << TxIFGShift),    /* 8.8us / 880ns (10 / 100Mbps) */
21849ab747fSPaolo Bonzini         TxIFG92 = (2 << TxIFGShift),    /* 9.2us / 920ns (10 / 100Mbps) */
21949ab747fSPaolo Bonzini         TxIFG96 = (3 << TxIFGShift),    /* 9.6us / 960ns (10 / 100Mbps) */
22049ab747fSPaolo Bonzini 
22149ab747fSPaolo Bonzini     TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
22249ab747fSPaolo Bonzini     TxCRC = (1 << 16),    /* DISABLE appending CRC to end of Tx packets */
22349ab747fSPaolo Bonzini     TxClearAbt = (1 << 0),    /* Clear abort (WO) */
22449ab747fSPaolo Bonzini     TxDMAShift = 8,        /* DMA burst value (0-7) is shifted this many bits */
22549ab747fSPaolo Bonzini     TxRetryShift = 4,    /* TXRR value (0-15) is shifted this many bits */
22649ab747fSPaolo Bonzini 
22749ab747fSPaolo Bonzini     TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
22849ab747fSPaolo Bonzini };
22949ab747fSPaolo Bonzini 
23049ab747fSPaolo Bonzini 
23149ab747fSPaolo Bonzini /* Transmit Status of All Descriptors (TSAD) Register */
23249ab747fSPaolo Bonzini enum TSAD_bits {
23349ab747fSPaolo Bonzini  TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3
23449ab747fSPaolo Bonzini  TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2
23549ab747fSPaolo Bonzini  TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1
23649ab747fSPaolo Bonzini  TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0
23749ab747fSPaolo Bonzini  TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3
23849ab747fSPaolo Bonzini  TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2
23949ab747fSPaolo Bonzini  TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1
24049ab747fSPaolo Bonzini  TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0
24149ab747fSPaolo Bonzini  TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3
24249ab747fSPaolo Bonzini  TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2
24349ab747fSPaolo Bonzini  TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1
24449ab747fSPaolo Bonzini  TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0
24549ab747fSPaolo Bonzini  TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3
24649ab747fSPaolo Bonzini  TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2
24749ab747fSPaolo Bonzini  TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1
24849ab747fSPaolo Bonzini  TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0
24949ab747fSPaolo Bonzini };
25049ab747fSPaolo Bonzini 
25149ab747fSPaolo Bonzini 
25249ab747fSPaolo Bonzini /* Bits in Config1 */
25349ab747fSPaolo Bonzini enum Config1Bits {
25449ab747fSPaolo Bonzini     Cfg1_PM_Enable = 0x01,
25549ab747fSPaolo Bonzini     Cfg1_VPD_Enable = 0x02,
25649ab747fSPaolo Bonzini     Cfg1_PIO = 0x04,
25749ab747fSPaolo Bonzini     Cfg1_MMIO = 0x08,
25849ab747fSPaolo Bonzini     LWAKE = 0x10,        /* not on 8139, 8139A */
25949ab747fSPaolo Bonzini     Cfg1_Driver_Load = 0x20,
26049ab747fSPaolo Bonzini     Cfg1_LED0 = 0x40,
26149ab747fSPaolo Bonzini     Cfg1_LED1 = 0x80,
26249ab747fSPaolo Bonzini     SLEEP = (1 << 1),    /* only on 8139, 8139A */
26349ab747fSPaolo Bonzini     PWRDN = (1 << 0),    /* only on 8139, 8139A */
26449ab747fSPaolo Bonzini };
26549ab747fSPaolo Bonzini 
26649ab747fSPaolo Bonzini /* Bits in Config3 */
26749ab747fSPaolo Bonzini enum Config3Bits {
26849ab747fSPaolo Bonzini     Cfg3_FBtBEn    = (1 << 0), /* 1 = Fast Back to Back */
26949ab747fSPaolo Bonzini     Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
27049ab747fSPaolo Bonzini     Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
27149ab747fSPaolo Bonzini     Cfg3_CardB_En  = (1 << 3), /* 1 = enable CardBus registers */
27249ab747fSPaolo Bonzini     Cfg3_LinkUp    = (1 << 4), /* 1 = wake up on link up */
27349ab747fSPaolo Bonzini     Cfg3_Magic     = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
27449ab747fSPaolo Bonzini     Cfg3_PARM_En   = (1 << 6), /* 0 = software can set twister parameters */
27549ab747fSPaolo Bonzini     Cfg3_GNTSel    = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
27649ab747fSPaolo Bonzini };
27749ab747fSPaolo Bonzini 
27849ab747fSPaolo Bonzini /* Bits in Config4 */
27949ab747fSPaolo Bonzini enum Config4Bits {
28049ab747fSPaolo Bonzini     LWPTN = (1 << 2),    /* not on 8139, 8139A */
28149ab747fSPaolo Bonzini };
28249ab747fSPaolo Bonzini 
28349ab747fSPaolo Bonzini /* Bits in Config5 */
28449ab747fSPaolo Bonzini enum Config5Bits {
28549ab747fSPaolo Bonzini     Cfg5_PME_STS     = (1 << 0), /* 1 = PCI reset resets PME_Status */
28649ab747fSPaolo Bonzini     Cfg5_LANWake     = (1 << 1), /* 1 = enable LANWake signal */
28749ab747fSPaolo Bonzini     Cfg5_LDPS        = (1 << 2), /* 0 = save power when link is down */
28849ab747fSPaolo Bonzini     Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
28949ab747fSPaolo Bonzini     Cfg5_UWF         = (1 << 4), /* 1 = accept unicast wakeup frame */
29049ab747fSPaolo Bonzini     Cfg5_MWF         = (1 << 5), /* 1 = accept multicast wakeup frame */
29149ab747fSPaolo Bonzini     Cfg5_BWF         = (1 << 6), /* 1 = accept broadcast wakeup frame */
29249ab747fSPaolo Bonzini };
29349ab747fSPaolo Bonzini 
29449ab747fSPaolo Bonzini enum RxConfigBits {
29549ab747fSPaolo Bonzini     /* rx fifo threshold */
29649ab747fSPaolo Bonzini     RxCfgFIFOShift = 13,
29749ab747fSPaolo Bonzini     RxCfgFIFONone = (7 << RxCfgFIFOShift),
29849ab747fSPaolo Bonzini 
29949ab747fSPaolo Bonzini     /* Max DMA burst */
30049ab747fSPaolo Bonzini     RxCfgDMAShift = 8,
30149ab747fSPaolo Bonzini     RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
30249ab747fSPaolo Bonzini 
30349ab747fSPaolo Bonzini     /* rx ring buffer length */
30449ab747fSPaolo Bonzini     RxCfgRcv8K = 0,
30549ab747fSPaolo Bonzini     RxCfgRcv16K = (1 << 11),
30649ab747fSPaolo Bonzini     RxCfgRcv32K = (1 << 12),
30749ab747fSPaolo Bonzini     RxCfgRcv64K = (1 << 11) | (1 << 12),
30849ab747fSPaolo Bonzini 
30949ab747fSPaolo Bonzini     /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
31049ab747fSPaolo Bonzini     RxNoWrap = (1 << 7),
31149ab747fSPaolo Bonzini };
31249ab747fSPaolo Bonzini 
31349ab747fSPaolo Bonzini /* Twister tuning parameters from RealTek.
31449ab747fSPaolo Bonzini    Completely undocumented, but required to tune bad links on some boards. */
31549ab747fSPaolo Bonzini /*
31649ab747fSPaolo Bonzini enum CSCRBits {
31749ab747fSPaolo Bonzini     CSCR_LinkOKBit = 0x0400,
31849ab747fSPaolo Bonzini     CSCR_LinkChangeBit = 0x0800,
31949ab747fSPaolo Bonzini     CSCR_LinkStatusBits = 0x0f000,
32049ab747fSPaolo Bonzini     CSCR_LinkDownOffCmd = 0x003c0,
32149ab747fSPaolo Bonzini     CSCR_LinkDownCmd = 0x0f3c0,
32249ab747fSPaolo Bonzini */
32349ab747fSPaolo Bonzini enum CSCRBits {
32449ab747fSPaolo Bonzini     CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */
32549ab747fSPaolo Bonzini     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*/
32649ab747fSPaolo Bonzini     CSCR_HEART_BIT = 1<<8,  /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/
32749ab747fSPaolo Bonzini     CSCR_JBEN = 1<<7,  /* 1 = enable jabber function. 0 = disable jabber function, def 1*/
32849ab747fSPaolo Bonzini     CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/
32949ab747fSPaolo Bonzini     CSCR_F_Connect  = 1<<5,  /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/
33049ab747fSPaolo Bonzini     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*/
33149ab747fSPaolo Bonzini     CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/
33249ab747fSPaolo Bonzini     CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/
33349ab747fSPaolo Bonzini };
33449ab747fSPaolo Bonzini 
33549ab747fSPaolo Bonzini enum Cfg9346Bits {
33649ab747fSPaolo Bonzini     Cfg9346_Normal = 0x00,
33749ab747fSPaolo Bonzini     Cfg9346_Autoload = 0x40,
33849ab747fSPaolo Bonzini     Cfg9346_Programming = 0x80,
33949ab747fSPaolo Bonzini     Cfg9346_ConfigWrite = 0xC0,
34049ab747fSPaolo Bonzini };
34149ab747fSPaolo Bonzini 
34249ab747fSPaolo Bonzini typedef enum {
34349ab747fSPaolo Bonzini     CH_8139 = 0,
34449ab747fSPaolo Bonzini     CH_8139_K,
34549ab747fSPaolo Bonzini     CH_8139A,
34649ab747fSPaolo Bonzini     CH_8139A_G,
34749ab747fSPaolo Bonzini     CH_8139B,
34849ab747fSPaolo Bonzini     CH_8130,
34949ab747fSPaolo Bonzini     CH_8139C,
35049ab747fSPaolo Bonzini     CH_8100,
35149ab747fSPaolo Bonzini     CH_8100B_8139D,
35249ab747fSPaolo Bonzini     CH_8101,
35349ab747fSPaolo Bonzini } chip_t;
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini enum chip_flags {
35649ab747fSPaolo Bonzini     HasHltClk = (1 << 0),
35749ab747fSPaolo Bonzini     HasLWake = (1 << 1),
35849ab747fSPaolo Bonzini };
35949ab747fSPaolo Bonzini 
36049ab747fSPaolo Bonzini #define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
36149ab747fSPaolo Bonzini     (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
36249ab747fSPaolo Bonzini #define HW_REVID_MASK    HW_REVID(1, 1, 1, 1, 1, 1, 1)
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini #define RTL8139_PCI_REVID_8139      0x10
36549ab747fSPaolo Bonzini #define RTL8139_PCI_REVID_8139CPLUS 0x20
36649ab747fSPaolo Bonzini 
36749ab747fSPaolo Bonzini #define RTL8139_PCI_REVID           RTL8139_PCI_REVID_8139CPLUS
36849ab747fSPaolo Bonzini 
36949ab747fSPaolo Bonzini /* Size is 64 * 16bit words */
37049ab747fSPaolo Bonzini #define EEPROM_9346_ADDR_BITS 6
37149ab747fSPaolo Bonzini #define EEPROM_9346_SIZE  (1 << EEPROM_9346_ADDR_BITS)
37249ab747fSPaolo Bonzini #define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)
37349ab747fSPaolo Bonzini 
37449ab747fSPaolo Bonzini enum Chip9346Operation
37549ab747fSPaolo Bonzini {
37649ab747fSPaolo Bonzini     Chip9346_op_mask = 0xc0,          /* 10 zzzzzz */
37749ab747fSPaolo Bonzini     Chip9346_op_read = 0x80,          /* 10 AAAAAA */
37849ab747fSPaolo Bonzini     Chip9346_op_write = 0x40,         /* 01 AAAAAA D(15)..D(0) */
37949ab747fSPaolo Bonzini     Chip9346_op_ext_mask = 0xf0,      /* 11 zzzzzz */
38049ab747fSPaolo Bonzini     Chip9346_op_write_enable = 0x30,  /* 00 11zzzz */
38149ab747fSPaolo Bonzini     Chip9346_op_write_all = 0x10,     /* 00 01zzzz */
38249ab747fSPaolo Bonzini     Chip9346_op_write_disable = 0x00, /* 00 00zzzz */
38349ab747fSPaolo Bonzini };
38449ab747fSPaolo Bonzini 
38549ab747fSPaolo Bonzini enum Chip9346Mode
38649ab747fSPaolo Bonzini {
38749ab747fSPaolo Bonzini     Chip9346_none = 0,
38849ab747fSPaolo Bonzini     Chip9346_enter_command_mode,
38949ab747fSPaolo Bonzini     Chip9346_read_command,
39049ab747fSPaolo Bonzini     Chip9346_data_read,      /* from output register */
39149ab747fSPaolo Bonzini     Chip9346_data_write,     /* to input register, then to contents at specified address */
39249ab747fSPaolo Bonzini     Chip9346_data_write_all, /* to input register, then filling contents */
39349ab747fSPaolo Bonzini };
39449ab747fSPaolo Bonzini 
39549ab747fSPaolo Bonzini typedef struct EEprom9346
39649ab747fSPaolo Bonzini {
39749ab747fSPaolo Bonzini     uint16_t contents[EEPROM_9346_SIZE];
39849ab747fSPaolo Bonzini     int      mode;
39949ab747fSPaolo Bonzini     uint32_t tick;
40049ab747fSPaolo Bonzini     uint8_t  address;
40149ab747fSPaolo Bonzini     uint16_t input;
40249ab747fSPaolo Bonzini     uint16_t output;
40349ab747fSPaolo Bonzini 
40449ab747fSPaolo Bonzini     uint8_t eecs;
40549ab747fSPaolo Bonzini     uint8_t eesk;
40649ab747fSPaolo Bonzini     uint8_t eedi;
40749ab747fSPaolo Bonzini     uint8_t eedo;
40849ab747fSPaolo Bonzini } EEprom9346;
40949ab747fSPaolo Bonzini 
41049ab747fSPaolo Bonzini typedef struct RTL8139TallyCounters
41149ab747fSPaolo Bonzini {
41249ab747fSPaolo Bonzini     /* Tally counters */
41349ab747fSPaolo Bonzini     uint64_t   TxOk;
41449ab747fSPaolo Bonzini     uint64_t   RxOk;
41549ab747fSPaolo Bonzini     uint64_t   TxERR;
41649ab747fSPaolo Bonzini     uint32_t   RxERR;
41749ab747fSPaolo Bonzini     uint16_t   MissPkt;
41849ab747fSPaolo Bonzini     uint16_t   FAE;
41949ab747fSPaolo Bonzini     uint32_t   Tx1Col;
42049ab747fSPaolo Bonzini     uint32_t   TxMCol;
42149ab747fSPaolo Bonzini     uint64_t   RxOkPhy;
42249ab747fSPaolo Bonzini     uint64_t   RxOkBrd;
42349ab747fSPaolo Bonzini     uint32_t   RxOkMul;
42449ab747fSPaolo Bonzini     uint16_t   TxAbt;
42549ab747fSPaolo Bonzini     uint16_t   TxUndrn;
42649ab747fSPaolo Bonzini } RTL8139TallyCounters;
42749ab747fSPaolo Bonzini 
42849ab747fSPaolo Bonzini /* Clears all tally counters */
42949ab747fSPaolo Bonzini static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters);
43049ab747fSPaolo Bonzini 
431db1015e9SEduardo Habkost struct RTL8139State {
43288a411a8SAndreas Färber     /*< private >*/
43388a411a8SAndreas Färber     PCIDevice parent_obj;
43488a411a8SAndreas Färber     /*< public >*/
43588a411a8SAndreas Färber 
43649ab747fSPaolo Bonzini     uint8_t phys[8]; /* mac address */
43749ab747fSPaolo Bonzini     uint8_t mult[8]; /* multicast mask array */
43849ab747fSPaolo Bonzini 
43949ab747fSPaolo Bonzini     uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */
44049ab747fSPaolo Bonzini     uint32_t TxAddr[4];   /* TxAddr0 */
44149ab747fSPaolo Bonzini     uint32_t RxBuf;       /* Receive buffer */
44249ab747fSPaolo Bonzini     uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */
44349ab747fSPaolo Bonzini     uint32_t RxBufPtr;
44449ab747fSPaolo Bonzini     uint32_t RxBufAddr;
44549ab747fSPaolo Bonzini 
44649ab747fSPaolo Bonzini     uint16_t IntrStatus;
44749ab747fSPaolo Bonzini     uint16_t IntrMask;
44849ab747fSPaolo Bonzini 
44949ab747fSPaolo Bonzini     uint32_t TxConfig;
45049ab747fSPaolo Bonzini     uint32_t RxConfig;
45149ab747fSPaolo Bonzini     uint32_t RxMissed;
45249ab747fSPaolo Bonzini 
45349ab747fSPaolo Bonzini     uint16_t CSCR;
45449ab747fSPaolo Bonzini 
45549ab747fSPaolo Bonzini     uint8_t  Cfg9346;
45649ab747fSPaolo Bonzini     uint8_t  Config0;
45749ab747fSPaolo Bonzini     uint8_t  Config1;
45849ab747fSPaolo Bonzini     uint8_t  Config3;
45949ab747fSPaolo Bonzini     uint8_t  Config4;
46049ab747fSPaolo Bonzini     uint8_t  Config5;
46149ab747fSPaolo Bonzini 
46249ab747fSPaolo Bonzini     uint8_t  clock_enabled;
46349ab747fSPaolo Bonzini     uint8_t  bChipCmdState;
46449ab747fSPaolo Bonzini 
46549ab747fSPaolo Bonzini     uint16_t MultiIntr;
46649ab747fSPaolo Bonzini 
46749ab747fSPaolo Bonzini     uint16_t BasicModeCtrl;
46849ab747fSPaolo Bonzini     uint16_t BasicModeStatus;
46949ab747fSPaolo Bonzini     uint16_t NWayAdvert;
47049ab747fSPaolo Bonzini     uint16_t NWayLPAR;
47149ab747fSPaolo Bonzini     uint16_t NWayExpansion;
47249ab747fSPaolo Bonzini 
47349ab747fSPaolo Bonzini     uint16_t CpCmd;
47449ab747fSPaolo Bonzini     uint8_t  TxThresh;
47549ab747fSPaolo Bonzini 
47649ab747fSPaolo Bonzini     NICState *nic;
47749ab747fSPaolo Bonzini     NICConf conf;
47849ab747fSPaolo Bonzini 
47949ab747fSPaolo Bonzini     /* C ring mode */
48049ab747fSPaolo Bonzini     uint32_t   currTxDesc;
48149ab747fSPaolo Bonzini 
48249ab747fSPaolo Bonzini     /* C+ mode */
48349ab747fSPaolo Bonzini     uint32_t   cplus_enabled;
48449ab747fSPaolo Bonzini 
48549ab747fSPaolo Bonzini     uint32_t   currCPlusRxDesc;
48649ab747fSPaolo Bonzini     uint32_t   currCPlusTxDesc;
48749ab747fSPaolo Bonzini 
48849ab747fSPaolo Bonzini     uint32_t   RxRingAddrLO;
48949ab747fSPaolo Bonzini     uint32_t   RxRingAddrHI;
49049ab747fSPaolo Bonzini 
49149ab747fSPaolo Bonzini     EEprom9346 eeprom;
49249ab747fSPaolo Bonzini 
49349ab747fSPaolo Bonzini     uint32_t   TCTR;
49449ab747fSPaolo Bonzini     uint32_t   TimerInt;
49549ab747fSPaolo Bonzini     int64_t    TCTR_base;
49649ab747fSPaolo Bonzini 
49749ab747fSPaolo Bonzini     /* Tally counters */
49849ab747fSPaolo Bonzini     RTL8139TallyCounters tally_counters;
49949ab747fSPaolo Bonzini 
50049ab747fSPaolo Bonzini     /* Non-persistent data */
50149ab747fSPaolo Bonzini     uint8_t   *cplus_txbuffer;
50249ab747fSPaolo Bonzini     int        cplus_txbuffer_len;
50349ab747fSPaolo Bonzini     int        cplus_txbuffer_offset;
50449ab747fSPaolo Bonzini 
50549ab747fSPaolo Bonzini     /* PCI interrupt timer */
50649ab747fSPaolo Bonzini     QEMUTimer *timer;
50749ab747fSPaolo Bonzini 
50849ab747fSPaolo Bonzini     MemoryRegion bar_io;
50949ab747fSPaolo Bonzini     MemoryRegion bar_mem;
51049ab747fSPaolo Bonzini 
51149ab747fSPaolo Bonzini     /* Support migration to/from old versions */
51249ab747fSPaolo Bonzini     int rtl8139_mmio_io_addr_dummy;
513db1015e9SEduardo Habkost };
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini /* Writes tally counters to memory via DMA */
51649ab747fSPaolo Bonzini static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr);
51749ab747fSPaolo Bonzini 
518237c255cSPaolo Bonzini static void rtl8139_set_next_tctr_time(RTL8139State *s);
51949ab747fSPaolo Bonzini 
prom9346_decode_command(EEprom9346 * eeprom,uint8_t command)52049ab747fSPaolo Bonzini static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
52149ab747fSPaolo Bonzini {
52249ab747fSPaolo Bonzini     DPRINTF("eeprom command 0x%02x\n", command);
52349ab747fSPaolo Bonzini 
52449ab747fSPaolo Bonzini     switch (command & Chip9346_op_mask)
52549ab747fSPaolo Bonzini     {
52649ab747fSPaolo Bonzini         case Chip9346_op_read:
52749ab747fSPaolo Bonzini         {
52849ab747fSPaolo Bonzini             eeprom->address = command & EEPROM_9346_ADDR_MASK;
52949ab747fSPaolo Bonzini             eeprom->output = eeprom->contents[eeprom->address];
53049ab747fSPaolo Bonzini             eeprom->eedo = 0;
53149ab747fSPaolo Bonzini             eeprom->tick = 0;
53249ab747fSPaolo Bonzini             eeprom->mode = Chip9346_data_read;
53349ab747fSPaolo Bonzini             DPRINTF("eeprom read from address 0x%02x data=0x%04x\n",
53449ab747fSPaolo Bonzini                 eeprom->address, eeprom->output);
53549ab747fSPaolo Bonzini         }
53649ab747fSPaolo Bonzini         break;
53749ab747fSPaolo Bonzini 
53849ab747fSPaolo Bonzini         case Chip9346_op_write:
53949ab747fSPaolo Bonzini         {
54049ab747fSPaolo Bonzini             eeprom->address = command & EEPROM_9346_ADDR_MASK;
54149ab747fSPaolo Bonzini             eeprom->input = 0;
54249ab747fSPaolo Bonzini             eeprom->tick = 0;
54349ab747fSPaolo Bonzini             eeprom->mode = Chip9346_none; /* Chip9346_data_write */
54449ab747fSPaolo Bonzini             DPRINTF("eeprom begin write to address 0x%02x\n",
54549ab747fSPaolo Bonzini                 eeprom->address);
54649ab747fSPaolo Bonzini         }
54749ab747fSPaolo Bonzini         break;
54849ab747fSPaolo Bonzini         default:
54949ab747fSPaolo Bonzini             eeprom->mode = Chip9346_none;
55049ab747fSPaolo Bonzini             switch (command & Chip9346_op_ext_mask)
55149ab747fSPaolo Bonzini             {
55249ab747fSPaolo Bonzini                 case Chip9346_op_write_enable:
55349ab747fSPaolo Bonzini                     DPRINTF("eeprom write enabled\n");
55449ab747fSPaolo Bonzini                     break;
55549ab747fSPaolo Bonzini                 case Chip9346_op_write_all:
55649ab747fSPaolo Bonzini                     DPRINTF("eeprom begin write all\n");
55749ab747fSPaolo Bonzini                     break;
55849ab747fSPaolo Bonzini                 case Chip9346_op_write_disable:
55949ab747fSPaolo Bonzini                     DPRINTF("eeprom write disabled\n");
56049ab747fSPaolo Bonzini                     break;
56149ab747fSPaolo Bonzini             }
56249ab747fSPaolo Bonzini             break;
56349ab747fSPaolo Bonzini     }
56449ab747fSPaolo Bonzini }
56549ab747fSPaolo Bonzini 
prom9346_shift_clock(EEprom9346 * eeprom)56649ab747fSPaolo Bonzini static void prom9346_shift_clock(EEprom9346 *eeprom)
56749ab747fSPaolo Bonzini {
56849ab747fSPaolo Bonzini     int bit = eeprom->eedi?1:0;
56949ab747fSPaolo Bonzini 
57049ab747fSPaolo Bonzini     ++ eeprom->tick;
57149ab747fSPaolo Bonzini 
57249ab747fSPaolo Bonzini     DPRINTF("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi,
57349ab747fSPaolo Bonzini         eeprom->eedo);
57449ab747fSPaolo Bonzini 
57549ab747fSPaolo Bonzini     switch (eeprom->mode)
57649ab747fSPaolo Bonzini     {
57749ab747fSPaolo Bonzini         case Chip9346_enter_command_mode:
57849ab747fSPaolo Bonzini             if (bit)
57949ab747fSPaolo Bonzini             {
58049ab747fSPaolo Bonzini                 eeprom->mode = Chip9346_read_command;
58149ab747fSPaolo Bonzini                 eeprom->tick = 0;
58249ab747fSPaolo Bonzini                 eeprom->input = 0;
58349ab747fSPaolo Bonzini                 DPRINTF("eeprom: +++ synchronized, begin command read\n");
58449ab747fSPaolo Bonzini             }
58549ab747fSPaolo Bonzini             break;
58649ab747fSPaolo Bonzini 
58749ab747fSPaolo Bonzini         case Chip9346_read_command:
58849ab747fSPaolo Bonzini             eeprom->input = (eeprom->input << 1) | (bit & 1);
58949ab747fSPaolo Bonzini             if (eeprom->tick == 8)
59049ab747fSPaolo Bonzini             {
59149ab747fSPaolo Bonzini                 prom9346_decode_command(eeprom, eeprom->input & 0xff);
59249ab747fSPaolo Bonzini             }
59349ab747fSPaolo Bonzini             break;
59449ab747fSPaolo Bonzini 
59549ab747fSPaolo Bonzini         case Chip9346_data_read:
59649ab747fSPaolo Bonzini             eeprom->eedo = (eeprom->output & 0x8000)?1:0;
59749ab747fSPaolo Bonzini             eeprom->output <<= 1;
59849ab747fSPaolo Bonzini             if (eeprom->tick == 16)
59949ab747fSPaolo Bonzini             {
60049ab747fSPaolo Bonzini #if 1
60149ab747fSPaolo Bonzini         // the FreeBSD drivers (rl and re) don't explicitly toggle
60249ab747fSPaolo Bonzini         // CS between reads (or does setting Cfg9346 to 0 count too?),
60349ab747fSPaolo Bonzini         // so we need to enter wait-for-command state here
60449ab747fSPaolo Bonzini                 eeprom->mode = Chip9346_enter_command_mode;
60549ab747fSPaolo Bonzini                 eeprom->input = 0;
60649ab747fSPaolo Bonzini                 eeprom->tick = 0;
60749ab747fSPaolo Bonzini 
60849ab747fSPaolo Bonzini                 DPRINTF("eeprom: +++ end of read, awaiting next command\n");
60949ab747fSPaolo Bonzini #else
61049ab747fSPaolo Bonzini         // original behaviour
61149ab747fSPaolo Bonzini                 ++eeprom->address;
61249ab747fSPaolo Bonzini                 eeprom->address &= EEPROM_9346_ADDR_MASK;
61349ab747fSPaolo Bonzini                 eeprom->output = eeprom->contents[eeprom->address];
61449ab747fSPaolo Bonzini                 eeprom->tick = 0;
61549ab747fSPaolo Bonzini 
61649ab747fSPaolo Bonzini                 DPRINTF("eeprom: +++ read next address 0x%02x data=0x%04x\n",
61749ab747fSPaolo Bonzini                     eeprom->address, eeprom->output);
61849ab747fSPaolo Bonzini #endif
61949ab747fSPaolo Bonzini             }
62049ab747fSPaolo Bonzini             break;
62149ab747fSPaolo Bonzini 
62249ab747fSPaolo Bonzini         case Chip9346_data_write:
62349ab747fSPaolo Bonzini             eeprom->input = (eeprom->input << 1) | (bit & 1);
62449ab747fSPaolo Bonzini             if (eeprom->tick == 16)
62549ab747fSPaolo Bonzini             {
62649ab747fSPaolo Bonzini                 DPRINTF("eeprom write to address 0x%02x data=0x%04x\n",
62749ab747fSPaolo Bonzini                     eeprom->address, eeprom->input);
62849ab747fSPaolo Bonzini 
62949ab747fSPaolo Bonzini                 eeprom->contents[eeprom->address] = eeprom->input;
63049ab747fSPaolo Bonzini                 eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
63149ab747fSPaolo Bonzini                 eeprom->tick = 0;
63249ab747fSPaolo Bonzini                 eeprom->input = 0;
63349ab747fSPaolo Bonzini             }
63449ab747fSPaolo Bonzini             break;
63549ab747fSPaolo Bonzini 
63649ab747fSPaolo Bonzini         case Chip9346_data_write_all:
63749ab747fSPaolo Bonzini             eeprom->input = (eeprom->input << 1) | (bit & 1);
63849ab747fSPaolo Bonzini             if (eeprom->tick == 16)
63949ab747fSPaolo Bonzini             {
64049ab747fSPaolo Bonzini                 int i;
64149ab747fSPaolo Bonzini                 for (i = 0; i < EEPROM_9346_SIZE; i++)
64249ab747fSPaolo Bonzini                 {
64349ab747fSPaolo Bonzini                     eeprom->contents[i] = eeprom->input;
64449ab747fSPaolo Bonzini                 }
64549ab747fSPaolo Bonzini                 DPRINTF("eeprom filled with data=0x%04x\n", eeprom->input);
64649ab747fSPaolo Bonzini 
64749ab747fSPaolo Bonzini                 eeprom->mode = Chip9346_enter_command_mode;
64849ab747fSPaolo Bonzini                 eeprom->tick = 0;
64949ab747fSPaolo Bonzini                 eeprom->input = 0;
65049ab747fSPaolo Bonzini             }
65149ab747fSPaolo Bonzini             break;
65249ab747fSPaolo Bonzini 
65349ab747fSPaolo Bonzini         default:
65449ab747fSPaolo Bonzini             break;
65549ab747fSPaolo Bonzini     }
65649ab747fSPaolo Bonzini }
65749ab747fSPaolo Bonzini 
prom9346_get_wire(RTL8139State * s)65849ab747fSPaolo Bonzini static int prom9346_get_wire(RTL8139State *s)
65949ab747fSPaolo Bonzini {
66049ab747fSPaolo Bonzini     EEprom9346 *eeprom = &s->eeprom;
66149ab747fSPaolo Bonzini     if (!eeprom->eecs)
66249ab747fSPaolo Bonzini         return 0;
66349ab747fSPaolo Bonzini 
66449ab747fSPaolo Bonzini     return eeprom->eedo;
66549ab747fSPaolo Bonzini }
66649ab747fSPaolo Bonzini 
66749ab747fSPaolo Bonzini /* FIXME: This should be merged into/replaced by eeprom93xx.c.  */
prom9346_set_wire(RTL8139State * s,int eecs,int eesk,int eedi)66849ab747fSPaolo Bonzini static void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi)
66949ab747fSPaolo Bonzini {
67049ab747fSPaolo Bonzini     EEprom9346 *eeprom = &s->eeprom;
67149ab747fSPaolo Bonzini     uint8_t old_eecs = eeprom->eecs;
67249ab747fSPaolo Bonzini     uint8_t old_eesk = eeprom->eesk;
67349ab747fSPaolo Bonzini 
67449ab747fSPaolo Bonzini     eeprom->eecs = eecs;
67549ab747fSPaolo Bonzini     eeprom->eesk = eesk;
67649ab747fSPaolo Bonzini     eeprom->eedi = eedi;
67749ab747fSPaolo Bonzini 
67849ab747fSPaolo Bonzini     DPRINTF("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs,
67949ab747fSPaolo Bonzini         eeprom->eesk, eeprom->eedi, eeprom->eedo);
68049ab747fSPaolo Bonzini 
68149ab747fSPaolo Bonzini     if (!old_eecs && eecs)
68249ab747fSPaolo Bonzini     {
68349ab747fSPaolo Bonzini         /* Synchronize start */
68449ab747fSPaolo Bonzini         eeprom->tick = 0;
68549ab747fSPaolo Bonzini         eeprom->input = 0;
68649ab747fSPaolo Bonzini         eeprom->output = 0;
68749ab747fSPaolo Bonzini         eeprom->mode = Chip9346_enter_command_mode;
68849ab747fSPaolo Bonzini 
68949ab747fSPaolo Bonzini         DPRINTF("=== eeprom: begin access, enter command mode\n");
69049ab747fSPaolo Bonzini     }
69149ab747fSPaolo Bonzini 
69249ab747fSPaolo Bonzini     if (!eecs)
69349ab747fSPaolo Bonzini     {
69449ab747fSPaolo Bonzini         DPRINTF("=== eeprom: end access\n");
69549ab747fSPaolo Bonzini         return;
69649ab747fSPaolo Bonzini     }
69749ab747fSPaolo Bonzini 
69849ab747fSPaolo Bonzini     if (!old_eesk && eesk)
69949ab747fSPaolo Bonzini     {
70049ab747fSPaolo Bonzini         /* SK front rules */
70149ab747fSPaolo Bonzini         prom9346_shift_clock(eeprom);
70249ab747fSPaolo Bonzini     }
70349ab747fSPaolo Bonzini }
70449ab747fSPaolo Bonzini 
rtl8139_update_irq(RTL8139State * s)70549ab747fSPaolo Bonzini static void rtl8139_update_irq(RTL8139State *s)
70649ab747fSPaolo Bonzini {
70788a411a8SAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
70849ab747fSPaolo Bonzini     int isr;
70949ab747fSPaolo Bonzini     isr = (s->IntrStatus & s->IntrMask) & 0xffff;
71049ab747fSPaolo Bonzini 
71149ab747fSPaolo Bonzini     DPRINTF("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus,
71249ab747fSPaolo Bonzini         s->IntrMask);
71349ab747fSPaolo Bonzini 
7149e64f8a3SMarcel Apfelbaum     pci_set_irq(d, (isr != 0));
71549ab747fSPaolo Bonzini }
71649ab747fSPaolo Bonzini 
rtl8139_RxWrap(RTL8139State * s)71749ab747fSPaolo Bonzini static int rtl8139_RxWrap(RTL8139State *s)
71849ab747fSPaolo Bonzini {
71949ab747fSPaolo Bonzini     /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */
72049ab747fSPaolo Bonzini     return (s->RxConfig & (1 << 7));
72149ab747fSPaolo Bonzini }
72249ab747fSPaolo Bonzini 
rtl8139_receiver_enabled(RTL8139State * s)72349ab747fSPaolo Bonzini static int rtl8139_receiver_enabled(RTL8139State *s)
72449ab747fSPaolo Bonzini {
72549ab747fSPaolo Bonzini     return s->bChipCmdState & CmdRxEnb;
72649ab747fSPaolo Bonzini }
72749ab747fSPaolo Bonzini 
rtl8139_transmitter_enabled(RTL8139State * s)72849ab747fSPaolo Bonzini static int rtl8139_transmitter_enabled(RTL8139State *s)
72949ab747fSPaolo Bonzini {
73049ab747fSPaolo Bonzini     return s->bChipCmdState & CmdTxEnb;
73149ab747fSPaolo Bonzini }
73249ab747fSPaolo Bonzini 
rtl8139_cp_receiver_enabled(RTL8139State * s)73349ab747fSPaolo Bonzini static int rtl8139_cp_receiver_enabled(RTL8139State *s)
73449ab747fSPaolo Bonzini {
73549ab747fSPaolo Bonzini     return s->CpCmd & CPlusRxEnb;
73649ab747fSPaolo Bonzini }
73749ab747fSPaolo Bonzini 
rtl8139_cp_transmitter_enabled(RTL8139State * s)73849ab747fSPaolo Bonzini static int rtl8139_cp_transmitter_enabled(RTL8139State *s)
73949ab747fSPaolo Bonzini {
74049ab747fSPaolo Bonzini     return s->CpCmd & CPlusTxEnb;
74149ab747fSPaolo Bonzini }
74249ab747fSPaolo Bonzini 
rtl8139_write_buffer(RTL8139State * s,const void * buf,int size)74349ab747fSPaolo Bonzini static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
74449ab747fSPaolo Bonzini {
74588a411a8SAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
74688a411a8SAndreas Färber 
74749ab747fSPaolo Bonzini     if (s->RxBufAddr + size > s->RxBufferSize)
74849ab747fSPaolo Bonzini     {
74949ab747fSPaolo Bonzini         int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize);
75049ab747fSPaolo Bonzini 
75149ab747fSPaolo Bonzini         /* write packet data */
75249ab747fSPaolo Bonzini         if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s)))
75349ab747fSPaolo Bonzini         {
75449ab747fSPaolo Bonzini             DPRINTF(">>> rx packet wrapped in buffer at %d\n", size - wrapped);
75549ab747fSPaolo Bonzini 
75649ab747fSPaolo Bonzini             if (size > wrapped)
75749ab747fSPaolo Bonzini             {
75888a411a8SAndreas Färber                 pci_dma_write(d, s->RxBuf + s->RxBufAddr,
75949ab747fSPaolo Bonzini                               buf, size-wrapped);
76049ab747fSPaolo Bonzini             }
76149ab747fSPaolo Bonzini 
76249ab747fSPaolo Bonzini             /* reset buffer pointer */
76349ab747fSPaolo Bonzini             s->RxBufAddr = 0;
76449ab747fSPaolo Bonzini 
76588a411a8SAndreas Färber             pci_dma_write(d, s->RxBuf + s->RxBufAddr,
76649ab747fSPaolo Bonzini                           buf + (size-wrapped), wrapped);
76749ab747fSPaolo Bonzini 
76849ab747fSPaolo Bonzini             s->RxBufAddr = wrapped;
76949ab747fSPaolo Bonzini 
77049ab747fSPaolo Bonzini             return;
77149ab747fSPaolo Bonzini         }
77249ab747fSPaolo Bonzini     }
77349ab747fSPaolo Bonzini 
77449ab747fSPaolo Bonzini     /* non-wrapping path or overwrapping enabled */
77588a411a8SAndreas Färber     pci_dma_write(d, s->RxBuf + s->RxBufAddr, buf, size);
77649ab747fSPaolo Bonzini 
77749ab747fSPaolo Bonzini     s->RxBufAddr += size;
77849ab747fSPaolo Bonzini }
77949ab747fSPaolo Bonzini 
78049ab747fSPaolo Bonzini #define MIN_BUF_SIZE 60
rtl8139_addr64(uint32_t low,uint32_t high)78149ab747fSPaolo Bonzini static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
78249ab747fSPaolo Bonzini {
78349ab747fSPaolo Bonzini     return low | ((uint64_t)high << 32);
78449ab747fSPaolo Bonzini }
78549ab747fSPaolo Bonzini 
78649ab747fSPaolo Bonzini /* Workaround for buggy guest driver such as linux who allocates rx
78749ab747fSPaolo Bonzini  * rings after the receiver were enabled. */
rtl8139_cp_rx_valid(RTL8139State * s)78849ab747fSPaolo Bonzini static bool rtl8139_cp_rx_valid(RTL8139State *s)
78949ab747fSPaolo Bonzini {
79049ab747fSPaolo Bonzini     return !(s->RxRingAddrLO == 0 && s->RxRingAddrHI == 0);
79149ab747fSPaolo Bonzini }
79249ab747fSPaolo Bonzini 
rtl8139_can_receive(NetClientState * nc)793b8c4b67eSPhilippe Mathieu-Daudé static bool rtl8139_can_receive(NetClientState *nc)
79449ab747fSPaolo Bonzini {
79549ab747fSPaolo Bonzini     RTL8139State *s = qemu_get_nic_opaque(nc);
79649ab747fSPaolo Bonzini     int avail;
79749ab747fSPaolo Bonzini 
79849ab747fSPaolo Bonzini     /* Receive (drop) packets if card is disabled.  */
7993317db74SPhilippe Mathieu-Daudé     if (!s->clock_enabled) {
800b8c4b67eSPhilippe Mathieu-Daudé         return true;
8013317db74SPhilippe Mathieu-Daudé     }
8023317db74SPhilippe Mathieu-Daudé     if (!rtl8139_receiver_enabled(s)) {
803b8c4b67eSPhilippe Mathieu-Daudé         return true;
8043317db74SPhilippe Mathieu-Daudé     }
80549ab747fSPaolo Bonzini 
80649ab747fSPaolo Bonzini     if (rtl8139_cp_receiver_enabled(s) && rtl8139_cp_rx_valid(s)) {
80749ab747fSPaolo Bonzini         /* ??? Flow control not implemented in c+ mode.
80849ab747fSPaolo Bonzini            This is a hack to work around slirp deficiencies anyway.  */
809b8c4b67eSPhilippe Mathieu-Daudé         return true;
8102fa3d2d4SPhilippe Mathieu-Daudé     }
8112fa3d2d4SPhilippe Mathieu-Daudé 
81249ab747fSPaolo Bonzini     avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr,
81349ab747fSPaolo Bonzini                  s->RxBufferSize);
8142fa3d2d4SPhilippe Mathieu-Daudé     return avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow);
81549ab747fSPaolo Bonzini }
81649ab747fSPaolo Bonzini 
rtl8139_do_receive(NetClientState * nc,const uint8_t * buf,size_t size_,int do_interrupt)81749ab747fSPaolo Bonzini static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt)
81849ab747fSPaolo Bonzini {
81949ab747fSPaolo Bonzini     RTL8139State *s = qemu_get_nic_opaque(nc);
82088a411a8SAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
82149ab747fSPaolo Bonzini     /* size is the length of the buffer passed to the driver */
8221a326646SJason Wang     size_t size = size_;
82349ab747fSPaolo Bonzini     const uint8_t *dot1q_buf = NULL;
82449ab747fSPaolo Bonzini 
82549ab747fSPaolo Bonzini     uint32_t packet_header = 0;
82649ab747fSPaolo Bonzini 
82749ab747fSPaolo Bonzini     static const uint8_t broadcast_macaddr[6] =
82849ab747fSPaolo Bonzini         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
82949ab747fSPaolo Bonzini 
8301a326646SJason Wang     DPRINTF(">>> received len=%zu\n", size);
83149ab747fSPaolo Bonzini 
83249ab747fSPaolo Bonzini     /* test if board clock is stopped */
83349ab747fSPaolo Bonzini     if (!s->clock_enabled)
83449ab747fSPaolo Bonzini     {
83549ab747fSPaolo Bonzini         DPRINTF("stopped ==========================\n");
83649ab747fSPaolo Bonzini         return -1;
83749ab747fSPaolo Bonzini     }
83849ab747fSPaolo Bonzini 
83949ab747fSPaolo Bonzini     /* first check if receiver is enabled */
84049ab747fSPaolo Bonzini 
84149ab747fSPaolo Bonzini     if (!rtl8139_receiver_enabled(s))
84249ab747fSPaolo Bonzini     {
84349ab747fSPaolo Bonzini         DPRINTF("receiver disabled ================\n");
84449ab747fSPaolo Bonzini         return -1;
84549ab747fSPaolo Bonzini     }
84649ab747fSPaolo Bonzini 
84749ab747fSPaolo Bonzini     /* XXX: check this */
84849ab747fSPaolo Bonzini     if (s->RxConfig & AcceptAllPhys) {
84949ab747fSPaolo Bonzini         /* promiscuous: receive all */
85049ab747fSPaolo Bonzini         DPRINTF(">>> packet received in promiscuous mode\n");
85149ab747fSPaolo Bonzini 
85249ab747fSPaolo Bonzini     } else {
85349ab747fSPaolo Bonzini         if (!memcmp(buf,  broadcast_macaddr, 6)) {
85449ab747fSPaolo Bonzini             /* broadcast address */
85549ab747fSPaolo Bonzini             if (!(s->RxConfig & AcceptBroadcast))
85649ab747fSPaolo Bonzini             {
85749ab747fSPaolo Bonzini                 DPRINTF(">>> broadcast packet rejected\n");
85849ab747fSPaolo Bonzini 
85949ab747fSPaolo Bonzini                 /* update tally counter */
86049ab747fSPaolo Bonzini                 ++s->tally_counters.RxERR;
86149ab747fSPaolo Bonzini 
86249ab747fSPaolo Bonzini                 return size;
86349ab747fSPaolo Bonzini             }
86449ab747fSPaolo Bonzini 
86549ab747fSPaolo Bonzini             packet_header |= RxBroadcast;
86649ab747fSPaolo Bonzini 
86749ab747fSPaolo Bonzini             DPRINTF(">>> broadcast packet received\n");
86849ab747fSPaolo Bonzini 
86949ab747fSPaolo Bonzini             /* update tally counter */
87049ab747fSPaolo Bonzini             ++s->tally_counters.RxOkBrd;
87149ab747fSPaolo Bonzini 
87249ab747fSPaolo Bonzini         } else if (buf[0] & 0x01) {
87349ab747fSPaolo Bonzini             /* multicast */
87449ab747fSPaolo Bonzini             if (!(s->RxConfig & AcceptMulticast))
87549ab747fSPaolo Bonzini             {
87649ab747fSPaolo Bonzini                 DPRINTF(">>> multicast packet rejected\n");
87749ab747fSPaolo Bonzini 
87849ab747fSPaolo Bonzini                 /* update tally counter */
87949ab747fSPaolo Bonzini                 ++s->tally_counters.RxERR;
88049ab747fSPaolo Bonzini 
88149ab747fSPaolo Bonzini                 return size;
88249ab747fSPaolo Bonzini             }
88349ab747fSPaolo Bonzini 
884e7a58fc7SMark Cave-Ayland             int mcast_idx = net_crc32(buf, ETH_ALEN) >> 26;
88549ab747fSPaolo Bonzini 
88649ab747fSPaolo Bonzini             if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
88749ab747fSPaolo Bonzini             {
88849ab747fSPaolo Bonzini                 DPRINTF(">>> multicast address mismatch\n");
88949ab747fSPaolo Bonzini 
89049ab747fSPaolo Bonzini                 /* update tally counter */
89149ab747fSPaolo Bonzini                 ++s->tally_counters.RxERR;
89249ab747fSPaolo Bonzini 
89349ab747fSPaolo Bonzini                 return size;
89449ab747fSPaolo Bonzini             }
89549ab747fSPaolo Bonzini 
89649ab747fSPaolo Bonzini             packet_header |= RxMulticast;
89749ab747fSPaolo Bonzini 
89849ab747fSPaolo Bonzini             DPRINTF(">>> multicast packet received\n");
89949ab747fSPaolo Bonzini 
90049ab747fSPaolo Bonzini             /* update tally counter */
90149ab747fSPaolo Bonzini             ++s->tally_counters.RxOkMul;
90249ab747fSPaolo Bonzini 
90349ab747fSPaolo Bonzini         } else if (s->phys[0] == buf[0] &&
90449ab747fSPaolo Bonzini                    s->phys[1] == buf[1] &&
90549ab747fSPaolo Bonzini                    s->phys[2] == buf[2] &&
90649ab747fSPaolo Bonzini                    s->phys[3] == buf[3] &&
90749ab747fSPaolo Bonzini                    s->phys[4] == buf[4] &&
90849ab747fSPaolo Bonzini                    s->phys[5] == buf[5]) {
90949ab747fSPaolo Bonzini             /* match */
91049ab747fSPaolo Bonzini             if (!(s->RxConfig & AcceptMyPhys))
91149ab747fSPaolo Bonzini             {
91249ab747fSPaolo Bonzini                 DPRINTF(">>> rejecting physical address matching packet\n");
91349ab747fSPaolo Bonzini 
91449ab747fSPaolo Bonzini                 /* update tally counter */
91549ab747fSPaolo Bonzini                 ++s->tally_counters.RxERR;
91649ab747fSPaolo Bonzini 
91749ab747fSPaolo Bonzini                 return size;
91849ab747fSPaolo Bonzini             }
91949ab747fSPaolo Bonzini 
92049ab747fSPaolo Bonzini             packet_header |= RxPhysical;
92149ab747fSPaolo Bonzini 
92249ab747fSPaolo Bonzini             DPRINTF(">>> physical address matching packet received\n");
92349ab747fSPaolo Bonzini 
92449ab747fSPaolo Bonzini             /* update tally counter */
92549ab747fSPaolo Bonzini             ++s->tally_counters.RxOkPhy;
92649ab747fSPaolo Bonzini 
92749ab747fSPaolo Bonzini         } else {
92849ab747fSPaolo Bonzini 
92949ab747fSPaolo Bonzini             DPRINTF(">>> unknown packet\n");
93049ab747fSPaolo Bonzini 
93149ab747fSPaolo Bonzini             /* update tally counter */
93249ab747fSPaolo Bonzini             ++s->tally_counters.RxERR;
93349ab747fSPaolo Bonzini 
93449ab747fSPaolo Bonzini             return size;
93549ab747fSPaolo Bonzini         }
93649ab747fSPaolo Bonzini     }
93749ab747fSPaolo Bonzini 
93849ab747fSPaolo Bonzini     if (rtl8139_cp_receiver_enabled(s))
93949ab747fSPaolo Bonzini     {
94049ab747fSPaolo Bonzini         if (!rtl8139_cp_rx_valid(s)) {
94149ab747fSPaolo Bonzini             return size;
94249ab747fSPaolo Bonzini         }
94349ab747fSPaolo Bonzini 
94449ab747fSPaolo Bonzini         DPRINTF("in C+ Rx mode ================\n");
94549ab747fSPaolo Bonzini 
94649ab747fSPaolo Bonzini         /* begin C+ receiver mode */
94749ab747fSPaolo Bonzini 
94849ab747fSPaolo Bonzini /* w0 ownership flag */
94949ab747fSPaolo Bonzini #define CP_RX_OWN (1<<31)
95049ab747fSPaolo Bonzini /* w0 end of ring flag */
95149ab747fSPaolo Bonzini #define CP_RX_EOR (1<<30)
95249ab747fSPaolo Bonzini /* w0 bits 0...12 : buffer size */
95349ab747fSPaolo Bonzini #define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1)
95449ab747fSPaolo Bonzini /* w1 tag available flag */
95549ab747fSPaolo Bonzini #define CP_RX_TAVA (1<<16)
95649ab747fSPaolo Bonzini /* w1 bits 0...15 : VLAN tag */
95749ab747fSPaolo Bonzini #define CP_RX_VLAN_TAG_MASK ((1<<16) - 1)
95849ab747fSPaolo Bonzini /* w2 low  32bit of Rx buffer ptr */
95949ab747fSPaolo Bonzini /* w3 high 32bit of Rx buffer ptr */
96049ab747fSPaolo Bonzini 
96149ab747fSPaolo Bonzini         int descriptor = s->currCPlusRxDesc;
96249ab747fSPaolo Bonzini         dma_addr_t cplus_rx_ring_desc;
96349ab747fSPaolo Bonzini 
96449ab747fSPaolo Bonzini         cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI);
96549ab747fSPaolo Bonzini         cplus_rx_ring_desc += 16 * descriptor;
96649ab747fSPaolo Bonzini 
96749ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at "
96849ab747fSPaolo Bonzini             "%08x %08x = "DMA_ADDR_FMT"\n", descriptor, s->RxRingAddrHI,
96949ab747fSPaolo Bonzini             s->RxRingAddrLO, cplus_rx_ring_desc);
97049ab747fSPaolo Bonzini 
97149ab747fSPaolo Bonzini         uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI;
97249ab747fSPaolo Bonzini 
97388a411a8SAndreas Färber         pci_dma_read(d, cplus_rx_ring_desc, &val, 4);
97449ab747fSPaolo Bonzini         rxdw0 = le32_to_cpu(val);
97588a411a8SAndreas Färber         pci_dma_read(d, cplus_rx_ring_desc+4, &val, 4);
97649ab747fSPaolo Bonzini         rxdw1 = le32_to_cpu(val);
97788a411a8SAndreas Färber         pci_dma_read(d, cplus_rx_ring_desc+8, &val, 4);
97849ab747fSPaolo Bonzini         rxbufLO = le32_to_cpu(val);
97988a411a8SAndreas Färber         pci_dma_read(d, cplus_rx_ring_desc+12, &val, 4);
98049ab747fSPaolo Bonzini         rxbufHI = le32_to_cpu(val);
98149ab747fSPaolo Bonzini 
98249ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n",
98349ab747fSPaolo Bonzini             descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI);
98449ab747fSPaolo Bonzini 
98549ab747fSPaolo Bonzini         if (!(rxdw0 & CP_RX_OWN))
98649ab747fSPaolo Bonzini         {
98749ab747fSPaolo Bonzini             DPRINTF("C+ Rx mode : descriptor %d is owned by host\n",
98849ab747fSPaolo Bonzini                 descriptor);
98949ab747fSPaolo Bonzini 
99049ab747fSPaolo Bonzini             s->IntrStatus |= RxOverflow;
99149ab747fSPaolo Bonzini             ++s->RxMissed;
99249ab747fSPaolo Bonzini 
99349ab747fSPaolo Bonzini             /* update tally counter */
99449ab747fSPaolo Bonzini             ++s->tally_counters.RxERR;
99549ab747fSPaolo Bonzini             ++s->tally_counters.MissPkt;
99649ab747fSPaolo Bonzini 
99749ab747fSPaolo Bonzini             rtl8139_update_irq(s);
99849ab747fSPaolo Bonzini             return size_;
99949ab747fSPaolo Bonzini         }
100049ab747fSPaolo Bonzini 
100149ab747fSPaolo Bonzini         uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
100249ab747fSPaolo Bonzini 
100349ab747fSPaolo Bonzini         /* write VLAN info to descriptor variables. */
10046960bfcaSPeter Maydell         if (s->CpCmd & CPlusRxVLAN &&
10056960bfcaSPeter Maydell             lduw_be_p(&buf[ETH_ALEN * 2]) == ETH_P_VLAN) {
10061bf11332SStefan Hajnoczi             dot1q_buf = &buf[ETH_ALEN * 2];
100749ab747fSPaolo Bonzini             size -= VLAN_HLEN;
100849ab747fSPaolo Bonzini             /* if too small buffer, use the tailroom added duing expansion */
100949ab747fSPaolo Bonzini             if (size < MIN_BUF_SIZE) {
101049ab747fSPaolo Bonzini                 size = MIN_BUF_SIZE;
101149ab747fSPaolo Bonzini             }
101249ab747fSPaolo Bonzini 
101349ab747fSPaolo Bonzini             rxdw1 &= ~CP_RX_VLAN_TAG_MASK;
101449ab747fSPaolo Bonzini             /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */
10156960bfcaSPeter Maydell             rxdw1 |= CP_RX_TAVA | lduw_le_p(&dot1q_buf[ETHER_TYPE_LEN]);
101649ab747fSPaolo Bonzini 
101749ab747fSPaolo Bonzini             DPRINTF("C+ Rx mode : extracted vlan tag with tci: ""%u\n",
10186960bfcaSPeter Maydell                 lduw_be_p(&dot1q_buf[ETHER_TYPE_LEN]));
101949ab747fSPaolo Bonzini         } else {
102049ab747fSPaolo Bonzini             /* reset VLAN tag flag */
102149ab747fSPaolo Bonzini             rxdw1 &= ~CP_RX_TAVA;
102249ab747fSPaolo Bonzini         }
102349ab747fSPaolo Bonzini 
102449ab747fSPaolo Bonzini         /* TODO: scatter the packet over available receive ring descriptors space */
102549ab747fSPaolo Bonzini 
102649ab747fSPaolo Bonzini         if (size+4 > rx_space)
102749ab747fSPaolo Bonzini         {
10281a326646SJason Wang             DPRINTF("C+ Rx mode : descriptor %d size %d received %zu + 4\n",
102949ab747fSPaolo Bonzini                 descriptor, rx_space, size);
103049ab747fSPaolo Bonzini 
103149ab747fSPaolo Bonzini             s->IntrStatus |= RxOverflow;
103249ab747fSPaolo Bonzini             ++s->RxMissed;
103349ab747fSPaolo Bonzini 
103449ab747fSPaolo Bonzini             /* update tally counter */
103549ab747fSPaolo Bonzini             ++s->tally_counters.RxERR;
103649ab747fSPaolo Bonzini             ++s->tally_counters.MissPkt;
103749ab747fSPaolo Bonzini 
103849ab747fSPaolo Bonzini             rtl8139_update_irq(s);
103949ab747fSPaolo Bonzini             return size_;
104049ab747fSPaolo Bonzini         }
104149ab747fSPaolo Bonzini 
104249ab747fSPaolo Bonzini         dma_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
104349ab747fSPaolo Bonzini 
104449ab747fSPaolo Bonzini         /* receive/copy to target memory */
104549ab747fSPaolo Bonzini         if (dot1q_buf) {
10461bf11332SStefan Hajnoczi             pci_dma_write(d, rx_addr, buf, 2 * ETH_ALEN);
10471bf11332SStefan Hajnoczi             pci_dma_write(d, rx_addr + 2 * ETH_ALEN,
10481bf11332SStefan Hajnoczi                           buf + 2 * ETH_ALEN + VLAN_HLEN,
10491bf11332SStefan Hajnoczi                           size - 2 * ETH_ALEN);
105049ab747fSPaolo Bonzini         } else {
105188a411a8SAndreas Färber             pci_dma_write(d, rx_addr, buf, size);
105249ab747fSPaolo Bonzini         }
105349ab747fSPaolo Bonzini 
105449ab747fSPaolo Bonzini         if (s->CpCmd & CPlusRxChkSum)
105549ab747fSPaolo Bonzini         {
105649ab747fSPaolo Bonzini             /* do some packet checksumming */
105749ab747fSPaolo Bonzini         }
105849ab747fSPaolo Bonzini 
105949ab747fSPaolo Bonzini         /* write checksum */
106049ab747fSPaolo Bonzini         val = cpu_to_le32(crc32(0, buf, size_));
106188a411a8SAndreas Färber         pci_dma_write(d, rx_addr+size, (uint8_t *)&val, 4);
106249ab747fSPaolo Bonzini 
106349ab747fSPaolo Bonzini /* first segment of received packet flag */
106449ab747fSPaolo Bonzini #define CP_RX_STATUS_FS (1<<29)
106549ab747fSPaolo Bonzini /* last segment of received packet flag */
106649ab747fSPaolo Bonzini #define CP_RX_STATUS_LS (1<<28)
106749ab747fSPaolo Bonzini /* multicast packet flag */
106849ab747fSPaolo Bonzini #define CP_RX_STATUS_MAR (1<<26)
106949ab747fSPaolo Bonzini /* physical-matching packet flag */
107049ab747fSPaolo Bonzini #define CP_RX_STATUS_PAM (1<<25)
107149ab747fSPaolo Bonzini /* broadcast packet flag */
107249ab747fSPaolo Bonzini #define CP_RX_STATUS_BAR (1<<24)
107349ab747fSPaolo Bonzini /* runt packet flag */
107449ab747fSPaolo Bonzini #define CP_RX_STATUS_RUNT (1<<19)
107549ab747fSPaolo Bonzini /* crc error flag */
107649ab747fSPaolo Bonzini #define CP_RX_STATUS_CRC (1<<18)
107749ab747fSPaolo Bonzini /* IP checksum error flag */
107849ab747fSPaolo Bonzini #define CP_RX_STATUS_IPF (1<<15)
107949ab747fSPaolo Bonzini /* UDP checksum error flag */
108049ab747fSPaolo Bonzini #define CP_RX_STATUS_UDPF (1<<14)
108149ab747fSPaolo Bonzini /* TCP checksum error flag */
108249ab747fSPaolo Bonzini #define CP_RX_STATUS_TCPF (1<<13)
108349ab747fSPaolo Bonzini 
108449ab747fSPaolo Bonzini         /* transfer ownership to target */
108549ab747fSPaolo Bonzini         rxdw0 &= ~CP_RX_OWN;
108649ab747fSPaolo Bonzini 
108749ab747fSPaolo Bonzini         /* set first segment bit */
108849ab747fSPaolo Bonzini         rxdw0 |= CP_RX_STATUS_FS;
108949ab747fSPaolo Bonzini 
109049ab747fSPaolo Bonzini         /* set last segment bit */
109149ab747fSPaolo Bonzini         rxdw0 |= CP_RX_STATUS_LS;
109249ab747fSPaolo Bonzini 
109349ab747fSPaolo Bonzini         /* set received packet type flags */
109449ab747fSPaolo Bonzini         if (packet_header & RxBroadcast)
109549ab747fSPaolo Bonzini             rxdw0 |= CP_RX_STATUS_BAR;
109649ab747fSPaolo Bonzini         if (packet_header & RxMulticast)
109749ab747fSPaolo Bonzini             rxdw0 |= CP_RX_STATUS_MAR;
109849ab747fSPaolo Bonzini         if (packet_header & RxPhysical)
109949ab747fSPaolo Bonzini             rxdw0 |= CP_RX_STATUS_PAM;
110049ab747fSPaolo Bonzini 
110149ab747fSPaolo Bonzini         /* set received size */
110249ab747fSPaolo Bonzini         rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK;
110349ab747fSPaolo Bonzini         rxdw0 |= (size+4);
110449ab747fSPaolo Bonzini 
110549ab747fSPaolo Bonzini         /* update ring data */
110649ab747fSPaolo Bonzini         val = cpu_to_le32(rxdw0);
110788a411a8SAndreas Färber         pci_dma_write(d, cplus_rx_ring_desc, (uint8_t *)&val, 4);
110849ab747fSPaolo Bonzini         val = cpu_to_le32(rxdw1);
110988a411a8SAndreas Färber         pci_dma_write(d, cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
111049ab747fSPaolo Bonzini 
111149ab747fSPaolo Bonzini         /* update tally counter */
111249ab747fSPaolo Bonzini         ++s->tally_counters.RxOk;
111349ab747fSPaolo Bonzini 
111449ab747fSPaolo Bonzini         /* seek to next Rx descriptor */
111549ab747fSPaolo Bonzini         if (rxdw0 & CP_RX_EOR)
111649ab747fSPaolo Bonzini         {
111749ab747fSPaolo Bonzini             s->currCPlusRxDesc = 0;
111849ab747fSPaolo Bonzini         }
111949ab747fSPaolo Bonzini         else
112049ab747fSPaolo Bonzini         {
112149ab747fSPaolo Bonzini             ++s->currCPlusRxDesc;
112249ab747fSPaolo Bonzini         }
112349ab747fSPaolo Bonzini 
112449ab747fSPaolo Bonzini         DPRINTF("done C+ Rx mode ----------------\n");
112549ab747fSPaolo Bonzini 
112649ab747fSPaolo Bonzini     }
112749ab747fSPaolo Bonzini     else
112849ab747fSPaolo Bonzini     {
112949ab747fSPaolo Bonzini         DPRINTF("in ring Rx mode ================\n");
113049ab747fSPaolo Bonzini 
113149ab747fSPaolo Bonzini         /* begin ring receiver mode */
113249ab747fSPaolo Bonzini         int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize);
113349ab747fSPaolo Bonzini 
113449ab747fSPaolo Bonzini         /* if receiver buffer is empty then avail == 0 */
113549ab747fSPaolo Bonzini 
1136fabdcd33SVladislav Yasevich #define RX_ALIGN(x) (((x) + 3) & ~0x3)
1137fabdcd33SVladislav Yasevich 
1138fabdcd33SVladislav Yasevich         if (avail != 0 && RX_ALIGN(size + 8) >= avail)
113949ab747fSPaolo Bonzini         {
114049ab747fSPaolo Bonzini             DPRINTF("rx overflow: rx buffer length %d head 0x%04x "
11411a326646SJason Wang                 "read 0x%04x === available 0x%04x need 0x%04zx\n",
114249ab747fSPaolo Bonzini                 s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8);
114349ab747fSPaolo Bonzini 
114449ab747fSPaolo Bonzini             s->IntrStatus |= RxOverflow;
114549ab747fSPaolo Bonzini             ++s->RxMissed;
114649ab747fSPaolo Bonzini             rtl8139_update_irq(s);
114726c4e7caSVladislav Yasevich             return 0;
114849ab747fSPaolo Bonzini         }
114949ab747fSPaolo Bonzini 
115049ab747fSPaolo Bonzini         packet_header |= RxStatusOK;
115149ab747fSPaolo Bonzini 
115249ab747fSPaolo Bonzini         packet_header |= (((size+4) << 16) & 0xffff0000);
115349ab747fSPaolo Bonzini 
115449ab747fSPaolo Bonzini         /* write header */
115549ab747fSPaolo Bonzini         uint32_t val = cpu_to_le32(packet_header);
115649ab747fSPaolo Bonzini 
115749ab747fSPaolo Bonzini         rtl8139_write_buffer(s, (uint8_t *)&val, 4);
115849ab747fSPaolo Bonzini 
115949ab747fSPaolo Bonzini         rtl8139_write_buffer(s, buf, size);
116049ab747fSPaolo Bonzini 
116149ab747fSPaolo Bonzini         /* write checksum */
116249ab747fSPaolo Bonzini         val = cpu_to_le32(crc32(0, buf, size));
116349ab747fSPaolo Bonzini         rtl8139_write_buffer(s, (uint8_t *)&val, 4);
116449ab747fSPaolo Bonzini 
116549ab747fSPaolo Bonzini         /* correct buffer write pointer */
1166fabdcd33SVladislav Yasevich         s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize);
116749ab747fSPaolo Bonzini 
116849ab747fSPaolo Bonzini         /* now we can signal we have received something */
116949ab747fSPaolo Bonzini 
117049ab747fSPaolo Bonzini         DPRINTF("received: rx buffer length %d head 0x%04x read 0x%04x\n",
117149ab747fSPaolo Bonzini             s->RxBufferSize, s->RxBufAddr, s->RxBufPtr);
117249ab747fSPaolo Bonzini     }
117349ab747fSPaolo Bonzini 
117449ab747fSPaolo Bonzini     s->IntrStatus |= RxOK;
117549ab747fSPaolo Bonzini 
117649ab747fSPaolo Bonzini     if (do_interrupt)
117749ab747fSPaolo Bonzini     {
117849ab747fSPaolo Bonzini         rtl8139_update_irq(s);
117949ab747fSPaolo Bonzini     }
118049ab747fSPaolo Bonzini 
118149ab747fSPaolo Bonzini     return size_;
118249ab747fSPaolo Bonzini }
118349ab747fSPaolo Bonzini 
rtl8139_receive(NetClientState * nc,const uint8_t * buf,size_t size)118449ab747fSPaolo Bonzini static ssize_t rtl8139_receive(NetClientState *nc, const uint8_t *buf, size_t size)
118549ab747fSPaolo Bonzini {
118649ab747fSPaolo Bonzini     return rtl8139_do_receive(nc, buf, size, 1);
118749ab747fSPaolo Bonzini }
118849ab747fSPaolo Bonzini 
rtl8139_reset_rxring(RTL8139State * s,uint32_t bufferSize)118949ab747fSPaolo Bonzini static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
119049ab747fSPaolo Bonzini {
119149ab747fSPaolo Bonzini     s->RxBufferSize = bufferSize;
119249ab747fSPaolo Bonzini     s->RxBufPtr  = 0;
119349ab747fSPaolo Bonzini     s->RxBufAddr = 0;
119449ab747fSPaolo Bonzini }
119549ab747fSPaolo Bonzini 
rtl8139_reset_phy(RTL8139State * s)119630a3e701SHervé Poussineau static void rtl8139_reset_phy(RTL8139State *s)
119730a3e701SHervé Poussineau {
119830a3e701SHervé Poussineau     s->BasicModeStatus  = 0x7809;
119930a3e701SHervé Poussineau     s->BasicModeStatus |= 0x0020; /* autonegotiation completed */
120030a3e701SHervé Poussineau     /* preserve link state */
120130a3e701SHervé Poussineau     s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04;
120230a3e701SHervé Poussineau 
120330a3e701SHervé Poussineau     s->NWayAdvert    = 0x05e1; /* all modes, full duplex */
120430a3e701SHervé Poussineau     s->NWayLPAR      = 0x05e1; /* all modes, full duplex */
120530a3e701SHervé Poussineau     s->NWayExpansion = 0x0001; /* autonegotiation supported */
120630a3e701SHervé Poussineau 
120730a3e701SHervé Poussineau     s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
120830a3e701SHervé Poussineau }
120930a3e701SHervé Poussineau 
rtl8139_reset(DeviceState * d)121049ab747fSPaolo Bonzini static void rtl8139_reset(DeviceState *d)
121149ab747fSPaolo Bonzini {
121239257515SPeter Crosthwaite     RTL8139State *s = RTL8139(d);
121349ab747fSPaolo Bonzini     int i;
121449ab747fSPaolo Bonzini 
121549ab747fSPaolo Bonzini     /* restore MAC address */
121649ab747fSPaolo Bonzini     memcpy(s->phys, s->conf.macaddr.a, 6);
1217655d3b63SAmos Kong     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys);
121849ab747fSPaolo Bonzini 
121949ab747fSPaolo Bonzini     /* reset interrupt mask */
122049ab747fSPaolo Bonzini     s->IntrStatus = 0;
122149ab747fSPaolo Bonzini     s->IntrMask = 0;
122249ab747fSPaolo Bonzini 
122349ab747fSPaolo Bonzini     rtl8139_update_irq(s);
122449ab747fSPaolo Bonzini 
122549ab747fSPaolo Bonzini     /* mark all status registers as owned by host */
122649ab747fSPaolo Bonzini     for (i = 0; i < 4; ++i)
122749ab747fSPaolo Bonzini     {
122849ab747fSPaolo Bonzini         s->TxStatus[i] = TxHostOwns;
122949ab747fSPaolo Bonzini     }
123049ab747fSPaolo Bonzini 
123149ab747fSPaolo Bonzini     s->currTxDesc = 0;
123249ab747fSPaolo Bonzini     s->currCPlusRxDesc = 0;
123349ab747fSPaolo Bonzini     s->currCPlusTxDesc = 0;
123449ab747fSPaolo Bonzini 
123549ab747fSPaolo Bonzini     s->RxRingAddrLO = 0;
123649ab747fSPaolo Bonzini     s->RxRingAddrHI = 0;
123749ab747fSPaolo Bonzini 
123849ab747fSPaolo Bonzini     s->RxBuf = 0;
123949ab747fSPaolo Bonzini 
124049ab747fSPaolo Bonzini     rtl8139_reset_rxring(s, 8192);
124149ab747fSPaolo Bonzini 
124249ab747fSPaolo Bonzini     /* ACK the reset */
124349ab747fSPaolo Bonzini     s->TxConfig = 0;
124449ab747fSPaolo Bonzini 
124549ab747fSPaolo Bonzini #if 0
124649ab747fSPaolo Bonzini //    s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139  HasHltClk
124749ab747fSPaolo Bonzini     s->clock_enabled = 0;
124849ab747fSPaolo Bonzini #else
124949ab747fSPaolo Bonzini     s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
125049ab747fSPaolo Bonzini     s->clock_enabled = 1;
125149ab747fSPaolo Bonzini #endif
125249ab747fSPaolo Bonzini 
125349ab747fSPaolo Bonzini     s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;
125449ab747fSPaolo Bonzini 
125549ab747fSPaolo Bonzini     /* set initial state data */
125649ab747fSPaolo Bonzini     s->Config0 = 0x0; /* No boot ROM */
125749ab747fSPaolo Bonzini     s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
125849ab747fSPaolo Bonzini     s->Config3 = 0x1; /* fast back-to-back compatible */
125949ab747fSPaolo Bonzini     s->Config5 = 0x0;
126049ab747fSPaolo Bonzini 
126149ab747fSPaolo Bonzini     s->CpCmd   = 0x0; /* reset C+ mode */
126249ab747fSPaolo Bonzini     s->cplus_enabled = 0;
126349ab747fSPaolo Bonzini 
126449ab747fSPaolo Bonzini //    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
126549ab747fSPaolo Bonzini //    s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
126649ab747fSPaolo Bonzini     s->BasicModeCtrl = 0x1000; // autonegotiation
126749ab747fSPaolo Bonzini 
126830a3e701SHervé Poussineau     rtl8139_reset_phy(s);
126949ab747fSPaolo Bonzini 
127049ab747fSPaolo Bonzini     /* also reset timer and disable timer interrupt */
127149ab747fSPaolo Bonzini     s->TCTR = 0;
127249ab747fSPaolo Bonzini     s->TimerInt = 0;
127349ab747fSPaolo Bonzini     s->TCTR_base = 0;
1274237c255cSPaolo Bonzini     rtl8139_set_next_tctr_time(s);
127549ab747fSPaolo Bonzini 
127649ab747fSPaolo Bonzini     /* reset tally counters */
127749ab747fSPaolo Bonzini     RTL8139TallyCounters_clear(&s->tally_counters);
127849ab747fSPaolo Bonzini }
127949ab747fSPaolo Bonzini 
RTL8139TallyCounters_clear(RTL8139TallyCounters * counters)128049ab747fSPaolo Bonzini static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
128149ab747fSPaolo Bonzini {
128249ab747fSPaolo Bonzini     counters->TxOk = 0;
128349ab747fSPaolo Bonzini     counters->RxOk = 0;
128449ab747fSPaolo Bonzini     counters->TxERR = 0;
128549ab747fSPaolo Bonzini     counters->RxERR = 0;
128649ab747fSPaolo Bonzini     counters->MissPkt = 0;
128749ab747fSPaolo Bonzini     counters->FAE = 0;
128849ab747fSPaolo Bonzini     counters->Tx1Col = 0;
128949ab747fSPaolo Bonzini     counters->TxMCol = 0;
129049ab747fSPaolo Bonzini     counters->RxOkPhy = 0;
129149ab747fSPaolo Bonzini     counters->RxOkBrd = 0;
129249ab747fSPaolo Bonzini     counters->RxOkMul = 0;
129349ab747fSPaolo Bonzini     counters->TxAbt = 0;
129449ab747fSPaolo Bonzini     counters->TxUndrn = 0;
129549ab747fSPaolo Bonzini }
129649ab747fSPaolo Bonzini 
RTL8139TallyCounters_dma_write(RTL8139State * s,dma_addr_t tc_addr)129749ab747fSPaolo Bonzini static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr)
129849ab747fSPaolo Bonzini {
129988a411a8SAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
130049ab747fSPaolo Bonzini     RTL8139TallyCounters *tally_counters = &s->tally_counters;
130149ab747fSPaolo Bonzini     uint16_t val16;
130249ab747fSPaolo Bonzini     uint32_t val32;
130349ab747fSPaolo Bonzini     uint64_t val64;
130449ab747fSPaolo Bonzini 
130549ab747fSPaolo Bonzini     val64 = cpu_to_le64(tally_counters->TxOk);
130688a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 0,     (uint8_t *)&val64, 8);
130749ab747fSPaolo Bonzini 
130849ab747fSPaolo Bonzini     val64 = cpu_to_le64(tally_counters->RxOk);
130988a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 8,     (uint8_t *)&val64, 8);
131049ab747fSPaolo Bonzini 
131149ab747fSPaolo Bonzini     val64 = cpu_to_le64(tally_counters->TxERR);
131288a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 16,    (uint8_t *)&val64, 8);
131349ab747fSPaolo Bonzini 
131449ab747fSPaolo Bonzini     val32 = cpu_to_le32(tally_counters->RxERR);
131588a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 24,    (uint8_t *)&val32, 4);
131649ab747fSPaolo Bonzini 
131749ab747fSPaolo Bonzini     val16 = cpu_to_le16(tally_counters->MissPkt);
131888a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 28,    (uint8_t *)&val16, 2);
131949ab747fSPaolo Bonzini 
132049ab747fSPaolo Bonzini     val16 = cpu_to_le16(tally_counters->FAE);
132188a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 30,    (uint8_t *)&val16, 2);
132249ab747fSPaolo Bonzini 
132349ab747fSPaolo Bonzini     val32 = cpu_to_le32(tally_counters->Tx1Col);
132488a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 32,    (uint8_t *)&val32, 4);
132549ab747fSPaolo Bonzini 
132649ab747fSPaolo Bonzini     val32 = cpu_to_le32(tally_counters->TxMCol);
132788a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 36,    (uint8_t *)&val32, 4);
132849ab747fSPaolo Bonzini 
132949ab747fSPaolo Bonzini     val64 = cpu_to_le64(tally_counters->RxOkPhy);
133088a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 40,    (uint8_t *)&val64, 8);
133149ab747fSPaolo Bonzini 
133249ab747fSPaolo Bonzini     val64 = cpu_to_le64(tally_counters->RxOkBrd);
133388a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 48,    (uint8_t *)&val64, 8);
133449ab747fSPaolo Bonzini 
133549ab747fSPaolo Bonzini     val32 = cpu_to_le32(tally_counters->RxOkMul);
133688a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 56,    (uint8_t *)&val32, 4);
133749ab747fSPaolo Bonzini 
133849ab747fSPaolo Bonzini     val16 = cpu_to_le16(tally_counters->TxAbt);
133988a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 60,    (uint8_t *)&val16, 2);
134049ab747fSPaolo Bonzini 
134149ab747fSPaolo Bonzini     val16 = cpu_to_le16(tally_counters->TxUndrn);
134288a411a8SAndreas Färber     pci_dma_write(d, tc_addr + 62,    (uint8_t *)&val16, 2);
134349ab747fSPaolo Bonzini }
134449ab747fSPaolo Bonzini 
rtl8139_ChipCmd_write(RTL8139State * s,uint32_t val)134549ab747fSPaolo Bonzini static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
134649ab747fSPaolo Bonzini {
134739257515SPeter Crosthwaite     DeviceState *d = DEVICE(s);
134839257515SPeter Crosthwaite 
134949ab747fSPaolo Bonzini     val &= 0xff;
135049ab747fSPaolo Bonzini 
135149ab747fSPaolo Bonzini     DPRINTF("ChipCmd write val=0x%08x\n", val);
135249ab747fSPaolo Bonzini 
135349ab747fSPaolo Bonzini     if (val & CmdReset)
135449ab747fSPaolo Bonzini     {
135549ab747fSPaolo Bonzini         DPRINTF("ChipCmd reset\n");
135639257515SPeter Crosthwaite         rtl8139_reset(d);
135749ab747fSPaolo Bonzini     }
135849ab747fSPaolo Bonzini     if (val & CmdRxEnb)
135949ab747fSPaolo Bonzini     {
136049ab747fSPaolo Bonzini         DPRINTF("ChipCmd enable receiver\n");
136149ab747fSPaolo Bonzini 
136249ab747fSPaolo Bonzini         s->currCPlusRxDesc = 0;
136349ab747fSPaolo Bonzini     }
136449ab747fSPaolo Bonzini     if (val & CmdTxEnb)
136549ab747fSPaolo Bonzini     {
136649ab747fSPaolo Bonzini         DPRINTF("ChipCmd enable transmitter\n");
136749ab747fSPaolo Bonzini 
136849ab747fSPaolo Bonzini         s->currCPlusTxDesc = 0;
136949ab747fSPaolo Bonzini     }
137049ab747fSPaolo Bonzini 
137149ab747fSPaolo Bonzini     /* mask unwritable bits */
137249ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xe3, s->bChipCmdState);
137349ab747fSPaolo Bonzini 
137449ab747fSPaolo Bonzini     /* Deassert reset pin before next read */
137549ab747fSPaolo Bonzini     val &= ~CmdReset;
137649ab747fSPaolo Bonzini 
137749ab747fSPaolo Bonzini     s->bChipCmdState = val;
137849ab747fSPaolo Bonzini }
137949ab747fSPaolo Bonzini 
rtl8139_RxBufferEmpty(RTL8139State * s)138049ab747fSPaolo Bonzini static int rtl8139_RxBufferEmpty(RTL8139State *s)
138149ab747fSPaolo Bonzini {
138249ab747fSPaolo Bonzini     int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize);
138349ab747fSPaolo Bonzini 
138449ab747fSPaolo Bonzini     if (unread != 0)
138549ab747fSPaolo Bonzini     {
138649ab747fSPaolo Bonzini         DPRINTF("receiver buffer data available 0x%04x\n", unread);
138749ab747fSPaolo Bonzini         return 0;
138849ab747fSPaolo Bonzini     }
138949ab747fSPaolo Bonzini 
139049ab747fSPaolo Bonzini     DPRINTF("receiver buffer is empty\n");
139149ab747fSPaolo Bonzini 
139249ab747fSPaolo Bonzini     return 1;
139349ab747fSPaolo Bonzini }
139449ab747fSPaolo Bonzini 
rtl8139_ChipCmd_read(RTL8139State * s)139549ab747fSPaolo Bonzini static uint32_t rtl8139_ChipCmd_read(RTL8139State *s)
139649ab747fSPaolo Bonzini {
139749ab747fSPaolo Bonzini     uint32_t ret = s->bChipCmdState;
139849ab747fSPaolo Bonzini 
139949ab747fSPaolo Bonzini     if (rtl8139_RxBufferEmpty(s))
140049ab747fSPaolo Bonzini         ret |= RxBufEmpty;
140149ab747fSPaolo Bonzini 
140249ab747fSPaolo Bonzini     DPRINTF("ChipCmd read val=0x%04x\n", ret);
140349ab747fSPaolo Bonzini 
140449ab747fSPaolo Bonzini     return ret;
140549ab747fSPaolo Bonzini }
140649ab747fSPaolo Bonzini 
rtl8139_CpCmd_write(RTL8139State * s,uint32_t val)140749ab747fSPaolo Bonzini static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
140849ab747fSPaolo Bonzini {
140949ab747fSPaolo Bonzini     val &= 0xffff;
141049ab747fSPaolo Bonzini 
141149ab747fSPaolo Bonzini     DPRINTF("C+ command register write(w) val=0x%04x\n", val);
141249ab747fSPaolo Bonzini 
141349ab747fSPaolo Bonzini     s->cplus_enabled = 1;
141449ab747fSPaolo Bonzini 
141549ab747fSPaolo Bonzini     /* mask unwritable bits */
141649ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xff84, s->CpCmd);
141749ab747fSPaolo Bonzini 
141849ab747fSPaolo Bonzini     s->CpCmd = val;
141949ab747fSPaolo Bonzini }
142049ab747fSPaolo Bonzini 
rtl8139_CpCmd_read(RTL8139State * s)142149ab747fSPaolo Bonzini static uint32_t rtl8139_CpCmd_read(RTL8139State *s)
142249ab747fSPaolo Bonzini {
142349ab747fSPaolo Bonzini     uint32_t ret = s->CpCmd;
142449ab747fSPaolo Bonzini 
142549ab747fSPaolo Bonzini     DPRINTF("C+ command register read(w) val=0x%04x\n", ret);
142649ab747fSPaolo Bonzini 
142749ab747fSPaolo Bonzini     return ret;
142849ab747fSPaolo Bonzini }
142949ab747fSPaolo Bonzini 
rtl8139_IntrMitigate_write(RTL8139State * s,uint32_t val)143049ab747fSPaolo Bonzini static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val)
143149ab747fSPaolo Bonzini {
143249ab747fSPaolo Bonzini     DPRINTF("C+ IntrMitigate register write(w) val=0x%04x\n", val);
143349ab747fSPaolo Bonzini }
143449ab747fSPaolo Bonzini 
rtl8139_IntrMitigate_read(RTL8139State * s)143549ab747fSPaolo Bonzini static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s)
143649ab747fSPaolo Bonzini {
143749ab747fSPaolo Bonzini     uint32_t ret = 0;
143849ab747fSPaolo Bonzini 
143949ab747fSPaolo Bonzini     DPRINTF("C+ IntrMitigate register read(w) val=0x%04x\n", ret);
144049ab747fSPaolo Bonzini 
144149ab747fSPaolo Bonzini     return ret;
144249ab747fSPaolo Bonzini }
144349ab747fSPaolo Bonzini 
rtl8139_config_writable(RTL8139State * s)144449ab747fSPaolo Bonzini static int rtl8139_config_writable(RTL8139State *s)
144549ab747fSPaolo Bonzini {
144649ab747fSPaolo Bonzini     if ((s->Cfg9346 & Chip9346_op_mask) == Cfg9346_ConfigWrite)
144749ab747fSPaolo Bonzini     {
144849ab747fSPaolo Bonzini         return 1;
144949ab747fSPaolo Bonzini     }
145049ab747fSPaolo Bonzini 
145149ab747fSPaolo Bonzini     DPRINTF("Configuration registers are write-protected\n");
145249ab747fSPaolo Bonzini 
145349ab747fSPaolo Bonzini     return 0;
145449ab747fSPaolo Bonzini }
145549ab747fSPaolo Bonzini 
rtl8139_BasicModeCtrl_write(RTL8139State * s,uint32_t val)145649ab747fSPaolo Bonzini static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val)
145749ab747fSPaolo Bonzini {
145849ab747fSPaolo Bonzini     val &= 0xffff;
145949ab747fSPaolo Bonzini 
146049ab747fSPaolo Bonzini     DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val);
146149ab747fSPaolo Bonzini 
146249ab747fSPaolo Bonzini     /* mask unwritable bits */
146330a3e701SHervé Poussineau     uint32_t mask = 0xccff;
146449ab747fSPaolo Bonzini 
146549ab747fSPaolo Bonzini     if (1 || !rtl8139_config_writable(s))
146649ab747fSPaolo Bonzini     {
146749ab747fSPaolo Bonzini         /* Speed setting and autonegotiation enable bits are read-only */
146849ab747fSPaolo Bonzini         mask |= 0x3000;
146949ab747fSPaolo Bonzini         /* Duplex mode setting is read-only */
147049ab747fSPaolo Bonzini         mask |= 0x0100;
147149ab747fSPaolo Bonzini     }
147249ab747fSPaolo Bonzini 
147330a3e701SHervé Poussineau     if (val & 0x8000) {
147430a3e701SHervé Poussineau         /* Reset PHY */
147530a3e701SHervé Poussineau         rtl8139_reset_phy(s);
147630a3e701SHervé Poussineau     }
147730a3e701SHervé Poussineau 
147849ab747fSPaolo Bonzini     val = SET_MASKED(val, mask, s->BasicModeCtrl);
147949ab747fSPaolo Bonzini 
148049ab747fSPaolo Bonzini     s->BasicModeCtrl = val;
148149ab747fSPaolo Bonzini }
148249ab747fSPaolo Bonzini 
rtl8139_BasicModeCtrl_read(RTL8139State * s)148349ab747fSPaolo Bonzini static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s)
148449ab747fSPaolo Bonzini {
148549ab747fSPaolo Bonzini     uint32_t ret = s->BasicModeCtrl;
148649ab747fSPaolo Bonzini 
148749ab747fSPaolo Bonzini     DPRINTF("BasicModeCtrl register read(w) val=0x%04x\n", ret);
148849ab747fSPaolo Bonzini 
148949ab747fSPaolo Bonzini     return ret;
149049ab747fSPaolo Bonzini }
149149ab747fSPaolo Bonzini 
rtl8139_BasicModeStatus_write(RTL8139State * s,uint32_t val)149249ab747fSPaolo Bonzini static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val)
149349ab747fSPaolo Bonzini {
149449ab747fSPaolo Bonzini     val &= 0xffff;
149549ab747fSPaolo Bonzini 
149649ab747fSPaolo Bonzini     DPRINTF("BasicModeStatus register write(w) val=0x%04x\n", val);
149749ab747fSPaolo Bonzini 
149849ab747fSPaolo Bonzini     /* mask unwritable bits */
149949ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xff3f, s->BasicModeStatus);
150049ab747fSPaolo Bonzini 
150149ab747fSPaolo Bonzini     s->BasicModeStatus = val;
150249ab747fSPaolo Bonzini }
150349ab747fSPaolo Bonzini 
rtl8139_BasicModeStatus_read(RTL8139State * s)150449ab747fSPaolo Bonzini static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s)
150549ab747fSPaolo Bonzini {
150649ab747fSPaolo Bonzini     uint32_t ret = s->BasicModeStatus;
150749ab747fSPaolo Bonzini 
150849ab747fSPaolo Bonzini     DPRINTF("BasicModeStatus register read(w) val=0x%04x\n", ret);
150949ab747fSPaolo Bonzini 
151049ab747fSPaolo Bonzini     return ret;
151149ab747fSPaolo Bonzini }
151249ab747fSPaolo Bonzini 
rtl8139_Cfg9346_write(RTL8139State * s,uint32_t val)151349ab747fSPaolo Bonzini static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
151449ab747fSPaolo Bonzini {
151539257515SPeter Crosthwaite     DeviceState *d = DEVICE(s);
151639257515SPeter Crosthwaite 
151749ab747fSPaolo Bonzini     val &= 0xff;
151849ab747fSPaolo Bonzini 
151949ab747fSPaolo Bonzini     DPRINTF("Cfg9346 write val=0x%02x\n", val);
152049ab747fSPaolo Bonzini 
152149ab747fSPaolo Bonzini     /* mask unwritable bits */
152249ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x31, s->Cfg9346);
152349ab747fSPaolo Bonzini 
152449ab747fSPaolo Bonzini     uint32_t opmode = val & 0xc0;
152549ab747fSPaolo Bonzini     uint32_t eeprom_val = val & 0xf;
152649ab747fSPaolo Bonzini 
152749ab747fSPaolo Bonzini     if (opmode == 0x80) {
152849ab747fSPaolo Bonzini         /* eeprom access */
152949ab747fSPaolo Bonzini         int eecs = (eeprom_val & 0x08)?1:0;
153049ab747fSPaolo Bonzini         int eesk = (eeprom_val & 0x04)?1:0;
153149ab747fSPaolo Bonzini         int eedi = (eeprom_val & 0x02)?1:0;
153249ab747fSPaolo Bonzini         prom9346_set_wire(s, eecs, eesk, eedi);
153349ab747fSPaolo Bonzini     } else if (opmode == 0x40) {
153449ab747fSPaolo Bonzini         /* Reset.  */
153549ab747fSPaolo Bonzini         val = 0;
153639257515SPeter Crosthwaite         rtl8139_reset(d);
153749ab747fSPaolo Bonzini     }
153849ab747fSPaolo Bonzini 
153949ab747fSPaolo Bonzini     s->Cfg9346 = val;
154049ab747fSPaolo Bonzini }
154149ab747fSPaolo Bonzini 
rtl8139_Cfg9346_read(RTL8139State * s)154249ab747fSPaolo Bonzini static uint32_t rtl8139_Cfg9346_read(RTL8139State *s)
154349ab747fSPaolo Bonzini {
154449ab747fSPaolo Bonzini     uint32_t ret = s->Cfg9346;
154549ab747fSPaolo Bonzini 
154649ab747fSPaolo Bonzini     uint32_t opmode = ret & 0xc0;
154749ab747fSPaolo Bonzini 
154849ab747fSPaolo Bonzini     if (opmode == 0x80)
154949ab747fSPaolo Bonzini     {
155049ab747fSPaolo Bonzini         /* eeprom access */
155149ab747fSPaolo Bonzini         int eedo = prom9346_get_wire(s);
155249ab747fSPaolo Bonzini         if (eedo)
155349ab747fSPaolo Bonzini         {
155449ab747fSPaolo Bonzini             ret |=  0x01;
155549ab747fSPaolo Bonzini         }
155649ab747fSPaolo Bonzini         else
155749ab747fSPaolo Bonzini         {
155849ab747fSPaolo Bonzini             ret &= ~0x01;
155949ab747fSPaolo Bonzini         }
156049ab747fSPaolo Bonzini     }
156149ab747fSPaolo Bonzini 
156249ab747fSPaolo Bonzini     DPRINTF("Cfg9346 read val=0x%02x\n", ret);
156349ab747fSPaolo Bonzini 
156449ab747fSPaolo Bonzini     return ret;
156549ab747fSPaolo Bonzini }
156649ab747fSPaolo Bonzini 
rtl8139_Config0_write(RTL8139State * s,uint32_t val)156749ab747fSPaolo Bonzini static void rtl8139_Config0_write(RTL8139State *s, uint32_t val)
156849ab747fSPaolo Bonzini {
156949ab747fSPaolo Bonzini     val &= 0xff;
157049ab747fSPaolo Bonzini 
157149ab747fSPaolo Bonzini     DPRINTF("Config0 write val=0x%02x\n", val);
157249ab747fSPaolo Bonzini 
157349ab747fSPaolo Bonzini     if (!rtl8139_config_writable(s)) {
157449ab747fSPaolo Bonzini         return;
157549ab747fSPaolo Bonzini     }
157649ab747fSPaolo Bonzini 
157749ab747fSPaolo Bonzini     /* mask unwritable bits */
157849ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xf8, s->Config0);
157949ab747fSPaolo Bonzini 
158049ab747fSPaolo Bonzini     s->Config0 = val;
158149ab747fSPaolo Bonzini }
158249ab747fSPaolo Bonzini 
rtl8139_Config0_read(RTL8139State * s)158349ab747fSPaolo Bonzini static uint32_t rtl8139_Config0_read(RTL8139State *s)
158449ab747fSPaolo Bonzini {
158549ab747fSPaolo Bonzini     uint32_t ret = s->Config0;
158649ab747fSPaolo Bonzini 
158749ab747fSPaolo Bonzini     DPRINTF("Config0 read val=0x%02x\n", ret);
158849ab747fSPaolo Bonzini 
158949ab747fSPaolo Bonzini     return ret;
159049ab747fSPaolo Bonzini }
159149ab747fSPaolo Bonzini 
rtl8139_Config1_write(RTL8139State * s,uint32_t val)159249ab747fSPaolo Bonzini static void rtl8139_Config1_write(RTL8139State *s, uint32_t val)
159349ab747fSPaolo Bonzini {
159449ab747fSPaolo Bonzini     val &= 0xff;
159549ab747fSPaolo Bonzini 
159649ab747fSPaolo Bonzini     DPRINTF("Config1 write val=0x%02x\n", val);
159749ab747fSPaolo Bonzini 
159849ab747fSPaolo Bonzini     if (!rtl8139_config_writable(s)) {
159949ab747fSPaolo Bonzini         return;
160049ab747fSPaolo Bonzini     }
160149ab747fSPaolo Bonzini 
160249ab747fSPaolo Bonzini     /* mask unwritable bits */
160349ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xC, s->Config1);
160449ab747fSPaolo Bonzini 
160549ab747fSPaolo Bonzini     s->Config1 = val;
160649ab747fSPaolo Bonzini }
160749ab747fSPaolo Bonzini 
rtl8139_Config1_read(RTL8139State * s)160849ab747fSPaolo Bonzini static uint32_t rtl8139_Config1_read(RTL8139State *s)
160949ab747fSPaolo Bonzini {
161049ab747fSPaolo Bonzini     uint32_t ret = s->Config1;
161149ab747fSPaolo Bonzini 
161249ab747fSPaolo Bonzini     DPRINTF("Config1 read val=0x%02x\n", ret);
161349ab747fSPaolo Bonzini 
161449ab747fSPaolo Bonzini     return ret;
161549ab747fSPaolo Bonzini }
161649ab747fSPaolo Bonzini 
rtl8139_Config3_write(RTL8139State * s,uint32_t val)161749ab747fSPaolo Bonzini static void rtl8139_Config3_write(RTL8139State *s, uint32_t val)
161849ab747fSPaolo Bonzini {
161949ab747fSPaolo Bonzini     val &= 0xff;
162049ab747fSPaolo Bonzini 
162149ab747fSPaolo Bonzini     DPRINTF("Config3 write val=0x%02x\n", val);
162249ab747fSPaolo Bonzini 
162349ab747fSPaolo Bonzini     if (!rtl8139_config_writable(s)) {
162449ab747fSPaolo Bonzini         return;
162549ab747fSPaolo Bonzini     }
162649ab747fSPaolo Bonzini 
162749ab747fSPaolo Bonzini     /* mask unwritable bits */
162849ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x8F, s->Config3);
162949ab747fSPaolo Bonzini 
163049ab747fSPaolo Bonzini     s->Config3 = val;
163149ab747fSPaolo Bonzini }
163249ab747fSPaolo Bonzini 
rtl8139_Config3_read(RTL8139State * s)163349ab747fSPaolo Bonzini static uint32_t rtl8139_Config3_read(RTL8139State *s)
163449ab747fSPaolo Bonzini {
163549ab747fSPaolo Bonzini     uint32_t ret = s->Config3;
163649ab747fSPaolo Bonzini 
163749ab747fSPaolo Bonzini     DPRINTF("Config3 read val=0x%02x\n", ret);
163849ab747fSPaolo Bonzini 
163949ab747fSPaolo Bonzini     return ret;
164049ab747fSPaolo Bonzini }
164149ab747fSPaolo Bonzini 
rtl8139_Config4_write(RTL8139State * s,uint32_t val)164249ab747fSPaolo Bonzini static void rtl8139_Config4_write(RTL8139State *s, uint32_t val)
164349ab747fSPaolo Bonzini {
164449ab747fSPaolo Bonzini     val &= 0xff;
164549ab747fSPaolo Bonzini 
164649ab747fSPaolo Bonzini     DPRINTF("Config4 write val=0x%02x\n", val);
164749ab747fSPaolo Bonzini 
164849ab747fSPaolo Bonzini     if (!rtl8139_config_writable(s)) {
164949ab747fSPaolo Bonzini         return;
165049ab747fSPaolo Bonzini     }
165149ab747fSPaolo Bonzini 
165249ab747fSPaolo Bonzini     /* mask unwritable bits */
165349ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x0a, s->Config4);
165449ab747fSPaolo Bonzini 
165549ab747fSPaolo Bonzini     s->Config4 = val;
165649ab747fSPaolo Bonzini }
165749ab747fSPaolo Bonzini 
rtl8139_Config4_read(RTL8139State * s)165849ab747fSPaolo Bonzini static uint32_t rtl8139_Config4_read(RTL8139State *s)
165949ab747fSPaolo Bonzini {
166049ab747fSPaolo Bonzini     uint32_t ret = s->Config4;
166149ab747fSPaolo Bonzini 
166249ab747fSPaolo Bonzini     DPRINTF("Config4 read val=0x%02x\n", ret);
166349ab747fSPaolo Bonzini 
166449ab747fSPaolo Bonzini     return ret;
166549ab747fSPaolo Bonzini }
166649ab747fSPaolo Bonzini 
rtl8139_Config5_write(RTL8139State * s,uint32_t val)166749ab747fSPaolo Bonzini static void rtl8139_Config5_write(RTL8139State *s, uint32_t val)
166849ab747fSPaolo Bonzini {
166949ab747fSPaolo Bonzini     val &= 0xff;
167049ab747fSPaolo Bonzini 
167149ab747fSPaolo Bonzini     DPRINTF("Config5 write val=0x%02x\n", val);
167249ab747fSPaolo Bonzini 
167349ab747fSPaolo Bonzini     /* mask unwritable bits */
167449ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x80, s->Config5);
167549ab747fSPaolo Bonzini 
167649ab747fSPaolo Bonzini     s->Config5 = val;
167749ab747fSPaolo Bonzini }
167849ab747fSPaolo Bonzini 
rtl8139_Config5_read(RTL8139State * s)167949ab747fSPaolo Bonzini static uint32_t rtl8139_Config5_read(RTL8139State *s)
168049ab747fSPaolo Bonzini {
168149ab747fSPaolo Bonzini     uint32_t ret = s->Config5;
168249ab747fSPaolo Bonzini 
168349ab747fSPaolo Bonzini     DPRINTF("Config5 read val=0x%02x\n", ret);
168449ab747fSPaolo Bonzini 
168549ab747fSPaolo Bonzini     return ret;
168649ab747fSPaolo Bonzini }
168749ab747fSPaolo Bonzini 
rtl8139_TxConfig_write(RTL8139State * s,uint32_t val)168849ab747fSPaolo Bonzini static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
168949ab747fSPaolo Bonzini {
169049ab747fSPaolo Bonzini     if (!rtl8139_transmitter_enabled(s))
169149ab747fSPaolo Bonzini     {
169249ab747fSPaolo Bonzini         DPRINTF("transmitter disabled; no TxConfig write val=0x%08x\n", val);
169349ab747fSPaolo Bonzini         return;
169449ab747fSPaolo Bonzini     }
169549ab747fSPaolo Bonzini 
169649ab747fSPaolo Bonzini     DPRINTF("TxConfig write val=0x%08x\n", val);
169749ab747fSPaolo Bonzini 
169849ab747fSPaolo Bonzini     val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
169949ab747fSPaolo Bonzini 
170049ab747fSPaolo Bonzini     s->TxConfig = val;
170149ab747fSPaolo Bonzini }
170249ab747fSPaolo Bonzini 
rtl8139_TxConfig_writeb(RTL8139State * s,uint32_t val)170349ab747fSPaolo Bonzini static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
170449ab747fSPaolo Bonzini {
170549ab747fSPaolo Bonzini     DPRINTF("RTL8139C TxConfig via write(b) val=0x%02x\n", val);
170649ab747fSPaolo Bonzini 
170749ab747fSPaolo Bonzini     uint32_t tc = s->TxConfig;
170849ab747fSPaolo Bonzini     tc &= 0xFFFFFF00;
170949ab747fSPaolo Bonzini     tc |= (val & 0x000000FF);
171049ab747fSPaolo Bonzini     rtl8139_TxConfig_write(s, tc);
171149ab747fSPaolo Bonzini }
171249ab747fSPaolo Bonzini 
rtl8139_TxConfig_read(RTL8139State * s)171349ab747fSPaolo Bonzini static uint32_t rtl8139_TxConfig_read(RTL8139State *s)
171449ab747fSPaolo Bonzini {
171549ab747fSPaolo Bonzini     uint32_t ret = s->TxConfig;
171649ab747fSPaolo Bonzini 
171749ab747fSPaolo Bonzini     DPRINTF("TxConfig read val=0x%04x\n", ret);
171849ab747fSPaolo Bonzini 
171949ab747fSPaolo Bonzini     return ret;
172049ab747fSPaolo Bonzini }
172149ab747fSPaolo Bonzini 
rtl8139_RxConfig_write(RTL8139State * s,uint32_t val)172249ab747fSPaolo Bonzini static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val)
172349ab747fSPaolo Bonzini {
172449ab747fSPaolo Bonzini     DPRINTF("RxConfig write val=0x%08x\n", val);
172549ab747fSPaolo Bonzini 
172649ab747fSPaolo Bonzini     /* mask unwritable bits */
172749ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xf0fc0040, s->RxConfig);
172849ab747fSPaolo Bonzini 
172949ab747fSPaolo Bonzini     s->RxConfig = val;
173049ab747fSPaolo Bonzini 
173149ab747fSPaolo Bonzini     /* reset buffer size and read/write pointers */
173249ab747fSPaolo Bonzini     rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3));
173349ab747fSPaolo Bonzini 
173449ab747fSPaolo Bonzini     DPRINTF("RxConfig write reset buffer size to %d\n", s->RxBufferSize);
173549ab747fSPaolo Bonzini }
173649ab747fSPaolo Bonzini 
rtl8139_RxConfig_read(RTL8139State * s)173749ab747fSPaolo Bonzini static uint32_t rtl8139_RxConfig_read(RTL8139State *s)
173849ab747fSPaolo Bonzini {
173949ab747fSPaolo Bonzini     uint32_t ret = s->RxConfig;
174049ab747fSPaolo Bonzini 
174149ab747fSPaolo Bonzini     DPRINTF("RxConfig read val=0x%08x\n", ret);
174249ab747fSPaolo Bonzini 
174349ab747fSPaolo Bonzini     return ret;
174449ab747fSPaolo Bonzini }
174549ab747fSPaolo Bonzini 
rtl8139_transfer_frame(RTL8139State * s,uint8_t * buf,int size,int do_interrupt,const uint8_t * dot1q_buf)174649ab747fSPaolo Bonzini static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
174749ab747fSPaolo Bonzini     int do_interrupt, const uint8_t *dot1q_buf)
174849ab747fSPaolo Bonzini {
174949ab747fSPaolo Bonzini     struct iovec *iov = NULL;
1750b0af8440SGonglei     struct iovec vlan_iov[3];
175149ab747fSPaolo Bonzini 
175249ab747fSPaolo Bonzini     if (!size)
175349ab747fSPaolo Bonzini     {
175449ab747fSPaolo Bonzini         DPRINTF("+++ empty ethernet frame\n");
175549ab747fSPaolo Bonzini         return;
175649ab747fSPaolo Bonzini     }
175749ab747fSPaolo Bonzini 
17581bf11332SStefan Hajnoczi     if (dot1q_buf && size >= ETH_ALEN * 2) {
175949ab747fSPaolo Bonzini         iov = (struct iovec[3]) {
17601bf11332SStefan Hajnoczi             { .iov_base = buf, .iov_len = ETH_ALEN * 2 },
176149ab747fSPaolo Bonzini             { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN },
17621bf11332SStefan Hajnoczi             { .iov_base = buf + ETH_ALEN * 2,
17631bf11332SStefan Hajnoczi                 .iov_len = size - ETH_ALEN * 2 },
176449ab747fSPaolo Bonzini         };
1765b0af8440SGonglei 
1766b0af8440SGonglei         memcpy(vlan_iov, iov, sizeof(vlan_iov));
1767b0af8440SGonglei         iov = vlan_iov;
176849ab747fSPaolo Bonzini     }
176949ab747fSPaolo Bonzini 
177049ab747fSPaolo Bonzini     if (TxLoopBack == (s->TxConfig & TxLoopBack))
177149ab747fSPaolo Bonzini     {
177249ab747fSPaolo Bonzini         size_t buf2_size;
177349ab747fSPaolo Bonzini         uint8_t *buf2;
177449ab747fSPaolo Bonzini 
177549ab747fSPaolo Bonzini         if (iov) {
177649ab747fSPaolo Bonzini             buf2_size = iov_size(iov, 3);
177749ab747fSPaolo Bonzini             buf2 = g_malloc(buf2_size);
177849ab747fSPaolo Bonzini             iov_to_buf(iov, 3, 0, buf2, buf2_size);
177949ab747fSPaolo Bonzini             buf = buf2;
178049ab747fSPaolo Bonzini         }
178149ab747fSPaolo Bonzini 
178249ab747fSPaolo Bonzini         DPRINTF("+++ transmit loopback mode\n");
17835311fb80SAlexander Bulekov         qemu_receive_packet(qemu_get_queue(s->nic), buf, size);
178449ab747fSPaolo Bonzini 
178549ab747fSPaolo Bonzini         if (iov) {
178649ab747fSPaolo Bonzini             g_free(buf2);
178749ab747fSPaolo Bonzini         }
178849ab747fSPaolo Bonzini     }
178949ab747fSPaolo Bonzini     else
179049ab747fSPaolo Bonzini     {
179149ab747fSPaolo Bonzini         if (iov) {
179249ab747fSPaolo Bonzini             qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3);
179349ab747fSPaolo Bonzini         } else {
179449ab747fSPaolo Bonzini             qemu_send_packet(qemu_get_queue(s->nic), buf, size);
179549ab747fSPaolo Bonzini         }
179649ab747fSPaolo Bonzini     }
179749ab747fSPaolo Bonzini }
179849ab747fSPaolo Bonzini 
rtl8139_transmit_one(RTL8139State * s,int descriptor)179949ab747fSPaolo Bonzini static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
180049ab747fSPaolo Bonzini {
180149ab747fSPaolo Bonzini     if (!rtl8139_transmitter_enabled(s))
180249ab747fSPaolo Bonzini     {
180349ab747fSPaolo Bonzini         DPRINTF("+++ cannot transmit from descriptor %d: transmitter "
180449ab747fSPaolo Bonzini             "disabled\n", descriptor);
180549ab747fSPaolo Bonzini         return 0;
180649ab747fSPaolo Bonzini     }
180749ab747fSPaolo Bonzini 
180849ab747fSPaolo Bonzini     if (s->TxStatus[descriptor] & TxHostOwns)
180949ab747fSPaolo Bonzini     {
181049ab747fSPaolo Bonzini         DPRINTF("+++ cannot transmit from descriptor %d: owned by host "
181149ab747fSPaolo Bonzini             "(%08x)\n", descriptor, s->TxStatus[descriptor]);
181249ab747fSPaolo Bonzini         return 0;
181349ab747fSPaolo Bonzini     }
181449ab747fSPaolo Bonzini 
181549ab747fSPaolo Bonzini     DPRINTF("+++ transmitting from descriptor %d\n", descriptor);
181649ab747fSPaolo Bonzini 
181788a411a8SAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
181849ab747fSPaolo Bonzini     int txsize = s->TxStatus[descriptor] & 0x1fff;
181949ab747fSPaolo Bonzini     uint8_t txbuffer[0x2000];
182049ab747fSPaolo Bonzini 
182149ab747fSPaolo Bonzini     DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n",
182249ab747fSPaolo Bonzini         txsize, s->TxAddr[descriptor]);
182349ab747fSPaolo Bonzini 
182488a411a8SAndreas Färber     pci_dma_read(d, s->TxAddr[descriptor], txbuffer, txsize);
182549ab747fSPaolo Bonzini 
182649ab747fSPaolo Bonzini     /* Mark descriptor as transferred */
182749ab747fSPaolo Bonzini     s->TxStatus[descriptor] |= TxHostOwns;
182849ab747fSPaolo Bonzini     s->TxStatus[descriptor] |= TxStatOK;
182949ab747fSPaolo Bonzini 
183049ab747fSPaolo Bonzini     rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
183149ab747fSPaolo Bonzini 
183249ab747fSPaolo Bonzini     DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize,
183349ab747fSPaolo Bonzini         descriptor);
183449ab747fSPaolo Bonzini 
183549ab747fSPaolo Bonzini     /* update interrupt */
183649ab747fSPaolo Bonzini     s->IntrStatus |= TxOK;
183749ab747fSPaolo Bonzini     rtl8139_update_irq(s);
183849ab747fSPaolo Bonzini 
183949ab747fSPaolo Bonzini     return 1;
184049ab747fSPaolo Bonzini }
184149ab747fSPaolo Bonzini 
184249ab747fSPaolo Bonzini #define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
184349ab747fSPaolo Bonzini 
184449ab747fSPaolo Bonzini /* produces ones' complement sum of data */
ones_complement_sum(uint8_t * data,size_t len)184549ab747fSPaolo Bonzini static uint16_t ones_complement_sum(uint8_t *data, size_t len)
184649ab747fSPaolo Bonzini {
184749ab747fSPaolo Bonzini     uint32_t result = 0;
184849ab747fSPaolo Bonzini 
184949ab747fSPaolo Bonzini     for (; len > 1; data+=2, len-=2)
185049ab747fSPaolo Bonzini     {
185149ab747fSPaolo Bonzini         result += *(uint16_t*)data;
185249ab747fSPaolo Bonzini     }
185349ab747fSPaolo Bonzini 
185449ab747fSPaolo Bonzini     /* add the remainder byte */
185549ab747fSPaolo Bonzini     if (len)
185649ab747fSPaolo Bonzini     {
185749ab747fSPaolo Bonzini         uint8_t odd[2] = {*data, 0};
185849ab747fSPaolo Bonzini         result += *(uint16_t*)odd;
185949ab747fSPaolo Bonzini     }
186049ab747fSPaolo Bonzini 
186149ab747fSPaolo Bonzini     while (result>>16)
186249ab747fSPaolo Bonzini         result = (result & 0xffff) + (result >> 16);
186349ab747fSPaolo Bonzini 
186449ab747fSPaolo Bonzini     return result;
186549ab747fSPaolo Bonzini }
186649ab747fSPaolo Bonzini 
ip_checksum(void * data,size_t len)186749ab747fSPaolo Bonzini static uint16_t ip_checksum(void *data, size_t len)
186849ab747fSPaolo Bonzini {
186949ab747fSPaolo Bonzini     return ~ones_complement_sum((uint8_t*)data, len);
187049ab747fSPaolo Bonzini }
187149ab747fSPaolo Bonzini 
rtl8139_cplus_transmit_one(RTL8139State * s)187249ab747fSPaolo Bonzini static int rtl8139_cplus_transmit_one(RTL8139State *s)
187349ab747fSPaolo Bonzini {
187449ab747fSPaolo Bonzini     if (!rtl8139_transmitter_enabled(s))
187549ab747fSPaolo Bonzini     {
187649ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode: transmitter disabled\n");
187749ab747fSPaolo Bonzini         return 0;
187849ab747fSPaolo Bonzini     }
187949ab747fSPaolo Bonzini 
188049ab747fSPaolo Bonzini     if (!rtl8139_cp_transmitter_enabled(s))
188149ab747fSPaolo Bonzini     {
188249ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode: C+ transmitter disabled\n");
188349ab747fSPaolo Bonzini         return 0 ;
188449ab747fSPaolo Bonzini     }
188549ab747fSPaolo Bonzini 
188688a411a8SAndreas Färber     PCIDevice *d = PCI_DEVICE(s);
188749ab747fSPaolo Bonzini     int descriptor = s->currCPlusTxDesc;
188849ab747fSPaolo Bonzini 
188949ab747fSPaolo Bonzini     dma_addr_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]);
189049ab747fSPaolo Bonzini 
189149ab747fSPaolo Bonzini     /* Normal priority ring */
189249ab747fSPaolo Bonzini     cplus_tx_ring_desc += 16 * descriptor;
189349ab747fSPaolo Bonzini 
189449ab747fSPaolo Bonzini     DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at "
189549ab747fSPaolo Bonzini         "%08x %08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1],
189649ab747fSPaolo Bonzini         s->TxAddr[0], cplus_tx_ring_desc);
189749ab747fSPaolo Bonzini 
189849ab747fSPaolo Bonzini     uint32_t val, txdw0,txdw1,txbufLO,txbufHI;
189949ab747fSPaolo Bonzini 
190088a411a8SAndreas Färber     pci_dma_read(d, cplus_tx_ring_desc,    (uint8_t *)&val, 4);
190149ab747fSPaolo Bonzini     txdw0 = le32_to_cpu(val);
190288a411a8SAndreas Färber     pci_dma_read(d, cplus_tx_ring_desc+4,  (uint8_t *)&val, 4);
190349ab747fSPaolo Bonzini     txdw1 = le32_to_cpu(val);
190488a411a8SAndreas Färber     pci_dma_read(d, cplus_tx_ring_desc+8,  (uint8_t *)&val, 4);
190549ab747fSPaolo Bonzini     txbufLO = le32_to_cpu(val);
190688a411a8SAndreas Färber     pci_dma_read(d, cplus_tx_ring_desc+12, (uint8_t *)&val, 4);
190749ab747fSPaolo Bonzini     txbufHI = le32_to_cpu(val);
190849ab747fSPaolo Bonzini 
190949ab747fSPaolo Bonzini     DPRINTF("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor,
191049ab747fSPaolo Bonzini         txdw0, txdw1, txbufLO, txbufHI);
191149ab747fSPaolo Bonzini 
191249ab747fSPaolo Bonzini /* w0 ownership flag */
191349ab747fSPaolo Bonzini #define CP_TX_OWN (1<<31)
191449ab747fSPaolo Bonzini /* w0 end of ring flag */
191549ab747fSPaolo Bonzini #define CP_TX_EOR (1<<30)
191649ab747fSPaolo Bonzini /* first segment of received packet flag */
191749ab747fSPaolo Bonzini #define CP_TX_FS (1<<29)
191849ab747fSPaolo Bonzini /* last segment of received packet flag */
191949ab747fSPaolo Bonzini #define CP_TX_LS (1<<28)
192049ab747fSPaolo Bonzini /* large send packet flag */
192149ab747fSPaolo Bonzini #define CP_TX_LGSEN (1<<27)
19226d71357aSStefan Hajnoczi /* large send MSS mask, bits 16...26 */
19236d71357aSStefan Hajnoczi #define CP_TC_LGSEN_MSS_SHIFT 16
19246d71357aSStefan Hajnoczi #define CP_TC_LGSEN_MSS_MASK ((1 << 11) - 1)
192549ab747fSPaolo Bonzini 
192649ab747fSPaolo Bonzini /* IP checksum offload flag */
192749ab747fSPaolo Bonzini #define CP_TX_IPCS (1<<18)
192849ab747fSPaolo Bonzini /* UDP checksum offload flag */
192949ab747fSPaolo Bonzini #define CP_TX_UDPCS (1<<17)
193049ab747fSPaolo Bonzini /* TCP checksum offload flag */
193149ab747fSPaolo Bonzini #define CP_TX_TCPCS (1<<16)
193249ab747fSPaolo Bonzini 
193349ab747fSPaolo Bonzini /* w0 bits 0...15 : buffer size */
193449ab747fSPaolo Bonzini #define CP_TX_BUFFER_SIZE (1<<16)
193549ab747fSPaolo Bonzini #define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1)
193649ab747fSPaolo Bonzini /* w1 add tag flag */
193749ab747fSPaolo Bonzini #define CP_TX_TAGC (1<<17)
193849ab747fSPaolo Bonzini /* w1 bits 0...15 : VLAN tag (big endian) */
193949ab747fSPaolo Bonzini #define CP_TX_VLAN_TAG_MASK ((1<<16) - 1)
194049ab747fSPaolo Bonzini /* w2 low  32bit of Rx buffer ptr */
194149ab747fSPaolo Bonzini /* w3 high 32bit of Rx buffer ptr */
194249ab747fSPaolo Bonzini 
194349ab747fSPaolo Bonzini /* set after transmission */
194449ab747fSPaolo Bonzini /* FIFO underrun flag */
194549ab747fSPaolo Bonzini #define CP_TX_STATUS_UNF (1<<25)
194649ab747fSPaolo Bonzini /* transmit error summary flag, valid if set any of three below */
194749ab747fSPaolo Bonzini #define CP_TX_STATUS_TES (1<<23)
194849ab747fSPaolo Bonzini /* out-of-window collision flag */
194949ab747fSPaolo Bonzini #define CP_TX_STATUS_OWC (1<<22)
195049ab747fSPaolo Bonzini /* link failure flag */
195149ab747fSPaolo Bonzini #define CP_TX_STATUS_LNKF (1<<21)
195249ab747fSPaolo Bonzini /* excessive collisions flag */
195349ab747fSPaolo Bonzini #define CP_TX_STATUS_EXC (1<<20)
195449ab747fSPaolo Bonzini 
195549ab747fSPaolo Bonzini     if (!(txdw0 & CP_TX_OWN))
195649ab747fSPaolo Bonzini     {
195749ab747fSPaolo Bonzini         DPRINTF("C+ Tx mode : descriptor %d is owned by host\n", descriptor);
195849ab747fSPaolo Bonzini         return 0 ;
195949ab747fSPaolo Bonzini     }
196049ab747fSPaolo Bonzini 
196149ab747fSPaolo Bonzini     DPRINTF("+++ C+ Tx mode : transmitting from descriptor %d\n", descriptor);
196249ab747fSPaolo Bonzini 
196349ab747fSPaolo Bonzini     if (txdw0 & CP_TX_FS)
196449ab747fSPaolo Bonzini     {
196549ab747fSPaolo Bonzini         DPRINTF("+++ C+ Tx mode : descriptor %d is first segment "
196649ab747fSPaolo Bonzini             "descriptor\n", descriptor);
196749ab747fSPaolo Bonzini 
196849ab747fSPaolo Bonzini         /* reset internal buffer offset */
196949ab747fSPaolo Bonzini         s->cplus_txbuffer_offset = 0;
197049ab747fSPaolo Bonzini     }
197149ab747fSPaolo Bonzini 
197249ab747fSPaolo Bonzini     int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK;
197349ab747fSPaolo Bonzini     dma_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI);
197449ab747fSPaolo Bonzini 
197549ab747fSPaolo Bonzini     /* make sure we have enough space to assemble the packet */
197649ab747fSPaolo Bonzini     if (!s->cplus_txbuffer)
197749ab747fSPaolo Bonzini     {
197849ab747fSPaolo Bonzini         s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE;
197949ab747fSPaolo Bonzini         s->cplus_txbuffer = g_malloc(s->cplus_txbuffer_len);
198049ab747fSPaolo Bonzini         s->cplus_txbuffer_offset = 0;
198149ab747fSPaolo Bonzini 
198249ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode transmission buffer allocated space %d\n",
198349ab747fSPaolo Bonzini             s->cplus_txbuffer_len);
198449ab747fSPaolo Bonzini     }
198549ab747fSPaolo Bonzini 
198649ab747fSPaolo Bonzini     if (s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len)
198749ab747fSPaolo Bonzini     {
198849ab747fSPaolo Bonzini         /* The spec didn't tell the maximum size, stick to CP_TX_BUFFER_SIZE */
198949ab747fSPaolo Bonzini         txsize = s->cplus_txbuffer_len - s->cplus_txbuffer_offset;
199049ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode transmission buffer overrun, truncated descriptor"
199149ab747fSPaolo Bonzini                 "length to %d\n", txsize);
199249ab747fSPaolo Bonzini     }
199349ab747fSPaolo Bonzini 
199449ab747fSPaolo Bonzini     /* append more data to the packet */
199549ab747fSPaolo Bonzini 
199649ab747fSPaolo Bonzini     DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at "
199749ab747fSPaolo Bonzini             DMA_ADDR_FMT" to offset %d\n", txsize, tx_addr,
199849ab747fSPaolo Bonzini             s->cplus_txbuffer_offset);
199949ab747fSPaolo Bonzini 
200088a411a8SAndreas Färber     pci_dma_read(d, tx_addr,
200149ab747fSPaolo Bonzini                  s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize);
200249ab747fSPaolo Bonzini     s->cplus_txbuffer_offset += txsize;
200349ab747fSPaolo Bonzini 
200449ab747fSPaolo Bonzini     /* seek to next Rx descriptor */
200549ab747fSPaolo Bonzini     if (txdw0 & CP_TX_EOR)
200649ab747fSPaolo Bonzini     {
200749ab747fSPaolo Bonzini         s->currCPlusTxDesc = 0;
200849ab747fSPaolo Bonzini     }
200949ab747fSPaolo Bonzini     else
201049ab747fSPaolo Bonzini     {
201149ab747fSPaolo Bonzini         ++s->currCPlusTxDesc;
201249ab747fSPaolo Bonzini         if (s->currCPlusTxDesc >= 64)
201349ab747fSPaolo Bonzini             s->currCPlusTxDesc = 0;
201449ab747fSPaolo Bonzini     }
201549ab747fSPaolo Bonzini 
2016bd142b23SStefan Hajnoczi     /* Build the Tx Status Descriptor */
2017bd142b23SStefan Hajnoczi     uint32_t tx_status = txdw0;
2018bd142b23SStefan Hajnoczi 
201949ab747fSPaolo Bonzini     /* transfer ownership to target */
2020bd142b23SStefan Hajnoczi     tx_status &= ~CP_TX_OWN;
202149ab747fSPaolo Bonzini 
202249ab747fSPaolo Bonzini     /* reset error indicator bits */
2023bd142b23SStefan Hajnoczi     tx_status &= ~CP_TX_STATUS_UNF;
2024bd142b23SStefan Hajnoczi     tx_status &= ~CP_TX_STATUS_TES;
2025bd142b23SStefan Hajnoczi     tx_status &= ~CP_TX_STATUS_OWC;
2026bd142b23SStefan Hajnoczi     tx_status &= ~CP_TX_STATUS_LNKF;
2027bd142b23SStefan Hajnoczi     tx_status &= ~CP_TX_STATUS_EXC;
202849ab747fSPaolo Bonzini 
202949ab747fSPaolo Bonzini     /* update ring data */
2030bd142b23SStefan Hajnoczi     val = cpu_to_le32(tx_status);
203188a411a8SAndreas Färber     pci_dma_write(d, cplus_tx_ring_desc, (uint8_t *)&val, 4);
203249ab747fSPaolo Bonzini 
203349ab747fSPaolo Bonzini     /* Now decide if descriptor being processed is holding the last segment of packet */
203449ab747fSPaolo Bonzini     if (txdw0 & CP_TX_LS)
203549ab747fSPaolo Bonzini     {
203649ab747fSPaolo Bonzini         uint8_t dot1q_buffer_space[VLAN_HLEN];
203749ab747fSPaolo Bonzini         uint16_t *dot1q_buffer;
203849ab747fSPaolo Bonzini 
203949ab747fSPaolo Bonzini         DPRINTF("+++ C+ Tx mode : descriptor %d is last segment descriptor\n",
204049ab747fSPaolo Bonzini             descriptor);
204149ab747fSPaolo Bonzini 
204249ab747fSPaolo Bonzini         /* can transfer fully assembled packet */
204349ab747fSPaolo Bonzini 
204449ab747fSPaolo Bonzini         uint8_t *saved_buffer  = s->cplus_txbuffer;
204549ab747fSPaolo Bonzini         int      saved_size    = s->cplus_txbuffer_offset;
204649ab747fSPaolo Bonzini         int      saved_buffer_len = s->cplus_txbuffer_len;
204749ab747fSPaolo Bonzini 
204849ab747fSPaolo Bonzini         /* create vlan tag */
204949ab747fSPaolo Bonzini         if (txdw1 & CP_TX_TAGC) {
205049ab747fSPaolo Bonzini             /* the vlan tag is in BE byte order in the descriptor
205149ab747fSPaolo Bonzini              * BE + le_to_cpu() + ~swap()~ = cpu */
205249ab747fSPaolo Bonzini             DPRINTF("+++ C+ Tx mode : inserting vlan tag with ""tci: %u\n",
205349ab747fSPaolo Bonzini                 bswap16(txdw1 & CP_TX_VLAN_TAG_MASK));
205449ab747fSPaolo Bonzini 
205549ab747fSPaolo Bonzini             dot1q_buffer = (uint16_t *) dot1q_buffer_space;
20561bf11332SStefan Hajnoczi             dot1q_buffer[0] = cpu_to_be16(ETH_P_VLAN);
205749ab747fSPaolo Bonzini             /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */
205849ab747fSPaolo Bonzini             dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK);
205949ab747fSPaolo Bonzini         } else {
206049ab747fSPaolo Bonzini             dot1q_buffer = NULL;
206149ab747fSPaolo Bonzini         }
206249ab747fSPaolo Bonzini 
206349ab747fSPaolo Bonzini         /* reset the card space to protect from recursive call */
206449ab747fSPaolo Bonzini         s->cplus_txbuffer = NULL;
206549ab747fSPaolo Bonzini         s->cplus_txbuffer_offset = 0;
206649ab747fSPaolo Bonzini         s->cplus_txbuffer_len = 0;
206749ab747fSPaolo Bonzini 
206849ab747fSPaolo Bonzini         if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))
206949ab747fSPaolo Bonzini         {
207049ab747fSPaolo Bonzini             DPRINTF("+++ C+ mode offloaded task checksum\n");
207149ab747fSPaolo Bonzini 
2072e1c120a9SStefan Hajnoczi             /* Large enough for Ethernet and IP headers? */
20735d61721aSStefan Hajnoczi             if (saved_size < ETH_HLEN + sizeof(struct ip_header)) {
2074e1c120a9SStefan Hajnoczi                 goto skip_offload;
2075e1c120a9SStefan Hajnoczi             }
2076e1c120a9SStefan Hajnoczi 
207749ab747fSPaolo Bonzini             /* ip packet header */
20785d61721aSStefan Hajnoczi             struct ip_header *ip = NULL;
207949ab747fSPaolo Bonzini             int hlen = 0;
208049ab747fSPaolo Bonzini             uint8_t  ip_protocol = 0;
208149ab747fSPaolo Bonzini             uint16_t ip_data_len = 0;
208249ab747fSPaolo Bonzini 
208349ab747fSPaolo Bonzini             uint8_t *eth_payload_data = NULL;
208449ab747fSPaolo Bonzini             size_t   eth_payload_len  = 0;
208549ab747fSPaolo Bonzini 
208649ab747fSPaolo Bonzini             int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
208739b8e7dcSStefan Hajnoczi             if (proto != ETH_P_IP)
208849ab747fSPaolo Bonzini             {
208939b8e7dcSStefan Hajnoczi                 goto skip_offload;
209039b8e7dcSStefan Hajnoczi             }
209139b8e7dcSStefan Hajnoczi 
209249ab747fSPaolo Bonzini             DPRINTF("+++ C+ mode has IP packet\n");
209349ab747fSPaolo Bonzini 
209426c0114dSStefan Hajnoczi             /* Note on memory alignment: eth_payload_data is 16-bit aligned
209526c0114dSStefan Hajnoczi              * since saved_buffer is allocated with g_malloc() and ETH_HLEN is
209626c0114dSStefan Hajnoczi              * even.  32-bit accesses must use ldl/stl wrappers to avoid
209726c0114dSStefan Hajnoczi              * unaligned accesses.
209826c0114dSStefan Hajnoczi              */
209949ab747fSPaolo Bonzini             eth_payload_data = saved_buffer + ETH_HLEN;
210049ab747fSPaolo Bonzini             eth_payload_len  = saved_size   - ETH_HLEN;
210149ab747fSPaolo Bonzini 
21025d61721aSStefan Hajnoczi             ip = (struct ip_header*)eth_payload_data;
210349ab747fSPaolo Bonzini 
210449ab747fSPaolo Bonzini             if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
210549ab747fSPaolo Bonzini                 DPRINTF("+++ C+ mode packet has bad IP version %d "
210649ab747fSPaolo Bonzini                     "expected %d\n", IP_HEADER_VERSION(ip),
210749ab747fSPaolo Bonzini                     IP_HEADER_VERSION_4);
210839b8e7dcSStefan Hajnoczi                 goto skip_offload;
210939b8e7dcSStefan Hajnoczi             }
211039b8e7dcSStefan Hajnoczi 
21111bf11332SStefan Hajnoczi             hlen = IP_HDR_GET_LEN(ip);
21125d61721aSStefan Hajnoczi             if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) {
211303247d43SStefan Hajnoczi                 goto skip_offload;
211403247d43SStefan Hajnoczi             }
211503247d43SStefan Hajnoczi 
211649ab747fSPaolo Bonzini             ip_protocol = ip->ip_p;
2117c6296ea8SStefan Hajnoczi 
2118c6296ea8SStefan Hajnoczi             ip_data_len = be16_to_cpu(ip->ip_len);
2119c6296ea8SStefan Hajnoczi             if (ip_data_len < hlen || ip_data_len > eth_payload_len) {
2120c6296ea8SStefan Hajnoczi                 goto skip_offload;
2121c6296ea8SStefan Hajnoczi             }
2122c6296ea8SStefan Hajnoczi             ip_data_len -= hlen;
212349ab747fSPaolo Bonzini 
2124c74831a0SStefan Hajnoczi             if (!(txdw0 & CP_TX_LGSEN) && (txdw0 & CP_TX_IPCS))
212549ab747fSPaolo Bonzini             {
212649ab747fSPaolo Bonzini                 DPRINTF("+++ C+ mode need IP checksum\n");
212749ab747fSPaolo Bonzini 
212849ab747fSPaolo Bonzini                 ip->ip_sum = 0;
212949ab747fSPaolo Bonzini                 ip->ip_sum = ip_checksum(ip, hlen);
213049ab747fSPaolo Bonzini                 DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
213149ab747fSPaolo Bonzini                     hlen, ip->ip_sum);
213249ab747fSPaolo Bonzini             }
213349ab747fSPaolo Bonzini 
213449ab747fSPaolo Bonzini             if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
213549ab747fSPaolo Bonzini             {
21364240be45SStefan Hajnoczi                 /* Large enough for the TCP header? */
21374240be45SStefan Hajnoczi                 if (ip_data_len < sizeof(tcp_header)) {
21384240be45SStefan Hajnoczi                     goto skip_offload;
21394240be45SStefan Hajnoczi                 }
21404240be45SStefan Hajnoczi 
21416d71357aSStefan Hajnoczi                 int large_send_mss = (txdw0 >> CP_TC_LGSEN_MSS_SHIFT) &
21426d71357aSStefan Hajnoczi                                      CP_TC_LGSEN_MSS_MASK;
2143792676c1SStefan Hajnoczi                 if (large_send_mss == 0) {
2144792676c1SStefan Hajnoczi                     goto skip_offload;
2145792676c1SStefan Hajnoczi                 }
214649ab747fSPaolo Bonzini 
21476d71357aSStefan Hajnoczi                 DPRINTF("+++ C+ mode offloaded task TSO IP data %d "
21486d71357aSStefan Hajnoczi                     "frame data %d specified MSS=%d\n",
214949ab747fSPaolo Bonzini                     ip_data_len, saved_size - ETH_HLEN, large_send_mss);
215049ab747fSPaolo Bonzini 
215149ab747fSPaolo Bonzini                 int tcp_send_offset = 0;
215249ab747fSPaolo Bonzini 
215349ab747fSPaolo Bonzini                 /* maximum IP header length is 60 bytes */
215449ab747fSPaolo Bonzini                 uint8_t saved_ip_header[60];
215549ab747fSPaolo Bonzini 
215649ab747fSPaolo Bonzini                 /* save IP header template; data area is used in tcp checksum calculation */
215749ab747fSPaolo Bonzini                 memcpy(saved_ip_header, eth_payload_data, hlen);
215849ab747fSPaolo Bonzini 
215949ab747fSPaolo Bonzini                 /* a placeholder for checksum calculation routine in tcp case */
216049ab747fSPaolo Bonzini                 uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
216149ab747fSPaolo Bonzini                 //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
216249ab747fSPaolo Bonzini 
216349ab747fSPaolo Bonzini                 /* pointer to TCP header */
216449ab747fSPaolo Bonzini                 tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
216549ab747fSPaolo Bonzini 
216649ab747fSPaolo Bonzini                 int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
216749ab747fSPaolo Bonzini 
21688357946bSStefan Hajnoczi                 /* Invalid TCP data offset? */
21698357946bSStefan Hajnoczi                 if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) {
21708357946bSStefan Hajnoczi                     goto skip_offload;
21718357946bSStefan Hajnoczi                 }
21728357946bSStefan Hajnoczi 
217349ab747fSPaolo Bonzini                 int tcp_data_len = ip_data_len - tcp_hlen;
217449ab747fSPaolo Bonzini 
217549ab747fSPaolo Bonzini                 DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
21766d71357aSStefan Hajnoczi                     "data len %d\n", ip_data_len, tcp_hlen, tcp_data_len);
217749ab747fSPaolo Bonzini 
217849ab747fSPaolo Bonzini                 /* note the cycle below overwrites IP header data,
217949ab747fSPaolo Bonzini                    but restores it from saved_ip_header before sending packet */
218049ab747fSPaolo Bonzini 
218149ab747fSPaolo Bonzini                 int is_last_frame = 0;
218249ab747fSPaolo Bonzini 
21836d71357aSStefan Hajnoczi                 for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += large_send_mss)
218449ab747fSPaolo Bonzini                 {
21856d71357aSStefan Hajnoczi                     uint16_t chunk_size = large_send_mss;
218649ab747fSPaolo Bonzini 
218749ab747fSPaolo Bonzini                     /* check if this is the last frame */
21886d71357aSStefan Hajnoczi                     if (tcp_send_offset + large_send_mss >= tcp_data_len)
218949ab747fSPaolo Bonzini                     {
219049ab747fSPaolo Bonzini                         is_last_frame = 1;
219149ab747fSPaolo Bonzini                         chunk_size = tcp_data_len - tcp_send_offset;
219249ab747fSPaolo Bonzini                     }
219349ab747fSPaolo Bonzini 
219449ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
219526c0114dSStefan Hajnoczi                             ldl_be_p(&p_tcp_hdr->th_seq));
219649ab747fSPaolo Bonzini 
219749ab747fSPaolo Bonzini                     /* add 4 TCP pseudoheader fields */
219849ab747fSPaolo Bonzini                     /* copy IP source and destination fields */
219949ab747fSPaolo Bonzini                     memcpy(data_to_checksum, saved_ip_header + 12, 8);
220049ab747fSPaolo Bonzini 
220149ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
220249ab747fSPaolo Bonzini                         "packet with %d bytes data\n", tcp_hlen +
220349ab747fSPaolo Bonzini                         chunk_size);
220449ab747fSPaolo Bonzini 
220549ab747fSPaolo Bonzini                     if (tcp_send_offset)
220649ab747fSPaolo Bonzini                     {
220749ab747fSPaolo Bonzini                         memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
220849ab747fSPaolo Bonzini                     }
220949ab747fSPaolo Bonzini 
221049ab747fSPaolo Bonzini                     /* keep PUSH and FIN flags only for the last frame */
221149ab747fSPaolo Bonzini                     if (!is_last_frame)
221249ab747fSPaolo Bonzini                     {
22131bf11332SStefan Hajnoczi                         TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN);
221449ab747fSPaolo Bonzini                     }
221549ab747fSPaolo Bonzini 
221649ab747fSPaolo Bonzini                     /* recalculate TCP checksum */
221749ab747fSPaolo Bonzini                     ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
221849ab747fSPaolo Bonzini                     p_tcpip_hdr->zeros      = 0;
221949ab747fSPaolo Bonzini                     p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
222049ab747fSPaolo Bonzini                     p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
222149ab747fSPaolo Bonzini 
222249ab747fSPaolo Bonzini                     p_tcp_hdr->th_sum = 0;
222349ab747fSPaolo Bonzini 
222449ab747fSPaolo Bonzini                     int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
222549ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
222649ab747fSPaolo Bonzini                         tcp_checksum);
222749ab747fSPaolo Bonzini 
222849ab747fSPaolo Bonzini                     p_tcp_hdr->th_sum = tcp_checksum;
222949ab747fSPaolo Bonzini 
223049ab747fSPaolo Bonzini                     /* restore IP header */
223149ab747fSPaolo Bonzini                     memcpy(eth_payload_data, saved_ip_header, hlen);
223249ab747fSPaolo Bonzini 
223349ab747fSPaolo Bonzini                     /* set IP data length and recalculate IP checksum */
223449ab747fSPaolo Bonzini                     ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
223549ab747fSPaolo Bonzini 
223649ab747fSPaolo Bonzini                     /* increment IP id for subsequent frames */
22376d71357aSStefan Hajnoczi                     ip->ip_id = cpu_to_be16(tcp_send_offset/large_send_mss + be16_to_cpu(ip->ip_id));
223849ab747fSPaolo Bonzini 
223949ab747fSPaolo Bonzini                     ip->ip_sum = 0;
224049ab747fSPaolo Bonzini                     ip->ip_sum = ip_checksum(eth_payload_data, hlen);
224149ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode TSO IP header len=%d "
224249ab747fSPaolo Bonzini                         "checksum=%04x\n", hlen, ip->ip_sum);
224349ab747fSPaolo Bonzini 
224449ab747fSPaolo Bonzini                     int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
224549ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode TSO transferring packet size "
224649ab747fSPaolo Bonzini                         "%d\n", tso_send_size);
224749ab747fSPaolo Bonzini                     rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
224849ab747fSPaolo Bonzini                         0, (uint8_t *) dot1q_buffer);
224949ab747fSPaolo Bonzini 
225049ab747fSPaolo Bonzini                     /* add transferred count to TCP sequence number */
225126c0114dSStefan Hajnoczi                     stl_be_p(&p_tcp_hdr->th_seq,
225226c0114dSStefan Hajnoczi                              chunk_size + ldl_be_p(&p_tcp_hdr->th_seq));
225349ab747fSPaolo Bonzini                 }
225449ab747fSPaolo Bonzini 
225549ab747fSPaolo Bonzini                 /* Stop sending this frame */
225649ab747fSPaolo Bonzini                 saved_size = 0;
225749ab747fSPaolo Bonzini             }
2258c74831a0SStefan Hajnoczi             else if (!(txdw0 & CP_TX_LGSEN) && (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)))
225949ab747fSPaolo Bonzini             {
226049ab747fSPaolo Bonzini                 DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
226149ab747fSPaolo Bonzini 
226249ab747fSPaolo Bonzini                 /* maximum IP header length is 60 bytes */
226349ab747fSPaolo Bonzini                 uint8_t saved_ip_header[60];
226449ab747fSPaolo Bonzini                 memcpy(saved_ip_header, eth_payload_data, hlen);
226549ab747fSPaolo Bonzini 
226649ab747fSPaolo Bonzini                 uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
226749ab747fSPaolo Bonzini                 //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
226849ab747fSPaolo Bonzini 
226949ab747fSPaolo Bonzini                 /* add 4 TCP pseudoheader fields */
227049ab747fSPaolo Bonzini                 /* copy IP source and destination fields */
227149ab747fSPaolo Bonzini                 memcpy(data_to_checksum, saved_ip_header + 12, 8);
227249ab747fSPaolo Bonzini 
227349ab747fSPaolo Bonzini                 if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
227449ab747fSPaolo Bonzini                 {
227549ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode calculating TCP checksum for "
227649ab747fSPaolo Bonzini                         "packet with %d bytes data\n", ip_data_len);
227749ab747fSPaolo Bonzini 
227849ab747fSPaolo Bonzini                     ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
227949ab747fSPaolo Bonzini                     p_tcpip_hdr->zeros      = 0;
228049ab747fSPaolo Bonzini                     p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
228149ab747fSPaolo Bonzini                     p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
228249ab747fSPaolo Bonzini 
228349ab747fSPaolo Bonzini                     tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
228449ab747fSPaolo Bonzini 
228549ab747fSPaolo Bonzini                     p_tcp_hdr->th_sum = 0;
228649ab747fSPaolo Bonzini 
228749ab747fSPaolo Bonzini                     int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
228849ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode TCP checksum %04x\n",
228949ab747fSPaolo Bonzini                         tcp_checksum);
229049ab747fSPaolo Bonzini 
229149ab747fSPaolo Bonzini                     p_tcp_hdr->th_sum = tcp_checksum;
229249ab747fSPaolo Bonzini                 }
229349ab747fSPaolo Bonzini                 else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
229449ab747fSPaolo Bonzini                 {
229549ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode calculating UDP checksum for "
229649ab747fSPaolo Bonzini                         "packet with %d bytes data\n", ip_data_len);
229749ab747fSPaolo Bonzini 
229849ab747fSPaolo Bonzini                     ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
229949ab747fSPaolo Bonzini                     p_udpip_hdr->zeros      = 0;
230049ab747fSPaolo Bonzini                     p_udpip_hdr->ip_proto   = IP_PROTO_UDP;
230149ab747fSPaolo Bonzini                     p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
230249ab747fSPaolo Bonzini 
230349ab747fSPaolo Bonzini                     udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
230449ab747fSPaolo Bonzini 
230549ab747fSPaolo Bonzini                     p_udp_hdr->uh_sum = 0;
230649ab747fSPaolo Bonzini 
230749ab747fSPaolo Bonzini                     int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
230849ab747fSPaolo Bonzini                     DPRINTF("+++ C+ mode UDP checksum %04x\n",
230949ab747fSPaolo Bonzini                         udp_checksum);
231049ab747fSPaolo Bonzini 
231149ab747fSPaolo Bonzini                     p_udp_hdr->uh_sum = udp_checksum;
231249ab747fSPaolo Bonzini                 }
231349ab747fSPaolo Bonzini 
231449ab747fSPaolo Bonzini                 /* restore IP header */
231549ab747fSPaolo Bonzini                 memcpy(eth_payload_data, saved_ip_header, hlen);
231649ab747fSPaolo Bonzini             }
231749ab747fSPaolo Bonzini         }
231849ab747fSPaolo Bonzini 
231939b8e7dcSStefan Hajnoczi skip_offload:
232049ab747fSPaolo Bonzini         /* update tally counter */
232149ab747fSPaolo Bonzini         ++s->tally_counters.TxOk;
232249ab747fSPaolo Bonzini 
232349ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode transmitting %d bytes packet\n", saved_size);
232449ab747fSPaolo Bonzini 
232549ab747fSPaolo Bonzini         rtl8139_transfer_frame(s, saved_buffer, saved_size, 1,
232649ab747fSPaolo Bonzini             (uint8_t *) dot1q_buffer);
232749ab747fSPaolo Bonzini 
232849ab747fSPaolo Bonzini         /* restore card space if there was no recursion and reset offset */
232949ab747fSPaolo Bonzini         if (!s->cplus_txbuffer)
233049ab747fSPaolo Bonzini         {
233149ab747fSPaolo Bonzini             s->cplus_txbuffer        = saved_buffer;
233249ab747fSPaolo Bonzini             s->cplus_txbuffer_len    = saved_buffer_len;
233349ab747fSPaolo Bonzini             s->cplus_txbuffer_offset = 0;
233449ab747fSPaolo Bonzini         }
233549ab747fSPaolo Bonzini         else
233649ab747fSPaolo Bonzini         {
233749ab747fSPaolo Bonzini             g_free(saved_buffer);
233849ab747fSPaolo Bonzini         }
233949ab747fSPaolo Bonzini     }
234049ab747fSPaolo Bonzini     else
234149ab747fSPaolo Bonzini     {
234249ab747fSPaolo Bonzini         DPRINTF("+++ C+ mode transmission continue to next descriptor\n");
234349ab747fSPaolo Bonzini     }
234449ab747fSPaolo Bonzini 
234549ab747fSPaolo Bonzini     return 1;
234649ab747fSPaolo Bonzini }
234749ab747fSPaolo Bonzini 
rtl8139_cplus_transmit(RTL8139State * s)234849ab747fSPaolo Bonzini static void rtl8139_cplus_transmit(RTL8139State *s)
234949ab747fSPaolo Bonzini {
235049ab747fSPaolo Bonzini     int txcount = 0;
235149ab747fSPaolo Bonzini 
2352c7c35916SPrasad J Pandit     while (txcount < 64 && rtl8139_cplus_transmit_one(s))
235349ab747fSPaolo Bonzini     {
235449ab747fSPaolo Bonzini         ++txcount;
235549ab747fSPaolo Bonzini     }
235649ab747fSPaolo Bonzini 
235749ab747fSPaolo Bonzini     /* Mark transfer completed */
235849ab747fSPaolo Bonzini     if (!txcount)
235949ab747fSPaolo Bonzini     {
236049ab747fSPaolo Bonzini         DPRINTF("C+ mode : transmitter queue stalled, current TxDesc = %d\n",
236149ab747fSPaolo Bonzini             s->currCPlusTxDesc);
236249ab747fSPaolo Bonzini     }
236349ab747fSPaolo Bonzini     else
236449ab747fSPaolo Bonzini     {
236549ab747fSPaolo Bonzini         /* update interrupt status */
236649ab747fSPaolo Bonzini         s->IntrStatus |= TxOK;
236749ab747fSPaolo Bonzini         rtl8139_update_irq(s);
236849ab747fSPaolo Bonzini     }
236949ab747fSPaolo Bonzini }
237049ab747fSPaolo Bonzini 
rtl8139_transmit(RTL8139State * s)237149ab747fSPaolo Bonzini static void rtl8139_transmit(RTL8139State *s)
237249ab747fSPaolo Bonzini {
237349ab747fSPaolo Bonzini     int descriptor = s->currTxDesc, txcount = 0;
237449ab747fSPaolo Bonzini 
237549ab747fSPaolo Bonzini     /*while*/
237649ab747fSPaolo Bonzini     if (rtl8139_transmit_one(s, descriptor))
237749ab747fSPaolo Bonzini     {
237849ab747fSPaolo Bonzini         ++s->currTxDesc;
237949ab747fSPaolo Bonzini         s->currTxDesc %= 4;
238049ab747fSPaolo Bonzini         ++txcount;
238149ab747fSPaolo Bonzini     }
238249ab747fSPaolo Bonzini 
238349ab747fSPaolo Bonzini     /* Mark transfer completed */
238449ab747fSPaolo Bonzini     if (!txcount)
238549ab747fSPaolo Bonzini     {
238649ab747fSPaolo Bonzini         DPRINTF("transmitter queue stalled, current TxDesc = %d\n",
238749ab747fSPaolo Bonzini             s->currTxDesc);
238849ab747fSPaolo Bonzini     }
238949ab747fSPaolo Bonzini }
239049ab747fSPaolo Bonzini 
rtl8139_TxStatus_write(RTL8139State * s,uint32_t txRegOffset,uint32_t val)239149ab747fSPaolo Bonzini static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val)
239249ab747fSPaolo Bonzini {
239349ab747fSPaolo Bonzini 
239449ab747fSPaolo Bonzini     int descriptor = txRegOffset/4;
239549ab747fSPaolo Bonzini 
239649ab747fSPaolo Bonzini     /* handle C+ transmit mode register configuration */
239749ab747fSPaolo Bonzini 
239849ab747fSPaolo Bonzini     if (s->cplus_enabled)
239949ab747fSPaolo Bonzini     {
240049ab747fSPaolo Bonzini         DPRINTF("RTL8139C+ DTCCR write offset=0x%x val=0x%08x "
240149ab747fSPaolo Bonzini             "descriptor=%d\n", txRegOffset, val, descriptor);
240249ab747fSPaolo Bonzini 
240349ab747fSPaolo Bonzini         /* handle Dump Tally Counters command */
240449ab747fSPaolo Bonzini         s->TxStatus[descriptor] = val;
240549ab747fSPaolo Bonzini 
240649ab747fSPaolo Bonzini         if (descriptor == 0 && (val & 0x8))
240749ab747fSPaolo Bonzini         {
240849ab747fSPaolo Bonzini             hwaddr tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]);
240949ab747fSPaolo Bonzini 
241049ab747fSPaolo Bonzini             /* dump tally counters to specified memory location */
241149ab747fSPaolo Bonzini             RTL8139TallyCounters_dma_write(s, tc_addr);
241249ab747fSPaolo Bonzini 
241349ab747fSPaolo Bonzini             /* mark dump completed */
241449ab747fSPaolo Bonzini             s->TxStatus[0] &= ~0x8;
241549ab747fSPaolo Bonzini         }
241649ab747fSPaolo Bonzini 
241749ab747fSPaolo Bonzini         return;
241849ab747fSPaolo Bonzini     }
241949ab747fSPaolo Bonzini 
242049ab747fSPaolo Bonzini     DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n",
242149ab747fSPaolo Bonzini         txRegOffset, val, descriptor);
242249ab747fSPaolo Bonzini 
242349ab747fSPaolo Bonzini     /* mask only reserved bits */
242449ab747fSPaolo Bonzini     val &= ~0xff00c000; /* these bits are reset on write */
242549ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
242649ab747fSPaolo Bonzini 
242749ab747fSPaolo Bonzini     s->TxStatus[descriptor] = val;
242849ab747fSPaolo Bonzini 
242949ab747fSPaolo Bonzini     /* attempt to start transmission */
243049ab747fSPaolo Bonzini     rtl8139_transmit(s);
243149ab747fSPaolo Bonzini }
243249ab747fSPaolo Bonzini 
rtl8139_TxStatus_TxAddr_read(RTL8139State * s,uint32_t regs[],uint32_t base,uint8_t addr,int size)243349ab747fSPaolo Bonzini static uint32_t rtl8139_TxStatus_TxAddr_read(RTL8139State *s, uint32_t regs[],
243449ab747fSPaolo Bonzini                                              uint32_t base, uint8_t addr,
243549ab747fSPaolo Bonzini                                              int size)
243649ab747fSPaolo Bonzini {
243749ab747fSPaolo Bonzini     uint32_t reg = (addr - base) / 4;
243849ab747fSPaolo Bonzini     uint32_t offset = addr & 0x3;
243949ab747fSPaolo Bonzini     uint32_t ret = 0;
244049ab747fSPaolo Bonzini 
244149ab747fSPaolo Bonzini     if (addr & (size - 1)) {
244249ab747fSPaolo Bonzini         DPRINTF("not implemented read for TxStatus/TxAddr "
244349ab747fSPaolo Bonzini                 "addr=0x%x size=0x%x\n", addr, size);
244449ab747fSPaolo Bonzini         return ret;
244549ab747fSPaolo Bonzini     }
244649ab747fSPaolo Bonzini 
244749ab747fSPaolo Bonzini     switch (size) {
244849ab747fSPaolo Bonzini     case 1: /* fall through */
244949ab747fSPaolo Bonzini     case 2: /* fall through */
245049ab747fSPaolo Bonzini     case 4:
245149ab747fSPaolo Bonzini         ret = (regs[reg] >> offset * 8) & (((uint64_t)1 << (size * 8)) - 1);
245249ab747fSPaolo Bonzini         DPRINTF("TxStatus/TxAddr[%d] read addr=0x%x size=0x%x val=0x%08x\n",
245349ab747fSPaolo Bonzini                 reg, addr, size, ret);
245449ab747fSPaolo Bonzini         break;
245549ab747fSPaolo Bonzini     default:
245649ab747fSPaolo Bonzini         DPRINTF("unsupported size 0x%x of TxStatus/TxAddr reading\n", size);
245749ab747fSPaolo Bonzini         break;
245849ab747fSPaolo Bonzini     }
245949ab747fSPaolo Bonzini 
246049ab747fSPaolo Bonzini     return ret;
246149ab747fSPaolo Bonzini }
246249ab747fSPaolo Bonzini 
rtl8139_TSAD_read(RTL8139State * s)246349ab747fSPaolo Bonzini static uint16_t rtl8139_TSAD_read(RTL8139State *s)
246449ab747fSPaolo Bonzini {
246549ab747fSPaolo Bonzini     uint16_t ret = 0;
246649ab747fSPaolo Bonzini 
246749ab747fSPaolo Bonzini     /* Simulate TSAD, it is read only anyway */
246849ab747fSPaolo Bonzini 
246949ab747fSPaolo Bonzini     ret = ((s->TxStatus[3] & TxStatOK  )?TSAD_TOK3:0)
247049ab747fSPaolo Bonzini          |((s->TxStatus[2] & TxStatOK  )?TSAD_TOK2:0)
247149ab747fSPaolo Bonzini          |((s->TxStatus[1] & TxStatOK  )?TSAD_TOK1:0)
247249ab747fSPaolo Bonzini          |((s->TxStatus[0] & TxStatOK  )?TSAD_TOK0:0)
247349ab747fSPaolo Bonzini 
247449ab747fSPaolo Bonzini          |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0)
247549ab747fSPaolo Bonzini          |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0)
247649ab747fSPaolo Bonzini          |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0)
247749ab747fSPaolo Bonzini          |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0)
247849ab747fSPaolo Bonzini 
247949ab747fSPaolo Bonzini          |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0)
248049ab747fSPaolo Bonzini          |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0)
248149ab747fSPaolo Bonzini          |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0)
248249ab747fSPaolo Bonzini          |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0)
248349ab747fSPaolo Bonzini 
248449ab747fSPaolo Bonzini          |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0)
248549ab747fSPaolo Bonzini          |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0)
248649ab747fSPaolo Bonzini          |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0)
248749ab747fSPaolo Bonzini          |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ;
248849ab747fSPaolo Bonzini 
248949ab747fSPaolo Bonzini 
249049ab747fSPaolo Bonzini     DPRINTF("TSAD read val=0x%04x\n", ret);
249149ab747fSPaolo Bonzini 
249249ab747fSPaolo Bonzini     return ret;
249349ab747fSPaolo Bonzini }
249449ab747fSPaolo Bonzini 
rtl8139_CSCR_read(RTL8139State * s)249549ab747fSPaolo Bonzini static uint16_t rtl8139_CSCR_read(RTL8139State *s)
249649ab747fSPaolo Bonzini {
249749ab747fSPaolo Bonzini     uint16_t ret = s->CSCR;
249849ab747fSPaolo Bonzini 
249949ab747fSPaolo Bonzini     DPRINTF("CSCR read val=0x%04x\n", ret);
250049ab747fSPaolo Bonzini 
250149ab747fSPaolo Bonzini     return ret;
250249ab747fSPaolo Bonzini }
250349ab747fSPaolo Bonzini 
rtl8139_TxAddr_write(RTL8139State * s,uint32_t txAddrOffset,uint32_t val)250449ab747fSPaolo Bonzini static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val)
250549ab747fSPaolo Bonzini {
250649ab747fSPaolo Bonzini     DPRINTF("TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val);
250749ab747fSPaolo Bonzini 
250849ab747fSPaolo Bonzini     s->TxAddr[txAddrOffset/4] = val;
250949ab747fSPaolo Bonzini }
251049ab747fSPaolo Bonzini 
rtl8139_TxAddr_read(RTL8139State * s,uint32_t txAddrOffset)251149ab747fSPaolo Bonzini static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset)
251249ab747fSPaolo Bonzini {
251349ab747fSPaolo Bonzini     uint32_t ret = s->TxAddr[txAddrOffset/4];
251449ab747fSPaolo Bonzini 
251549ab747fSPaolo Bonzini     DPRINTF("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret);
251649ab747fSPaolo Bonzini 
251749ab747fSPaolo Bonzini     return ret;
251849ab747fSPaolo Bonzini }
251949ab747fSPaolo Bonzini 
rtl8139_RxBufPtr_write(RTL8139State * s,uint32_t val)252049ab747fSPaolo Bonzini static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val)
252149ab747fSPaolo Bonzini {
252249ab747fSPaolo Bonzini     DPRINTF("RxBufPtr write val=0x%04x\n", val);
252349ab747fSPaolo Bonzini 
252449ab747fSPaolo Bonzini     /* this value is off by 16 */
252549ab747fSPaolo Bonzini     s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize);
252649ab747fSPaolo Bonzini 
252700b7ade8SStefan Hajnoczi     /* more buffer space may be available so try to receive */
252800b7ade8SStefan Hajnoczi     qemu_flush_queued_packets(qemu_get_queue(s->nic));
252900b7ade8SStefan Hajnoczi 
253049ab747fSPaolo Bonzini     DPRINTF(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n",
253149ab747fSPaolo Bonzini         s->RxBufferSize, s->RxBufAddr, s->RxBufPtr);
253249ab747fSPaolo Bonzini }
253349ab747fSPaolo Bonzini 
rtl8139_RxBufPtr_read(RTL8139State * s)253449ab747fSPaolo Bonzini static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s)
253549ab747fSPaolo Bonzini {
253649ab747fSPaolo Bonzini     /* this value is off by 16 */
253749ab747fSPaolo Bonzini     uint32_t ret = s->RxBufPtr - 0x10;
253849ab747fSPaolo Bonzini 
253949ab747fSPaolo Bonzini     DPRINTF("RxBufPtr read val=0x%04x\n", ret);
254049ab747fSPaolo Bonzini 
254149ab747fSPaolo Bonzini     return ret;
254249ab747fSPaolo Bonzini }
254349ab747fSPaolo Bonzini 
rtl8139_RxBufAddr_read(RTL8139State * s)254449ab747fSPaolo Bonzini static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s)
254549ab747fSPaolo Bonzini {
254649ab747fSPaolo Bonzini     /* this value is NOT off by 16 */
254749ab747fSPaolo Bonzini     uint32_t ret = s->RxBufAddr;
254849ab747fSPaolo Bonzini 
254949ab747fSPaolo Bonzini     DPRINTF("RxBufAddr read val=0x%04x\n", ret);
255049ab747fSPaolo Bonzini 
255149ab747fSPaolo Bonzini     return ret;
255249ab747fSPaolo Bonzini }
255349ab747fSPaolo Bonzini 
rtl8139_RxBuf_write(RTL8139State * s,uint32_t val)255449ab747fSPaolo Bonzini static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val)
255549ab747fSPaolo Bonzini {
255649ab747fSPaolo Bonzini     DPRINTF("RxBuf write val=0x%08x\n", val);
255749ab747fSPaolo Bonzini 
255849ab747fSPaolo Bonzini     s->RxBuf = val;
255949ab747fSPaolo Bonzini 
256049ab747fSPaolo Bonzini     /* may need to reset rxring here */
256149ab747fSPaolo Bonzini }
256249ab747fSPaolo Bonzini 
rtl8139_RxBuf_read(RTL8139State * s)256349ab747fSPaolo Bonzini static uint32_t rtl8139_RxBuf_read(RTL8139State *s)
256449ab747fSPaolo Bonzini {
256549ab747fSPaolo Bonzini     uint32_t ret = s->RxBuf;
256649ab747fSPaolo Bonzini 
256749ab747fSPaolo Bonzini     DPRINTF("RxBuf read val=0x%08x\n", ret);
256849ab747fSPaolo Bonzini 
256949ab747fSPaolo Bonzini     return ret;
257049ab747fSPaolo Bonzini }
257149ab747fSPaolo Bonzini 
rtl8139_IntrMask_write(RTL8139State * s,uint32_t val)257249ab747fSPaolo Bonzini static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val)
257349ab747fSPaolo Bonzini {
257449ab747fSPaolo Bonzini     DPRINTF("IntrMask write(w) val=0x%04x\n", val);
257549ab747fSPaolo Bonzini 
257649ab747fSPaolo Bonzini     /* mask unwritable bits */
257749ab747fSPaolo Bonzini     val = SET_MASKED(val, 0x1e00, s->IntrMask);
257849ab747fSPaolo Bonzini 
257949ab747fSPaolo Bonzini     s->IntrMask = val;
258049ab747fSPaolo Bonzini 
258149ab747fSPaolo Bonzini     rtl8139_update_irq(s);
258249ab747fSPaolo Bonzini 
258349ab747fSPaolo Bonzini }
258449ab747fSPaolo Bonzini 
rtl8139_IntrMask_read(RTL8139State * s)258549ab747fSPaolo Bonzini static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
258649ab747fSPaolo Bonzini {
258749ab747fSPaolo Bonzini     uint32_t ret = s->IntrMask;
258849ab747fSPaolo Bonzini 
258949ab747fSPaolo Bonzini     DPRINTF("IntrMask read(w) val=0x%04x\n", ret);
259049ab747fSPaolo Bonzini 
259149ab747fSPaolo Bonzini     return ret;
259249ab747fSPaolo Bonzini }
259349ab747fSPaolo Bonzini 
rtl8139_IntrStatus_write(RTL8139State * s,uint32_t val)259449ab747fSPaolo Bonzini static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
259549ab747fSPaolo Bonzini {
259649ab747fSPaolo Bonzini     DPRINTF("IntrStatus write(w) val=0x%04x\n", val);
259749ab747fSPaolo Bonzini 
259849ab747fSPaolo Bonzini #if 0
259949ab747fSPaolo Bonzini 
260049ab747fSPaolo Bonzini     /* writing to ISR has no effect */
260149ab747fSPaolo Bonzini 
260249ab747fSPaolo Bonzini     return;
260349ab747fSPaolo Bonzini 
260449ab747fSPaolo Bonzini #else
260549ab747fSPaolo Bonzini     uint16_t newStatus = s->IntrStatus & ~val;
260649ab747fSPaolo Bonzini 
260749ab747fSPaolo Bonzini     /* mask unwritable bits */
260849ab747fSPaolo Bonzini     newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);
260949ab747fSPaolo Bonzini 
261049ab747fSPaolo Bonzini     /* writing 1 to interrupt status register bit clears it */
261149ab747fSPaolo Bonzini     s->IntrStatus = 0;
261249ab747fSPaolo Bonzini     rtl8139_update_irq(s);
261349ab747fSPaolo Bonzini 
261449ab747fSPaolo Bonzini     s->IntrStatus = newStatus;
2615237c255cSPaolo Bonzini     rtl8139_set_next_tctr_time(s);
261649ab747fSPaolo Bonzini     rtl8139_update_irq(s);
261749ab747fSPaolo Bonzini 
261849ab747fSPaolo Bonzini #endif
261949ab747fSPaolo Bonzini }
262049ab747fSPaolo Bonzini 
rtl8139_IntrStatus_read(RTL8139State * s)262149ab747fSPaolo Bonzini static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
262249ab747fSPaolo Bonzini {
262349ab747fSPaolo Bonzini     uint32_t ret = s->IntrStatus;
262449ab747fSPaolo Bonzini 
262549ab747fSPaolo Bonzini     DPRINTF("IntrStatus read(w) val=0x%04x\n", ret);
262649ab747fSPaolo Bonzini 
262749ab747fSPaolo Bonzini #if 0
262849ab747fSPaolo Bonzini 
262949ab747fSPaolo Bonzini     /* reading ISR clears all interrupts */
263049ab747fSPaolo Bonzini     s->IntrStatus = 0;
263149ab747fSPaolo Bonzini 
263249ab747fSPaolo Bonzini     rtl8139_update_irq(s);
263349ab747fSPaolo Bonzini 
263449ab747fSPaolo Bonzini #endif
263549ab747fSPaolo Bonzini 
263649ab747fSPaolo Bonzini     return ret;
263749ab747fSPaolo Bonzini }
263849ab747fSPaolo Bonzini 
rtl8139_MultiIntr_write(RTL8139State * s,uint32_t val)263949ab747fSPaolo Bonzini static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val)
264049ab747fSPaolo Bonzini {
264149ab747fSPaolo Bonzini     DPRINTF("MultiIntr write(w) val=0x%04x\n", val);
264249ab747fSPaolo Bonzini 
264349ab747fSPaolo Bonzini     /* mask unwritable bits */
264449ab747fSPaolo Bonzini     val = SET_MASKED(val, 0xf000, s->MultiIntr);
264549ab747fSPaolo Bonzini 
264649ab747fSPaolo Bonzini     s->MultiIntr = val;
264749ab747fSPaolo Bonzini }
264849ab747fSPaolo Bonzini 
rtl8139_MultiIntr_read(RTL8139State * s)264949ab747fSPaolo Bonzini static uint32_t rtl8139_MultiIntr_read(RTL8139State *s)
265049ab747fSPaolo Bonzini {
265149ab747fSPaolo Bonzini     uint32_t ret = s->MultiIntr;
265249ab747fSPaolo Bonzini 
265349ab747fSPaolo Bonzini     DPRINTF("MultiIntr read(w) val=0x%04x\n", ret);
265449ab747fSPaolo Bonzini 
265549ab747fSPaolo Bonzini     return ret;
265649ab747fSPaolo Bonzini }
265749ab747fSPaolo Bonzini 
rtl8139_io_writeb(void * opaque,uint8_t addr,uint32_t val)265849ab747fSPaolo Bonzini static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
265949ab747fSPaolo Bonzini {
266049ab747fSPaolo Bonzini     RTL8139State *s = opaque;
266149ab747fSPaolo Bonzini 
266249ab747fSPaolo Bonzini     switch (addr)
266349ab747fSPaolo Bonzini     {
266490d131fbSMichael S. Tsirkin         case MAC0 ... MAC0+4:
266590d131fbSMichael S. Tsirkin             s->phys[addr - MAC0] = val;
266690d131fbSMichael S. Tsirkin             break;
266790d131fbSMichael S. Tsirkin         case MAC0+5:
266823c37c37SAmos Kong             s->phys[addr - MAC0] = val;
266923c37c37SAmos Kong             qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys);
267023c37c37SAmos Kong             break;
267149ab747fSPaolo Bonzini         case MAC0+6 ... MAC0+7:
267249ab747fSPaolo Bonzini             /* reserved */
267349ab747fSPaolo Bonzini             break;
267449ab747fSPaolo Bonzini         case MAR0 ... MAR0+7:
267549ab747fSPaolo Bonzini             s->mult[addr - MAR0] = val;
267649ab747fSPaolo Bonzini             break;
267749ab747fSPaolo Bonzini         case ChipCmd:
267849ab747fSPaolo Bonzini             rtl8139_ChipCmd_write(s, val);
267949ab747fSPaolo Bonzini             break;
268049ab747fSPaolo Bonzini         case Cfg9346:
268149ab747fSPaolo Bonzini             rtl8139_Cfg9346_write(s, val);
268249ab747fSPaolo Bonzini             break;
268349ab747fSPaolo Bonzini         case TxConfig: /* windows driver sometimes writes using byte-lenth call */
268449ab747fSPaolo Bonzini             rtl8139_TxConfig_writeb(s, val);
268549ab747fSPaolo Bonzini             break;
268649ab747fSPaolo Bonzini         case Config0:
268749ab747fSPaolo Bonzini             rtl8139_Config0_write(s, val);
268849ab747fSPaolo Bonzini             break;
268949ab747fSPaolo Bonzini         case Config1:
269049ab747fSPaolo Bonzini             rtl8139_Config1_write(s, val);
269149ab747fSPaolo Bonzini             break;
269249ab747fSPaolo Bonzini         case Config3:
269349ab747fSPaolo Bonzini             rtl8139_Config3_write(s, val);
269449ab747fSPaolo Bonzini             break;
269549ab747fSPaolo Bonzini         case Config4:
269649ab747fSPaolo Bonzini             rtl8139_Config4_write(s, val);
269749ab747fSPaolo Bonzini             break;
269849ab747fSPaolo Bonzini         case Config5:
269949ab747fSPaolo Bonzini             rtl8139_Config5_write(s, val);
270049ab747fSPaolo Bonzini             break;
270149ab747fSPaolo Bonzini         case MediaStatus:
270249ab747fSPaolo Bonzini             /* ignore */
270349ab747fSPaolo Bonzini             DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n",
270449ab747fSPaolo Bonzini                 val);
270549ab747fSPaolo Bonzini             break;
270649ab747fSPaolo Bonzini 
270749ab747fSPaolo Bonzini         case HltClk:
270849ab747fSPaolo Bonzini             DPRINTF("HltClk write val=0x%08x\n", val);
270949ab747fSPaolo Bonzini             if (val == 'R')
271049ab747fSPaolo Bonzini             {
271149ab747fSPaolo Bonzini                 s->clock_enabled = 1;
271249ab747fSPaolo Bonzini             }
271349ab747fSPaolo Bonzini             else if (val == 'H')
271449ab747fSPaolo Bonzini             {
271549ab747fSPaolo Bonzini                 s->clock_enabled = 0;
271649ab747fSPaolo Bonzini             }
271749ab747fSPaolo Bonzini             break;
271849ab747fSPaolo Bonzini 
271949ab747fSPaolo Bonzini         case TxThresh:
272049ab747fSPaolo Bonzini             DPRINTF("C+ TxThresh write(b) val=0x%02x\n", val);
272149ab747fSPaolo Bonzini             s->TxThresh = val;
272249ab747fSPaolo Bonzini             break;
272349ab747fSPaolo Bonzini 
272449ab747fSPaolo Bonzini         case TxPoll:
272549ab747fSPaolo Bonzini             DPRINTF("C+ TxPoll write(b) val=0x%02x\n", val);
272649ab747fSPaolo Bonzini             if (val & (1 << 7))
272749ab747fSPaolo Bonzini             {
272849ab747fSPaolo Bonzini                 DPRINTF("C+ TxPoll high priority transmission (not "
272949ab747fSPaolo Bonzini                     "implemented)\n");
273049ab747fSPaolo Bonzini                 //rtl8139_cplus_transmit(s);
273149ab747fSPaolo Bonzini             }
273249ab747fSPaolo Bonzini             if (val & (1 << 6))
273349ab747fSPaolo Bonzini             {
273449ab747fSPaolo Bonzini                 DPRINTF("C+ TxPoll normal priority transmission\n");
273549ab747fSPaolo Bonzini                 rtl8139_cplus_transmit(s);
273649ab747fSPaolo Bonzini             }
273749ab747fSPaolo Bonzini 
273849ab747fSPaolo Bonzini             break;
27399e3b9f27SHans         case RxConfig:
27409e3b9f27SHans             DPRINTF("RxConfig write(b) val=0x%02x\n", val);
27419e3b9f27SHans             rtl8139_RxConfig_write(s,
27429e3b9f27SHans                 (rtl8139_RxConfig_read(s) & 0xFFFFFF00) | val);
27439e3b9f27SHans             break;
274449ab747fSPaolo Bonzini         default:
274549ab747fSPaolo Bonzini             DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr,
274649ab747fSPaolo Bonzini                 val);
274749ab747fSPaolo Bonzini             break;
274849ab747fSPaolo Bonzini     }
274949ab747fSPaolo Bonzini }
275049ab747fSPaolo Bonzini 
rtl8139_io_writew(void * opaque,uint8_t addr,uint32_t val)275149ab747fSPaolo Bonzini static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
275249ab747fSPaolo Bonzini {
275349ab747fSPaolo Bonzini     RTL8139State *s = opaque;
275449ab747fSPaolo Bonzini 
275549ab747fSPaolo Bonzini     switch (addr)
275649ab747fSPaolo Bonzini     {
275749ab747fSPaolo Bonzini         case IntrMask:
275849ab747fSPaolo Bonzini             rtl8139_IntrMask_write(s, val);
275949ab747fSPaolo Bonzini             break;
276049ab747fSPaolo Bonzini 
276149ab747fSPaolo Bonzini         case IntrStatus:
276249ab747fSPaolo Bonzini             rtl8139_IntrStatus_write(s, val);
276349ab747fSPaolo Bonzini             break;
276449ab747fSPaolo Bonzini 
276549ab747fSPaolo Bonzini         case MultiIntr:
276649ab747fSPaolo Bonzini             rtl8139_MultiIntr_write(s, val);
276749ab747fSPaolo Bonzini             break;
276849ab747fSPaolo Bonzini 
276949ab747fSPaolo Bonzini         case RxBufPtr:
277049ab747fSPaolo Bonzini             rtl8139_RxBufPtr_write(s, val);
277149ab747fSPaolo Bonzini             break;
277249ab747fSPaolo Bonzini 
277349ab747fSPaolo Bonzini         case BasicModeCtrl:
277449ab747fSPaolo Bonzini             rtl8139_BasicModeCtrl_write(s, val);
277549ab747fSPaolo Bonzini             break;
277649ab747fSPaolo Bonzini         case BasicModeStatus:
277749ab747fSPaolo Bonzini             rtl8139_BasicModeStatus_write(s, val);
277849ab747fSPaolo Bonzini             break;
277949ab747fSPaolo Bonzini         case NWayAdvert:
278049ab747fSPaolo Bonzini             DPRINTF("NWayAdvert write(w) val=0x%04x\n", val);
278149ab747fSPaolo Bonzini             s->NWayAdvert = val;
278249ab747fSPaolo Bonzini             break;
278349ab747fSPaolo Bonzini         case NWayLPAR:
278449ab747fSPaolo Bonzini             DPRINTF("forbidden NWayLPAR write(w) val=0x%04x\n", val);
278549ab747fSPaolo Bonzini             break;
278649ab747fSPaolo Bonzini         case NWayExpansion:
278749ab747fSPaolo Bonzini             DPRINTF("NWayExpansion write(w) val=0x%04x\n", val);
278849ab747fSPaolo Bonzini             s->NWayExpansion = val;
278949ab747fSPaolo Bonzini             break;
279049ab747fSPaolo Bonzini 
279149ab747fSPaolo Bonzini         case CpCmd:
279249ab747fSPaolo Bonzini             rtl8139_CpCmd_write(s, val);
279349ab747fSPaolo Bonzini             break;
279449ab747fSPaolo Bonzini 
279549ab747fSPaolo Bonzini         case IntrMitigate:
279649ab747fSPaolo Bonzini             rtl8139_IntrMitigate_write(s, val);
279749ab747fSPaolo Bonzini             break;
279849ab747fSPaolo Bonzini 
279949ab747fSPaolo Bonzini         default:
280049ab747fSPaolo Bonzini             DPRINTF("ioport write(w) addr=0x%x val=0x%04x via write(b)\n",
280149ab747fSPaolo Bonzini                 addr, val);
280249ab747fSPaolo Bonzini 
280349ab747fSPaolo Bonzini             rtl8139_io_writeb(opaque, addr, val & 0xff);
280449ab747fSPaolo Bonzini             rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
280549ab747fSPaolo Bonzini             break;
280649ab747fSPaolo Bonzini     }
280749ab747fSPaolo Bonzini }
280849ab747fSPaolo Bonzini 
rtl8139_set_next_tctr_time(RTL8139State * s)2809237c255cSPaolo Bonzini static void rtl8139_set_next_tctr_time(RTL8139State *s)
281049ab747fSPaolo Bonzini {
281137b9ab92SLaurent Vivier     const uint64_t ns_per_period = (uint64_t)PCI_PERIOD << 32;
281249ab747fSPaolo Bonzini 
281349ab747fSPaolo Bonzini     DPRINTF("entered rtl8139_set_next_tctr_time\n");
281449ab747fSPaolo Bonzini 
2815237c255cSPaolo Bonzini     /* This function is called at least once per period, so it is a good
2816237c255cSPaolo Bonzini      * place to update the timer base.
2817237c255cSPaolo Bonzini      *
2818237c255cSPaolo Bonzini      * After one iteration of this loop the value in the Timer register does
2819237c255cSPaolo Bonzini      * not change, but the device model is counting up by 2^32 ticks (approx.
2820237c255cSPaolo Bonzini      * 130 seconds).
282149ab747fSPaolo Bonzini      */
2822237c255cSPaolo Bonzini     while (s->TCTR_base + ns_per_period <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
2823237c255cSPaolo Bonzini         s->TCTR_base += ns_per_period;
2824237c255cSPaolo Bonzini     }
2825237c255cSPaolo Bonzini 
282649ab747fSPaolo Bonzini     if (!s->TimerInt) {
2827237c255cSPaolo Bonzini         timer_del(s->timer);
2828237c255cSPaolo Bonzini     } else {
282937b9ab92SLaurent Vivier         uint64_t delta = (uint64_t)s->TimerInt * PCI_PERIOD;
2830237c255cSPaolo Bonzini         if (s->TCTR_base + delta <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
2831237c255cSPaolo Bonzini             delta += ns_per_period;
283249ab747fSPaolo Bonzini         }
2833237c255cSPaolo Bonzini         timer_mod(s->timer, s->TCTR_base + delta);
283449ab747fSPaolo Bonzini     }
283549ab747fSPaolo Bonzini }
283649ab747fSPaolo Bonzini 
rtl8139_io_writel(void * opaque,uint8_t addr,uint32_t val)283749ab747fSPaolo Bonzini static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
283849ab747fSPaolo Bonzini {
283949ab747fSPaolo Bonzini     RTL8139State *s = opaque;
284049ab747fSPaolo Bonzini 
284149ab747fSPaolo Bonzini     switch (addr)
284249ab747fSPaolo Bonzini     {
284349ab747fSPaolo Bonzini         case RxMissed:
284449ab747fSPaolo Bonzini             DPRINTF("RxMissed clearing on write\n");
284549ab747fSPaolo Bonzini             s->RxMissed = 0;
284649ab747fSPaolo Bonzini             break;
284749ab747fSPaolo Bonzini 
284849ab747fSPaolo Bonzini         case TxConfig:
284949ab747fSPaolo Bonzini             rtl8139_TxConfig_write(s, val);
285049ab747fSPaolo Bonzini             break;
285149ab747fSPaolo Bonzini 
285249ab747fSPaolo Bonzini         case RxConfig:
285349ab747fSPaolo Bonzini             rtl8139_RxConfig_write(s, val);
285449ab747fSPaolo Bonzini             break;
285549ab747fSPaolo Bonzini 
285649ab747fSPaolo Bonzini         case TxStatus0 ... TxStatus0+4*4-1:
285749ab747fSPaolo Bonzini             rtl8139_TxStatus_write(s, addr-TxStatus0, val);
285849ab747fSPaolo Bonzini             break;
285949ab747fSPaolo Bonzini 
286049ab747fSPaolo Bonzini         case TxAddr0 ... TxAddr0+4*4-1:
286149ab747fSPaolo Bonzini             rtl8139_TxAddr_write(s, addr-TxAddr0, val);
286249ab747fSPaolo Bonzini             break;
286349ab747fSPaolo Bonzini 
286449ab747fSPaolo Bonzini         case RxBuf:
286549ab747fSPaolo Bonzini             rtl8139_RxBuf_write(s, val);
286649ab747fSPaolo Bonzini             break;
286749ab747fSPaolo Bonzini 
286849ab747fSPaolo Bonzini         case RxRingAddrLO:
286949ab747fSPaolo Bonzini             DPRINTF("C+ RxRing low bits write val=0x%08x\n", val);
287049ab747fSPaolo Bonzini             s->RxRingAddrLO = val;
287149ab747fSPaolo Bonzini             break;
287249ab747fSPaolo Bonzini 
287349ab747fSPaolo Bonzini         case RxRingAddrHI:
287449ab747fSPaolo Bonzini             DPRINTF("C+ RxRing high bits write val=0x%08x\n", val);
287549ab747fSPaolo Bonzini             s->RxRingAddrHI = val;
287649ab747fSPaolo Bonzini             break;
287749ab747fSPaolo Bonzini 
287849ab747fSPaolo Bonzini         case Timer:
287949ab747fSPaolo Bonzini             DPRINTF("TCTR Timer reset on write\n");
2880bc72ad67SAlex Bligh             s->TCTR_base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2881237c255cSPaolo Bonzini             rtl8139_set_next_tctr_time(s);
288249ab747fSPaolo Bonzini             break;
288349ab747fSPaolo Bonzini 
288449ab747fSPaolo Bonzini         case FlashReg:
288549ab747fSPaolo Bonzini             DPRINTF("FlashReg TimerInt write val=0x%08x\n", val);
288649ab747fSPaolo Bonzini             if (s->TimerInt != val) {
288749ab747fSPaolo Bonzini                 s->TimerInt = val;
2888237c255cSPaolo Bonzini                 rtl8139_set_next_tctr_time(s);
288949ab747fSPaolo Bonzini             }
289049ab747fSPaolo Bonzini             break;
289149ab747fSPaolo Bonzini 
289249ab747fSPaolo Bonzini         default:
289349ab747fSPaolo Bonzini             DPRINTF("ioport write(l) addr=0x%x val=0x%08x via write(b)\n",
289449ab747fSPaolo Bonzini                 addr, val);
289549ab747fSPaolo Bonzini             rtl8139_io_writeb(opaque, addr, val & 0xff);
289649ab747fSPaolo Bonzini             rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
289749ab747fSPaolo Bonzini             rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff);
289849ab747fSPaolo Bonzini             rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff);
289949ab747fSPaolo Bonzini             break;
290049ab747fSPaolo Bonzini     }
290149ab747fSPaolo Bonzini }
290249ab747fSPaolo Bonzini 
rtl8139_io_readb(void * opaque,uint8_t addr)290349ab747fSPaolo Bonzini static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
290449ab747fSPaolo Bonzini {
290549ab747fSPaolo Bonzini     RTL8139State *s = opaque;
290649ab747fSPaolo Bonzini     int ret;
290749ab747fSPaolo Bonzini 
290849ab747fSPaolo Bonzini     switch (addr)
290949ab747fSPaolo Bonzini     {
291049ab747fSPaolo Bonzini         case MAC0 ... MAC0+5:
291149ab747fSPaolo Bonzini             ret = s->phys[addr - MAC0];
291249ab747fSPaolo Bonzini             break;
291349ab747fSPaolo Bonzini         case MAC0+6 ... MAC0+7:
291449ab747fSPaolo Bonzini             ret = 0;
291549ab747fSPaolo Bonzini             break;
291649ab747fSPaolo Bonzini         case MAR0 ... MAR0+7:
291749ab747fSPaolo Bonzini             ret = s->mult[addr - MAR0];
291849ab747fSPaolo Bonzini             break;
291949ab747fSPaolo Bonzini         case TxStatus0 ... TxStatus0+4*4-1:
292049ab747fSPaolo Bonzini             ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0,
292149ab747fSPaolo Bonzini                                                addr, 1);
292249ab747fSPaolo Bonzini             break;
292349ab747fSPaolo Bonzini         case ChipCmd:
292449ab747fSPaolo Bonzini             ret = rtl8139_ChipCmd_read(s);
292549ab747fSPaolo Bonzini             break;
292649ab747fSPaolo Bonzini         case Cfg9346:
292749ab747fSPaolo Bonzini             ret = rtl8139_Cfg9346_read(s);
292849ab747fSPaolo Bonzini             break;
292949ab747fSPaolo Bonzini         case Config0:
293049ab747fSPaolo Bonzini             ret = rtl8139_Config0_read(s);
293149ab747fSPaolo Bonzini             break;
293249ab747fSPaolo Bonzini         case Config1:
293349ab747fSPaolo Bonzini             ret = rtl8139_Config1_read(s);
293449ab747fSPaolo Bonzini             break;
293549ab747fSPaolo Bonzini         case Config3:
293649ab747fSPaolo Bonzini             ret = rtl8139_Config3_read(s);
293749ab747fSPaolo Bonzini             break;
293849ab747fSPaolo Bonzini         case Config4:
293949ab747fSPaolo Bonzini             ret = rtl8139_Config4_read(s);
294049ab747fSPaolo Bonzini             break;
294149ab747fSPaolo Bonzini         case Config5:
294249ab747fSPaolo Bonzini             ret = rtl8139_Config5_read(s);
294349ab747fSPaolo Bonzini             break;
294449ab747fSPaolo Bonzini 
294549ab747fSPaolo Bonzini         case MediaStatus:
294649ab747fSPaolo Bonzini             /* The LinkDown bit of MediaStatus is inverse with link status */
294749ab747fSPaolo Bonzini             ret = 0xd0 | (~s->BasicModeStatus & 0x04);
294849ab747fSPaolo Bonzini             DPRINTF("MediaStatus read 0x%x\n", ret);
294949ab747fSPaolo Bonzini             break;
295049ab747fSPaolo Bonzini 
295149ab747fSPaolo Bonzini         case HltClk:
295249ab747fSPaolo Bonzini             ret = s->clock_enabled;
295349ab747fSPaolo Bonzini             DPRINTF("HltClk read 0x%x\n", ret);
295449ab747fSPaolo Bonzini             break;
295549ab747fSPaolo Bonzini 
295649ab747fSPaolo Bonzini         case PCIRevisionID:
295749ab747fSPaolo Bonzini             ret = RTL8139_PCI_REVID;
295849ab747fSPaolo Bonzini             DPRINTF("PCI Revision ID read 0x%x\n", ret);
295949ab747fSPaolo Bonzini             break;
296049ab747fSPaolo Bonzini 
296149ab747fSPaolo Bonzini         case TxThresh:
296249ab747fSPaolo Bonzini             ret = s->TxThresh;
296349ab747fSPaolo Bonzini             DPRINTF("C+ TxThresh read(b) val=0x%02x\n", ret);
296449ab747fSPaolo Bonzini             break;
296549ab747fSPaolo Bonzini 
296649ab747fSPaolo Bonzini         case 0x43: /* Part of TxConfig register. Windows driver tries to read it */
296749ab747fSPaolo Bonzini             ret = s->TxConfig >> 24;
296849ab747fSPaolo Bonzini             DPRINTF("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret);
296949ab747fSPaolo Bonzini             break;
297049ab747fSPaolo Bonzini 
297149ab747fSPaolo Bonzini         default:
297249ab747fSPaolo Bonzini             DPRINTF("not implemented read(b) addr=0x%x\n", addr);
297349ab747fSPaolo Bonzini             ret = 0;
297449ab747fSPaolo Bonzini             break;
297549ab747fSPaolo Bonzini     }
297649ab747fSPaolo Bonzini 
297749ab747fSPaolo Bonzini     return ret;
297849ab747fSPaolo Bonzini }
297949ab747fSPaolo Bonzini 
rtl8139_io_readw(void * opaque,uint8_t addr)298049ab747fSPaolo Bonzini static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
298149ab747fSPaolo Bonzini {
298249ab747fSPaolo Bonzini     RTL8139State *s = opaque;
298349ab747fSPaolo Bonzini     uint32_t ret;
298449ab747fSPaolo Bonzini 
298549ab747fSPaolo Bonzini     switch (addr)
298649ab747fSPaolo Bonzini     {
298749ab747fSPaolo Bonzini         case TxAddr0 ... TxAddr0+4*4-1:
298849ab747fSPaolo Bonzini             ret = rtl8139_TxStatus_TxAddr_read(s, s->TxAddr, TxAddr0, addr, 2);
298949ab747fSPaolo Bonzini             break;
299049ab747fSPaolo Bonzini         case IntrMask:
299149ab747fSPaolo Bonzini             ret = rtl8139_IntrMask_read(s);
299249ab747fSPaolo Bonzini             break;
299349ab747fSPaolo Bonzini 
299449ab747fSPaolo Bonzini         case IntrStatus:
299549ab747fSPaolo Bonzini             ret = rtl8139_IntrStatus_read(s);
299649ab747fSPaolo Bonzini             break;
299749ab747fSPaolo Bonzini 
299849ab747fSPaolo Bonzini         case MultiIntr:
299949ab747fSPaolo Bonzini             ret = rtl8139_MultiIntr_read(s);
300049ab747fSPaolo Bonzini             break;
300149ab747fSPaolo Bonzini 
300249ab747fSPaolo Bonzini         case RxBufPtr:
300349ab747fSPaolo Bonzini             ret = rtl8139_RxBufPtr_read(s);
300449ab747fSPaolo Bonzini             break;
300549ab747fSPaolo Bonzini 
300649ab747fSPaolo Bonzini         case RxBufAddr:
300749ab747fSPaolo Bonzini             ret = rtl8139_RxBufAddr_read(s);
300849ab747fSPaolo Bonzini             break;
300949ab747fSPaolo Bonzini 
301049ab747fSPaolo Bonzini         case BasicModeCtrl:
301149ab747fSPaolo Bonzini             ret = rtl8139_BasicModeCtrl_read(s);
301249ab747fSPaolo Bonzini             break;
301349ab747fSPaolo Bonzini         case BasicModeStatus:
301449ab747fSPaolo Bonzini             ret = rtl8139_BasicModeStatus_read(s);
301549ab747fSPaolo Bonzini             break;
301649ab747fSPaolo Bonzini         case NWayAdvert:
301749ab747fSPaolo Bonzini             ret = s->NWayAdvert;
301849ab747fSPaolo Bonzini             DPRINTF("NWayAdvert read(w) val=0x%04x\n", ret);
301949ab747fSPaolo Bonzini             break;
302049ab747fSPaolo Bonzini         case NWayLPAR:
302149ab747fSPaolo Bonzini             ret = s->NWayLPAR;
302249ab747fSPaolo Bonzini             DPRINTF("NWayLPAR read(w) val=0x%04x\n", ret);
302349ab747fSPaolo Bonzini             break;
302449ab747fSPaolo Bonzini         case NWayExpansion:
302549ab747fSPaolo Bonzini             ret = s->NWayExpansion;
302649ab747fSPaolo Bonzini             DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret);
302749ab747fSPaolo Bonzini             break;
302849ab747fSPaolo Bonzini 
302949ab747fSPaolo Bonzini         case CpCmd:
303049ab747fSPaolo Bonzini             ret = rtl8139_CpCmd_read(s);
303149ab747fSPaolo Bonzini             break;
303249ab747fSPaolo Bonzini 
303349ab747fSPaolo Bonzini         case IntrMitigate:
303449ab747fSPaolo Bonzini             ret = rtl8139_IntrMitigate_read(s);
303549ab747fSPaolo Bonzini             break;
303649ab747fSPaolo Bonzini 
303749ab747fSPaolo Bonzini         case TxSummary:
303849ab747fSPaolo Bonzini             ret = rtl8139_TSAD_read(s);
303949ab747fSPaolo Bonzini             break;
304049ab747fSPaolo Bonzini 
304149ab747fSPaolo Bonzini         case CSCR:
304249ab747fSPaolo Bonzini             ret = rtl8139_CSCR_read(s);
304349ab747fSPaolo Bonzini             break;
304449ab747fSPaolo Bonzini 
304549ab747fSPaolo Bonzini         default:
304649ab747fSPaolo Bonzini             DPRINTF("ioport read(w) addr=0x%x via read(b)\n", addr);
304749ab747fSPaolo Bonzini 
304849ab747fSPaolo Bonzini             ret  = rtl8139_io_readb(opaque, addr);
304949ab747fSPaolo Bonzini             ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
305049ab747fSPaolo Bonzini 
305149ab747fSPaolo Bonzini             DPRINTF("ioport read(w) addr=0x%x val=0x%04x\n", addr, ret);
305249ab747fSPaolo Bonzini             break;
305349ab747fSPaolo Bonzini     }
305449ab747fSPaolo Bonzini 
305549ab747fSPaolo Bonzini     return ret;
305649ab747fSPaolo Bonzini }
305749ab747fSPaolo Bonzini 
rtl8139_io_readl(void * opaque,uint8_t addr)305849ab747fSPaolo Bonzini static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
305949ab747fSPaolo Bonzini {
306049ab747fSPaolo Bonzini     RTL8139State *s = opaque;
306149ab747fSPaolo Bonzini     uint32_t ret;
306249ab747fSPaolo Bonzini 
306349ab747fSPaolo Bonzini     switch (addr)
306449ab747fSPaolo Bonzini     {
306549ab747fSPaolo Bonzini         case RxMissed:
306649ab747fSPaolo Bonzini             ret = s->RxMissed;
306749ab747fSPaolo Bonzini 
306849ab747fSPaolo Bonzini             DPRINTF("RxMissed read val=0x%08x\n", ret);
306949ab747fSPaolo Bonzini             break;
307049ab747fSPaolo Bonzini 
307149ab747fSPaolo Bonzini         case TxConfig:
307249ab747fSPaolo Bonzini             ret = rtl8139_TxConfig_read(s);
307349ab747fSPaolo Bonzini             break;
307449ab747fSPaolo Bonzini 
307549ab747fSPaolo Bonzini         case RxConfig:
307649ab747fSPaolo Bonzini             ret = rtl8139_RxConfig_read(s);
307749ab747fSPaolo Bonzini             break;
307849ab747fSPaolo Bonzini 
307949ab747fSPaolo Bonzini         case TxStatus0 ... TxStatus0+4*4-1:
308049ab747fSPaolo Bonzini             ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0,
308149ab747fSPaolo Bonzini                                                addr, 4);
308249ab747fSPaolo Bonzini             break;
308349ab747fSPaolo Bonzini 
308449ab747fSPaolo Bonzini         case TxAddr0 ... TxAddr0+4*4-1:
308549ab747fSPaolo Bonzini             ret = rtl8139_TxAddr_read(s, addr-TxAddr0);
308649ab747fSPaolo Bonzini             break;
308749ab747fSPaolo Bonzini 
308849ab747fSPaolo Bonzini         case RxBuf:
308949ab747fSPaolo Bonzini             ret = rtl8139_RxBuf_read(s);
309049ab747fSPaolo Bonzini             break;
309149ab747fSPaolo Bonzini 
309249ab747fSPaolo Bonzini         case RxRingAddrLO:
309349ab747fSPaolo Bonzini             ret = s->RxRingAddrLO;
309449ab747fSPaolo Bonzini             DPRINTF("C+ RxRing low bits read val=0x%08x\n", ret);
309549ab747fSPaolo Bonzini             break;
309649ab747fSPaolo Bonzini 
309749ab747fSPaolo Bonzini         case RxRingAddrHI:
309849ab747fSPaolo Bonzini             ret = s->RxRingAddrHI;
309949ab747fSPaolo Bonzini             DPRINTF("C+ RxRing high bits read val=0x%08x\n", ret);
310049ab747fSPaolo Bonzini             break;
310149ab747fSPaolo Bonzini 
310249ab747fSPaolo Bonzini         case Timer:
310337b9ab92SLaurent Vivier             ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) /
310437b9ab92SLaurent Vivier                   PCI_PERIOD;
310549ab747fSPaolo Bonzini             DPRINTF("TCTR Timer read val=0x%08x\n", ret);
310649ab747fSPaolo Bonzini             break;
310749ab747fSPaolo Bonzini 
310849ab747fSPaolo Bonzini         case FlashReg:
310949ab747fSPaolo Bonzini             ret = s->TimerInt;
311049ab747fSPaolo Bonzini             DPRINTF("FlashReg TimerInt read val=0x%08x\n", ret);
311149ab747fSPaolo Bonzini             break;
311249ab747fSPaolo Bonzini 
311349ab747fSPaolo Bonzini         default:
311449ab747fSPaolo Bonzini             DPRINTF("ioport read(l) addr=0x%x via read(b)\n", addr);
311549ab747fSPaolo Bonzini 
311649ab747fSPaolo Bonzini             ret  = rtl8139_io_readb(opaque, addr);
311749ab747fSPaolo Bonzini             ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
311849ab747fSPaolo Bonzini             ret |= rtl8139_io_readb(opaque, addr + 2) << 16;
311949ab747fSPaolo Bonzini             ret |= rtl8139_io_readb(opaque, addr + 3) << 24;
312049ab747fSPaolo Bonzini 
312149ab747fSPaolo Bonzini             DPRINTF("read(l) addr=0x%x val=%08x\n", addr, ret);
312249ab747fSPaolo Bonzini             break;
312349ab747fSPaolo Bonzini     }
312449ab747fSPaolo Bonzini 
312549ab747fSPaolo Bonzini     return ret;
312649ab747fSPaolo Bonzini }
312749ab747fSPaolo Bonzini 
312849ab747fSPaolo Bonzini /* */
312949ab747fSPaolo Bonzini 
rtl8139_post_load(void * opaque,int version_id)313049ab747fSPaolo Bonzini static int rtl8139_post_load(void *opaque, int version_id)
313149ab747fSPaolo Bonzini {
313249ab747fSPaolo Bonzini     RTL8139State* s = opaque;
3133237c255cSPaolo Bonzini     rtl8139_set_next_tctr_time(s);
313449ab747fSPaolo Bonzini     if (version_id < 4) {
313549ab747fSPaolo Bonzini         s->cplus_enabled = s->CpCmd != 0;
313649ab747fSPaolo Bonzini     }
313749ab747fSPaolo Bonzini 
313849ab747fSPaolo Bonzini     /* nc.link_down can't be migrated, so infer link_down according
313949ab747fSPaolo Bonzini      * to link status bit in BasicModeStatus */
314049ab747fSPaolo Bonzini     qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0;
314149ab747fSPaolo Bonzini 
314249ab747fSPaolo Bonzini     return 0;
314349ab747fSPaolo Bonzini }
314449ab747fSPaolo Bonzini 
rtl8139_hotplug_ready_needed(void * opaque)314549ab747fSPaolo Bonzini static bool rtl8139_hotplug_ready_needed(void *opaque)
314649ab747fSPaolo Bonzini {
314749ab747fSPaolo Bonzini     return qdev_machine_modified();
314849ab747fSPaolo Bonzini }
314949ab747fSPaolo Bonzini 
315049ab747fSPaolo Bonzini static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
315149ab747fSPaolo Bonzini     .name = "rtl8139/hotplug_ready",
315249ab747fSPaolo Bonzini     .version_id = 1,
315349ab747fSPaolo Bonzini     .minimum_version_id = 1,
31545cd8cadaSJuan Quintela     .needed = rtl8139_hotplug_ready_needed,
31551de81b42SRichard Henderson     .fields = (const VMStateField[]) {
315649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
315749ab747fSPaolo Bonzini     }
315849ab747fSPaolo Bonzini };
315949ab747fSPaolo Bonzini 
rtl8139_pre_save(void * opaque)316044b1ff31SDr. David Alan Gilbert static int rtl8139_pre_save(void *opaque)
316149ab747fSPaolo Bonzini {
316249ab747fSPaolo Bonzini     RTL8139State* s = opaque;
3163bc72ad67SAlex Bligh     int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
316449ab747fSPaolo Bonzini 
3165237c255cSPaolo Bonzini     /* for migration to older versions */
316637b9ab92SLaurent Vivier     s->TCTR = (current_time - s->TCTR_base) / PCI_PERIOD;
316749ab747fSPaolo Bonzini     s->rtl8139_mmio_io_addr_dummy = 0;
316844b1ff31SDr. David Alan Gilbert 
316944b1ff31SDr. David Alan Gilbert     return 0;
317049ab747fSPaolo Bonzini }
317149ab747fSPaolo Bonzini 
317249ab747fSPaolo Bonzini static const VMStateDescription vmstate_rtl8139 = {
317349ab747fSPaolo Bonzini     .name = "rtl8139",
317446fe8befSDavid Vrabel     .version_id = 5,
317549ab747fSPaolo Bonzini     .minimum_version_id = 3,
317649ab747fSPaolo Bonzini     .post_load = rtl8139_post_load,
317749ab747fSPaolo Bonzini     .pre_save  = rtl8139_pre_save,
31781de81b42SRichard Henderson     .fields = (const VMStateField[]) {
317988a411a8SAndreas Färber         VMSTATE_PCI_DEVICE(parent_obj, RTL8139State),
318049ab747fSPaolo Bonzini         VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6),
318149ab747fSPaolo Bonzini         VMSTATE_BUFFER(mult, RTL8139State),
318249ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(TxStatus, RTL8139State, 4),
318349ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(TxAddr, RTL8139State, 4),
318449ab747fSPaolo Bonzini 
318549ab747fSPaolo Bonzini         VMSTATE_UINT32(RxBuf, RTL8139State),
318649ab747fSPaolo Bonzini         VMSTATE_UINT32(RxBufferSize, RTL8139State),
318749ab747fSPaolo Bonzini         VMSTATE_UINT32(RxBufPtr, RTL8139State),
318849ab747fSPaolo Bonzini         VMSTATE_UINT32(RxBufAddr, RTL8139State),
318949ab747fSPaolo Bonzini 
319049ab747fSPaolo Bonzini         VMSTATE_UINT16(IntrStatus, RTL8139State),
319149ab747fSPaolo Bonzini         VMSTATE_UINT16(IntrMask, RTL8139State),
319249ab747fSPaolo Bonzini 
319349ab747fSPaolo Bonzini         VMSTATE_UINT32(TxConfig, RTL8139State),
319449ab747fSPaolo Bonzini         VMSTATE_UINT32(RxConfig, RTL8139State),
319549ab747fSPaolo Bonzini         VMSTATE_UINT32(RxMissed, RTL8139State),
319649ab747fSPaolo Bonzini         VMSTATE_UINT16(CSCR, RTL8139State),
319749ab747fSPaolo Bonzini 
319849ab747fSPaolo Bonzini         VMSTATE_UINT8(Cfg9346, RTL8139State),
319949ab747fSPaolo Bonzini         VMSTATE_UINT8(Config0, RTL8139State),
320049ab747fSPaolo Bonzini         VMSTATE_UINT8(Config1, RTL8139State),
320149ab747fSPaolo Bonzini         VMSTATE_UINT8(Config3, RTL8139State),
320249ab747fSPaolo Bonzini         VMSTATE_UINT8(Config4, RTL8139State),
320349ab747fSPaolo Bonzini         VMSTATE_UINT8(Config5, RTL8139State),
320449ab747fSPaolo Bonzini 
320549ab747fSPaolo Bonzini         VMSTATE_UINT8(clock_enabled, RTL8139State),
320649ab747fSPaolo Bonzini         VMSTATE_UINT8(bChipCmdState, RTL8139State),
320749ab747fSPaolo Bonzini 
320849ab747fSPaolo Bonzini         VMSTATE_UINT16(MultiIntr, RTL8139State),
320949ab747fSPaolo Bonzini 
321049ab747fSPaolo Bonzini         VMSTATE_UINT16(BasicModeCtrl, RTL8139State),
321149ab747fSPaolo Bonzini         VMSTATE_UINT16(BasicModeStatus, RTL8139State),
321249ab747fSPaolo Bonzini         VMSTATE_UINT16(NWayAdvert, RTL8139State),
321349ab747fSPaolo Bonzini         VMSTATE_UINT16(NWayLPAR, RTL8139State),
321449ab747fSPaolo Bonzini         VMSTATE_UINT16(NWayExpansion, RTL8139State),
321549ab747fSPaolo Bonzini 
321649ab747fSPaolo Bonzini         VMSTATE_UINT16(CpCmd, RTL8139State),
321749ab747fSPaolo Bonzini         VMSTATE_UINT8(TxThresh, RTL8139State),
321849ab747fSPaolo Bonzini 
321949ab747fSPaolo Bonzini         VMSTATE_UNUSED(4),
322049ab747fSPaolo Bonzini         VMSTATE_MACADDR(conf.macaddr, RTL8139State),
322149ab747fSPaolo Bonzini         VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State),
322249ab747fSPaolo Bonzini 
322349ab747fSPaolo Bonzini         VMSTATE_UINT32(currTxDesc, RTL8139State),
322449ab747fSPaolo Bonzini         VMSTATE_UINT32(currCPlusRxDesc, RTL8139State),
322549ab747fSPaolo Bonzini         VMSTATE_UINT32(currCPlusTxDesc, RTL8139State),
322649ab747fSPaolo Bonzini         VMSTATE_UINT32(RxRingAddrLO, RTL8139State),
322749ab747fSPaolo Bonzini         VMSTATE_UINT32(RxRingAddrHI, RTL8139State),
322849ab747fSPaolo Bonzini 
322949ab747fSPaolo Bonzini         VMSTATE_UINT16_ARRAY(eeprom.contents, RTL8139State, EEPROM_9346_SIZE),
323049ab747fSPaolo Bonzini         VMSTATE_INT32(eeprom.mode, RTL8139State),
323149ab747fSPaolo Bonzini         VMSTATE_UINT32(eeprom.tick, RTL8139State),
323249ab747fSPaolo Bonzini         VMSTATE_UINT8(eeprom.address, RTL8139State),
323349ab747fSPaolo Bonzini         VMSTATE_UINT16(eeprom.input, RTL8139State),
323449ab747fSPaolo Bonzini         VMSTATE_UINT16(eeprom.output, RTL8139State),
323549ab747fSPaolo Bonzini 
323649ab747fSPaolo Bonzini         VMSTATE_UINT8(eeprom.eecs, RTL8139State),
323749ab747fSPaolo Bonzini         VMSTATE_UINT8(eeprom.eesk, RTL8139State),
323849ab747fSPaolo Bonzini         VMSTATE_UINT8(eeprom.eedi, RTL8139State),
323949ab747fSPaolo Bonzini         VMSTATE_UINT8(eeprom.eedo, RTL8139State),
324049ab747fSPaolo Bonzini 
324149ab747fSPaolo Bonzini         VMSTATE_UINT32(TCTR, RTL8139State),
324249ab747fSPaolo Bonzini         VMSTATE_UINT32(TimerInt, RTL8139State),
324349ab747fSPaolo Bonzini         VMSTATE_INT64(TCTR_base, RTL8139State),
324449ab747fSPaolo Bonzini 
324546fe8befSDavid Vrabel         VMSTATE_UINT64(tally_counters.TxOk, RTL8139State),
324646fe8befSDavid Vrabel         VMSTATE_UINT64(tally_counters.RxOk, RTL8139State),
324746fe8befSDavid Vrabel         VMSTATE_UINT64(tally_counters.TxERR, RTL8139State),
324846fe8befSDavid Vrabel         VMSTATE_UINT32(tally_counters.RxERR, RTL8139State),
324946fe8befSDavid Vrabel         VMSTATE_UINT16(tally_counters.MissPkt, RTL8139State),
325046fe8befSDavid Vrabel         VMSTATE_UINT16(tally_counters.FAE, RTL8139State),
325146fe8befSDavid Vrabel         VMSTATE_UINT32(tally_counters.Tx1Col, RTL8139State),
325246fe8befSDavid Vrabel         VMSTATE_UINT32(tally_counters.TxMCol, RTL8139State),
325346fe8befSDavid Vrabel         VMSTATE_UINT64(tally_counters.RxOkPhy, RTL8139State),
325446fe8befSDavid Vrabel         VMSTATE_UINT64(tally_counters.RxOkBrd, RTL8139State),
325546fe8befSDavid Vrabel         VMSTATE_UINT32_V(tally_counters.RxOkMul, RTL8139State, 5),
325646fe8befSDavid Vrabel         VMSTATE_UINT16(tally_counters.TxAbt, RTL8139State),
325746fe8befSDavid Vrabel         VMSTATE_UINT16(tally_counters.TxUndrn, RTL8139State),
325849ab747fSPaolo Bonzini 
325949ab747fSPaolo Bonzini         VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
326049ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
326149ab747fSPaolo Bonzini     },
32621de81b42SRichard Henderson     .subsections = (const VMStateDescription * const []) {
32635cd8cadaSJuan Quintela         &vmstate_rtl8139_hotplug_ready,
32645cd8cadaSJuan Quintela         NULL
326549ab747fSPaolo Bonzini     }
326649ab747fSPaolo Bonzini };
326749ab747fSPaolo Bonzini 
326849ab747fSPaolo Bonzini /***********************************************************/
326949ab747fSPaolo Bonzini /* PCI RTL8139 definitions */
327049ab747fSPaolo Bonzini 
rtl8139_ioport_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)327149ab747fSPaolo Bonzini static void rtl8139_ioport_write(void *opaque, hwaddr addr,
327249ab747fSPaolo Bonzini                                  uint64_t val, unsigned size)
327349ab747fSPaolo Bonzini {
327449ab747fSPaolo Bonzini     switch (size) {
327549ab747fSPaolo Bonzini     case 1:
327649ab747fSPaolo Bonzini         rtl8139_io_writeb(opaque, addr, val);
327749ab747fSPaolo Bonzini         break;
327849ab747fSPaolo Bonzini     case 2:
327949ab747fSPaolo Bonzini         rtl8139_io_writew(opaque, addr, val);
328049ab747fSPaolo Bonzini         break;
328149ab747fSPaolo Bonzini     case 4:
328249ab747fSPaolo Bonzini         rtl8139_io_writel(opaque, addr, val);
328349ab747fSPaolo Bonzini         break;
328449ab747fSPaolo Bonzini     }
328549ab747fSPaolo Bonzini }
328649ab747fSPaolo Bonzini 
rtl8139_ioport_read(void * opaque,hwaddr addr,unsigned size)328749ab747fSPaolo Bonzini static uint64_t rtl8139_ioport_read(void *opaque, hwaddr addr,
328849ab747fSPaolo Bonzini                                     unsigned size)
328949ab747fSPaolo Bonzini {
329049ab747fSPaolo Bonzini     switch (size) {
329149ab747fSPaolo Bonzini     case 1:
329249ab747fSPaolo Bonzini         return rtl8139_io_readb(opaque, addr);
329349ab747fSPaolo Bonzini     case 2:
329449ab747fSPaolo Bonzini         return rtl8139_io_readw(opaque, addr);
329549ab747fSPaolo Bonzini     case 4:
329649ab747fSPaolo Bonzini         return rtl8139_io_readl(opaque, addr);
329749ab747fSPaolo Bonzini     }
329849ab747fSPaolo Bonzini 
329949ab747fSPaolo Bonzini     return -1;
330049ab747fSPaolo Bonzini }
330149ab747fSPaolo Bonzini 
330249ab747fSPaolo Bonzini static const MemoryRegionOps rtl8139_io_ops = {
330349ab747fSPaolo Bonzini     .read = rtl8139_ioport_read,
330449ab747fSPaolo Bonzini     .write = rtl8139_ioport_write,
330549ab747fSPaolo Bonzini     .impl = {
330649ab747fSPaolo Bonzini         .min_access_size = 1,
330749ab747fSPaolo Bonzini         .max_access_size = 4,
330849ab747fSPaolo Bonzini     },
330949ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
331049ab747fSPaolo Bonzini };
331149ab747fSPaolo Bonzini 
rtl8139_timer(void * opaque)331249ab747fSPaolo Bonzini static void rtl8139_timer(void *opaque)
331349ab747fSPaolo Bonzini {
331449ab747fSPaolo Bonzini     RTL8139State *s = opaque;
331549ab747fSPaolo Bonzini 
331649ab747fSPaolo Bonzini     if (!s->clock_enabled)
331749ab747fSPaolo Bonzini     {
331849ab747fSPaolo Bonzini         DPRINTF(">>> timer: clock is not running\n");
331949ab747fSPaolo Bonzini         return;
332049ab747fSPaolo Bonzini     }
332149ab747fSPaolo Bonzini 
332249ab747fSPaolo Bonzini     s->IntrStatus |= PCSTimeout;
332349ab747fSPaolo Bonzini     rtl8139_update_irq(s);
3324237c255cSPaolo Bonzini     rtl8139_set_next_tctr_time(s);
332549ab747fSPaolo Bonzini }
332649ab747fSPaolo Bonzini 
pci_rtl8139_uninit(PCIDevice * dev)332749ab747fSPaolo Bonzini static void pci_rtl8139_uninit(PCIDevice *dev)
332849ab747fSPaolo Bonzini {
332939257515SPeter Crosthwaite     RTL8139State *s = RTL8139(dev);
333049ab747fSPaolo Bonzini 
333149ab747fSPaolo Bonzini     g_free(s->cplus_txbuffer);
333249ab747fSPaolo Bonzini     s->cplus_txbuffer = NULL;
3333bc72ad67SAlex Bligh     timer_free(s->timer);
333449ab747fSPaolo Bonzini     qemu_del_nic(s->nic);
333549ab747fSPaolo Bonzini }
333649ab747fSPaolo Bonzini 
rtl8139_set_link_status(NetClientState * nc)333749ab747fSPaolo Bonzini static void rtl8139_set_link_status(NetClientState *nc)
333849ab747fSPaolo Bonzini {
333949ab747fSPaolo Bonzini     RTL8139State *s = qemu_get_nic_opaque(nc);
334049ab747fSPaolo Bonzini 
334149ab747fSPaolo Bonzini     if (nc->link_down) {
334249ab747fSPaolo Bonzini         s->BasicModeStatus &= ~0x04;
334349ab747fSPaolo Bonzini     } else {
334449ab747fSPaolo Bonzini         s->BasicModeStatus |= 0x04;
334549ab747fSPaolo Bonzini     }
334649ab747fSPaolo Bonzini 
334749ab747fSPaolo Bonzini     s->IntrStatus |= RxUnderrun;
334849ab747fSPaolo Bonzini     rtl8139_update_irq(s);
334949ab747fSPaolo Bonzini }
335049ab747fSPaolo Bonzini 
335149ab747fSPaolo Bonzini static NetClientInfo net_rtl8139_info = {
3352f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
335349ab747fSPaolo Bonzini     .size = sizeof(NICState),
335449ab747fSPaolo Bonzini     .can_receive = rtl8139_can_receive,
335549ab747fSPaolo Bonzini     .receive = rtl8139_receive,
335649ab747fSPaolo Bonzini     .link_status_changed = rtl8139_set_link_status,
335749ab747fSPaolo Bonzini };
335849ab747fSPaolo Bonzini 
pci_rtl8139_realize(PCIDevice * dev,Error ** errp)33599af21dbeSMarkus Armbruster static void pci_rtl8139_realize(PCIDevice *dev, Error **errp)
336049ab747fSPaolo Bonzini {
336139257515SPeter Crosthwaite     RTL8139State *s = RTL8139(dev);
336239257515SPeter Crosthwaite     DeviceState *d = DEVICE(dev);
336349ab747fSPaolo Bonzini     uint8_t *pci_conf;
336449ab747fSPaolo Bonzini 
336588a411a8SAndreas Färber     pci_conf = dev->config;
336649ab747fSPaolo Bonzini     pci_conf[PCI_INTERRUPT_PIN] = 1;    /* interrupt pin A */
336749ab747fSPaolo Bonzini     /* TODO: start of capability list, but no capability
336849ab747fSPaolo Bonzini      * list bit in status register, and offset 0xdc seems unused. */
336949ab747fSPaolo Bonzini     pci_conf[PCI_CAPABILITY_LIST] = 0xdc;
337049ab747fSPaolo Bonzini 
3371eedfac6fSPaolo Bonzini     memory_region_init_io(&s->bar_io, OBJECT(s), &rtl8139_io_ops, s,
3372eedfac6fSPaolo Bonzini                           "rtl8139", 0x100);
3373726ec828SMatt Parker     memory_region_init_alias(&s->bar_mem, OBJECT(s), "rtl8139-mem", &s->bar_io,
3374726ec828SMatt Parker                              0, 0x100);
3375726ec828SMatt Parker 
337688a411a8SAndreas Färber     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);
337788a411a8SAndreas Färber     pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem);
337849ab747fSPaolo Bonzini 
337949ab747fSPaolo Bonzini     qemu_macaddr_default_if_unset(&s->conf.macaddr);
338049ab747fSPaolo Bonzini 
338149ab747fSPaolo Bonzini     /* prepare eeprom */
338249ab747fSPaolo Bonzini     s->eeprom.contents[0] = 0x8129;
338349ab747fSPaolo Bonzini #if 1
338449ab747fSPaolo Bonzini     /* PCI vendor and device ID should be mirrored here */
338549ab747fSPaolo Bonzini     s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK;
338649ab747fSPaolo Bonzini     s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139;
338749ab747fSPaolo Bonzini #endif
338849ab747fSPaolo Bonzini     s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8;
338949ab747fSPaolo Bonzini     s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
339049ab747fSPaolo Bonzini     s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
339149ab747fSPaolo Bonzini 
339249ab747fSPaolo Bonzini     s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
33937d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), d->id,
33947d0fefdfSAkihiko Odaki                           &d->mem_reentrancy_guard, s);
339549ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
339649ab747fSPaolo Bonzini 
339749ab747fSPaolo Bonzini     s->cplus_txbuffer = NULL;
339849ab747fSPaolo Bonzini     s->cplus_txbuffer_len = 0;
339949ab747fSPaolo Bonzini     s->cplus_txbuffer_offset = 0;
340049ab747fSPaolo Bonzini 
3401bc72ad67SAlex Bligh     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rtl8139_timer, s);
340249ab747fSPaolo Bonzini }
340349ab747fSPaolo Bonzini 
rtl8139_instance_init(Object * obj)3404afd7c850SGonglei static void rtl8139_instance_init(Object *obj)
3405afd7c850SGonglei {
3406afd7c850SGonglei     RTL8139State *s = RTL8139(obj);
3407afd7c850SGonglei 
3408afd7c850SGonglei     device_add_bootindex_property(obj, &s->conf.bootindex,
3409afd7c850SGonglei                                   "bootindex", "/ethernet-phy@0",
341040c2281cSMarkus Armbruster                                   DEVICE(obj));
3411afd7c850SGonglei }
3412afd7c850SGonglei 
341349ab747fSPaolo Bonzini static Property rtl8139_properties[] = {
341449ab747fSPaolo Bonzini     DEFINE_NIC_PROPERTIES(RTL8139State, conf),
341549ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
341649ab747fSPaolo Bonzini };
341749ab747fSPaolo Bonzini 
rtl8139_class_init(ObjectClass * klass,void * data)341849ab747fSPaolo Bonzini static void rtl8139_class_init(ObjectClass *klass, void *data)
341949ab747fSPaolo Bonzini {
342049ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
342149ab747fSPaolo Bonzini     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
342249ab747fSPaolo Bonzini 
34239af21dbeSMarkus Armbruster     k->realize = pci_rtl8139_realize;
342449ab747fSPaolo Bonzini     k->exit = pci_rtl8139_uninit;
342549ab747fSPaolo Bonzini     k->romfile = "efi-rtl8139.rom";
342649ab747fSPaolo Bonzini     k->vendor_id = PCI_VENDOR_ID_REALTEK;
342749ab747fSPaolo Bonzini     k->device_id = PCI_DEVICE_ID_REALTEK_8139;
342849ab747fSPaolo Bonzini     k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
342949ab747fSPaolo Bonzini     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
3430e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, rtl8139_reset);
343149ab747fSPaolo Bonzini     dc->vmsd = &vmstate_rtl8139;
34324f67d30bSMarc-André Lureau     device_class_set_props(dc, rtl8139_properties);
3433125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
343449ab747fSPaolo Bonzini }
343549ab747fSPaolo Bonzini 
343649ab747fSPaolo Bonzini static const TypeInfo rtl8139_info = {
343739257515SPeter Crosthwaite     .name          = TYPE_RTL8139,
343849ab747fSPaolo Bonzini     .parent        = TYPE_PCI_DEVICE,
343949ab747fSPaolo Bonzini     .instance_size = sizeof(RTL8139State),
344049ab747fSPaolo Bonzini     .class_init    = rtl8139_class_init,
3441afd7c850SGonglei     .instance_init = rtl8139_instance_init,
3442fd3b02c8SEduardo Habkost     .interfaces = (InterfaceInfo[]) {
3443fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
3444fd3b02c8SEduardo Habkost         { },
3445fd3b02c8SEduardo Habkost     },
344649ab747fSPaolo Bonzini };
344749ab747fSPaolo Bonzini 
rtl8139_register_types(void)344849ab747fSPaolo Bonzini static void rtl8139_register_types(void)
344949ab747fSPaolo Bonzini {
345049ab747fSPaolo Bonzini     type_register_static(&rtl8139_info);
345149ab747fSPaolo Bonzini }
345249ab747fSPaolo Bonzini 
345349ab747fSPaolo Bonzini type_init(rtl8139_register_types)
3454