*** if_fxp.c.orig Tue Feb 25 23:43:15 2003 --- if_fxp.c Tue Feb 25 23:43:00 2003 *************** *** 64,69 **** --- 64,76 ---- #include #include + #ifdef FXP_IP_CSUM_WAR + #include + #include + #include + #include + #endif + #include #include /* for PCIM_CMD_xxx */ *************** *** 166,171 **** --- 173,184 ---- { 0, NULL }, }; + #ifdef FXP_IP_CSUM_WAR + #define FXP_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + #else + #define FXP_CSUM_FEATURES (CSUM_TCP | CSUM_UDP) + #endif + static int fxp_probe(device_t dev); static int fxp_attach(device_t dev); static int fxp_detach(device_t dev); *************** *** 675,680 **** --- 688,708 ---- } /* + * Enable use of extended RFDs and TCBs for 82550 + * and later chips. Note: we need extended TXCB support + * too, but that's already enabled by the code above. + */ + + if (sc->revision >= FXP_REV_82550) { + sc->rfa_size = sizeof (struct fxp_rfa); + sc->tx_cmd = FXP_CB_COMMAND_IPCBXMIT; + sc->flags |= FXP_FLAG_EXT_RFA; + } else { + sc->rfa_size = sizeof (struct fxp_rfa) - FXP_RFAX_LEN; + sc->tx_cmd = FXP_CB_COMMAND_XMIT; + } + + /* * Read MAC address. */ fxp_read_eeprom(sc, (u_int16_t *)sc->arpcom.ac_enaddr, 0, 3); *************** *** 726,731 **** --- 754,766 ---- ifp->if_start = fxp_start; ifp->if_watchdog = fxp_watchdog; + /* Enable checksum offload for 82550 or better chips */ + + if (sc->revision >= FXP_REV_82550) { + ifp->if_hwassist = FXP_CSUM_FEATURES; + ifp->if_capabilities = IFCAP_HWCSUM; + } + /* * Attach the interface. */ *************** *** 1110,1116 **** fxp_dma_map_txbuf(void *arg, bus_dma_segment_t *segs, int nseg, bus_size_t mapsize, int error) { ! struct fxp_cb_tx *txp; int i; if (error) --- 1145,1153 ---- fxp_dma_map_txbuf(void *arg, bus_dma_segment_t *segs, int nseg, bus_size_t mapsize, int error) { ! struct fxp_tx *txp; ! struct fxp_softc *sc; ! volatile struct fxp_tbd *bdptr; int i; if (error) *************** *** 1119,1130 **** KASSERT(nseg <= FXP_NTXSEG, ("too many DMA segments")); txp = arg; for (i = 0; i < nseg; i++) { KASSERT(segs[i].ds_len <= MCLBYTES, ("segment size too large")); ! txp->tbd[i].tb_addr = segs[i].ds_addr; ! txp->tbd[i].tb_size = segs[i].ds_len; } ! txp->tbd_number = nseg; } /* --- 1156,1190 ---- KASSERT(nseg <= FXP_NTXSEG, ("too many DMA segments")); txp = arg; + sc = (struct fxp_softc *)txp->tx_mbuf; + + /* + * If this is an 82550/82551, then we're using extended + * TxCBs _and_ we're using checksum offload. This means + * that the TxCB is really an IPCB. One major difference + * between the two is that with plain extended TxCBs, + * the bottom half of the TxCB contains two entries from + * the TBD array, whereas IPCBs contain just one entry: + * one entry (8 bytes) has been sacrificed for the TCP/IP + * checksum offload control bits. So to make things work + * right, we have to start filling in the TBD array + * starting from a different place depending on whether + * the chip is an 82550/82551 or not. + */ + + bdptr = &txp->tx_cb->tbd[0]; + if (sc->flags & FXP_FLAG_EXT_RFA) + bdptr++; + for (i = 0; i < nseg; i++) { KASSERT(segs[i].ds_len <= MCLBYTES, ("segment size too large")); ! bdptr[i].tb_addr = segs[i].ds_addr; ! bdptr[i].tb_size = segs[i].ds_len; } ! ! txp->tx_cb->tbd_number = nseg; ! ! return; } /* *************** *** 1168,1179 **** txp = sc->fxp_desc.tx_last->tx_next; /* * Go through each of the mbufs in the chain and initialize * the transmit buffer descriptors with the physical address * and size of the mbuf. */ error = bus_dmamap_load_mbuf(sc->fxp_mtag, txp->tx_map, ! mb_head, fxp_dma_map_txbuf, txp->tx_cb, 0); if (error && error != EFBIG) { device_printf(sc->dev, "can't map mbuf (error %d)\n", --- 1228,1301 ---- txp = sc->fxp_desc.tx_last->tx_next; /* + * Deal with TCP/IP checksum offload. Note that + * in order for TCP checksum offload to work, + * the pseudo header checksum must have already + * been computed and stored in the checksum field + * in the TCP header. The stack should have + * already done this for us. + */ + + txp->tx_mbuf = (struct mbuf *)sc; /* XXX */ + + if (mb_head->m_pkthdr.csum_flags) { + if (mb_head->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + txp->tx_cb->ipcb_ip_activation_high = + FXP_IPCB_HARDWAREPARSING_ENABLE; + txp->tx_cb->ipcb_ip_schedule = + FXP_IPCB_TCPUDP_CHECKSUM_ENABLE; + if (mb_head->m_pkthdr.csum_flags & CSUM_TCP) + txp->tx_cb->ipcb_ip_schedule |= + FXP_IPCB_TCP_PACKET; + } + #ifdef FXP_IP_CSUM_WAR + /* + * XXX The 82550 chip appears to have trouble + * dealing with IP header checksums in very small + * datagrams, namely fragments from 1 to 3 bytes + * in size. For example, say you want to transmit + * a UDP packet of 1473 bytes. The packet will be + * fragmented over two IP datagrams, the latter + * containing only one byte of data. The 82550 will + * botch the header checksum on the 1-byte fragment. + * As long as the datagram contains 4 or more bytes + * of data, you're ok. + * + * The following code attempts to work around this + * problem: if the datagram is less than 38 bytes + * in size (14 bytes ether header, 20 bytes IP header, + * plus 4 bytes of data), we punt and compute the IP + * header checksum by hand. This workaround doesn't + * work very well, however, since it can be fooled + * by things like VLAN tags and IP options that make + * the header sizes/offsets vary. + */ + + if (mb_head->m_pkthdr.csum_flags & CSUM_IP) { + if (mb_head->m_pkthdr.len < 38) { + struct ip *ip; + mb_head->m_data += ETHER_HDR_LEN; + ip = mtod(mb_head, struct ip *); + ip->ip_sum = in_cksum(mb_head, + ip->ip_hl << 2); + mb_head->m_data -= ETHER_HDR_LEN; + } else { + txp->tx_cb->ipcb_ip_activation_high = + FXP_IPCB_HARDWAREPARSING_ENABLE; + txp->tx->cb->ipcb_ip_schedule |= + FXP_IPCB_IP_CHECKSUM_ENABLE; + } + } + #endif + } + + /* * Go through each of the mbufs in the chain and initialize * the transmit buffer descriptors with the physical address * and size of the mbuf. */ error = bus_dmamap_load_mbuf(sc->fxp_mtag, txp->tx_map, ! mb_head, fxp_dma_map_txbuf, txp, 0); if (error && error != EFBIG) { device_printf(sc->dev, "can't map mbuf (error %d)\n", *************** *** 1221,1234 **** bus_dmamap_sync(sc->fxp_mtag, txp->tx_map, BUS_DMASYNC_PREWRITE); txp->tx_mbuf = mb_head; txp->tx_cb->cb_status = 0; if (sc->tx_queued != FXP_CXINT_THRESH - 1) { txp->tx_cb->cb_command = ! FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; } else { txp->tx_cb->cb_command = ! FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Set a 5 second timer just in case we don't hear --- 1343,1357 ---- bus_dmamap_sync(sc->fxp_mtag, txp->tx_map, BUS_DMASYNC_PREWRITE); txp->tx_mbuf = mb_head; + txp->tx_cb->byte_count = 0; txp->tx_cb->cb_status = 0; if (sc->tx_queued != FXP_CXINT_THRESH - 1) { txp->tx_cb->cb_command = ! sc->tx_cmd | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; } else { txp->tx_cb->cb_command = ! sc->tx_cmd | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Set a 5 second timer just in case we don't hear *************** *** 1373,1378 **** --- 1496,1503 ---- bus_dmamap_unload(sc->fxp_mtag, txp->tx_map); m_freem(txp->tx_mbuf); txp->tx_mbuf = NULL; + /* clear this to reset csum offload bits */ + txp->tx_cb->tbd[0].tb_addr = 0; } sc->tx_queued--; } *************** *** 1495,1500 **** --- 1620,1645 ---- continue; } + /* Do IP checksum checking. */ + if (rfa->rfa_status & FXP_RFA_STATUS_PARSE) { + if (rfa->rfax_csum_sts & + FXP_RFDX_CS_IP_CSUM_BIT_VALID) + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED; + if (rfa->rfax_csum_sts & + FXP_RFDX_CS_IP_CSUM_VALID) + m->m_pkthdr.csum_flags |= + CSUM_IP_VALID; + if ((rfa->rfax_csum_sts & + FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID) && + (rfa->rfax_csum_sts & + FXP_RFDX_CS_TCPUDP_CSUM_VALID)) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID|CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + m->m_pkthdr.len = m->m_len = total_len; m->m_pkthdr.rcvif = ifp; *************** *** 1657,1662 **** --- 1802,1809 ---- bus_dmamap_destroy(sc->fxp_mtag, txp[i].tx_map); m_freem(txp[i].tx_mbuf); txp[i].tx_mbuf = NULL; + /* clear this to reset csum offload bits */ + txp[i].tx_cb->tbd[0].tb_addr = 0; } } } *************** *** 1806,1812 **** cbp->cb_status = 0; cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; cbp->link_addr = -1; /* (no) next command */ ! cbp->byte_count = 22; /* (22) bytes to config */ cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ --- 1953,1959 ---- cbp->cb_status = 0; cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL; cbp->link_addr = -1; /* (no) next command */ ! cbp->byte_count = sc->flags & FXP_FLAG_EXT_RFA ? 32 : 22; cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ *************** *** 1829,1834 **** --- 1976,1982 ---- cbp->underrun_retry = 1; /* retry mode (once) on DMA underrun */ cbp->two_frames = 0; /* do not limit FIFO to 2 frames */ cbp->dyn_tbd = 0; /* (no) dynamic TBD mode */ + cbp->ext_rfa = sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0; cbp->mediatype = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 0 : 1; cbp->csma_dis = 0; /* (don't) disable link */ cbp->tcp_udp_cksum = 0; /* (don't) enable checksum */ *************** *** 1860,1865 **** --- 2008,2014 ---- cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ cbp->multi_ia = 0; /* (don't) accept multiple IAs */ cbp->mc_all = sc->flags & FXP_FLAG_ALL_MCAST ? 1 : 0; + cbp->gamla_rx = sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0; if (sc->revision == FXP_REV_82557) { /* *************** *** 1927,1932 **** --- 2076,2083 ---- for (i = 0; i < FXP_NTXCB; i++) { txp[i].tx_cb = tcbp + i; txp[i].tx_mbuf = NULL; + /* clear this to reset csum offload bits */ + txp[i].tx_cb->tbd[0].tb_addr = 0; bus_dmamap_create(sc->fxp_mtag, 0, &txp[i].tx_map); tcbp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK; tcbp[i].cb_command = FXP_CB_COMMAND_NOP; *************** *** 2077,2084 **** * data start past it. */ rfa = mtod(m, struct fxp_rfa *); ! m->m_data += sizeof(struct fxp_rfa); ! rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE); /* * Initialize the rest of the RFA. Note that since the RFA --- 2228,2236 ---- * data start past it. */ rfa = mtod(m, struct fxp_rfa *); ! m->m_data += sc->rfa_size; ! rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) - ! RFA_ALIGNMENT_FUDGE); /* * Initialize the rest of the RFA. Note that since the RFA *** if_fxpreg.h.orig Tue Feb 25 16:14:06 2003 --- if_fxpreg.h Tue Feb 25 16:16:08 2003 *************** *** 151,157 **** save_bf:1; volatile u_int disc_short_rx:1, underrun_retry:2, ! :3, two_frames:1, /* 8,9 */ dyn_tbd:1; /* 8,9 */ volatile u_int mediatype:1, /* 7 */ --- 151,158 ---- save_bf:1; volatile u_int disc_short_rx:1, underrun_retry:2, ! :2, ! ext_rfa:1, /* 550 */ two_frames:1, /* 8,9 */ dyn_tbd:1; /* 8,9 */ volatile u_int mediatype:1, /* 7 */ *************** *** 205,210 **** --- 206,213 ---- volatile u_int :3, mc_all:1, :4; + volatile u_int8_t gamla_rx:1; /* 550 */ + volatile u_int8_t pad[9]; /* 550 */ }; #define MAXMCADDR 80 *************** *** 237,242 **** --- 240,266 ---- volatile u_int32_t tb_addr; volatile u_int32_t tb_size; }; + + struct fxp_ipcb { + /* + * The following fields are valid only when + * using the IPCB command block for TX checksum offload + * (and TCP large send, VLANs, and (I think) IPsec). To use + * them, you must enable extended TxCBs (available only + * on the 82559 and later) and use the IPCBXMIT command. + * Note that Intel defines the IPCB to be 32 bytes long, + * the last 8 bytes of which comprise the first entry + * in the TBD array (see note below). This means we only + * have to define 8 extra bytes here. + */ + volatile u_int16_t ipcb_schedule_low; + volatile u_int8_t ipcb_ip_schedule; + volatile u_int8_t ipcb_ip_activation_high; + volatile u_int16_t ipcb_vlan_id; + volatile u_int8_t ipcb_ip_header_offset; + volatile u_int8_t ipcb_tcp_header_offset; + }; + struct fxp_cb_tx { volatile u_int16_t cb_status; volatile u_int16_t cb_command; *************** *** 245,259 **** volatile u_int16_t byte_count; volatile u_int8_t tx_threshold; volatile u_int8_t tbd_number; /* * The following structure isn't actually part of the TxCB, * unless the extended TxCB feature is being used. In this * case, the first two elements of the structure below are * fetched along with the TxCB. */ ! volatile struct fxp_tbd tbd[FXP_NTXSEG]; }; /* * Control Block (CB) definitions */ --- 269,305 ---- volatile u_int16_t byte_count; volatile u_int8_t tx_threshold; volatile u_int8_t tbd_number; + /* * The following structure isn't actually part of the TxCB, * unless the extended TxCB feature is being used. In this * case, the first two elements of the structure below are * fetched along with the TxCB. */ ! union { ! volatile struct fxp_ipcb; ! volatile struct fxp_tbd tbd[FXP_NTXSEG]; ! } tx_cb_u; }; + #define tbd tx_cb_u.tbd + #define ipcb_schedule_low tx_cb_u.ipcb_schedule_low + #define ipcb_ip_schedule tx_cb_u.ipcb_ip_schedule + #define ipcb_ip_activation_high tx_cb_u.ipcb_ip_activation_high + #define ipcb_vlan_id tx_cb_u.ipcb_vlan_id + #define ipcb_ip_header_offset tx_cb_u.ipcb_ip_header_offset + #define ipcb_tcp_header_offset tx_cb_u.ipcb_tcp_header_offset + + /* + * IPCB field definitions + */ + #define FXP_IPCB_IP_CHECKSUM_ENABLE 0x10 + #define FXP_IPCB_TCPUDP_CHECKSUM_ENABLE 0x20 + #define FXP_IPCB_TCP_PACKET 0x40 + #define FXP_IPCB_LARGESEND_ENABLE 0x80 + #define FXP_IPCB_HARDWAREPARSING_ENABLE 0x01 + #define FXP_IPCB_INSERTVLAN_ENABLE 0x02 + /* * Control Block (CB) definitions */ *************** *** 270,275 **** --- 316,324 ---- #define FXP_CB_COMMAND_UCODE 0x5 #define FXP_CB_COMMAND_DUMP 0x6 #define FXP_CB_COMMAND_DIAG 0x7 + #define FXP_CB_COMMAND_LOADFILT 0x8 + #define FXP_CB_COMMAND_IPCBXMIT 0x9 + /* command flags */ #define FXP_CB_COMMAND_SF 0x0008 /* simple/flexible mode */ #define FXP_CB_COMMAND_I 0x2000 /* generate interrupt on completion */ *************** *** 287,295 **** --- 336,360 ---- volatile u_int8_t rbd_addr[4]; volatile u_int16_t actual_size; volatile u_int16_t size; + + /* + * The following fields are only available when using + * extended receive mode on an 82550/82551 chipset. + */ + volatile u_int16_t rfax_vlan_id; + volatile u_int8_t rfax_rx_parser_sts; + volatile u_int8_t rfax_rsvd0; + volatile u_int16_t rfax_security_sts; + volatile u_int8_t rfax_csum_sts; + volatile u_int8_t rfax_zerocopy_sts; + volatile u_int8_t rfax_pad[8]; }; + #define FXP_RFAX_LEN 16 + #define FXP_RFA_STATUS_RCOL 0x0001 /* receive collision */ #define FXP_RFA_STATUS_IAMATCH 0x0002 /* 0 = matches station address */ + #define FXP_RFA_STATUS_NOAMATCH 0x0004 /* 1 = doesn't match anything */ + #define FXP_RFA_STATUS_PARSE 0x0008 /* pkt parse ok (82550/1 only) */ #define FXP_RFA_STATUS_S4 0x0010 /* receive error from PHY */ #define FXP_RFA_STATUS_TL 0x0020 /* type/length */ #define FXP_RFA_STATUS_FTS 0x0080 /* frame too short */ *************** *** 303,308 **** --- 368,386 ---- #define FXP_RFA_CONTROL_H 0x10 /* header RFD */ #define FXP_RFA_CONTROL_S 0x4000 /* suspend after reception */ #define FXP_RFA_CONTROL_EL 0x8000 /* end of list */ + + /* Bits in the 'csum_sts' byte */ + #define FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID 0x10 + #define FXP_RFDX_CS_TCPUDP_CSUM_VALID 0x20 + #define FXP_RFDX_CS_IP_CSUM_BIT_VALID 0x01 + #define FXP_RFDX_CS_IP_CSUM_VALID 0x02 + + /* Bits in the 'packet parser' byte */ + #define FXP_RFDX_P_PARSE_BIT 0x08 + #define FXP_RFDX_P_CSUM_PROTOCOL_MASK 0x03 + #define FXP_RFDX_P_TCP_PACKET 0x00 + #define FXP_RFDX_P_UDP_PACKET 0x01 + #define FXP_RFDX_P_IP_PACKET 0x03 /* * Statistics dump area definitions *** if_fxpvar.h.orig Tue Feb 25 16:14:06 2003 --- if_fxpvar.h Tue Feb 25 16:16:08 2003 *************** *** 189,194 **** --- 189,196 ---- u_int8_t saved_intline; u_int8_t saved_cachelnsz; u_int8_t saved_lattimer; + u_int8_t rfa_size; + u_int32_t tx_cmd; }; #define FXP_FLAG_MWI_ENABLE 0x0001 /* MWI enable */ *************** *** 201,206 **** --- 203,209 ---- #define FXP_FLAG_CU_RESUME_BUG 0x0080 /* requires workaround for CU_RESUME */ #define FXP_FLAG_UCODE 0x0100 /* ucode is loaded */ #define FXP_FLAG_DEFERRED_RNR 0x0200 /* DEVICE_POLLING deferred RNR */ + #define FXP_FLAG_EXT_RFA 0x0400 /* extended RFDs for csum offload */ /* Macros to ease CSR access. */ #define CSR_READ_1(sc, reg) \