X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Frtl8139.c;h=a970eb3a955ffc64ebdc885ab10bd51413898a10;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=c41b9d6495d5e3563108a16c2f35b1dc59ed610b;hpb=6cadb320c70cdcbfa38afe9ac864eb77ae6012a3;p=qemu diff --git a/hw/rtl8139.c b/hw/rtl8139.c index c41b9d6..a970eb3 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -1,8 +1,8 @@ /** * QEMU RTL8139 emulation - * + * * Copyright (c) 2006 Igor Kovalenko - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -20,26 +20,31 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - + * Modifications: * 2006-Jan-28 Mark Malakanov : TSAD and CSCR implementation (for Windows driver) - * + * * 2006-Apr-28 Juergen Lock : EEPROM emulation changes for FreeBSD driver * HW revision ID changes for FreeBSD driver - * + * * 2006-Jul-01 Igor Kovalenko : Implemented loopback mode for FreeBSD driver * Corrected packet transfer reassembly routine for 8139C+ mode * Rearranged debugging print statements * Implemented PCI timer interrupt (disabled by default) * Implemented Tally Counters, increased VM load/save version * Implemented IP/TCP/UDP checksum task offloading + * + * 2006-Jul-04 Igor Kovalenko : Implemented TCP segmentation offloading + * Fixed MTU=1500 for produced ethernet frames + * + * 2006-Jul-09 Igor Kovalenko : Fixed TCP header length calculation while processing + * segmentation offloading + * Removed slirp.h dependency + * Added rx/tx buffer reset when enabling rx/tx operation */ #include "vl.h" -/* XXX: such dependency must be suppressed */ -#include - /* debug RTL8139 card */ //#define DEBUG_RTL8139 1 @@ -48,9 +53,8 @@ /* debug RTL8139 card C+ mode only */ //#define DEBUG_RTL8139CP 1 -/* RTL8139 provides frame CRC with received packet, this feature seems to be - ignored by most drivers, disabled by default */ -//#define RTL8139_CALCULATE_RXCRC 1 +/* Calculate CRCs properly on Rx packets */ +#define RTL8139_CALCULATE_RXCRC 1 /* Uncomment to enable on-board timer interrupts */ //#define RTL8139_ONBOARD_TIMER 1 @@ -301,11 +305,11 @@ enum CSCRBits { CSCR_LinkDownCmd = 0x0f3c0, */ enum CSCRBits { - CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */ + CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */ CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/ CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/ CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/ - CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/ + CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/ CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/ CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/ CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/ @@ -456,7 +460,6 @@ typedef struct RTL8139State { uint16_t CpCmd; uint8_t TxThresh; - int irq; PCIDevice *pci_dev; VLANClientState *vc; uint8_t macaddr[6]; @@ -680,16 +683,10 @@ static void rtl8139_update_irq(RTL8139State *s) int isr; isr = (s->IntrStatus & s->IntrMask) & 0xffff; - DEBUG_PRINT(("RTL8139: Set IRQ line %d to %d (%04x %04x)\n", - s->irq, isr ? 1 : 0, s->IntrStatus, s->IntrMask)); + DEBUG_PRINT(("RTL8139: Set IRQ to %d (%04x %04x)\n", + isr ? 1 : 0, s->IntrStatus, s->IntrMask)); - if (s->irq == 16) { - /* PCI irq */ - pci_set_irq(s->pci_dev, 0, (isr != 0)); - } else { - /* ISA irq */ - pic_set_irq(s->irq, (isr != 0)); - } + qemu_set_irq(s->pci_dev->irq[0], (isr != 0)); } #define POLYNOMIAL 0x04c11db6 @@ -749,7 +746,7 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize); /* write packet data */ - if (wrapped && s->RxBufferSize < 65536 && !rtl8139_RxWrap(s)) + if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s))) { DEBUG_PRINT((">>> RTL8139: rx packet wrapped in buffer at %d\n", size-wrapped)); @@ -792,7 +789,7 @@ static int rtl8139_can_receive(void *opaque) RTL8139State *s = opaque; int avail; - /* Recieve (drop) packets if card is disabled. */ + /* Receive (drop) packets if card is disabled. */ if (!s->clock_enabled) return 1; if (!rtl8139_receiver_enabled(s)) @@ -816,7 +813,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d uint32_t packet_header = 0; uint8_t buf1[60]; - static const uint8_t broadcast_macaddr[6] = + static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; DEBUG_PRINT((">>> RTL8139: received len=%d\n", size)); @@ -893,10 +890,10 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d ++s->tally_counters.RxOkMul; } else if (s->phys[0] == buf[0] && - s->phys[1] == buf[1] && - s->phys[2] == buf[2] && - s->phys[3] == buf[3] && - s->phys[4] == buf[4] && + s->phys[1] == buf[1] && + s->phys[2] == buf[2] && + s->phys[3] == buf[3] && + s->phys[4] == buf[4] && s->phys[5] == buf[5]) { /* match */ if (!(s->RxConfig & AcceptMyPhys)) @@ -1025,7 +1022,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d /* write checksum */ #if defined (RTL8139_CALCULATE_RXCRC) - val = cpu_to_le32(crc32(~0, buf, size)); + val = cpu_to_le32(crc32(0, buf, size)); #else val = 0; #endif @@ -1131,7 +1128,7 @@ static void rtl8139_do_receive(void *opaque, const uint8_t *buf, int size, int d /* write checksum */ #if defined (RTL8139_CALCULATE_RXCRC) - val = cpu_to_le32(crc32(~0, buf, size)); + val = cpu_to_le32(crc32(0, buf, size)); #else val = 0; #endif @@ -1187,7 +1184,10 @@ static void rtl8139_reset(RTL8139State *s) s->eeprom.contents[1] = 0x10ec; s->eeprom.contents[2] = 0x8139; #endif - memcpy(&s->eeprom.contents[7], s->macaddr, 6); + + s->eeprom.contents[7] = s->macaddr[0] | s->macaddr[1] << 8; + s->eeprom.contents[8] = s->macaddr[2] | s->macaddr[3] << 8; + s->eeprom.contents[9] = s->macaddr[4] | s->macaddr[5] << 8; /* mark all status registers as owned by host */ for (i = 0; i < 4; ++i) @@ -1225,7 +1225,7 @@ static void rtl8139_reset(RTL8139State *s) s->Config3 = 0x1; /* fast back-to-back compatible */ s->Config5 = 0x0; - s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; + s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; s->CpCmd = 0x0; /* reset C+ mode */ @@ -1364,10 +1364,14 @@ static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val) if (val & CmdRxEnb) { DEBUG_PRINT(("RTL8139: ChipCmd enable receiver\n")); + + s->currCPlusRxDesc = 0; } if (val & CmdTxEnb) { DEBUG_PRINT(("RTL8139: ChipCmd enable transmitter\n")); + + s->currCPlusTxDesc = 0; } /* mask unwriteable bits */ @@ -1732,6 +1736,25 @@ static uint32_t rtl8139_RxConfig_read(RTL8139State *s) return ret; } +static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt) +{ + if (!size) + { + DEBUG_PRINT(("RTL8139: +++ empty ethernet frame\n")); + return; + } + + if (TxLoopBack == (s->TxConfig & TxLoopBack)) + { + DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n")); + rtl8139_do_receive(s, buf, size, do_interrupt); + } + else + { + qemu_send_packet(s->vc, buf, size); + } +} + static int rtl8139_transmit_one(RTL8139State *s, int descriptor) { if (!rtl8139_transmitter_enabled(s)) @@ -1762,15 +1785,7 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) s->TxStatus[descriptor] |= TxHostOwns; s->TxStatus[descriptor] |= TxStatOK; - if (TxLoopBack == (s->TxConfig & TxLoopBack)) - { - DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n")); - rtl8139_do_receive(s, txbuffer, txsize, 0); - } - else - { - qemu_send_packet(s->vc, txbuffer, txsize); - } + rtl8139_transfer_frame(s, txbuffer, txsize, 0); DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor)); @@ -1781,6 +1796,93 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) return 1; } +/* structures and macros for task offloading */ +typedef struct ip_header +{ + uint8_t ip_ver_len; /* version and header length */ + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + uint32_t ip_src,ip_dst; /* source and dest address */ +} ip_header; + +#define IP_HEADER_VERSION_4 4 +#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf) +#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2) + +typedef struct tcp_header +{ + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ + uint32_t th_seq; /* sequence number */ + uint32_t th_ack; /* acknowledgement number */ + uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */ + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ +} tcp_header; + +typedef struct udp_header +{ + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + uint16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +} udp_header; + +typedef struct ip_pseudo_header +{ + uint32_t ip_src; + uint32_t ip_dst; + uint8_t zeros; + uint8_t ip_proto; + uint16_t ip_payload; +} ip_pseudo_header; + +#define IP_PROTO_TCP 6 +#define IP_PROTO_UDP 17 + +#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2) +#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f) +#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags)) + +#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off))) + +#define TCP_FLAG_FIN 0x01 +#define TCP_FLAG_PUSH 0x08 + +/* produces ones' complement sum of data */ +static uint16_t ones_complement_sum(uint8_t *data, size_t len) +{ + uint32_t result = 0; + + for (; len > 1; data+=2, len-=2) + { + result += *(uint16_t*)data; + } + + /* add the remainder byte */ + if (len) + { + uint8_t odd[2] = {*data, 0}; + result += *(uint16_t*)odd; + } + + while (result>>16) + result = (result & 0xffff) + (result >> 16); + + return result; +} + +static uint16_t ip_checksum(void *data, size_t len) +{ + return ~ones_complement_sum((uint8_t*)data, len); +} + static int rtl8139_cplus_transmit_one(RTL8139State *s) { if (!rtl8139_transmitter_enabled(s)) @@ -1831,6 +1933,9 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) #define CP_TX_LS (1<<28) /* large send packet flag */ #define CP_TX_LGSEN (1<<27) +/* large send MSS mask, bits 16...25 */ +#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1) + /* IP checksum offload flag */ #define CP_TX_IPCS (1<<18) /* UDP checksum offload flag */ @@ -1885,6 +1990,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE; s->cplus_txbuffer = malloc(s->cplus_txbuffer_len); s->cplus_txbuffer_offset = 0; + + DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len)); } while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) @@ -1960,35 +2067,41 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) s->cplus_txbuffer_offset = 0; s->cplus_txbuffer_len = 0; - if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS)) + if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN)) { DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task checksum\n")); #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_HLEN 14 + #define ETH_MTU 1500 /* ip packet header */ - register struct ip *ip = 0; + ip_header *ip = 0; int hlen = 0; + uint8_t ip_protocol = 0; + uint16_t ip_data_len = 0; - struct mbuf local_m; + uint8_t *eth_payload_data = 0; + size_t eth_payload_len = 0; - int proto = ntohs(*(uint16_t *)(saved_buffer + 12)); + int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12)); if (proto == ETH_P_IP) { DEBUG_PRINT(("RTL8139: +++ C+ mode has IP packet\n")); /* not aligned */ - local_m.m_data = saved_buffer + ETH_HLEN; - local_m.m_len = saved_size - ETH_HLEN; + eth_payload_data = saved_buffer + ETH_HLEN; + eth_payload_len = saved_size - ETH_HLEN; - ip = mtod(&local_m, struct ip *); + ip = (ip_header*)eth_payload_data; - if (ip->ip_v != IPVERSION) { - DEBUG_PRINT(("RTL8139: +++ C+ mode packet has bad IP version %d expected %d\n", ip->ip_v, IPVERSION)); + if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { + DEBUG_PRINT(("RTL8139: +++ C+ mode packet has bad IP version %d expected %d\n", IP_HEADER_VERSION(ip), IP_HEADER_VERSION_4)); ip = NULL; } else { - hlen = ip->ip_hl << 2; + hlen = IP_HEADER_LENGTH(ip); + ip_protocol = ip->ip_p; + ip_data_len = be16_to_cpu(ip->ip_len) - hlen; } } @@ -1998,84 +2111,178 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) { DEBUG_PRINT(("RTL8139: +++ C+ mode need IP checksum\n")); - if (hlenlocal_m.m_len) {/* min header length */ + if (hleneth_payload_len) {/* min header length */ /* bad packet header len */ /* or packet too short */ } else { ip->ip_sum = 0; - ip->ip_sum = cksum(&local_m, hlen); + ip->ip_sum = ip_checksum(ip, hlen); DEBUG_PRINT(("RTL8139: +++ C+ mode IP header len=%d checksum=%04x\n", hlen, ip->ip_sum)); } } - if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)) + if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP) { - DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n")); +#if defined (DEBUG_RTL8139) + int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK; +#endif + DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task TSO MTU=%d IP data %d frame data %d specified MSS=%d\n", + ETH_MTU, ip_data_len, saved_size - ETH_HLEN, large_send_mss)); - u_int8_t ip_protocol = ip->ip_p; - u_int16_t ip_data_len = ntohs(ip->ip_len) - hlen; + int tcp_send_offset = 0; + int send_count = 0; /* maximum IP header length is 60 bytes */ uint8_t saved_ip_header[60]; - memcpy(saved_ip_header, local_m.m_data, hlen); - struct mbuf local_checksum_m; + /* save IP header template; data area is used in tcp checksum calculation */ + memcpy(saved_ip_header, eth_payload_data, hlen); + + /* a placeholder for checksum calculation routine in tcp case */ + uint8_t *data_to_checksum = eth_payload_data + hlen - 12; + // size_t data_to_checksum_len = eth_payload_len - hlen + 12; + + /* pointer to TCP header */ + tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen); + + int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr); + + /* ETH_MTU = ip header len + tcp header len + payload */ + int tcp_data_len = ip_data_len - tcp_hlen; + int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen; + + DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP data len %d TCP hlen %d TCP data len %d TCP chunk size %d\n", + ip_data_len, tcp_hlen, tcp_data_len, tcp_chunk_size)); + + /* note the cycle below overwrites IP header data, + but restores it from saved_ip_header before sending packet */ + + int is_last_frame = 0; + + for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size) + { + uint16_t chunk_size = tcp_chunk_size; + + /* check if this is the last frame */ + if (tcp_send_offset + tcp_chunk_size >= tcp_data_len) + { + is_last_frame = 1; + chunk_size = tcp_data_len - tcp_send_offset; + } + + DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP seqno %08x\n", be32_to_cpu(p_tcp_hdr->th_seq))); + + /* add 4 TCP pseudoheader fields */ + /* copy IP source and destination fields */ + memcpy(data_to_checksum, saved_ip_header + 12, 8); + + DEBUG_PRINT(("RTL8139: +++ C+ mode TSO calculating TCP checksum for packet with %d bytes data\n", tcp_hlen + chunk_size)); + + if (tcp_send_offset) + { + memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size); + } + + /* keep PUSH and FIN flags only for the last frame */ + if (!is_last_frame) + { + TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN); + } + + /* recalculate TCP checksum */ + ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum; + p_tcpip_hdr->zeros = 0; + p_tcpip_hdr->ip_proto = IP_PROTO_TCP; + p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size); + + p_tcp_hdr->th_sum = 0; + + int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12); + DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP checksum %04x\n", tcp_checksum)); + + p_tcp_hdr->th_sum = tcp_checksum; + + /* restore IP header */ + memcpy(eth_payload_data, saved_ip_header, hlen); + + /* set IP data length and recalculate IP checksum */ + ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size); - local_checksum_m.m_data = local_m.m_data + hlen - 12; - local_checksum_m.m_len = local_m.m_len - hlen + 12; + /* increment IP id for subsequent frames */ + ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id)); + + ip->ip_sum = 0; + ip->ip_sum = ip_checksum(eth_payload_data, hlen); + DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP header len=%d checksum=%04x\n", hlen, ip->ip_sum)); + + int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size; + DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size)); + rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0); + + /* add transferred count to TCP sequence number */ + p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq)); + ++send_count; + } + + /* Stop sending this frame */ + saved_size = 0; + } + else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)) + { + DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n")); + + /* maximum IP header length is 60 bytes */ + uint8_t saved_ip_header[60]; + memcpy(saved_ip_header, eth_payload_data, hlen); + + uint8_t *data_to_checksum = eth_payload_data + hlen - 12; + // size_t data_to_checksum_len = eth_payload_len - hlen + 12; /* add 4 TCP pseudoheader fields */ /* copy IP source and destination fields */ - memcpy(local_checksum_m.m_data, saved_ip_header + 12, 8); + memcpy(data_to_checksum, saved_ip_header + 12, 8); - if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IPPROTO_TCP) + if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP) { DEBUG_PRINT(("RTL8139: +++ C+ mode calculating TCP checksum for packet with %d bytes data\n", ip_data_len)); - struct tcpiphdr * p_tcpip_hdr = (struct tcpiphdr *)local_checksum_m.m_data; - p_tcpip_hdr->ti_x1 = 0; - p_tcpip_hdr->ti_pr = IPPROTO_TCP; - p_tcpip_hdr->ti_len = htons(ip_data_len); + ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum; + p_tcpip_hdr->zeros = 0; + p_tcpip_hdr->ip_proto = IP_PROTO_TCP; + p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len); - struct tcphdr* p_tcp_hdr = (struct tcphdr*) (local_checksum_m.m_data+12); + tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12); p_tcp_hdr->th_sum = 0; - int tcp_checksum = cksum(&local_checksum_m, ip_data_len + 12); + int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); DEBUG_PRINT(("RTL8139: +++ C+ mode TCP checksum %04x\n", tcp_checksum)); p_tcp_hdr->th_sum = tcp_checksum; } - else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IPPROTO_UDP) + else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP) { DEBUG_PRINT(("RTL8139: +++ C+ mode calculating UDP checksum for packet with %d bytes data\n", ip_data_len)); - struct udpiphdr * p_udpip_hdr = (struct udpiphdr *)local_checksum_m.m_data; - p_udpip_hdr->ui_x1 = 0; - p_udpip_hdr->ui_pr = IPPROTO_UDP; - p_udpip_hdr->ui_len = htons(ip_data_len); + ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum; + p_udpip_hdr->zeros = 0; + p_udpip_hdr->ip_proto = IP_PROTO_UDP; + p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len); - struct udphdr* p_udp_hdr = (struct udphdr*) (local_checksum_m.m_data+12); + udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12); - int old_csum = p_udp_hdr->uh_sum; p_udp_hdr->uh_sum = 0; - int udp_checksum = cksum(&local_checksum_m, ip_data_len + 12); + int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); DEBUG_PRINT(("RTL8139: +++ C+ mode UDP checksum %04x\n", udp_checksum)); - if (old_csum != udp_checksum) - { - DEBUG_PRINT(("RTL8139: +++ C+ mode UDP checksum mismatch old=%04x new=%04x\n", - old_csum, udp_checksum)); - } - p_udp_hdr->uh_sum = udp_checksum; } /* restore IP header */ - memcpy(local_m.m_data, saved_ip_header, hlen); + memcpy(eth_payload_data, saved_ip_header, hlen); } } } @@ -2085,16 +2292,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s) DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size)); - if (TxLoopBack == (s->TxConfig & TxLoopBack)) - { - DEBUG_PRINT(("RTL8139: +++ C+ transmit loopback mode\n")); - rtl8139_receive(s, saved_buffer, saved_size); - } - else - { - /* transmit the packet */ - qemu_send_packet(s->vc, saved_buffer, saved_size); - } + rtl8139_transfer_frame(s, saved_buffer, saved_size, 1); /* restore card space if there was no recursion and reset offset */ if (!s->cplus_txbuffer) @@ -2222,17 +2420,17 @@ static uint16_t rtl8139_TSAD_read(RTL8139State *s) |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0) |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0) |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0) - + |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0) |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0) |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0) |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0) - + |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0) |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0) |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0) |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ; - + DEBUG_PRINT(("RTL8139: TSAD read val=0x%04x\n", ret)); @@ -2252,14 +2450,12 @@ static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_ { DEBUG_PRINT(("RTL8139: TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val)); - s->TxAddr[txAddrOffset/4] = le32_to_cpu(val); - - s->currCPlusTxDesc = 0; + s->TxAddr[txAddrOffset/4] = val; } static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset) { - uint32_t ret = cpu_to_le32(s->TxAddr[txAddrOffset/4]); + uint32_t ret = s->TxAddr[txAddrOffset/4]; DEBUG_PRINT(("RTL8139: TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret)); @@ -2921,6 +3117,8 @@ static void rtl8139_save(QEMUFile* f,void* opaque) RTL8139State* s=(RTL8139State*)opaque; int i; + pci_device_save(s->pci_dev, f); + qemu_put_buffer(f, s->phys, 6); qemu_put_buffer(f, s->mult, 8); @@ -2967,7 +3165,8 @@ static void rtl8139_save(QEMUFile* f,void* opaque) qemu_put_be16s(f, &s->CpCmd); qemu_put_8s(f, &s->TxThresh); - qemu_put_be32s(f, &s->irq); + i = 0; + qemu_put_be32s(f, &i); /* unused. */ qemu_put_buffer(f, s->macaddr, 6); qemu_put_be32s(f, &s->rtl8139_mmio_io_addr); @@ -3002,12 +3201,18 @@ static void rtl8139_save(QEMUFile* f,void* opaque) static int rtl8139_load(QEMUFile* f,void* opaque,int version_id) { RTL8139State* s=(RTL8139State*)opaque; - int i; + int i, ret; /* just 2 versions for now */ - if (version_id > 2) + if (version_id > 3) return -EINVAL; + if (version_id >= 3) { + ret = pci_device_load(s->pci_dev, f); + if (ret < 0) + return ret; + } + /* saved since version 1 */ qemu_get_buffer(f, s->phys, 6); qemu_get_buffer(f, s->mult, 8); @@ -3055,7 +3260,7 @@ static int rtl8139_load(QEMUFile* f,void* opaque,int version_id) qemu_get_be16s(f, &s->CpCmd); qemu_get_8s(f, &s->TxThresh); - qemu_get_be32s(f, &s->irq); + qemu_get_be32s(f, &i); /* unused. */ qemu_get_buffer(f, s->macaddr, 6); qemu_get_be32s(f, &s->rtl8139_mmio_io_addr); @@ -3110,7 +3315,7 @@ typedef struct PCIRTL8139State { RTL8139State rtl8139; } PCIRTL8139State; -static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num, +static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { PCIRTL8139State *d = (PCIRTL8139State *)pci_dev; @@ -3119,7 +3324,7 @@ static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num, cpu_register_physical_memory(addr + 0, 0x100, s->rtl8139_mmio_io_addr); } -static void rtl8139_ioport_map(PCIDevice *pci_dev, int region_num, +static void rtl8139_ioport_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { PCIRTL8139State *d = (PCIRTL8139State *)pci_dev; @@ -3149,7 +3354,7 @@ static CPUWriteMemoryFunc *rtl8139_mmio_write[3] = { static inline int64_t rtl8139_get_next_tctr_time(RTL8139State *s, int64_t current_time) { - int64_t next_time = current_time + + int64_t next_time = current_time + muldiv64(1, ticks_per_sec, PCI_FREQUENCY); if (next_time <= current_time) next_time = current_time + 1; @@ -3195,20 +3400,20 @@ static void rtl8139_timer(void *opaque) rtl8139_update_irq(s); } - qemu_mod_timer(s->timer, + qemu_mod_timer(s->timer, rtl8139_get_next_tctr_time(s,curr_time)); } #endif /* RTL8139_ONBOARD_TIMER */ -void pci_rtl8139_init(PCIBus *bus, NICInfo *nd) +void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn) { PCIRTL8139State *d; RTL8139State *s; uint8_t *pci_conf; - + d = (PCIRTL8139State *)pci_register_device(bus, "RTL8139", sizeof(PCIRTL8139State), - -1, + devfn, NULL, NULL); pci_conf = d->dev.config; pci_conf[0x00] = 0xec; /* Realtek 8139 */ @@ -3229,13 +3434,12 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd) s->rtl8139_mmio_io_addr = cpu_register_io_memory(0, rtl8139_mmio_read, rtl8139_mmio_write, s); - pci_register_io_region(&d->dev, 0, 0x100, + pci_register_io_region(&d->dev, 0, 0x100, PCI_ADDRESS_SPACE_IO, rtl8139_ioport_map); - pci_register_io_region(&d->dev, 1, 0x100, + pci_register_io_region(&d->dev, 1, 0x100, PCI_ADDRESS_SPACE_MEM, rtl8139_mmio_map); - s->irq = 16; /* PCI interrupt */ s->pci_dev = (PCIDevice *)d; memcpy(s->macaddr, nd->macaddr, 6); rtl8139_reset(s); @@ -3254,16 +3458,14 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd) s->cplus_txbuffer = NULL; s->cplus_txbuffer_len = 0; s->cplus_txbuffer_offset = 0; - + /* XXX: instance number ? */ - register_savevm("rtl8139", 0, 2, rtl8139_save, rtl8139_load, s); - register_savevm("rtl8139_pci", 0, 1, generic_pci_save, generic_pci_load, - &d->dev); + register_savevm("rtl8139", 0, 3, rtl8139_save, rtl8139_load, s); #if RTL8139_ONBOARD_TIMER s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s); - qemu_mod_timer(s->timer, + qemu_mod_timer(s->timer, rtl8139_get_next_tctr_time(s,qemu_get_clock(vm_clock))); #endif /* RTL8139_ONBOARD_TIMER */ }