?? rtl8139.c
字號:
static int rtl8139_transmit_one(RTL8139State *s, int descriptor){ if (!rtl8139_transmitter_enabled(s)) { DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: transmitter disabled\n", descriptor)); return 0; } if (s->TxStatus[descriptor] & TxHostOwns) { DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: owned by host (%08x)\n", descriptor, s->TxStatus[descriptor])); return 0; } DEBUG_PRINT(("RTL8139: +++ transmitting from descriptor %d\n", descriptor)); int txsize = s->TxStatus[descriptor] & 0x1fff; uint8_t txbuffer[0x2000]; DEBUG_PRINT(("RTL8139: +++ transmit reading %d bytes from host memory at 0x%08x\n", txsize, s->TxAddr[descriptor])); cpu_physical_memory_read(s->TxAddr[descriptor], txbuffer, txsize); /* Mark descriptor as transferred */ s->TxStatus[descriptor] |= TxHostOwns; s->TxStatus[descriptor] |= TxStatOK; rtl8139_transfer_frame(s, txbuffer, txsize, 0); DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor)); /* update interrupt */ s->IntrStatus |= TxOK; rtl8139_update_irq(s); 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)) { DEBUG_PRINT(("RTL8139: +++ C+ mode: transmitter disabled\n")); return 0; } if (!rtl8139_cp_transmitter_enabled(s)) { DEBUG_PRINT(("RTL8139: +++ C+ mode: C+ transmitter disabled\n")); return 0 ; } int descriptor = s->currCPlusTxDesc; target_phys_addr_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]); /* Normal priority ring */ cplus_tx_ring_desc += 16 * descriptor; DEBUG_PRINT(("RTL8139: +++ C+ mode reading TX descriptor %d from host memory at %08x0x%08x = 0x%8lx\n", descriptor, s->TxAddr[1], s->TxAddr[0], cplus_tx_ring_desc)); uint32_t val, txdw0,txdw1,txbufLO,txbufHI; cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4); txdw0 = le32_to_cpu(val); cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4); txdw1 = le32_to_cpu(val); cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4); txbufLO = le32_to_cpu(val); cpu_physical_memory_read(cplus_tx_ring_desc+12, (uint8_t *)&val, 4); txbufHI = le32_to_cpu(val); DEBUG_PRINT(("RTL8139: +++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor, txdw0, txdw1, txbufLO, txbufHI));/* w0 ownership flag */#define CP_TX_OWN (1<<31)/* w0 end of ring flag */#define CP_TX_EOR (1<<30)/* first segment of received packet flag */#define CP_TX_FS (1<<29)/* last segment of received packet flag */#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 */#define CP_TX_UDPCS (1<<17)/* TCP checksum offload flag */#define CP_TX_TCPCS (1<<16)/* w0 bits 0...15 : buffer size */#define CP_TX_BUFFER_SIZE (1<<16)#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1)/* w1 tag available flag */#define CP_RX_TAGC (1<<17)/* w1 bits 0...15 : VLAN tag */#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1)/* w2 low 32bit of Rx buffer ptr *//* w3 high 32bit of Rx buffer ptr *//* set after transmission *//* FIFO underrun flag */#define CP_TX_STATUS_UNF (1<<25)/* transmit error summary flag, valid if set any of three below */#define CP_TX_STATUS_TES (1<<23)/* out-of-window collision flag */#define CP_TX_STATUS_OWC (1<<22)/* link failure flag */#define CP_TX_STATUS_LNKF (1<<21)/* excessive collisions flag */#define CP_TX_STATUS_EXC (1<<20) if (!(txdw0 & CP_TX_OWN)) { DEBUG_PRINT(("RTL8139: C+ Tx mode : descriptor %d is owned by host\n", descriptor)); return 0 ; } DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : transmitting from descriptor %d\n", descriptor)); if (txdw0 & CP_TX_FS) { DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is first segment descriptor\n", descriptor)); /* reset internal buffer offset */ s->cplus_txbuffer_offset = 0; } int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK; target_phys_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI); /* make sure we have enough space to assemble the packet */ if (!s->cplus_txbuffer) { 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) { s->cplus_txbuffer_len += CP_TX_BUFFER_SIZE; s->cplus_txbuffer = realloc(s->cplus_txbuffer, s->cplus_txbuffer_len); DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space changed to %d\n", s->cplus_txbuffer_len)); } if (!s->cplus_txbuffer) { /* out of memory */ DEBUG_PRINT(("RTL8139: +++ C+ mode transmiter failed to reallocate %d bytes\n", s->cplus_txbuffer_len)); /* update tally counter */ ++s->tally_counters.TxERR; ++s->tally_counters.TxAbt; return 0; } /* append more data to the packet */ DEBUG_PRINT(("RTL8139: +++ C+ mode transmit reading %d bytes from host memory at %016" PRIx64 " to offset %d\n", txsize, (uint64_t)tx_addr, s->cplus_txbuffer_offset)); cpu_physical_memory_read(tx_addr, s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize); s->cplus_txbuffer_offset += txsize; /* seek to next Rx descriptor */ if (txdw0 & CP_TX_EOR) { s->currCPlusTxDesc = 0; } else { ++s->currCPlusTxDesc; if (s->currCPlusTxDesc >= 64) s->currCPlusTxDesc = 0; } /* transfer ownership to target */ txdw0 &= ~CP_RX_OWN; /* reset error indicator bits */ txdw0 &= ~CP_TX_STATUS_UNF; txdw0 &= ~CP_TX_STATUS_TES; txdw0 &= ~CP_TX_STATUS_OWC; txdw0 &= ~CP_TX_STATUS_LNKF; txdw0 &= ~CP_TX_STATUS_EXC; /* update ring data */ val = cpu_to_le32(txdw0); cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4);// val = cpu_to_le32(txdw1);// cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4); /* Now decide if descriptor being processed is holding the last segment of packet */ if (txdw0 & CP_TX_LS) { DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is last segment descriptor\n", descriptor)); /* can transfer fully assembled packet */ uint8_t *saved_buffer = s->cplus_txbuffer; int saved_size = s->cplus_txbuffer_offset; int saved_buffer_len = s->cplus_txbuffer_len; /* reset the card space to protect from recursive call */ s->cplus_txbuffer = NULL; s->cplus_txbuffer_offset = 0; s->cplus_txbuffer_len = 0; 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 */ ip_header *ip = 0; int hlen = 0; uint8_t ip_protocol = 0; uint16_t ip_data_len = 0; uint8_t *eth_payload_data = 0; size_t eth_payload_len = 0; 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 */ eth_payload_data = saved_buffer + ETH_HLEN; eth_payload_len = saved_size - ETH_HLEN; ip = (ip_header*)eth_payload_data; 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_HEADER_LENGTH(ip); ip_protocol = ip->ip_p; ip_data_len = be16_to_cpu(ip->ip_len) - hlen; } } if (ip) { if (txdw0 & CP_TX_IPCS) { DEBUG_PRINT(("RTL8139: +++ C+ mode need IP checksum\n")); if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */ /* bad packet header len */ /* or packet too short */ } else { ip->ip_sum = 0; 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_LGSEN) && ip_protocol == IP_PROTO_TCP) {#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)); int tcp_send_offset = 0; int send_count = 0; /* maximum IP header length is 60 bytes */ uint8_t saved_ip_header[60]; /* 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); } /* recal
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -