149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * QEMU AMD PC-Net II (Am79C970A) emulation
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * Copyright (c) 2004 Antony T Curtis
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
2549ab747fSPaolo Bonzini /* This software was written to be compatible with the specification:
2649ab747fSPaolo Bonzini * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
2749ab747fSPaolo Bonzini * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
2849ab747fSPaolo Bonzini */
2949ab747fSPaolo Bonzini
3049ab747fSPaolo Bonzini /*
3149ab747fSPaolo Bonzini * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
3249ab747fSPaolo Bonzini * produced as NCR89C100. See
3349ab747fSPaolo Bonzini * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
3449ab747fSPaolo Bonzini * and
3549ab747fSPaolo Bonzini * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
3649ab747fSPaolo Bonzini */
3749ab747fSPaolo Bonzini
38e8d40465SPeter Maydell #include "qemu/osdep.h"
398f599053SPhilippe Mathieu-Daudé #include "qemu/log.h"
4064552b6bSMarkus Armbruster #include "hw/irq.h"
41a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
42d6454270SMarkus Armbruster #include "migration/vmstate.h"
4349ab747fSPaolo Bonzini #include "net/net.h"
44cbbeca91SMark Cave-Ayland #include "net/eth.h"
4549ab747fSPaolo Bonzini #include "qemu/timer.h"
4615cae340SDon Koch #include "trace.h"
4749ab747fSPaolo Bonzini
4847b43a1fSPaolo Bonzini #include "pcnet.h"
4949ab747fSPaolo Bonzini
5049ab747fSPaolo Bonzini //#define PCNET_DEBUG
5149ab747fSPaolo Bonzini //#define PCNET_DEBUG_IO
5249ab747fSPaolo Bonzini //#define PCNET_DEBUG_BCR
5349ab747fSPaolo Bonzini //#define PCNET_DEBUG_CSR
5449ab747fSPaolo Bonzini //#define PCNET_DEBUG_RMD
5549ab747fSPaolo Bonzini //#define PCNET_DEBUG_TMD
5649ab747fSPaolo Bonzini //#define PCNET_DEBUG_MATCH
5749ab747fSPaolo Bonzini
5849ab747fSPaolo Bonzini
5949ab747fSPaolo Bonzini struct qemu_ether_header {
6049ab747fSPaolo Bonzini uint8_t ether_dhost[6];
6149ab747fSPaolo Bonzini uint8_t ether_shost[6];
6249ab747fSPaolo Bonzini uint16_t ether_type;
6349ab747fSPaolo Bonzini };
6449ab747fSPaolo Bonzini
6549ab747fSPaolo Bonzini #define CSR_INIT(S) !!(((S)->csr[0])&0x0001)
6649ab747fSPaolo Bonzini #define CSR_STRT(S) !!(((S)->csr[0])&0x0002)
6749ab747fSPaolo Bonzini #define CSR_STOP(S) !!(((S)->csr[0])&0x0004)
6849ab747fSPaolo Bonzini #define CSR_TDMD(S) !!(((S)->csr[0])&0x0008)
6949ab747fSPaolo Bonzini #define CSR_TXON(S) !!(((S)->csr[0])&0x0010)
7049ab747fSPaolo Bonzini #define CSR_RXON(S) !!(((S)->csr[0])&0x0020)
7149ab747fSPaolo Bonzini #define CSR_INEA(S) !!(((S)->csr[0])&0x0040)
7249ab747fSPaolo Bonzini #define CSR_BSWP(S) !!(((S)->csr[3])&0x0004)
7349ab747fSPaolo Bonzini #define CSR_LAPPEN(S) !!(((S)->csr[3])&0x0020)
7449ab747fSPaolo Bonzini #define CSR_DXSUFLO(S) !!(((S)->csr[3])&0x0040)
7549ab747fSPaolo Bonzini #define CSR_ASTRP_RCV(S) !!(((S)->csr[4])&0x0800)
7649ab747fSPaolo Bonzini #define CSR_DPOLL(S) !!(((S)->csr[4])&0x1000)
7749ab747fSPaolo Bonzini #define CSR_SPND(S) !!(((S)->csr[5])&0x0001)
7849ab747fSPaolo Bonzini #define CSR_LTINTEN(S) !!(((S)->csr[5])&0x4000)
7949ab747fSPaolo Bonzini #define CSR_TOKINTD(S) !!(((S)->csr[5])&0x8000)
8049ab747fSPaolo Bonzini #define CSR_DRX(S) !!(((S)->csr[15])&0x0001)
8149ab747fSPaolo Bonzini #define CSR_DTX(S) !!(((S)->csr[15])&0x0002)
8249ab747fSPaolo Bonzini #define CSR_LOOP(S) !!(((S)->csr[15])&0x0004)
8349ab747fSPaolo Bonzini #define CSR_DXMTFCS(S) !!(((S)->csr[15])&0x0008)
8449ab747fSPaolo Bonzini #define CSR_INTL(S) !!(((S)->csr[15])&0x0040)
8549ab747fSPaolo Bonzini #define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000)
8649ab747fSPaolo Bonzini #define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000)
8749ab747fSPaolo Bonzini #define CSR_PROM(S) !!(((S)->csr[15])&0x8000)
8849ab747fSPaolo Bonzini
8949ab747fSPaolo Bonzini #define CSR_CRBC(S) ((S)->csr[40])
9049ab747fSPaolo Bonzini #define CSR_CRST(S) ((S)->csr[41])
9149ab747fSPaolo Bonzini #define CSR_CXBC(S) ((S)->csr[42])
9249ab747fSPaolo Bonzini #define CSR_CXST(S) ((S)->csr[43])
9349ab747fSPaolo Bonzini #define CSR_NRBC(S) ((S)->csr[44])
9449ab747fSPaolo Bonzini #define CSR_NRST(S) ((S)->csr[45])
9549ab747fSPaolo Bonzini #define CSR_POLL(S) ((S)->csr[46])
9649ab747fSPaolo Bonzini #define CSR_PINT(S) ((S)->csr[47])
9749ab747fSPaolo Bonzini #define CSR_RCVRC(S) ((S)->csr[72])
9849ab747fSPaolo Bonzini #define CSR_XMTRC(S) ((S)->csr[74])
9949ab747fSPaolo Bonzini #define CSR_RCVRL(S) ((S)->csr[76])
10049ab747fSPaolo Bonzini #define CSR_XMTRL(S) ((S)->csr[78])
10149ab747fSPaolo Bonzini #define CSR_MISSC(S) ((S)->csr[112])
10249ab747fSPaolo Bonzini
10349ab747fSPaolo Bonzini #define CSR_IADR(S) ((S)->csr[ 1] | ((uint32_t)(S)->csr[ 2] << 16))
10449ab747fSPaolo Bonzini #define CSR_CRBA(S) ((S)->csr[18] | ((uint32_t)(S)->csr[19] << 16))
10549ab747fSPaolo Bonzini #define CSR_CXBA(S) ((S)->csr[20] | ((uint32_t)(S)->csr[21] << 16))
10649ab747fSPaolo Bonzini #define CSR_NRBA(S) ((S)->csr[22] | ((uint32_t)(S)->csr[23] << 16))
10749ab747fSPaolo Bonzini #define CSR_BADR(S) ((S)->csr[24] | ((uint32_t)(S)->csr[25] << 16))
10849ab747fSPaolo Bonzini #define CSR_NRDA(S) ((S)->csr[26] | ((uint32_t)(S)->csr[27] << 16))
10949ab747fSPaolo Bonzini #define CSR_CRDA(S) ((S)->csr[28] | ((uint32_t)(S)->csr[29] << 16))
11049ab747fSPaolo Bonzini #define CSR_BADX(S) ((S)->csr[30] | ((uint32_t)(S)->csr[31] << 16))
11149ab747fSPaolo Bonzini #define CSR_NXDA(S) ((S)->csr[32] | ((uint32_t)(S)->csr[33] << 16))
11249ab747fSPaolo Bonzini #define CSR_CXDA(S) ((S)->csr[34] | ((uint32_t)(S)->csr[35] << 16))
11349ab747fSPaolo Bonzini #define CSR_NNRD(S) ((S)->csr[36] | ((uint32_t)(S)->csr[37] << 16))
11449ab747fSPaolo Bonzini #define CSR_NNXD(S) ((S)->csr[38] | ((uint32_t)(S)->csr[39] << 16))
11549ab747fSPaolo Bonzini #define CSR_PXDA(S) ((S)->csr[60] | ((uint32_t)(S)->csr[61] << 16))
11649ab747fSPaolo Bonzini #define CSR_NXBA(S) ((S)->csr[64] | ((uint32_t)(S)->csr[65] << 16))
11749ab747fSPaolo Bonzini
11849ab747fSPaolo Bonzini #define PHYSADDR(S,A) \
11949ab747fSPaolo Bonzini (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(S)->csr[2])<<16))
12049ab747fSPaolo Bonzini
12149ab747fSPaolo Bonzini struct pcnet_initblk16 {
12249ab747fSPaolo Bonzini uint16_t mode;
12349ab747fSPaolo Bonzini uint16_t padr[3];
12449ab747fSPaolo Bonzini uint16_t ladrf[4];
12549ab747fSPaolo Bonzini uint32_t rdra;
12649ab747fSPaolo Bonzini uint32_t tdra;
12749ab747fSPaolo Bonzini };
12849ab747fSPaolo Bonzini
12949ab747fSPaolo Bonzini struct pcnet_initblk32 {
13049ab747fSPaolo Bonzini uint16_t mode;
13149ab747fSPaolo Bonzini uint8_t rlen;
13249ab747fSPaolo Bonzini uint8_t tlen;
13349ab747fSPaolo Bonzini uint16_t padr[3];
13449ab747fSPaolo Bonzini uint16_t _res;
13549ab747fSPaolo Bonzini uint16_t ladrf[4];
13649ab747fSPaolo Bonzini uint32_t rdra;
13749ab747fSPaolo Bonzini uint32_t tdra;
13849ab747fSPaolo Bonzini };
13949ab747fSPaolo Bonzini
14049ab747fSPaolo Bonzini struct pcnet_TMD {
14149ab747fSPaolo Bonzini uint32_t tbadr;
14249ab747fSPaolo Bonzini int16_t length;
14349ab747fSPaolo Bonzini int16_t status;
14449ab747fSPaolo Bonzini uint32_t misc;
14549ab747fSPaolo Bonzini uint32_t res;
14649ab747fSPaolo Bonzini };
14749ab747fSPaolo Bonzini
14849ab747fSPaolo Bonzini #define TMDL_BCNT_MASK 0x0fff
14949ab747fSPaolo Bonzini #define TMDL_BCNT_SH 0
15049ab747fSPaolo Bonzini #define TMDL_ONES_MASK 0xf000
15149ab747fSPaolo Bonzini #define TMDL_ONES_SH 12
15249ab747fSPaolo Bonzini
15349ab747fSPaolo Bonzini #define TMDS_BPE_MASK 0x0080
15449ab747fSPaolo Bonzini #define TMDS_BPE_SH 7
15549ab747fSPaolo Bonzini #define TMDS_ENP_MASK 0x0100
15649ab747fSPaolo Bonzini #define TMDS_ENP_SH 8
15749ab747fSPaolo Bonzini #define TMDS_STP_MASK 0x0200
15849ab747fSPaolo Bonzini #define TMDS_STP_SH 9
15949ab747fSPaolo Bonzini #define TMDS_DEF_MASK 0x0400
16049ab747fSPaolo Bonzini #define TMDS_DEF_SH 10
16149ab747fSPaolo Bonzini #define TMDS_ONE_MASK 0x0800
16249ab747fSPaolo Bonzini #define TMDS_ONE_SH 11
16349ab747fSPaolo Bonzini #define TMDS_LTINT_MASK 0x1000
16449ab747fSPaolo Bonzini #define TMDS_LTINT_SH 12
16549ab747fSPaolo Bonzini #define TMDS_NOFCS_MASK 0x2000
16649ab747fSPaolo Bonzini #define TMDS_NOFCS_SH 13
16749ab747fSPaolo Bonzini #define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK
16849ab747fSPaolo Bonzini #define TMDS_ADDFCS_SH TMDS_NOFCS_SH
16949ab747fSPaolo Bonzini #define TMDS_ERR_MASK 0x4000
17049ab747fSPaolo Bonzini #define TMDS_ERR_SH 14
17149ab747fSPaolo Bonzini #define TMDS_OWN_MASK 0x8000
17249ab747fSPaolo Bonzini #define TMDS_OWN_SH 15
17349ab747fSPaolo Bonzini
17449ab747fSPaolo Bonzini #define TMDM_TRC_MASK 0x0000000f
17549ab747fSPaolo Bonzini #define TMDM_TRC_SH 0
17649ab747fSPaolo Bonzini #define TMDM_TDR_MASK 0x03ff0000
17749ab747fSPaolo Bonzini #define TMDM_TDR_SH 16
17849ab747fSPaolo Bonzini #define TMDM_RTRY_MASK 0x04000000
17949ab747fSPaolo Bonzini #define TMDM_RTRY_SH 26
18049ab747fSPaolo Bonzini #define TMDM_LCAR_MASK 0x08000000
18149ab747fSPaolo Bonzini #define TMDM_LCAR_SH 27
18249ab747fSPaolo Bonzini #define TMDM_LCOL_MASK 0x10000000
18349ab747fSPaolo Bonzini #define TMDM_LCOL_SH 28
18449ab747fSPaolo Bonzini #define TMDM_EXDEF_MASK 0x20000000
18549ab747fSPaolo Bonzini #define TMDM_EXDEF_SH 29
18649ab747fSPaolo Bonzini #define TMDM_UFLO_MASK 0x40000000
18749ab747fSPaolo Bonzini #define TMDM_UFLO_SH 30
18849ab747fSPaolo Bonzini #define TMDM_BUFF_MASK 0x80000000
18949ab747fSPaolo Bonzini #define TMDM_BUFF_SH 31
19049ab747fSPaolo Bonzini
19149ab747fSPaolo Bonzini struct pcnet_RMD {
19249ab747fSPaolo Bonzini uint32_t rbadr;
19349ab747fSPaolo Bonzini int16_t buf_length;
19449ab747fSPaolo Bonzini int16_t status;
19549ab747fSPaolo Bonzini uint32_t msg_length;
19649ab747fSPaolo Bonzini uint32_t res;
19749ab747fSPaolo Bonzini };
19849ab747fSPaolo Bonzini
19949ab747fSPaolo Bonzini #define RMDL_BCNT_MASK 0x0fff
20049ab747fSPaolo Bonzini #define RMDL_BCNT_SH 0
20149ab747fSPaolo Bonzini #define RMDL_ONES_MASK 0xf000
20249ab747fSPaolo Bonzini #define RMDL_ONES_SH 12
20349ab747fSPaolo Bonzini
20449ab747fSPaolo Bonzini #define RMDS_BAM_MASK 0x0010
20549ab747fSPaolo Bonzini #define RMDS_BAM_SH 4
20649ab747fSPaolo Bonzini #define RMDS_LFAM_MASK 0x0020
20749ab747fSPaolo Bonzini #define RMDS_LFAM_SH 5
20849ab747fSPaolo Bonzini #define RMDS_PAM_MASK 0x0040
20949ab747fSPaolo Bonzini #define RMDS_PAM_SH 6
21049ab747fSPaolo Bonzini #define RMDS_BPE_MASK 0x0080
21149ab747fSPaolo Bonzini #define RMDS_BPE_SH 7
21249ab747fSPaolo Bonzini #define RMDS_ENP_MASK 0x0100
21349ab747fSPaolo Bonzini #define RMDS_ENP_SH 8
21449ab747fSPaolo Bonzini #define RMDS_STP_MASK 0x0200
21549ab747fSPaolo Bonzini #define RMDS_STP_SH 9
21649ab747fSPaolo Bonzini #define RMDS_BUFF_MASK 0x0400
21749ab747fSPaolo Bonzini #define RMDS_BUFF_SH 10
21849ab747fSPaolo Bonzini #define RMDS_CRC_MASK 0x0800
21949ab747fSPaolo Bonzini #define RMDS_CRC_SH 11
22049ab747fSPaolo Bonzini #define RMDS_OFLO_MASK 0x1000
22149ab747fSPaolo Bonzini #define RMDS_OFLO_SH 12
22249ab747fSPaolo Bonzini #define RMDS_FRAM_MASK 0x2000
22349ab747fSPaolo Bonzini #define RMDS_FRAM_SH 13
22449ab747fSPaolo Bonzini #define RMDS_ERR_MASK 0x4000
22549ab747fSPaolo Bonzini #define RMDS_ERR_SH 14
22649ab747fSPaolo Bonzini #define RMDS_OWN_MASK 0x8000
22749ab747fSPaolo Bonzini #define RMDS_OWN_SH 15
22849ab747fSPaolo Bonzini
22949ab747fSPaolo Bonzini #define RMDM_MCNT_MASK 0x00000fff
23049ab747fSPaolo Bonzini #define RMDM_MCNT_SH 0
23149ab747fSPaolo Bonzini #define RMDM_ZEROS_MASK 0x0000f000
23249ab747fSPaolo Bonzini #define RMDM_ZEROS_SH 12
23349ab747fSPaolo Bonzini #define RMDM_RPC_MASK 0x00ff0000
23449ab747fSPaolo Bonzini #define RMDM_RPC_SH 16
23549ab747fSPaolo Bonzini #define RMDM_RCC_MASK 0xff000000
23649ab747fSPaolo Bonzini #define RMDM_RCC_SH 24
23749ab747fSPaolo Bonzini
23849ab747fSPaolo Bonzini #define SET_FIELD(regp, name, field, value) \
23949ab747fSPaolo Bonzini (*(regp) = (*(regp) & ~(name ## _ ## field ## _MASK)) \
24049ab747fSPaolo Bonzini | ((value) << name ## _ ## field ## _SH))
24149ab747fSPaolo Bonzini
24249ab747fSPaolo Bonzini #define GET_FIELD(reg, name, field) \
24349ab747fSPaolo Bonzini (((reg) & name ## _ ## field ## _MASK) >> name ## _ ## field ## _SH)
24449ab747fSPaolo Bonzini
24549ab747fSPaolo Bonzini #define PRINT_TMD(T) printf( \
24649ab747fSPaolo Bonzini "TMD0 : TBADR=0x%08x\n" \
24749ab747fSPaolo Bonzini "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \
24849ab747fSPaolo Bonzini "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \
24949ab747fSPaolo Bonzini " BPE=%d, BCNT=%d\n" \
25049ab747fSPaolo Bonzini "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \
25149ab747fSPaolo Bonzini "LCA=%d, RTR=%d,\n" \
25249ab747fSPaolo Bonzini " TDR=%d, TRC=%d\n", \
25349ab747fSPaolo Bonzini (T)->tbadr, \
25449ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, OWN), \
25549ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, ERR), \
25649ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, NOFCS), \
25749ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, LTINT), \
25849ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, ONE), \
25949ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, DEF), \
26049ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, STP), \
26149ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, ENP), \
26249ab747fSPaolo Bonzini GET_FIELD((T)->status, TMDS, BPE), \
26349ab747fSPaolo Bonzini 4096-GET_FIELD((T)->length, TMDL, BCNT), \
26449ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, BUFF), \
26549ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, UFLO), \
26649ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, EXDEF), \
26749ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, LCOL), \
26849ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, LCAR), \
26949ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, RTRY), \
27049ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, TDR), \
27149ab747fSPaolo Bonzini GET_FIELD((T)->misc, TMDM, TRC))
27249ab747fSPaolo Bonzini
27349ab747fSPaolo Bonzini #define PRINT_RMD(R) printf( \
27449ab747fSPaolo Bonzini "RMD0 : RBADR=0x%08x\n" \
27549ab747fSPaolo Bonzini "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \
27649ab747fSPaolo Bonzini "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \
27749ab747fSPaolo Bonzini "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
27849ab747fSPaolo Bonzini "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \
27949ab747fSPaolo Bonzini (R)->rbadr, \
28049ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, OWN), \
28149ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, ERR), \
28249ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, FRAM), \
28349ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, OFLO), \
28449ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, CRC), \
28549ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, BUFF), \
28649ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, STP), \
28749ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, ENP), \
28849ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, BPE), \
28949ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, PAM), \
29049ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, LFAM), \
29149ab747fSPaolo Bonzini GET_FIELD((R)->status, RMDS, BAM), \
29249ab747fSPaolo Bonzini GET_FIELD((R)->buf_length, RMDL, ONES), \
29349ab747fSPaolo Bonzini 4096-GET_FIELD((R)->buf_length, RMDL, BCNT), \
29449ab747fSPaolo Bonzini GET_FIELD((R)->msg_length, RMDM, RCC), \
29549ab747fSPaolo Bonzini GET_FIELD((R)->msg_length, RMDM, RPC), \
29649ab747fSPaolo Bonzini GET_FIELD((R)->msg_length, RMDM, MCNT), \
29749ab747fSPaolo Bonzini GET_FIELD((R)->msg_length, RMDM, ZEROS))
29849ab747fSPaolo Bonzini
pcnet_tmd_load(PCNetState * s,struct pcnet_TMD * tmd,hwaddr addr)29949ab747fSPaolo Bonzini static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd,
30049ab747fSPaolo Bonzini hwaddr addr)
30149ab747fSPaolo Bonzini {
30249ab747fSPaolo Bonzini if (!BCR_SSIZE32(s)) {
30349ab747fSPaolo Bonzini struct {
30449ab747fSPaolo Bonzini uint32_t tbadr;
30549ab747fSPaolo Bonzini int16_t length;
30649ab747fSPaolo Bonzini int16_t status;
30749ab747fSPaolo Bonzini } xda;
30849ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
30949ab747fSPaolo Bonzini tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff;
31049ab747fSPaolo Bonzini tmd->length = le16_to_cpu(xda.length);
31149ab747fSPaolo Bonzini tmd->status = (le32_to_cpu(xda.tbadr) >> 16) & 0xff00;
31249ab747fSPaolo Bonzini tmd->misc = le16_to_cpu(xda.status) << 16;
31349ab747fSPaolo Bonzini tmd->res = 0;
31449ab747fSPaolo Bonzini } else {
31549ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, addr, (void *)tmd, sizeof(*tmd), 0);
31649ab747fSPaolo Bonzini le32_to_cpus(&tmd->tbadr);
31749ab747fSPaolo Bonzini le16_to_cpus((uint16_t *)&tmd->length);
31849ab747fSPaolo Bonzini le16_to_cpus((uint16_t *)&tmd->status);
31949ab747fSPaolo Bonzini le32_to_cpus(&tmd->misc);
32049ab747fSPaolo Bonzini le32_to_cpus(&tmd->res);
32149ab747fSPaolo Bonzini if (BCR_SWSTYLE(s) == 3) {
32249ab747fSPaolo Bonzini uint32_t tmp = tmd->tbadr;
32349ab747fSPaolo Bonzini tmd->tbadr = tmd->misc;
32449ab747fSPaolo Bonzini tmd->misc = tmp;
32549ab747fSPaolo Bonzini }
32649ab747fSPaolo Bonzini }
32749ab747fSPaolo Bonzini }
32849ab747fSPaolo Bonzini
pcnet_tmd_store(PCNetState * s,const struct pcnet_TMD * tmd,hwaddr addr)32949ab747fSPaolo Bonzini static inline void pcnet_tmd_store(PCNetState *s, const struct pcnet_TMD *tmd,
33049ab747fSPaolo Bonzini hwaddr addr)
33149ab747fSPaolo Bonzini {
33249ab747fSPaolo Bonzini if (!BCR_SSIZE32(s)) {
33349ab747fSPaolo Bonzini struct {
33449ab747fSPaolo Bonzini uint32_t tbadr;
33549ab747fSPaolo Bonzini int16_t length;
33649ab747fSPaolo Bonzini int16_t status;
33749ab747fSPaolo Bonzini } xda;
33849ab747fSPaolo Bonzini xda.tbadr = cpu_to_le32((tmd->tbadr & 0xffffff) |
33949ab747fSPaolo Bonzini ((tmd->status & 0xff00) << 16));
34049ab747fSPaolo Bonzini xda.length = cpu_to_le16(tmd->length);
34149ab747fSPaolo Bonzini xda.status = cpu_to_le16(tmd->misc >> 16);
34249ab747fSPaolo Bonzini s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
34349ab747fSPaolo Bonzini } else {
34449ab747fSPaolo Bonzini struct {
34549ab747fSPaolo Bonzini uint32_t tbadr;
34649ab747fSPaolo Bonzini int16_t length;
34749ab747fSPaolo Bonzini int16_t status;
34849ab747fSPaolo Bonzini uint32_t misc;
34949ab747fSPaolo Bonzini uint32_t res;
35049ab747fSPaolo Bonzini } xda;
35149ab747fSPaolo Bonzini xda.tbadr = cpu_to_le32(tmd->tbadr);
35249ab747fSPaolo Bonzini xda.length = cpu_to_le16(tmd->length);
35349ab747fSPaolo Bonzini xda.status = cpu_to_le16(tmd->status);
35449ab747fSPaolo Bonzini xda.misc = cpu_to_le32(tmd->misc);
35549ab747fSPaolo Bonzini xda.res = cpu_to_le32(tmd->res);
35649ab747fSPaolo Bonzini if (BCR_SWSTYLE(s) == 3) {
35749ab747fSPaolo Bonzini uint32_t tmp = xda.tbadr;
35849ab747fSPaolo Bonzini xda.tbadr = xda.misc;
35949ab747fSPaolo Bonzini xda.misc = tmp;
36049ab747fSPaolo Bonzini }
36149ab747fSPaolo Bonzini s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
36249ab747fSPaolo Bonzini }
36349ab747fSPaolo Bonzini }
36449ab747fSPaolo Bonzini
pcnet_rmd_load(PCNetState * s,struct pcnet_RMD * rmd,hwaddr addr)36549ab747fSPaolo Bonzini static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd,
36649ab747fSPaolo Bonzini hwaddr addr)
36749ab747fSPaolo Bonzini {
36849ab747fSPaolo Bonzini if (!BCR_SSIZE32(s)) {
36949ab747fSPaolo Bonzini struct {
37049ab747fSPaolo Bonzini uint32_t rbadr;
37149ab747fSPaolo Bonzini int16_t buf_length;
37249ab747fSPaolo Bonzini int16_t msg_length;
37349ab747fSPaolo Bonzini } rda;
37449ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
37549ab747fSPaolo Bonzini rmd->rbadr = le32_to_cpu(rda.rbadr) & 0xffffff;
37649ab747fSPaolo Bonzini rmd->buf_length = le16_to_cpu(rda.buf_length);
37749ab747fSPaolo Bonzini rmd->status = (le32_to_cpu(rda.rbadr) >> 16) & 0xff00;
37849ab747fSPaolo Bonzini rmd->msg_length = le16_to_cpu(rda.msg_length);
37949ab747fSPaolo Bonzini rmd->res = 0;
38049ab747fSPaolo Bonzini } else {
38149ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, addr, (void *)rmd, sizeof(*rmd), 0);
38249ab747fSPaolo Bonzini le32_to_cpus(&rmd->rbadr);
38349ab747fSPaolo Bonzini le16_to_cpus((uint16_t *)&rmd->buf_length);
38449ab747fSPaolo Bonzini le16_to_cpus((uint16_t *)&rmd->status);
38549ab747fSPaolo Bonzini le32_to_cpus(&rmd->msg_length);
38649ab747fSPaolo Bonzini le32_to_cpus(&rmd->res);
38749ab747fSPaolo Bonzini if (BCR_SWSTYLE(s) == 3) {
38849ab747fSPaolo Bonzini uint32_t tmp = rmd->rbadr;
38949ab747fSPaolo Bonzini rmd->rbadr = rmd->msg_length;
39049ab747fSPaolo Bonzini rmd->msg_length = tmp;
39149ab747fSPaolo Bonzini }
39249ab747fSPaolo Bonzini }
39349ab747fSPaolo Bonzini }
39449ab747fSPaolo Bonzini
pcnet_rmd_store(PCNetState * s,struct pcnet_RMD * rmd,hwaddr addr)39549ab747fSPaolo Bonzini static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd,
39649ab747fSPaolo Bonzini hwaddr addr)
39749ab747fSPaolo Bonzini {
39849ab747fSPaolo Bonzini if (!BCR_SSIZE32(s)) {
39949ab747fSPaolo Bonzini struct {
40049ab747fSPaolo Bonzini uint32_t rbadr;
40149ab747fSPaolo Bonzini int16_t buf_length;
40249ab747fSPaolo Bonzini int16_t msg_length;
40349ab747fSPaolo Bonzini } rda;
40449ab747fSPaolo Bonzini rda.rbadr = cpu_to_le32((rmd->rbadr & 0xffffff) |
40549ab747fSPaolo Bonzini ((rmd->status & 0xff00) << 16));
40649ab747fSPaolo Bonzini rda.buf_length = cpu_to_le16(rmd->buf_length);
40749ab747fSPaolo Bonzini rda.msg_length = cpu_to_le16(rmd->msg_length);
40849ab747fSPaolo Bonzini s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
40949ab747fSPaolo Bonzini } else {
41049ab747fSPaolo Bonzini struct {
41149ab747fSPaolo Bonzini uint32_t rbadr;
41249ab747fSPaolo Bonzini int16_t buf_length;
41349ab747fSPaolo Bonzini int16_t status;
41449ab747fSPaolo Bonzini uint32_t msg_length;
41549ab747fSPaolo Bonzini uint32_t res;
41649ab747fSPaolo Bonzini } rda;
41749ab747fSPaolo Bonzini rda.rbadr = cpu_to_le32(rmd->rbadr);
41849ab747fSPaolo Bonzini rda.buf_length = cpu_to_le16(rmd->buf_length);
41949ab747fSPaolo Bonzini rda.status = cpu_to_le16(rmd->status);
42049ab747fSPaolo Bonzini rda.msg_length = cpu_to_le32(rmd->msg_length);
42149ab747fSPaolo Bonzini rda.res = cpu_to_le32(rmd->res);
42249ab747fSPaolo Bonzini if (BCR_SWSTYLE(s) == 3) {
42349ab747fSPaolo Bonzini uint32_t tmp = rda.rbadr;
42449ab747fSPaolo Bonzini rda.rbadr = rda.msg_length;
42549ab747fSPaolo Bonzini rda.msg_length = tmp;
42649ab747fSPaolo Bonzini }
42749ab747fSPaolo Bonzini s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
42849ab747fSPaolo Bonzini }
42949ab747fSPaolo Bonzini }
43049ab747fSPaolo Bonzini
43149ab747fSPaolo Bonzini
43249ab747fSPaolo Bonzini #define TMDLOAD(TMD,ADDR) pcnet_tmd_load(s,TMD,ADDR)
43349ab747fSPaolo Bonzini
43449ab747fSPaolo Bonzini #define TMDSTORE(TMD,ADDR) pcnet_tmd_store(s,TMD,ADDR)
43549ab747fSPaolo Bonzini
43649ab747fSPaolo Bonzini #define RMDLOAD(RMD,ADDR) pcnet_rmd_load(s,RMD,ADDR)
43749ab747fSPaolo Bonzini
43849ab747fSPaolo Bonzini #define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR)
43949ab747fSPaolo Bonzini
44049ab747fSPaolo Bonzini #if 1
44149ab747fSPaolo Bonzini
44249ab747fSPaolo Bonzini #define CHECK_RMD(ADDR,RES) do { \
44349ab747fSPaolo Bonzini struct pcnet_RMD rmd; \
44449ab747fSPaolo Bonzini RMDLOAD(&rmd,(ADDR)); \
44549ab747fSPaolo Bonzini (RES) |= (GET_FIELD(rmd.buf_length, RMDL, ONES) != 15) \
44649ab747fSPaolo Bonzini || (GET_FIELD(rmd.msg_length, RMDM, ZEROS) != 0); \
44749ab747fSPaolo Bonzini } while (0)
44849ab747fSPaolo Bonzini
44949ab747fSPaolo Bonzini #define CHECK_TMD(ADDR,RES) do { \
45049ab747fSPaolo Bonzini struct pcnet_TMD tmd; \
45149ab747fSPaolo Bonzini TMDLOAD(&tmd,(ADDR)); \
45249ab747fSPaolo Bonzini (RES) |= (GET_FIELD(tmd.length, TMDL, ONES) != 15); \
45349ab747fSPaolo Bonzini } while (0)
45449ab747fSPaolo Bonzini
45549ab747fSPaolo Bonzini #else
45649ab747fSPaolo Bonzini
45749ab747fSPaolo Bonzini #define CHECK_RMD(ADDR,RES) do { \
45849ab747fSPaolo Bonzini switch (BCR_SWSTYLE(s)) { \
45949ab747fSPaolo Bonzini case 0x00: \
4601b4c0a04SEric Blake { \
46149ab747fSPaolo Bonzini uint16_t rda[4]; \
46249ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, (ADDR), \
46349ab747fSPaolo Bonzini (void *)&rda[0], sizeof(rda), 0); \
46449ab747fSPaolo Bonzini (RES) |= (rda[2] & 0xf000)!=0xf000; \
46549ab747fSPaolo Bonzini (RES) |= (rda[3] & 0xf000)!=0x0000; \
4661b4c0a04SEric Blake } \
46749ab747fSPaolo Bonzini break; \
46849ab747fSPaolo Bonzini case 0x01: \
46949ab747fSPaolo Bonzini case 0x02: \
4701b4c0a04SEric Blake { \
47149ab747fSPaolo Bonzini uint32_t rda[4]; \
47249ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, (ADDR), \
47349ab747fSPaolo Bonzini (void *)&rda[0], sizeof(rda), 0); \
47449ab747fSPaolo Bonzini (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
47549ab747fSPaolo Bonzini (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \
4761b4c0a04SEric Blake } \
47749ab747fSPaolo Bonzini break; \
47849ab747fSPaolo Bonzini case 0x03: \
4791b4c0a04SEric Blake { \
48049ab747fSPaolo Bonzini uint32_t rda[4]; \
48149ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, (ADDR), \
48249ab747fSPaolo Bonzini (void *)&rda[0], sizeof(rda), 0); \
48349ab747fSPaolo Bonzini (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \
48449ab747fSPaolo Bonzini (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
4851b4c0a04SEric Blake } \
48649ab747fSPaolo Bonzini break; \
48749ab747fSPaolo Bonzini } \
48849ab747fSPaolo Bonzini } while (0)
48949ab747fSPaolo Bonzini
49049ab747fSPaolo Bonzini #define CHECK_TMD(ADDR,RES) do { \
49149ab747fSPaolo Bonzini switch (BCR_SWSTYLE(s)) { \
49249ab747fSPaolo Bonzini case 0x00: \
4931b4c0a04SEric Blake { \
49449ab747fSPaolo Bonzini uint16_t xda[4]; \
49549ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, (ADDR), \
49649ab747fSPaolo Bonzini (void *)&xda[0], sizeof(xda), 0); \
49749ab747fSPaolo Bonzini (RES) |= (xda[2] & 0xf000)!=0xf000; \
4981b4c0a04SEric Blake } \
49949ab747fSPaolo Bonzini break; \
50049ab747fSPaolo Bonzini case 0x01: \
50149ab747fSPaolo Bonzini case 0x02: \
50249ab747fSPaolo Bonzini case 0x03: \
5031b4c0a04SEric Blake { \
50449ab747fSPaolo Bonzini uint32_t xda[4]; \
50549ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, (ADDR), \
50649ab747fSPaolo Bonzini (void *)&xda[0], sizeof(xda), 0); \
50749ab747fSPaolo Bonzini (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \
5081b4c0a04SEric Blake } \
50949ab747fSPaolo Bonzini break; \
51049ab747fSPaolo Bonzini } \
51149ab747fSPaolo Bonzini } while (0)
51249ab747fSPaolo Bonzini
51349ab747fSPaolo Bonzini #endif
51449ab747fSPaolo Bonzini
51549ab747fSPaolo Bonzini #define PRINT_PKTHDR(BUF) do { \
51649ab747fSPaolo Bonzini struct qemu_ether_header *hdr = (void *)(BUF); \
51749ab747fSPaolo Bonzini printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \
51849ab747fSPaolo Bonzini "shost=%02x:%02x:%02x:%02x:%02x:%02x, " \
51949ab747fSPaolo Bonzini "type=0x%04x\n", \
52049ab747fSPaolo Bonzini hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
52149ab747fSPaolo Bonzini hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
52249ab747fSPaolo Bonzini hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
52349ab747fSPaolo Bonzini hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
52449ab747fSPaolo Bonzini be16_to_cpu(hdr->ether_type)); \
52549ab747fSPaolo Bonzini } while (0)
52649ab747fSPaolo Bonzini
52749ab747fSPaolo Bonzini #define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
52849ab747fSPaolo Bonzini
52949ab747fSPaolo Bonzini /* generated using the AUTODIN II polynomial
53049ab747fSPaolo Bonzini * x^32 + x^26 + x^23 + x^22 + x^16 +
53149ab747fSPaolo Bonzini * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
53249ab747fSPaolo Bonzini */
53349ab747fSPaolo Bonzini static const uint32_t crctab[256] = {
53449ab747fSPaolo Bonzini 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
53549ab747fSPaolo Bonzini 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
53649ab747fSPaolo Bonzini 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
53749ab747fSPaolo Bonzini 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
53849ab747fSPaolo Bonzini 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
53949ab747fSPaolo Bonzini 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
54049ab747fSPaolo Bonzini 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
54149ab747fSPaolo Bonzini 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
54249ab747fSPaolo Bonzini 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
54349ab747fSPaolo Bonzini 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
54449ab747fSPaolo Bonzini 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
54549ab747fSPaolo Bonzini 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
54649ab747fSPaolo Bonzini 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
54749ab747fSPaolo Bonzini 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
54849ab747fSPaolo Bonzini 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
54949ab747fSPaolo Bonzini 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
55049ab747fSPaolo Bonzini 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
55149ab747fSPaolo Bonzini 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
55249ab747fSPaolo Bonzini 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
55349ab747fSPaolo Bonzini 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
55449ab747fSPaolo Bonzini 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
55549ab747fSPaolo Bonzini 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
55649ab747fSPaolo Bonzini 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
55749ab747fSPaolo Bonzini 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
55849ab747fSPaolo Bonzini 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
55949ab747fSPaolo Bonzini 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
56049ab747fSPaolo Bonzini 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
56149ab747fSPaolo Bonzini 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
56249ab747fSPaolo Bonzini 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
56349ab747fSPaolo Bonzini 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
56449ab747fSPaolo Bonzini 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
56549ab747fSPaolo Bonzini 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
56649ab747fSPaolo Bonzini 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
56749ab747fSPaolo Bonzini 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
56849ab747fSPaolo Bonzini 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
56949ab747fSPaolo Bonzini 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
57049ab747fSPaolo Bonzini 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
57149ab747fSPaolo Bonzini 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
57249ab747fSPaolo Bonzini 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
57349ab747fSPaolo Bonzini 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
57449ab747fSPaolo Bonzini 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
57549ab747fSPaolo Bonzini 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
57649ab747fSPaolo Bonzini 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
57749ab747fSPaolo Bonzini 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
57849ab747fSPaolo Bonzini 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
57949ab747fSPaolo Bonzini 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
58049ab747fSPaolo Bonzini 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
58149ab747fSPaolo Bonzini 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
58249ab747fSPaolo Bonzini 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
58349ab747fSPaolo Bonzini 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
58449ab747fSPaolo Bonzini 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
58549ab747fSPaolo Bonzini 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
58649ab747fSPaolo Bonzini 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
58749ab747fSPaolo Bonzini 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
58849ab747fSPaolo Bonzini 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
58949ab747fSPaolo Bonzini 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
59049ab747fSPaolo Bonzini 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
59149ab747fSPaolo Bonzini 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
59249ab747fSPaolo Bonzini 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
59349ab747fSPaolo Bonzini 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
59449ab747fSPaolo Bonzini 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
59549ab747fSPaolo Bonzini 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
59649ab747fSPaolo Bonzini 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
59749ab747fSPaolo Bonzini 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
59849ab747fSPaolo Bonzini };
59949ab747fSPaolo Bonzini
padr_match(PCNetState * s,const uint8_t * buf,int size)60049ab747fSPaolo Bonzini static inline int padr_match(PCNetState *s, const uint8_t *buf, int size)
60149ab747fSPaolo Bonzini {
60249ab747fSPaolo Bonzini struct qemu_ether_header *hdr = (void *)buf;
60349ab747fSPaolo Bonzini uint8_t padr[6] = {
60449ab747fSPaolo Bonzini s->csr[12] & 0xff, s->csr[12] >> 8,
60549ab747fSPaolo Bonzini s->csr[13] & 0xff, s->csr[13] >> 8,
60649ab747fSPaolo Bonzini s->csr[14] & 0xff, s->csr[14] >> 8
60749ab747fSPaolo Bonzini };
60849ab747fSPaolo Bonzini int result = (!CSR_DRCVPA(s)) && !memcmp(hdr->ether_dhost, padr, 6);
60949ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_MATCH
61049ab747fSPaolo Bonzini printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, "
61149ab747fSPaolo Bonzini "padr=%02x:%02x:%02x:%02x:%02x:%02x\n",
61249ab747fSPaolo Bonzini hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
61349ab747fSPaolo Bonzini hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
61449ab747fSPaolo Bonzini padr[0],padr[1],padr[2],padr[3],padr[4],padr[5]);
61549ab747fSPaolo Bonzini printf("padr_match result=%d\n", result);
61649ab747fSPaolo Bonzini #endif
61749ab747fSPaolo Bonzini return result;
61849ab747fSPaolo Bonzini }
61949ab747fSPaolo Bonzini
padr_bcast(PCNetState * s,const uint8_t * buf,int size)62049ab747fSPaolo Bonzini static inline int padr_bcast(PCNetState *s, const uint8_t *buf, int size)
62149ab747fSPaolo Bonzini {
62249ab747fSPaolo Bonzini static const uint8_t BCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
62349ab747fSPaolo Bonzini struct qemu_ether_header *hdr = (void *)buf;
62449ab747fSPaolo Bonzini int result = !CSR_DRCVBC(s) && !memcmp(hdr->ether_dhost, BCAST, 6);
62549ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_MATCH
62649ab747fSPaolo Bonzini printf("padr_bcast result=%d\n", result);
62749ab747fSPaolo Bonzini #endif
62849ab747fSPaolo Bonzini return result;
62949ab747fSPaolo Bonzini }
63049ab747fSPaolo Bonzini
ladr_match(PCNetState * s,const uint8_t * buf,int size)63149ab747fSPaolo Bonzini static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size)
63249ab747fSPaolo Bonzini {
63349ab747fSPaolo Bonzini struct qemu_ether_header *hdr = (void *)buf;
63449ab747fSPaolo Bonzini if ((*(hdr->ether_dhost)&0x01) &&
635*6a5287ceSNick Briggs (s->csr[8] | s->csr[9] | s->csr[10] | s->csr[11]) != 0) {
63649ab747fSPaolo Bonzini uint8_t ladr[8] = {
63749ab747fSPaolo Bonzini s->csr[8] & 0xff, s->csr[8] >> 8,
63849ab747fSPaolo Bonzini s->csr[9] & 0xff, s->csr[9] >> 8,
63949ab747fSPaolo Bonzini s->csr[10] & 0xff, s->csr[10] >> 8,
64049ab747fSPaolo Bonzini s->csr[11] & 0xff, s->csr[11] >> 8
64149ab747fSPaolo Bonzini };
642cbbeca91SMark Cave-Ayland int index = net_crc32_le(hdr->ether_dhost, ETH_ALEN) >> 26;
64349ab747fSPaolo Bonzini return !!(ladr[index >> 3] & (1 << (index & 7)));
64449ab747fSPaolo Bonzini }
64549ab747fSPaolo Bonzini return 0;
64649ab747fSPaolo Bonzini }
64749ab747fSPaolo Bonzini
pcnet_rdra_addr(PCNetState * s,int idx)64849ab747fSPaolo Bonzini static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx)
64949ab747fSPaolo Bonzini {
65067aa4493SPrasad J Pandit while (idx < 1) {
65167aa4493SPrasad J Pandit idx += CSR_RCVRL(s);
65267aa4493SPrasad J Pandit }
65349ab747fSPaolo Bonzini return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8));
65449ab747fSPaolo Bonzini }
65549ab747fSPaolo Bonzini
pcnet_get_next_poll_time(PCNetState * s,int64_t current_time)65649ab747fSPaolo Bonzini static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
65749ab747fSPaolo Bonzini {
65849ab747fSPaolo Bonzini int64_t next_time = current_time +
659c6acbe86SLaurent Vivier (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30;
66067aa4493SPrasad J Pandit
66167aa4493SPrasad J Pandit if (next_time <= current_time) {
66249ab747fSPaolo Bonzini next_time = current_time + 1;
66367aa4493SPrasad J Pandit }
66449ab747fSPaolo Bonzini return next_time;
66549ab747fSPaolo Bonzini }
66649ab747fSPaolo Bonzini
66749ab747fSPaolo Bonzini static void pcnet_poll(PCNetState *s);
66849ab747fSPaolo Bonzini static void pcnet_poll_timer(void *opaque);
66949ab747fSPaolo Bonzini
67049ab747fSPaolo Bonzini static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap);
67149ab747fSPaolo Bonzini static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value);
67249ab747fSPaolo Bonzini static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val);
67349ab747fSPaolo Bonzini
pcnet_s_reset(PCNetState * s)67449ab747fSPaolo Bonzini static void pcnet_s_reset(PCNetState *s)
67549ab747fSPaolo Bonzini {
67615cae340SDon Koch trace_pcnet_s_reset(s);
67749ab747fSPaolo Bonzini
67849ab747fSPaolo Bonzini s->rdra = 0;
67949ab747fSPaolo Bonzini s->tdra = 0;
68049ab747fSPaolo Bonzini s->rap = 0;
68149ab747fSPaolo Bonzini
68249ab747fSPaolo Bonzini s->bcr[BCR_BSBC] &= ~0x0080;
68349ab747fSPaolo Bonzini
68449ab747fSPaolo Bonzini s->csr[0] = 0x0004;
68549ab747fSPaolo Bonzini s->csr[3] = 0x0000;
68649ab747fSPaolo Bonzini s->csr[4] = 0x0115;
68749ab747fSPaolo Bonzini s->csr[5] = 0x0000;
68849ab747fSPaolo Bonzini s->csr[6] = 0x0000;
68949ab747fSPaolo Bonzini s->csr[8] = 0;
69049ab747fSPaolo Bonzini s->csr[9] = 0;
69149ab747fSPaolo Bonzini s->csr[10] = 0;
69249ab747fSPaolo Bonzini s->csr[11] = 0;
69349ab747fSPaolo Bonzini s->csr[12] = le16_to_cpu(((uint16_t *)&s->prom[0])[0]);
69449ab747fSPaolo Bonzini s->csr[13] = le16_to_cpu(((uint16_t *)&s->prom[0])[1]);
69549ab747fSPaolo Bonzini s->csr[14] = le16_to_cpu(((uint16_t *)&s->prom[0])[2]);
69649ab747fSPaolo Bonzini s->csr[15] &= 0x21c4;
69749ab747fSPaolo Bonzini s->csr[72] = 1;
69849ab747fSPaolo Bonzini s->csr[74] = 1;
69949ab747fSPaolo Bonzini s->csr[76] = 1;
70049ab747fSPaolo Bonzini s->csr[78] = 1;
70149ab747fSPaolo Bonzini s->csr[80] = 0x1410;
70249ab747fSPaolo Bonzini s->csr[88] = 0x1003;
70349ab747fSPaolo Bonzini s->csr[89] = 0x0262;
70449ab747fSPaolo Bonzini s->csr[94] = 0x0000;
70549ab747fSPaolo Bonzini s->csr[100] = 0x0200;
70649ab747fSPaolo Bonzini s->csr[103] = 0x0105;
70749ab747fSPaolo Bonzini s->csr[112] = 0x0000;
70849ab747fSPaolo Bonzini s->csr[114] = 0x0000;
70949ab747fSPaolo Bonzini s->csr[122] = 0x0000;
71049ab747fSPaolo Bonzini s->csr[124] = 0x0000;
71149ab747fSPaolo Bonzini
71249ab747fSPaolo Bonzini s->tx_busy = 0;
71349ab747fSPaolo Bonzini }
71449ab747fSPaolo Bonzini
pcnet_update_irq(PCNetState * s)71549ab747fSPaolo Bonzini static void pcnet_update_irq(PCNetState *s)
71649ab747fSPaolo Bonzini {
71749ab747fSPaolo Bonzini int isr = 0;
71849ab747fSPaolo Bonzini s->csr[0] &= ~0x0080;
71949ab747fSPaolo Bonzini
72049ab747fSPaolo Bonzini #if 1
72149ab747fSPaolo Bonzini if (((s->csr[0] & ~s->csr[3]) & 0x5f00) ||
72249ab747fSPaolo Bonzini (((s->csr[4]>>1) & ~s->csr[4]) & 0x0115) ||
72349ab747fSPaolo Bonzini (((s->csr[5]>>1) & s->csr[5]) & 0x0048))
72449ab747fSPaolo Bonzini #else
72549ab747fSPaolo Bonzini if ((!(s->csr[3] & 0x4000) && !!(s->csr[0] & 0x4000)) /* BABL */ ||
72649ab747fSPaolo Bonzini (!(s->csr[3] & 0x1000) && !!(s->csr[0] & 0x1000)) /* MISS */ ||
72749ab747fSPaolo Bonzini (!(s->csr[3] & 0x0100) && !!(s->csr[0] & 0x0100)) /* IDON */ ||
72849ab747fSPaolo Bonzini (!(s->csr[3] & 0x0200) && !!(s->csr[0] & 0x0200)) /* TINT */ ||
72949ab747fSPaolo Bonzini (!(s->csr[3] & 0x0400) && !!(s->csr[0] & 0x0400)) /* RINT */ ||
73049ab747fSPaolo Bonzini (!(s->csr[3] & 0x0800) && !!(s->csr[0] & 0x0800)) /* MERR */ ||
73149ab747fSPaolo Bonzini (!(s->csr[4] & 0x0001) && !!(s->csr[4] & 0x0002)) /* JAB */ ||
73249ab747fSPaolo Bonzini (!(s->csr[4] & 0x0004) && !!(s->csr[4] & 0x0008)) /* TXSTRT */ ||
73349ab747fSPaolo Bonzini (!(s->csr[4] & 0x0010) && !!(s->csr[4] & 0x0020)) /* RCVO */ ||
73449ab747fSPaolo Bonzini (!(s->csr[4] & 0x0100) && !!(s->csr[4] & 0x0200)) /* MFCO */ ||
73549ab747fSPaolo Bonzini (!!(s->csr[5] & 0x0040) && !!(s->csr[5] & 0x0080)) /* EXDINT */ ||
73649ab747fSPaolo Bonzini (!!(s->csr[5] & 0x0008) && !!(s->csr[5] & 0x0010)) /* MPINT */)
73749ab747fSPaolo Bonzini #endif
73849ab747fSPaolo Bonzini {
73949ab747fSPaolo Bonzini
74049ab747fSPaolo Bonzini isr = CSR_INEA(s);
74149ab747fSPaolo Bonzini s->csr[0] |= 0x0080;
74249ab747fSPaolo Bonzini }
74349ab747fSPaolo Bonzini
74449ab747fSPaolo Bonzini if (!!(s->csr[4] & 0x0080) && CSR_INEA(s)) { /* UINT */
74549ab747fSPaolo Bonzini s->csr[4] &= ~0x0080;
74649ab747fSPaolo Bonzini s->csr[4] |= 0x0040;
74749ab747fSPaolo Bonzini s->csr[0] |= 0x0080;
74849ab747fSPaolo Bonzini isr = 1;
74915cae340SDon Koch trace_pcnet_user_int(s);
75049ab747fSPaolo Bonzini }
75149ab747fSPaolo Bonzini
75249ab747fSPaolo Bonzini #if 1
75349ab747fSPaolo Bonzini if (((s->csr[5]>>1) & s->csr[5]) & 0x0500)
75449ab747fSPaolo Bonzini #else
75549ab747fSPaolo Bonzini if ((!!(s->csr[5] & 0x0400) && !!(s->csr[5] & 0x0800)) /* SINT */ ||
75649ab747fSPaolo Bonzini (!!(s->csr[5] & 0x0100) && !!(s->csr[5] & 0x0200)) /* SLPINT */ )
75749ab747fSPaolo Bonzini #endif
75849ab747fSPaolo Bonzini {
75949ab747fSPaolo Bonzini isr = 1;
76049ab747fSPaolo Bonzini s->csr[0] |= 0x0080;
76149ab747fSPaolo Bonzini }
76249ab747fSPaolo Bonzini
76349ab747fSPaolo Bonzini if (isr != s->isr) {
76415cae340SDon Koch trace_pcnet_isr_change(s, isr, s->isr);
76549ab747fSPaolo Bonzini }
76649ab747fSPaolo Bonzini qemu_set_irq(s->irq, isr);
76749ab747fSPaolo Bonzini s->isr = isr;
76849ab747fSPaolo Bonzini }
76949ab747fSPaolo Bonzini
pcnet_init(PCNetState * s)77049ab747fSPaolo Bonzini static void pcnet_init(PCNetState *s)
77149ab747fSPaolo Bonzini {
77249ab747fSPaolo Bonzini int rlen, tlen;
77349ab747fSPaolo Bonzini uint16_t padr[3], ladrf[4], mode;
77449ab747fSPaolo Bonzini uint32_t rdra, tdra;
77549ab747fSPaolo Bonzini
77615cae340SDon Koch trace_pcnet_init(s, PHYSADDR(s, CSR_IADR(s)));
77749ab747fSPaolo Bonzini
77849ab747fSPaolo Bonzini if (BCR_SSIZE32(s)) {
77949ab747fSPaolo Bonzini struct pcnet_initblk32 initblk;
78049ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
78149ab747fSPaolo Bonzini (uint8_t *)&initblk, sizeof(initblk), 0);
78249ab747fSPaolo Bonzini mode = le16_to_cpu(initblk.mode);
78349ab747fSPaolo Bonzini rlen = initblk.rlen >> 4;
78449ab747fSPaolo Bonzini tlen = initblk.tlen >> 4;
78549ab747fSPaolo Bonzini ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
78649ab747fSPaolo Bonzini ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
78749ab747fSPaolo Bonzini ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
78849ab747fSPaolo Bonzini ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
78949ab747fSPaolo Bonzini padr[0] = le16_to_cpu(initblk.padr[0]);
79049ab747fSPaolo Bonzini padr[1] = le16_to_cpu(initblk.padr[1]);
79149ab747fSPaolo Bonzini padr[2] = le16_to_cpu(initblk.padr[2]);
79249ab747fSPaolo Bonzini rdra = le32_to_cpu(initblk.rdra);
79349ab747fSPaolo Bonzini tdra = le32_to_cpu(initblk.tdra);
79449ab747fSPaolo Bonzini } else {
79549ab747fSPaolo Bonzini struct pcnet_initblk16 initblk;
79649ab747fSPaolo Bonzini s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
79749ab747fSPaolo Bonzini (uint8_t *)&initblk, sizeof(initblk), 0);
79849ab747fSPaolo Bonzini mode = le16_to_cpu(initblk.mode);
79949ab747fSPaolo Bonzini ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
80049ab747fSPaolo Bonzini ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
80149ab747fSPaolo Bonzini ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
80249ab747fSPaolo Bonzini ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
80349ab747fSPaolo Bonzini padr[0] = le16_to_cpu(initblk.padr[0]);
80449ab747fSPaolo Bonzini padr[1] = le16_to_cpu(initblk.padr[1]);
80549ab747fSPaolo Bonzini padr[2] = le16_to_cpu(initblk.padr[2]);
80649ab747fSPaolo Bonzini rdra = le32_to_cpu(initblk.rdra);
80749ab747fSPaolo Bonzini tdra = le32_to_cpu(initblk.tdra);
80849ab747fSPaolo Bonzini rlen = rdra >> 29;
80949ab747fSPaolo Bonzini tlen = tdra >> 29;
81049ab747fSPaolo Bonzini rdra &= 0x00ffffff;
81149ab747fSPaolo Bonzini tdra &= 0x00ffffff;
81249ab747fSPaolo Bonzini }
81349ab747fSPaolo Bonzini
81415cae340SDon Koch trace_pcnet_rlen_tlen(s, rlen, tlen);
81549ab747fSPaolo Bonzini
81649ab747fSPaolo Bonzini CSR_RCVRL(s) = (rlen < 9) ? (1 << rlen) : 512;
81749ab747fSPaolo Bonzini CSR_XMTRL(s) = (tlen < 9) ? (1 << tlen) : 512;
81849ab747fSPaolo Bonzini s->csr[ 6] = (tlen << 12) | (rlen << 8);
81949ab747fSPaolo Bonzini s->csr[15] = mode;
82049ab747fSPaolo Bonzini s->csr[ 8] = ladrf[0];
82149ab747fSPaolo Bonzini s->csr[ 9] = ladrf[1];
82249ab747fSPaolo Bonzini s->csr[10] = ladrf[2];
82349ab747fSPaolo Bonzini s->csr[11] = ladrf[3];
82449ab747fSPaolo Bonzini s->csr[12] = padr[0];
82549ab747fSPaolo Bonzini s->csr[13] = padr[1];
82649ab747fSPaolo Bonzini s->csr[14] = padr[2];
82749ab747fSPaolo Bonzini s->rdra = PHYSADDR(s, rdra);
82849ab747fSPaolo Bonzini s->tdra = PHYSADDR(s, tdra);
82949ab747fSPaolo Bonzini
83049ab747fSPaolo Bonzini CSR_RCVRC(s) = CSR_RCVRL(s);
83149ab747fSPaolo Bonzini CSR_XMTRC(s) = CSR_XMTRL(s);
83249ab747fSPaolo Bonzini
83315cae340SDon Koch trace_pcnet_ss32_rdra_tdra(s, BCR_SSIZE32(s),
83449ab747fSPaolo Bonzini s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s));
83549ab747fSPaolo Bonzini
83649ab747fSPaolo Bonzini s->csr[0] |= 0x0101;
83749ab747fSPaolo Bonzini s->csr[0] &= ~0x0004; /* clear STOP bit */
838ee76c1f8SJan Kiszka
839ee76c1f8SJan Kiszka qemu_flush_queued_packets(qemu_get_queue(s->nic));
84049ab747fSPaolo Bonzini }
84149ab747fSPaolo Bonzini
pcnet_start(PCNetState * s)84249ab747fSPaolo Bonzini static void pcnet_start(PCNetState *s)
84349ab747fSPaolo Bonzini {
84449ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
84549ab747fSPaolo Bonzini printf("pcnet_start\n");
84649ab747fSPaolo Bonzini #endif
84749ab747fSPaolo Bonzini
84867aa4493SPrasad J Pandit if (!CSR_DTX(s)) {
84949ab747fSPaolo Bonzini s->csr[0] |= 0x0010; /* set TXON */
85067aa4493SPrasad J Pandit }
85167aa4493SPrasad J Pandit if (!CSR_DRX(s)) {
85249ab747fSPaolo Bonzini s->csr[0] |= 0x0020; /* set RXON */
85367aa4493SPrasad J Pandit }
85449ab747fSPaolo Bonzini s->csr[0] &= ~0x0004; /* clear STOP bit */
85549ab747fSPaolo Bonzini s->csr[0] |= 0x0002;
85649ab747fSPaolo Bonzini pcnet_poll_timer(s);
857ee76c1f8SJan Kiszka
858ee76c1f8SJan Kiszka qemu_flush_queued_packets(qemu_get_queue(s->nic));
85949ab747fSPaolo Bonzini }
86049ab747fSPaolo Bonzini
pcnet_stop(PCNetState * s)86149ab747fSPaolo Bonzini static void pcnet_stop(PCNetState *s)
86249ab747fSPaolo Bonzini {
86349ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
86449ab747fSPaolo Bonzini printf("pcnet_stop\n");
86549ab747fSPaolo Bonzini #endif
86649ab747fSPaolo Bonzini s->csr[0] &= ~0xffeb;
86749ab747fSPaolo Bonzini s->csr[0] |= 0x0014;
86849ab747fSPaolo Bonzini s->csr[4] &= ~0x02c2;
86949ab747fSPaolo Bonzini s->csr[5] &= ~0x0011;
87049ab747fSPaolo Bonzini pcnet_poll_timer(s);
87149ab747fSPaolo Bonzini }
87249ab747fSPaolo Bonzini
pcnet_rdte_poll(PCNetState * s)87349ab747fSPaolo Bonzini static void pcnet_rdte_poll(PCNetState *s)
87449ab747fSPaolo Bonzini {
87549ab747fSPaolo Bonzini s->csr[28] = s->csr[29] = 0;
87649ab747fSPaolo Bonzini if (s->rdra) {
87749ab747fSPaolo Bonzini int bad = 0;
87849ab747fSPaolo Bonzini #if 1
87949ab747fSPaolo Bonzini hwaddr crda = pcnet_rdra_addr(s, CSR_RCVRC(s));
88049ab747fSPaolo Bonzini hwaddr nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s));
88149ab747fSPaolo Bonzini hwaddr nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s));
88249ab747fSPaolo Bonzini #else
88349ab747fSPaolo Bonzini hwaddr crda = s->rdra +
88449ab747fSPaolo Bonzini (CSR_RCVRL(s) - CSR_RCVRC(s)) *
88549ab747fSPaolo Bonzini (BCR_SWSTYLE(s) ? 16 : 8 );
88649ab747fSPaolo Bonzini int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1;
88749ab747fSPaolo Bonzini hwaddr nrda = s->rdra +
88849ab747fSPaolo Bonzini (CSR_RCVRL(s) - nrdc) *
88949ab747fSPaolo Bonzini (BCR_SWSTYLE(s) ? 16 : 8 );
89049ab747fSPaolo Bonzini int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1;
89149ab747fSPaolo Bonzini hwaddr nnrd = s->rdra +
89249ab747fSPaolo Bonzini (CSR_RCVRL(s) - nnrc) *
89349ab747fSPaolo Bonzini (BCR_SWSTYLE(s) ? 16 : 8 );
89449ab747fSPaolo Bonzini #endif
89549ab747fSPaolo Bonzini
89649ab747fSPaolo Bonzini CHECK_RMD(crda, bad);
89749ab747fSPaolo Bonzini if (!bad) {
89849ab747fSPaolo Bonzini CHECK_RMD(nrda, bad);
89949ab747fSPaolo Bonzini if (bad || (nrda == crda)) nrda = 0;
90049ab747fSPaolo Bonzini CHECK_RMD(nnrd, bad);
90149ab747fSPaolo Bonzini if (bad || (nnrd == crda)) nnrd = 0;
90249ab747fSPaolo Bonzini
90349ab747fSPaolo Bonzini s->csr[28] = crda & 0xffff;
90449ab747fSPaolo Bonzini s->csr[29] = crda >> 16;
90549ab747fSPaolo Bonzini s->csr[26] = nrda & 0xffff;
90649ab747fSPaolo Bonzini s->csr[27] = nrda >> 16;
90749ab747fSPaolo Bonzini s->csr[36] = nnrd & 0xffff;
90849ab747fSPaolo Bonzini s->csr[37] = nnrd >> 16;
90949ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
91049ab747fSPaolo Bonzini if (bad) {
911883f2c59SPhilippe Mathieu-Daudé printf("pcnet: BAD RMD RECORDS AFTER 0x" HWADDR_FMT_plx "\n",
91249ab747fSPaolo Bonzini crda);
91349ab747fSPaolo Bonzini }
91449ab747fSPaolo Bonzini } else {
915883f2c59SPhilippe Mathieu-Daudé printf("pcnet: BAD RMD RDA=0x" HWADDR_FMT_plx "\n", crda);
91649ab747fSPaolo Bonzini #endif
91749ab747fSPaolo Bonzini }
91849ab747fSPaolo Bonzini }
91949ab747fSPaolo Bonzini
92049ab747fSPaolo Bonzini if (CSR_CRDA(s)) {
92149ab747fSPaolo Bonzini struct pcnet_RMD rmd;
92249ab747fSPaolo Bonzini RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s)));
92349ab747fSPaolo Bonzini CSR_CRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
92449ab747fSPaolo Bonzini CSR_CRST(s) = rmd.status;
92549ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_RMD_X
92649ab747fSPaolo Bonzini printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMDL=0x%04x RMDS=0x%04x RMDM=0x%08x\n",
92749ab747fSPaolo Bonzini PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s),
92849ab747fSPaolo Bonzini rmd.buf_length, rmd.status, rmd.msg_length);
92949ab747fSPaolo Bonzini PRINT_RMD(&rmd);
93049ab747fSPaolo Bonzini #endif
93149ab747fSPaolo Bonzini } else {
93249ab747fSPaolo Bonzini CSR_CRBC(s) = CSR_CRST(s) = 0;
93349ab747fSPaolo Bonzini }
93449ab747fSPaolo Bonzini
93549ab747fSPaolo Bonzini if (CSR_NRDA(s)) {
93649ab747fSPaolo Bonzini struct pcnet_RMD rmd;
93749ab747fSPaolo Bonzini RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s)));
93849ab747fSPaolo Bonzini CSR_NRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
93949ab747fSPaolo Bonzini CSR_NRST(s) = rmd.status;
94049ab747fSPaolo Bonzini } else {
94149ab747fSPaolo Bonzini CSR_NRBC(s) = CSR_NRST(s) = 0;
94249ab747fSPaolo Bonzini }
94349ab747fSPaolo Bonzini
94449ab747fSPaolo Bonzini }
94549ab747fSPaolo Bonzini
pcnet_tdte_poll(PCNetState * s)94649ab747fSPaolo Bonzini static int pcnet_tdte_poll(PCNetState *s)
94749ab747fSPaolo Bonzini {
94849ab747fSPaolo Bonzini s->csr[34] = s->csr[35] = 0;
94949ab747fSPaolo Bonzini if (s->tdra) {
95049ab747fSPaolo Bonzini hwaddr cxda = s->tdra +
95149ab747fSPaolo Bonzini (CSR_XMTRL(s) - CSR_XMTRC(s)) *
95249ab747fSPaolo Bonzini (BCR_SWSTYLE(s) ? 16 : 8);
95349ab747fSPaolo Bonzini int bad = 0;
95449ab747fSPaolo Bonzini CHECK_TMD(cxda, bad);
95549ab747fSPaolo Bonzini if (!bad) {
95649ab747fSPaolo Bonzini if (CSR_CXDA(s) != cxda) {
95749ab747fSPaolo Bonzini s->csr[60] = s->csr[34];
95849ab747fSPaolo Bonzini s->csr[61] = s->csr[35];
95949ab747fSPaolo Bonzini s->csr[62] = CSR_CXBC(s);
96049ab747fSPaolo Bonzini s->csr[63] = CSR_CXST(s);
96149ab747fSPaolo Bonzini }
96249ab747fSPaolo Bonzini s->csr[34] = cxda & 0xffff;
96349ab747fSPaolo Bonzini s->csr[35] = cxda >> 16;
96449ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_X
96549ab747fSPaolo Bonzini printf("pcnet: BAD TMD XDA=0x%08x\n", cxda);
96649ab747fSPaolo Bonzini #endif
96749ab747fSPaolo Bonzini }
96849ab747fSPaolo Bonzini }
96949ab747fSPaolo Bonzini
97049ab747fSPaolo Bonzini if (CSR_CXDA(s)) {
97149ab747fSPaolo Bonzini struct pcnet_TMD tmd;
97249ab747fSPaolo Bonzini
97349ab747fSPaolo Bonzini TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
97449ab747fSPaolo Bonzini
97549ab747fSPaolo Bonzini CSR_CXBC(s) = GET_FIELD(tmd.length, TMDL, BCNT);
97649ab747fSPaolo Bonzini CSR_CXST(s) = tmd.status;
97749ab747fSPaolo Bonzini } else {
97849ab747fSPaolo Bonzini CSR_CXBC(s) = CSR_CXST(s) = 0;
97949ab747fSPaolo Bonzini }
98049ab747fSPaolo Bonzini
98149ab747fSPaolo Bonzini return !!(CSR_CXST(s) & 0x8000);
98249ab747fSPaolo Bonzini }
98349ab747fSPaolo Bonzini
98449ab747fSPaolo Bonzini #define MIN_BUF_SIZE 60
98549ab747fSPaolo Bonzini
pcnet_receive(NetClientState * nc,const uint8_t * buf,size_t size_)98649ab747fSPaolo Bonzini ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
98749ab747fSPaolo Bonzini {
98849ab747fSPaolo Bonzini PCNetState *s = qemu_get_nic_opaque(nc);
98949ab747fSPaolo Bonzini int is_padr = 0, is_bcast = 0, is_ladr = 0;
99049ab747fSPaolo Bonzini int remaining;
99149ab747fSPaolo Bonzini int crc_err = 0;
992b1d80d12SJason Wang size_t size = size_;
99349ab747fSPaolo Bonzini
99449ab747fSPaolo Bonzini if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
99549ab747fSPaolo Bonzini (CSR_LOOP(s) && !s->looptest)) {
99649ab747fSPaolo Bonzini return -1;
99749ab747fSPaolo Bonzini }
99849ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
999b1d80d12SJason Wang printf("pcnet_receive size=%zu\n", size);
100049ab747fSPaolo Bonzini #endif
100149ab747fSPaolo Bonzini
100249ab747fSPaolo Bonzini if (CSR_PROM(s)
100349ab747fSPaolo Bonzini || (is_padr=padr_match(s, buf, size))
100449ab747fSPaolo Bonzini || (is_bcast=padr_bcast(s, buf, size))
100549ab747fSPaolo Bonzini || (is_ladr=ladr_match(s, buf, size))) {
100649ab747fSPaolo Bonzini
100749ab747fSPaolo Bonzini pcnet_rdte_poll(s);
100849ab747fSPaolo Bonzini
100949ab747fSPaolo Bonzini if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
101049ab747fSPaolo Bonzini struct pcnet_RMD rmd;
101149ab747fSPaolo Bonzini int rcvrc = CSR_RCVRC(s)-1,i;
101249ab747fSPaolo Bonzini hwaddr nrda;
101349ab747fSPaolo Bonzini for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
101449ab747fSPaolo Bonzini if (rcvrc <= 1)
101549ab747fSPaolo Bonzini rcvrc = CSR_RCVRL(s);
101649ab747fSPaolo Bonzini nrda = s->rdra +
101749ab747fSPaolo Bonzini (CSR_RCVRL(s) - rcvrc) *
101849ab747fSPaolo Bonzini (BCR_SWSTYLE(s) ? 16 : 8 );
101949ab747fSPaolo Bonzini RMDLOAD(&rmd, nrda);
102049ab747fSPaolo Bonzini if (GET_FIELD(rmd.status, RMDS, OWN)) {
102149ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_RMD
102249ab747fSPaolo Bonzini printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
102349ab747fSPaolo Bonzini rcvrc, CSR_RCVRC(s));
102449ab747fSPaolo Bonzini #endif
102549ab747fSPaolo Bonzini CSR_RCVRC(s) = rcvrc;
102649ab747fSPaolo Bonzini pcnet_rdte_poll(s);
102749ab747fSPaolo Bonzini break;
102849ab747fSPaolo Bonzini }
102949ab747fSPaolo Bonzini }
103049ab747fSPaolo Bonzini }
103149ab747fSPaolo Bonzini
103249ab747fSPaolo Bonzini if (!(CSR_CRST(s) & 0x8000)) {
103349ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_RMD
103449ab747fSPaolo Bonzini printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
103549ab747fSPaolo Bonzini #endif
103649ab747fSPaolo Bonzini s->csr[0] |= 0x1000; /* Set MISS flag */
103749ab747fSPaolo Bonzini CSR_MISSC(s)++;
103849ab747fSPaolo Bonzini } else {
103949ab747fSPaolo Bonzini uint8_t *src = s->buffer;
104049ab747fSPaolo Bonzini hwaddr crda = CSR_CRDA(s);
104149ab747fSPaolo Bonzini struct pcnet_RMD rmd;
104249ab747fSPaolo Bonzini int pktcount = 0;
104349ab747fSPaolo Bonzini
104449ab747fSPaolo Bonzini if (!s->looptest) {
10458b98a2f0SJason Wang if (size > 4092) {
10468b98a2f0SJason Wang #ifdef PCNET_DEBUG_RMD
10478b98a2f0SJason Wang fprintf(stderr, "pcnet: truncates rx packet.\n");
10488b98a2f0SJason Wang #endif
10498b98a2f0SJason Wang size = 4092;
10508b98a2f0SJason Wang }
105149ab747fSPaolo Bonzini memcpy(src, buf, size);
105249ab747fSPaolo Bonzini /* no need to compute the CRC */
105349ab747fSPaolo Bonzini src[size] = 0;
105449ab747fSPaolo Bonzini src[size + 1] = 0;
105549ab747fSPaolo Bonzini src[size + 2] = 0;
105649ab747fSPaolo Bonzini src[size + 3] = 0;
105749ab747fSPaolo Bonzini size += 4;
105849ab747fSPaolo Bonzini } else if (s->looptest == PCNET_LOOPTEST_CRC ||
105949ab747fSPaolo Bonzini !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
106049ab747fSPaolo Bonzini uint32_t fcs = ~0;
106149ab747fSPaolo Bonzini uint8_t *p = src;
106249ab747fSPaolo Bonzini
106349ab747fSPaolo Bonzini while (p != &src[size])
106449ab747fSPaolo Bonzini CRC(fcs, *p++);
106549ab747fSPaolo Bonzini *(uint32_t *)p = htonl(fcs);
106649ab747fSPaolo Bonzini size += 4;
106749ab747fSPaolo Bonzini } else {
106849ab747fSPaolo Bonzini uint32_t fcs = ~0;
106949ab747fSPaolo Bonzini uint8_t *p = src;
107049ab747fSPaolo Bonzini
1071837f21aaSPrasad J Pandit while (p != &src[size])
107249ab747fSPaolo Bonzini CRC(fcs, *p++);
107349ab747fSPaolo Bonzini crc_err = (*(uint32_t *)p != htonl(fcs));
107449ab747fSPaolo Bonzini }
107549ab747fSPaolo Bonzini
107649ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_MATCH
107749ab747fSPaolo Bonzini PRINT_PKTHDR(buf);
107849ab747fSPaolo Bonzini #endif
107949ab747fSPaolo Bonzini
108049ab747fSPaolo Bonzini RMDLOAD(&rmd, PHYSADDR(s,crda));
108149ab747fSPaolo Bonzini /*if (!CSR_LAPPEN(s))*/
108249ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, STP, 1);
108349ab747fSPaolo Bonzini
108449ab747fSPaolo Bonzini #define PCNET_RECV_STORE() do { \
108549ab747fSPaolo Bonzini int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
108649ab747fSPaolo Bonzini hwaddr rbadr = PHYSADDR(s, rmd.rbadr); \
108749ab747fSPaolo Bonzini s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
108849ab747fSPaolo Bonzini src += count; remaining -= count; \
108949ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, OWN, 0); \
109049ab747fSPaolo Bonzini RMDSTORE(&rmd, PHYSADDR(s,crda)); \
109149ab747fSPaolo Bonzini pktcount++; \
109249ab747fSPaolo Bonzini } while (0)
109349ab747fSPaolo Bonzini
109449ab747fSPaolo Bonzini remaining = size;
109549ab747fSPaolo Bonzini PCNET_RECV_STORE();
109649ab747fSPaolo Bonzini if ((remaining > 0) && CSR_NRDA(s)) {
109749ab747fSPaolo Bonzini hwaddr nrda = CSR_NRDA(s);
109849ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_RMD
109949ab747fSPaolo Bonzini PRINT_RMD(&rmd);
110049ab747fSPaolo Bonzini #endif
110149ab747fSPaolo Bonzini RMDLOAD(&rmd, PHYSADDR(s,nrda));
110249ab747fSPaolo Bonzini if (GET_FIELD(rmd.status, RMDS, OWN)) {
110349ab747fSPaolo Bonzini crda = nrda;
110449ab747fSPaolo Bonzini PCNET_RECV_STORE();
110549ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_RMD
110649ab747fSPaolo Bonzini PRINT_RMD(&rmd);
110749ab747fSPaolo Bonzini #endif
110849ab747fSPaolo Bonzini if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
110949ab747fSPaolo Bonzini RMDLOAD(&rmd, PHYSADDR(s,nrda));
111049ab747fSPaolo Bonzini if (GET_FIELD(rmd.status, RMDS, OWN)) {
111149ab747fSPaolo Bonzini crda = nrda;
111249ab747fSPaolo Bonzini PCNET_RECV_STORE();
111349ab747fSPaolo Bonzini }
111449ab747fSPaolo Bonzini }
111549ab747fSPaolo Bonzini }
111649ab747fSPaolo Bonzini }
111749ab747fSPaolo Bonzini
111849ab747fSPaolo Bonzini #undef PCNET_RECV_STORE
111949ab747fSPaolo Bonzini
112049ab747fSPaolo Bonzini RMDLOAD(&rmd, PHYSADDR(s,crda));
112149ab747fSPaolo Bonzini if (remaining == 0) {
112249ab747fSPaolo Bonzini SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
112349ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, ENP, 1);
112449ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
112549ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
112649ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
112749ab747fSPaolo Bonzini if (crc_err) {
112849ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, CRC, 1);
112949ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, ERR, 1);
113049ab747fSPaolo Bonzini }
113149ab747fSPaolo Bonzini } else {
113249ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, OFLO, 1);
113349ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, BUFF, 1);
113449ab747fSPaolo Bonzini SET_FIELD(&rmd.status, RMDS, ERR, 1);
113549ab747fSPaolo Bonzini }
113649ab747fSPaolo Bonzini RMDSTORE(&rmd, PHYSADDR(s,crda));
113749ab747fSPaolo Bonzini s->csr[0] |= 0x0400;
113849ab747fSPaolo Bonzini
113949ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
114049ab747fSPaolo Bonzini printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
114149ab747fSPaolo Bonzini CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
114249ab747fSPaolo Bonzini #endif
114349ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_RMD
114449ab747fSPaolo Bonzini PRINT_RMD(&rmd);
114549ab747fSPaolo Bonzini #endif
114649ab747fSPaolo Bonzini
114749ab747fSPaolo Bonzini while (pktcount--) {
114867aa4493SPrasad J Pandit if (CSR_RCVRC(s) <= 1) {
114949ab747fSPaolo Bonzini CSR_RCVRC(s) = CSR_RCVRL(s);
115067aa4493SPrasad J Pandit } else {
115149ab747fSPaolo Bonzini CSR_RCVRC(s)--;
115249ab747fSPaolo Bonzini }
115367aa4493SPrasad J Pandit }
115449ab747fSPaolo Bonzini
115549ab747fSPaolo Bonzini pcnet_rdte_poll(s);
115649ab747fSPaolo Bonzini
115749ab747fSPaolo Bonzini }
115849ab747fSPaolo Bonzini }
115949ab747fSPaolo Bonzini
116049ab747fSPaolo Bonzini pcnet_poll(s);
116149ab747fSPaolo Bonzini pcnet_update_irq(s);
116249ab747fSPaolo Bonzini
116349ab747fSPaolo Bonzini return size_;
116449ab747fSPaolo Bonzini }
116549ab747fSPaolo Bonzini
pcnet_set_link_status(NetClientState * nc)116649ab747fSPaolo Bonzini void pcnet_set_link_status(NetClientState *nc)
116749ab747fSPaolo Bonzini {
116849ab747fSPaolo Bonzini PCNetState *d = qemu_get_nic_opaque(nc);
116949ab747fSPaolo Bonzini
117049ab747fSPaolo Bonzini d->lnkst = nc->link_down ? 0 : 0x40;
117149ab747fSPaolo Bonzini }
117249ab747fSPaolo Bonzini
pcnet_transmit(PCNetState * s)117349ab747fSPaolo Bonzini static void pcnet_transmit(PCNetState *s)
117449ab747fSPaolo Bonzini {
117549ab747fSPaolo Bonzini hwaddr xmit_cxda = 0;
117649ab747fSPaolo Bonzini int count = CSR_XMTRL(s)-1;
117749ab747fSPaolo Bonzini int add_crc = 0;
11787b50d009SGonglei int bcnt;
117949ab747fSPaolo Bonzini s->xmit_pos = -1;
118049ab747fSPaolo Bonzini
118149ab747fSPaolo Bonzini if (!CSR_TXON(s)) {
118249ab747fSPaolo Bonzini s->csr[0] &= ~0x0008;
118349ab747fSPaolo Bonzini return;
118449ab747fSPaolo Bonzini }
118549ab747fSPaolo Bonzini
118649ab747fSPaolo Bonzini s->tx_busy = 1;
118749ab747fSPaolo Bonzini
118849ab747fSPaolo Bonzini txagain:
118949ab747fSPaolo Bonzini if (pcnet_tdte_poll(s)) {
119049ab747fSPaolo Bonzini struct pcnet_TMD tmd;
119149ab747fSPaolo Bonzini
119249ab747fSPaolo Bonzini TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
119349ab747fSPaolo Bonzini
119449ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_TMD
119549ab747fSPaolo Bonzini printf(" TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
119649ab747fSPaolo Bonzini PRINT_TMD(&tmd);
119749ab747fSPaolo Bonzini #endif
119849ab747fSPaolo Bonzini if (GET_FIELD(tmd.status, TMDS, STP)) {
119949ab747fSPaolo Bonzini s->xmit_pos = 0;
120049ab747fSPaolo Bonzini xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
120149ab747fSPaolo Bonzini if (BCR_SWSTYLE(s) != 1)
120249ab747fSPaolo Bonzini add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
120349ab747fSPaolo Bonzini }
120449ab747fSPaolo Bonzini if (s->lnkst == 0 &&
120549ab747fSPaolo Bonzini (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
120649ab747fSPaolo Bonzini SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
120749ab747fSPaolo Bonzini SET_FIELD(&tmd.status, TMDS, ERR, 1);
120849ab747fSPaolo Bonzini SET_FIELD(&tmd.status, TMDS, OWN, 0);
120949ab747fSPaolo Bonzini s->csr[0] |= 0xa000; /* ERR | CERR */
121049ab747fSPaolo Bonzini s->xmit_pos = -1;
121149ab747fSPaolo Bonzini goto txdone;
121249ab747fSPaolo Bonzini }
12137b50d009SGonglei
12147b50d009SGonglei if (s->xmit_pos < 0) {
12157b50d009SGonglei goto txdone;
12167b50d009SGonglei }
12177b50d009SGonglei
12187b50d009SGonglei bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
12199f7c594cSPetr Matousek
12209f7c594cSPetr Matousek /* if multi-tmd packet outsizes s->buffer then skip it silently.
1221837f21aaSPrasad J Pandit * Note: this is not what real hw does.
1222837f21aaSPrasad J Pandit * Last four bytes of s->buffer are used to store CRC FCS code.
1223837f21aaSPrasad J Pandit */
1224837f21aaSPrasad J Pandit if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) {
12259f7c594cSPetr Matousek s->xmit_pos = -1;
12269f7c594cSPetr Matousek goto txdone;
12279f7c594cSPetr Matousek }
12289f7c594cSPetr Matousek
12297b50d009SGonglei s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
12307b50d009SGonglei s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
12317b50d009SGonglei s->xmit_pos += bcnt;
12327b50d009SGonglei
123349ab747fSPaolo Bonzini if (!GET_FIELD(tmd.status, TMDS, ENP)) {
12347b50d009SGonglei goto txdone;
12357b50d009SGonglei }
12367b50d009SGonglei
123749ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
123849ab747fSPaolo Bonzini printf("pcnet_transmit size=%d\n", s->xmit_pos);
123949ab747fSPaolo Bonzini #endif
124049ab747fSPaolo Bonzini if (CSR_LOOP(s)) {
124149ab747fSPaolo Bonzini if (BCR_SWSTYLE(s) == 1)
124249ab747fSPaolo Bonzini add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
124349ab747fSPaolo Bonzini s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
124499ccfaa1SAlexander Bulekov qemu_receive_packet(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
124549ab747fSPaolo Bonzini s->looptest = 0;
12467b50d009SGonglei } else {
12477b50d009SGonglei if (s->nic) {
124849ab747fSPaolo Bonzini qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
124949ab747fSPaolo Bonzini s->xmit_pos);
12507b50d009SGonglei }
12517b50d009SGonglei }
125249ab747fSPaolo Bonzini
125349ab747fSPaolo Bonzini s->csr[0] &= ~0x0008; /* clear TDMD */
125449ab747fSPaolo Bonzini s->csr[4] |= 0x0004; /* set TXSTRT */
125549ab747fSPaolo Bonzini s->xmit_pos = -1;
125649ab747fSPaolo Bonzini
125749ab747fSPaolo Bonzini txdone:
125849ab747fSPaolo Bonzini SET_FIELD(&tmd.status, TMDS, OWN, 0);
125949ab747fSPaolo Bonzini TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
126067aa4493SPrasad J Pandit if (!CSR_TOKINTD(s)
126167aa4493SPrasad J Pandit || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) {
126249ab747fSPaolo Bonzini s->csr[0] |= 0x0200; /* set TINT */
126367aa4493SPrasad J Pandit }
126467aa4493SPrasad J Pandit if (CSR_XMTRC(s) <= 1) {
126549ab747fSPaolo Bonzini CSR_XMTRC(s) = CSR_XMTRL(s);
126667aa4493SPrasad J Pandit } else {
126749ab747fSPaolo Bonzini CSR_XMTRC(s)--;
126867aa4493SPrasad J Pandit }
126967aa4493SPrasad J Pandit if (count--) {
127049ab747fSPaolo Bonzini goto txagain;
127167aa4493SPrasad J Pandit }
127267aa4493SPrasad J Pandit } else if (s->xmit_pos >= 0) {
127349ab747fSPaolo Bonzini struct pcnet_TMD tmd;
127449ab747fSPaolo Bonzini TMDLOAD(&tmd, xmit_cxda);
127549ab747fSPaolo Bonzini SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
127649ab747fSPaolo Bonzini SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
127749ab747fSPaolo Bonzini SET_FIELD(&tmd.status, TMDS, ERR, 1);
127849ab747fSPaolo Bonzini SET_FIELD(&tmd.status, TMDS, OWN, 0);
127949ab747fSPaolo Bonzini TMDSTORE(&tmd, xmit_cxda);
128049ab747fSPaolo Bonzini s->csr[0] |= 0x0200; /* set TINT */
128149ab747fSPaolo Bonzini if (!CSR_DXSUFLO(s)) {
128249ab747fSPaolo Bonzini s->csr[0] &= ~0x0010;
128367aa4493SPrasad J Pandit } else if (count--) {
128449ab747fSPaolo Bonzini goto txagain;
128549ab747fSPaolo Bonzini }
128667aa4493SPrasad J Pandit }
128749ab747fSPaolo Bonzini
128849ab747fSPaolo Bonzini s->tx_busy = 0;
128949ab747fSPaolo Bonzini }
129049ab747fSPaolo Bonzini
pcnet_poll(PCNetState * s)129149ab747fSPaolo Bonzini static void pcnet_poll(PCNetState *s)
129249ab747fSPaolo Bonzini {
129349ab747fSPaolo Bonzini if (CSR_RXON(s)) {
129449ab747fSPaolo Bonzini pcnet_rdte_poll(s);
129549ab747fSPaolo Bonzini }
129649ab747fSPaolo Bonzini
129767aa4493SPrasad J Pandit if (CSR_TDMD(s) || (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) {
129849ab747fSPaolo Bonzini /* prevent recursion */
129967aa4493SPrasad J Pandit if (s->tx_busy) {
130049ab747fSPaolo Bonzini return;
130167aa4493SPrasad J Pandit }
130249ab747fSPaolo Bonzini pcnet_transmit(s);
130349ab747fSPaolo Bonzini }
130449ab747fSPaolo Bonzini }
130549ab747fSPaolo Bonzini
pcnet_poll_timer(void * opaque)130649ab747fSPaolo Bonzini static void pcnet_poll_timer(void *opaque)
130749ab747fSPaolo Bonzini {
130849ab747fSPaolo Bonzini PCNetState *s = opaque;
130949ab747fSPaolo Bonzini
1310bc72ad67SAlex Bligh timer_del(s->poll_timer);
131149ab747fSPaolo Bonzini
131249ab747fSPaolo Bonzini if (CSR_TDMD(s)) {
131349ab747fSPaolo Bonzini pcnet_transmit(s);
131449ab747fSPaolo Bonzini }
131549ab747fSPaolo Bonzini
131649ab747fSPaolo Bonzini pcnet_update_irq(s);
131749ab747fSPaolo Bonzini
131849ab747fSPaolo Bonzini if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) {
1319bc72ad67SAlex Bligh uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) * 33;
132067aa4493SPrasad J Pandit if (!s->timer || !now) {
132149ab747fSPaolo Bonzini s->timer = now;
132267aa4493SPrasad J Pandit } else {
132349ab747fSPaolo Bonzini uint64_t t = now - s->timer + CSR_POLL(s);
132449ab747fSPaolo Bonzini if (t > 0xffffLL) {
132549ab747fSPaolo Bonzini pcnet_poll(s);
132649ab747fSPaolo Bonzini CSR_POLL(s) = CSR_PINT(s);
132767aa4493SPrasad J Pandit } else {
132849ab747fSPaolo Bonzini CSR_POLL(s) = t;
132949ab747fSPaolo Bonzini }
133067aa4493SPrasad J Pandit }
1331bc72ad67SAlex Bligh timer_mod(s->poll_timer,
1332bc72ad67SAlex Bligh pcnet_get_next_poll_time(s,qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)));
133349ab747fSPaolo Bonzini }
133449ab747fSPaolo Bonzini }
133549ab747fSPaolo Bonzini
133649ab747fSPaolo Bonzini
pcnet_csr_writew(PCNetState * s,uint32_t rap,uint32_t new_value)133749ab747fSPaolo Bonzini static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value)
133849ab747fSPaolo Bonzini {
133949ab747fSPaolo Bonzini uint16_t val = new_value;
134049ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_CSR
134149ab747fSPaolo Bonzini printf("pcnet_csr_writew rap=%d val=0x%04x\n", rap, val);
134249ab747fSPaolo Bonzini #endif
134349ab747fSPaolo Bonzini switch (rap) {
134449ab747fSPaolo Bonzini case 0:
134549ab747fSPaolo Bonzini s->csr[0] &= ~(val & 0x7f00); /* Clear any interrupt flags */
134649ab747fSPaolo Bonzini
134749ab747fSPaolo Bonzini s->csr[0] = (s->csr[0] & ~0x0040) | (val & 0x0048);
134849ab747fSPaolo Bonzini
134949ab747fSPaolo Bonzini val = (val & 0x007f) | (s->csr[0] & 0x7f00);
135049ab747fSPaolo Bonzini
135149ab747fSPaolo Bonzini /* IFF STOP, STRT and INIT are set, clear STRT and INIT */
135267aa4493SPrasad J Pandit if ((val & 7) == 7) {
135349ab747fSPaolo Bonzini val &= ~3;
135467aa4493SPrasad J Pandit }
135567aa4493SPrasad J Pandit if (!CSR_STOP(s) && (val & 4)) {
135649ab747fSPaolo Bonzini pcnet_stop(s);
135767aa4493SPrasad J Pandit }
135867aa4493SPrasad J Pandit if (!CSR_INIT(s) && (val & 1)) {
135949ab747fSPaolo Bonzini pcnet_init(s);
136067aa4493SPrasad J Pandit }
136167aa4493SPrasad J Pandit if (!CSR_STRT(s) && (val & 2)) {
136249ab747fSPaolo Bonzini pcnet_start(s);
136367aa4493SPrasad J Pandit }
136467aa4493SPrasad J Pandit if (CSR_TDMD(s)) {
136549ab747fSPaolo Bonzini pcnet_transmit(s);
136667aa4493SPrasad J Pandit }
136749ab747fSPaolo Bonzini return;
136849ab747fSPaolo Bonzini case 1:
136949ab747fSPaolo Bonzini case 2:
137049ab747fSPaolo Bonzini case 8:
137149ab747fSPaolo Bonzini case 9:
137249ab747fSPaolo Bonzini case 10:
137349ab747fSPaolo Bonzini case 11:
137449ab747fSPaolo Bonzini case 12:
137549ab747fSPaolo Bonzini case 13:
137649ab747fSPaolo Bonzini case 14:
137749ab747fSPaolo Bonzini case 15:
137849ab747fSPaolo Bonzini case 18: /* CRBAL */
137949ab747fSPaolo Bonzini case 19: /* CRBAU */
138049ab747fSPaolo Bonzini case 20: /* CXBAL */
138149ab747fSPaolo Bonzini case 21: /* CXBAU */
138249ab747fSPaolo Bonzini case 22: /* NRBAU */
138349ab747fSPaolo Bonzini case 23: /* NRBAU */
138449ab747fSPaolo Bonzini case 24:
138549ab747fSPaolo Bonzini case 25:
138649ab747fSPaolo Bonzini case 26:
138749ab747fSPaolo Bonzini case 27:
138849ab747fSPaolo Bonzini case 28:
138949ab747fSPaolo Bonzini case 29:
139049ab747fSPaolo Bonzini case 30:
139149ab747fSPaolo Bonzini case 31:
139249ab747fSPaolo Bonzini case 32:
139349ab747fSPaolo Bonzini case 33:
139449ab747fSPaolo Bonzini case 34:
139549ab747fSPaolo Bonzini case 35:
139649ab747fSPaolo Bonzini case 36:
139749ab747fSPaolo Bonzini case 37:
139849ab747fSPaolo Bonzini case 38:
139949ab747fSPaolo Bonzini case 39:
140049ab747fSPaolo Bonzini case 40: /* CRBC */
140149ab747fSPaolo Bonzini case 41:
140249ab747fSPaolo Bonzini case 42: /* CXBC */
140349ab747fSPaolo Bonzini case 43:
140449ab747fSPaolo Bonzini case 44:
140549ab747fSPaolo Bonzini case 45:
140649ab747fSPaolo Bonzini case 46: /* POLL */
140749ab747fSPaolo Bonzini case 47: /* POLLINT */
140849ab747fSPaolo Bonzini case 72:
140949ab747fSPaolo Bonzini case 74:
141034e29ce7SPrasad J Pandit break;
141149ab747fSPaolo Bonzini case 76: /* RCVRL */
141249ab747fSPaolo Bonzini case 78: /* XMTRL */
141334e29ce7SPrasad J Pandit val = (val > 0) ? val : 512;
141434e29ce7SPrasad J Pandit break;
141549ab747fSPaolo Bonzini case 112:
141667aa4493SPrasad J Pandit if (CSR_STOP(s) || CSR_SPND(s)) {
141749ab747fSPaolo Bonzini break;
141867aa4493SPrasad J Pandit }
141949ab747fSPaolo Bonzini return;
142049ab747fSPaolo Bonzini case 3:
142149ab747fSPaolo Bonzini break;
142249ab747fSPaolo Bonzini case 4:
142349ab747fSPaolo Bonzini s->csr[4] &= ~(val & 0x026a);
142449ab747fSPaolo Bonzini val &= ~0x026a; val |= s->csr[4] & 0x026a;
142549ab747fSPaolo Bonzini break;
142649ab747fSPaolo Bonzini case 5:
142749ab747fSPaolo Bonzini s->csr[5] &= ~(val & 0x0a90);
142849ab747fSPaolo Bonzini val &= ~0x0a90; val |= s->csr[5] & 0x0a90;
142949ab747fSPaolo Bonzini break;
143049ab747fSPaolo Bonzini case 16:
143149ab747fSPaolo Bonzini pcnet_csr_writew(s,1,val);
143249ab747fSPaolo Bonzini return;
143349ab747fSPaolo Bonzini case 17:
143449ab747fSPaolo Bonzini pcnet_csr_writew(s,2,val);
143549ab747fSPaolo Bonzini return;
143649ab747fSPaolo Bonzini case 58:
143749ab747fSPaolo Bonzini pcnet_bcr_writew(s,BCR_SWS,val);
143849ab747fSPaolo Bonzini break;
143949ab747fSPaolo Bonzini default:
144049ab747fSPaolo Bonzini return;
144149ab747fSPaolo Bonzini }
144249ab747fSPaolo Bonzini s->csr[rap] = val;
144349ab747fSPaolo Bonzini }
144449ab747fSPaolo Bonzini
pcnet_csr_readw(PCNetState * s,uint32_t rap)144549ab747fSPaolo Bonzini static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap)
144649ab747fSPaolo Bonzini {
144749ab747fSPaolo Bonzini uint32_t val;
144849ab747fSPaolo Bonzini switch (rap) {
144949ab747fSPaolo Bonzini case 0:
145049ab747fSPaolo Bonzini pcnet_update_irq(s);
145149ab747fSPaolo Bonzini val = s->csr[0];
145249ab747fSPaolo Bonzini val |= (val & 0x7800) ? 0x8000 : 0;
145349ab747fSPaolo Bonzini break;
145449ab747fSPaolo Bonzini case 16:
145549ab747fSPaolo Bonzini return pcnet_csr_readw(s,1);
145649ab747fSPaolo Bonzini case 17:
145749ab747fSPaolo Bonzini return pcnet_csr_readw(s,2);
145849ab747fSPaolo Bonzini case 58:
145949ab747fSPaolo Bonzini return pcnet_bcr_readw(s,BCR_SWS);
146049ab747fSPaolo Bonzini case 88:
146149ab747fSPaolo Bonzini val = s->csr[89];
146249ab747fSPaolo Bonzini val <<= 16;
146349ab747fSPaolo Bonzini val |= s->csr[88];
146449ab747fSPaolo Bonzini break;
146549ab747fSPaolo Bonzini default:
146649ab747fSPaolo Bonzini val = s->csr[rap];
146749ab747fSPaolo Bonzini }
146849ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_CSR
146949ab747fSPaolo Bonzini printf("pcnet_csr_readw rap=%d val=0x%04x\n", rap, val);
147049ab747fSPaolo Bonzini #endif
147149ab747fSPaolo Bonzini return val;
147249ab747fSPaolo Bonzini }
147349ab747fSPaolo Bonzini
pcnet_bcr_writew(PCNetState * s,uint32_t rap,uint32_t val)147449ab747fSPaolo Bonzini static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val)
147549ab747fSPaolo Bonzini {
147649ab747fSPaolo Bonzini rap &= 127;
147749ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_BCR
147849ab747fSPaolo Bonzini printf("pcnet_bcr_writew rap=%d val=0x%04x\n", rap, val);
147949ab747fSPaolo Bonzini #endif
148049ab747fSPaolo Bonzini switch (rap) {
148149ab747fSPaolo Bonzini case BCR_SWS:
148249ab747fSPaolo Bonzini if (!(CSR_STOP(s) || CSR_SPND(s)))
148349ab747fSPaolo Bonzini return;
148449ab747fSPaolo Bonzini val &= ~0x0300;
148549ab747fSPaolo Bonzini switch (val & 0x00ff) {
148649ab747fSPaolo Bonzini case 0:
148749ab747fSPaolo Bonzini val |= 0x0200;
148849ab747fSPaolo Bonzini break;
148949ab747fSPaolo Bonzini case 1:
149049ab747fSPaolo Bonzini val |= 0x0100;
149149ab747fSPaolo Bonzini break;
149249ab747fSPaolo Bonzini case 2:
149349ab747fSPaolo Bonzini case 3:
149449ab747fSPaolo Bonzini val |= 0x0300;
149549ab747fSPaolo Bonzini break;
149649ab747fSPaolo Bonzini default:
14978f599053SPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR, "pcnet: Bad SWSTYLE=0x%02x\n",
14988f599053SPhilippe Mathieu-Daudé val & 0xff);
149949ab747fSPaolo Bonzini val = 0x0200;
150049ab747fSPaolo Bonzini break;
150149ab747fSPaolo Bonzini }
150249ab747fSPaolo Bonzini #ifdef PCNET_DEBUG
150349ab747fSPaolo Bonzini printf("BCR_SWS=0x%04x\n", val);
150449ab747fSPaolo Bonzini #endif
150549ab747fSPaolo Bonzini /* fall through */
150649ab747fSPaolo Bonzini case BCR_LNKST:
150749ab747fSPaolo Bonzini case BCR_LED1:
150849ab747fSPaolo Bonzini case BCR_LED2:
150949ab747fSPaolo Bonzini case BCR_LED3:
151049ab747fSPaolo Bonzini case BCR_MC:
151149ab747fSPaolo Bonzini case BCR_FDC:
151249ab747fSPaolo Bonzini case BCR_BSBC:
151349ab747fSPaolo Bonzini case BCR_EECAS:
151449ab747fSPaolo Bonzini case BCR_PLAT:
151549ab747fSPaolo Bonzini s->bcr[rap] = val;
151649ab747fSPaolo Bonzini break;
151749ab747fSPaolo Bonzini default:
151849ab747fSPaolo Bonzini break;
151949ab747fSPaolo Bonzini }
152049ab747fSPaolo Bonzini }
152149ab747fSPaolo Bonzini
pcnet_bcr_readw(PCNetState * s,uint32_t rap)152249ab747fSPaolo Bonzini uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap)
152349ab747fSPaolo Bonzini {
152449ab747fSPaolo Bonzini uint32_t val;
152549ab747fSPaolo Bonzini rap &= 127;
152649ab747fSPaolo Bonzini switch (rap) {
152749ab747fSPaolo Bonzini case BCR_LNKST:
152849ab747fSPaolo Bonzini case BCR_LED1:
152949ab747fSPaolo Bonzini case BCR_LED2:
153049ab747fSPaolo Bonzini case BCR_LED3:
153149ab747fSPaolo Bonzini val = s->bcr[rap] & ~0x8000;
153249ab747fSPaolo Bonzini val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0;
153349ab747fSPaolo Bonzini break;
153449ab747fSPaolo Bonzini default:
153549ab747fSPaolo Bonzini val = rap < 32 ? s->bcr[rap] : 0;
153649ab747fSPaolo Bonzini break;
153749ab747fSPaolo Bonzini }
153849ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_BCR
153949ab747fSPaolo Bonzini printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val);
154049ab747fSPaolo Bonzini #endif
154149ab747fSPaolo Bonzini return val;
154249ab747fSPaolo Bonzini }
154349ab747fSPaolo Bonzini
pcnet_h_reset(void * opaque)154449ab747fSPaolo Bonzini void pcnet_h_reset(void *opaque)
154549ab747fSPaolo Bonzini {
154649ab747fSPaolo Bonzini PCNetState *s = opaque;
154749ab747fSPaolo Bonzini
154849ab747fSPaolo Bonzini s->bcr[BCR_MSRDA] = 0x0005;
154949ab747fSPaolo Bonzini s->bcr[BCR_MSWRA] = 0x0005;
155049ab747fSPaolo Bonzini s->bcr[BCR_MC ] = 0x0002;
155149ab747fSPaolo Bonzini s->bcr[BCR_LNKST] = 0x00c0;
155249ab747fSPaolo Bonzini s->bcr[BCR_LED1 ] = 0x0084;
155349ab747fSPaolo Bonzini s->bcr[BCR_LED2 ] = 0x0088;
155449ab747fSPaolo Bonzini s->bcr[BCR_LED3 ] = 0x0090;
155549ab747fSPaolo Bonzini s->bcr[BCR_FDC ] = 0x0000;
155649ab747fSPaolo Bonzini s->bcr[BCR_BSBC ] = 0x9001;
155749ab747fSPaolo Bonzini s->bcr[BCR_EECAS] = 0x0002;
155849ab747fSPaolo Bonzini s->bcr[BCR_SWS ] = 0x0200;
155949ab747fSPaolo Bonzini s->bcr[BCR_PLAT ] = 0xff06;
156049ab747fSPaolo Bonzini
156149ab747fSPaolo Bonzini pcnet_s_reset(s);
156249ab747fSPaolo Bonzini pcnet_update_irq(s);
156349ab747fSPaolo Bonzini pcnet_poll_timer(s);
156449ab747fSPaolo Bonzini }
156549ab747fSPaolo Bonzini
pcnet_ioport_writew(void * opaque,uint32_t addr,uint32_t val)156649ab747fSPaolo Bonzini void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
156749ab747fSPaolo Bonzini {
156849ab747fSPaolo Bonzini PCNetState *s = opaque;
156949ab747fSPaolo Bonzini pcnet_poll_timer(s);
157049ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_IO
157149ab747fSPaolo Bonzini printf("pcnet_ioport_writew addr=0x%08x val=0x%04x\n", addr, val);
157249ab747fSPaolo Bonzini #endif
157349ab747fSPaolo Bonzini if (!BCR_DWIO(s)) {
157449ab747fSPaolo Bonzini switch (addr & 0x0f) {
157549ab747fSPaolo Bonzini case 0x00: /* RDP */
157649ab747fSPaolo Bonzini pcnet_csr_writew(s, s->rap, val);
157749ab747fSPaolo Bonzini break;
157849ab747fSPaolo Bonzini case 0x02:
157949ab747fSPaolo Bonzini s->rap = val & 0x7f;
158049ab747fSPaolo Bonzini break;
158149ab747fSPaolo Bonzini case 0x06:
158249ab747fSPaolo Bonzini pcnet_bcr_writew(s, s->rap, val);
158349ab747fSPaolo Bonzini break;
158449ab747fSPaolo Bonzini }
158549ab747fSPaolo Bonzini }
158649ab747fSPaolo Bonzini pcnet_update_irq(s);
158749ab747fSPaolo Bonzini }
158849ab747fSPaolo Bonzini
pcnet_ioport_readw(void * opaque,uint32_t addr)158949ab747fSPaolo Bonzini uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr)
159049ab747fSPaolo Bonzini {
159149ab747fSPaolo Bonzini PCNetState *s = opaque;
159249ab747fSPaolo Bonzini uint32_t val = -1;
159349ab747fSPaolo Bonzini pcnet_poll_timer(s);
159449ab747fSPaolo Bonzini if (!BCR_DWIO(s)) {
159549ab747fSPaolo Bonzini switch (addr & 0x0f) {
159649ab747fSPaolo Bonzini case 0x00: /* RDP */
159749ab747fSPaolo Bonzini val = pcnet_csr_readw(s, s->rap);
159849ab747fSPaolo Bonzini break;
159949ab747fSPaolo Bonzini case 0x02:
160049ab747fSPaolo Bonzini val = s->rap;
160149ab747fSPaolo Bonzini break;
160249ab747fSPaolo Bonzini case 0x04:
160349ab747fSPaolo Bonzini pcnet_s_reset(s);
160449ab747fSPaolo Bonzini val = 0;
160549ab747fSPaolo Bonzini break;
160649ab747fSPaolo Bonzini case 0x06:
160749ab747fSPaolo Bonzini val = pcnet_bcr_readw(s, s->rap);
160849ab747fSPaolo Bonzini break;
160949ab747fSPaolo Bonzini }
161049ab747fSPaolo Bonzini }
161149ab747fSPaolo Bonzini pcnet_update_irq(s);
161249ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_IO
161349ab747fSPaolo Bonzini printf("pcnet_ioport_readw addr=0x%08x val=0x%04x\n", addr, val & 0xffff);
161449ab747fSPaolo Bonzini #endif
161549ab747fSPaolo Bonzini return val;
161649ab747fSPaolo Bonzini }
161749ab747fSPaolo Bonzini
pcnet_ioport_writel(void * opaque,uint32_t addr,uint32_t val)161849ab747fSPaolo Bonzini void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
161949ab747fSPaolo Bonzini {
162049ab747fSPaolo Bonzini PCNetState *s = opaque;
162149ab747fSPaolo Bonzini pcnet_poll_timer(s);
162249ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_IO
162349ab747fSPaolo Bonzini printf("pcnet_ioport_writel addr=0x%08x val=0x%08x\n", addr, val);
162449ab747fSPaolo Bonzini #endif
162549ab747fSPaolo Bonzini if (BCR_DWIO(s)) {
162649ab747fSPaolo Bonzini switch (addr & 0x0f) {
162749ab747fSPaolo Bonzini case 0x00: /* RDP */
162849ab747fSPaolo Bonzini pcnet_csr_writew(s, s->rap, val & 0xffff);
162949ab747fSPaolo Bonzini break;
163049ab747fSPaolo Bonzini case 0x04:
163149ab747fSPaolo Bonzini s->rap = val & 0x7f;
163249ab747fSPaolo Bonzini break;
163349ab747fSPaolo Bonzini case 0x0c:
163449ab747fSPaolo Bonzini pcnet_bcr_writew(s, s->rap, val & 0xffff);
163549ab747fSPaolo Bonzini break;
163649ab747fSPaolo Bonzini }
163767aa4493SPrasad J Pandit } else if ((addr & 0x0f) == 0) {
163849ab747fSPaolo Bonzini /* switch device to dword i/o mode */
163949ab747fSPaolo Bonzini pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080);
164049ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_IO
164149ab747fSPaolo Bonzini printf("device switched into dword i/o mode\n");
164249ab747fSPaolo Bonzini #endif
164349ab747fSPaolo Bonzini }
164449ab747fSPaolo Bonzini pcnet_update_irq(s);
164549ab747fSPaolo Bonzini }
164649ab747fSPaolo Bonzini
pcnet_ioport_readl(void * opaque,uint32_t addr)164749ab747fSPaolo Bonzini uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr)
164849ab747fSPaolo Bonzini {
164949ab747fSPaolo Bonzini PCNetState *s = opaque;
165049ab747fSPaolo Bonzini uint32_t val = -1;
165149ab747fSPaolo Bonzini pcnet_poll_timer(s);
165249ab747fSPaolo Bonzini if (BCR_DWIO(s)) {
165349ab747fSPaolo Bonzini switch (addr & 0x0f) {
165449ab747fSPaolo Bonzini case 0x00: /* RDP */
165549ab747fSPaolo Bonzini val = pcnet_csr_readw(s, s->rap);
165649ab747fSPaolo Bonzini break;
165749ab747fSPaolo Bonzini case 0x04:
165849ab747fSPaolo Bonzini val = s->rap;
165949ab747fSPaolo Bonzini break;
166049ab747fSPaolo Bonzini case 0x08:
166149ab747fSPaolo Bonzini pcnet_s_reset(s);
166249ab747fSPaolo Bonzini val = 0;
166349ab747fSPaolo Bonzini break;
166449ab747fSPaolo Bonzini case 0x0c:
166549ab747fSPaolo Bonzini val = pcnet_bcr_readw(s, s->rap);
166649ab747fSPaolo Bonzini break;
166749ab747fSPaolo Bonzini }
166849ab747fSPaolo Bonzini }
166949ab747fSPaolo Bonzini pcnet_update_irq(s);
167049ab747fSPaolo Bonzini #ifdef PCNET_DEBUG_IO
167149ab747fSPaolo Bonzini printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val);
167249ab747fSPaolo Bonzini #endif
167349ab747fSPaolo Bonzini return val;
167449ab747fSPaolo Bonzini }
167549ab747fSPaolo Bonzini
is_version_2(void * opaque,int version_id)167649ab747fSPaolo Bonzini static bool is_version_2(void *opaque, int version_id)
167749ab747fSPaolo Bonzini {
167849ab747fSPaolo Bonzini return version_id == 2;
167949ab747fSPaolo Bonzini }
168049ab747fSPaolo Bonzini
168149ab747fSPaolo Bonzini const VMStateDescription vmstate_pcnet = {
168249ab747fSPaolo Bonzini .name = "pcnet",
168349ab747fSPaolo Bonzini .version_id = 3,
168449ab747fSPaolo Bonzini .minimum_version_id = 2,
16851de81b42SRichard Henderson .fields = (const VMStateField[]) {
168649ab747fSPaolo Bonzini VMSTATE_INT32(rap, PCNetState),
168749ab747fSPaolo Bonzini VMSTATE_INT32(isr, PCNetState),
168849ab747fSPaolo Bonzini VMSTATE_INT32(lnkst, PCNetState),
168949ab747fSPaolo Bonzini VMSTATE_UINT32(rdra, PCNetState),
169049ab747fSPaolo Bonzini VMSTATE_UINT32(tdra, PCNetState),
169149ab747fSPaolo Bonzini VMSTATE_BUFFER(prom, PCNetState),
169249ab747fSPaolo Bonzini VMSTATE_UINT16_ARRAY(csr, PCNetState, 128),
169349ab747fSPaolo Bonzini VMSTATE_UINT16_ARRAY(bcr, PCNetState, 32),
169449ab747fSPaolo Bonzini VMSTATE_UINT64(timer, PCNetState),
169549ab747fSPaolo Bonzini VMSTATE_INT32(xmit_pos, PCNetState),
169649ab747fSPaolo Bonzini VMSTATE_BUFFER(buffer, PCNetState),
169749ab747fSPaolo Bonzini VMSTATE_UNUSED_TEST(is_version_2, 4),
169849ab747fSPaolo Bonzini VMSTATE_INT32(tx_busy, PCNetState),
1699e720677eSPaolo Bonzini VMSTATE_TIMER_PTR(poll_timer, PCNetState),
170049ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
170149ab747fSPaolo Bonzini }
170249ab747fSPaolo Bonzini };
170349ab747fSPaolo Bonzini
pcnet_common_init(DeviceState * dev,PCNetState * s,NetClientInfo * info)17044c3b2245SMarkus Armbruster void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
170549ab747fSPaolo Bonzini {
170649ab747fSPaolo Bonzini int i;
170749ab747fSPaolo Bonzini uint16_t checksum;
170849ab747fSPaolo Bonzini
1709bc72ad67SAlex Bligh s->poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pcnet_poll_timer, s);
171049ab747fSPaolo Bonzini
171149ab747fSPaolo Bonzini qemu_macaddr_default_if_unset(&s->conf.macaddr);
17127d0fefdfSAkihiko Odaki s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)),
17137d0fefdfSAkihiko Odaki dev->id, &dev->mem_reentrancy_guard, s);
171449ab747fSPaolo Bonzini qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
171549ab747fSPaolo Bonzini
171649ab747fSPaolo Bonzini /* Initialize the PROM */
171749ab747fSPaolo Bonzini
171849ab747fSPaolo Bonzini /*
171949ab747fSPaolo Bonzini Datasheet: http://pdfdata.datasheetsite.com/web/24528/AM79C970A.pdf
172049ab747fSPaolo Bonzini page 95
172149ab747fSPaolo Bonzini */
172249ab747fSPaolo Bonzini memcpy(s->prom, s->conf.macaddr.a, 6);
172349ab747fSPaolo Bonzini /* Reserved Location: must be 00h */
172449ab747fSPaolo Bonzini s->prom[6] = s->prom[7] = 0x00;
172549ab747fSPaolo Bonzini /* Reserved Location: must be 00h */
172649ab747fSPaolo Bonzini s->prom[8] = 0x00;
172749ab747fSPaolo Bonzini /* Hardware ID: must be 11h if compatibility to AMD drivers is desired */
172849ab747fSPaolo Bonzini s->prom[9] = 0x11;
172949ab747fSPaolo Bonzini /* User programmable space, init with 0 */
173049ab747fSPaolo Bonzini s->prom[10] = s->prom[11] = 0x00;
173149ab747fSPaolo Bonzini /* LSByte of two-byte checksum, which is the sum of bytes 00h-0Bh
173249ab747fSPaolo Bonzini and bytes 0Eh and 0Fh, must therefore be initialized with 0! */
173349ab747fSPaolo Bonzini s->prom[12] = s->prom[13] = 0x00;
173449ab747fSPaolo Bonzini /* Must be ASCII W (57h) if compatibility to AMD
173549ab747fSPaolo Bonzini driver software is desired */
173649ab747fSPaolo Bonzini s->prom[14] = s->prom[15] = 0x57;
173749ab747fSPaolo Bonzini
173849ab747fSPaolo Bonzini for (i = 0, checksum = 0; i < 16; i++) {
173949ab747fSPaolo Bonzini checksum += s->prom[i];
174049ab747fSPaolo Bonzini }
174149ab747fSPaolo Bonzini *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum);
174249ab747fSPaolo Bonzini
174349ab747fSPaolo Bonzini s->lnkst = 0x40; /* initial link state: up */
174449ab747fSPaolo Bonzini }
1745