summaryrefslogtreecommitdiffstats
path: root/sys/dev/fxp
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/fxp')
-rw-r--r--sys/dev/fxp/if_fxp.c137
-rw-r--r--sys/dev/fxp/if_fxpvar.h1
2 files changed, 110 insertions, 28 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index e969cdc..05632fd 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -59,15 +59,15 @@ __FBSDID("$FreeBSD$");
#include <net/if_types.h>
#include <net/if_vlan_var.h>
-#include <machine/bus.h>
-#include <machine/resource.h>
-
-#ifdef FXP_IP_CSUM_WAR
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <machine/bus.h>
#include <machine/in_cksum.h>
-#endif
+#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h> /* for PCIM_CMD_xxx */
@@ -213,6 +213,8 @@ static int fxp_suspend(device_t dev);
static int fxp_resume(device_t dev);
static void fxp_intr(void *xsc);
+static void fxp_rxcsum(struct fxp_softc *sc, struct ifnet *ifp,
+ struct mbuf *m, uint16_t status, int pos);
static void fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp,
uint8_t statack, int count);
static void fxp_init(void *xsc);
@@ -591,6 +593,9 @@ fxp_attach(device_t dev)
sc->flags |= FXP_FLAG_SAVE_BAD;
}
+ /* For 82559 or later chips, Rx checksum offload is supported. */
+ if (sc->revision >= FXP_REV_82559_A0)
+ sc->flags |= FXP_FLAG_82559_RXCSUM;
/*
* Enable use of extended RFDs and TCBs for 82550
* and later chips. Note: we need extended TXCB support
@@ -603,6 +608,8 @@ fxp_attach(device_t dev)
sc->rfa_size = sizeof (struct fxp_rfa);
sc->tx_cmd = FXP_CB_COMMAND_IPCBXMIT;
sc->flags |= FXP_FLAG_EXT_RFA;
+ /* Use extended RFA instead of 82559 checksum mode. */
+ sc->flags &= ~FXP_FLAG_82559_RXCSUM;
} else {
sc->rfa_size = sizeof (struct fxp_rfa) - FXP_RFAX_LEN;
sc->tx_cmd = FXP_CB_COMMAND_XMIT;
@@ -780,6 +787,11 @@ fxp_attach(device_t dev)
ifp->if_capenable |= IFCAP_HWCSUM;
}
+ if (sc->flags & FXP_FLAG_82559_RXCSUM) {
+ ifp->if_capabilities |= IFCAP_RXCSUM;
+ ifp->if_capenable |= IFCAP_RXCSUM;
+ }
+
#ifdef DEVICE_POLLING
/* Inform the world we support polling. */
ifp->if_capabilities |= IFCAP_POLLING;
@@ -1558,6 +1570,84 @@ fxp_txeof(struct fxp_softc *sc)
}
static void
+fxp_rxcsum(struct fxp_softc *sc, struct ifnet *ifp, struct mbuf *m,
+ uint16_t status, int pos)
+{
+ struct ether_header *eh;
+ struct ip *ip;
+ struct udphdr *uh;
+ int32_t hlen, len, pktlen, temp32;
+ uint16_t csum, *opts;
+
+ if ((sc->flags & FXP_FLAG_82559_RXCSUM) == 0) {
+ if ((status & FXP_RFA_STATUS_PARSE) != 0) {
+ if (status & FXP_RFDX_CS_IP_CSUM_BIT_VALID)
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
+ if (status & FXP_RFDX_CS_IP_CSUM_VALID)
+ m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+ if ((status & FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID) &&
+ (status & FXP_RFDX_CS_TCPUDP_CSUM_VALID)) {
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID |
+ CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+ return;
+ }
+
+ pktlen = m->m_pkthdr.len;
+ if (pktlen < sizeof(struct ether_header) + sizeof(struct ip))
+ return;
+ eh = mtod(m, struct ether_header *);
+ if (eh->ether_type != htons(ETHERTYPE_IP))
+ return;
+ ip = (struct ip *)(eh + 1);
+ if (ip->ip_v != IPVERSION)
+ return;
+
+ hlen = ip->ip_hl << 2;
+ pktlen -= sizeof(struct ether_header);
+ if (hlen < sizeof(struct ip))
+ return;
+ if (ntohs(ip->ip_len) < hlen)
+ return;
+ if (ntohs(ip->ip_len) != pktlen)
+ return;
+ if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
+ return; /* can't handle fragmented packet */
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ if (pktlen < (hlen + sizeof(struct tcphdr)))
+ return;
+ break;
+ case IPPROTO_UDP:
+ if (pktlen < (hlen + sizeof(struct udphdr)))
+ return;
+ uh = (struct udphdr *)((caddr_t)ip + hlen);
+ if (uh->uh_sum == 0)
+ return; /* no checksum */
+ break;
+ default:
+ return;
+ }
+ /* Extract computed checksum. */
+ csum = be16dec(mtod(m, char *) + pos);
+ /* checksum fixup for IP options */
+ len = hlen - sizeof(struct ip);
+ if (len > 0) {
+ opts = (uint16_t *)(ip + 1);
+ for (; len > 0; len -= sizeof(uint16_t), opts++) {
+ temp32 = csum - *opts;
+ temp32 = (temp32 >> 16) + (temp32 & 65535);
+ csum = temp32 & 65535;
+ }
+ }
+ m->m_pkthdr.csum_flags |= CSUM_DATA_VALID;
+ m->m_pkthdr.csum_data = csum;
+}
+
+static void
fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
int count)
{
@@ -1661,6 +1751,11 @@ fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
* of bogus length or CRC errors.
*/
total_len = le16toh(rfa->actual_size) & 0x3fff;
+ if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0 &&
+ (ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+ /* Adjust for appended checksum bytes. */
+ total_len -= 2;
+ }
if (total_len < sizeof(struct ether_header) ||
total_len > MCLBYTES - RFA_ALIGNMENT_FUDGE -
sc->rfa_size || status & FXP_RFA_STATUS_CRC) {
@@ -1668,30 +1763,12 @@ fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack,
continue;
}
- /* Do IP checksum checking. */
- if ((ifp->if_capenable & IFCAP_RXCSUM) != 0 &&
- (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;
+ /* Do IP checksum checking. */
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ fxp_rxcsum(sc, ifp, m, status, total_len);
/*
* Drop locks before calling if_input() since it
* may re-enter fxp_start() in the netisr case.
@@ -2024,7 +2101,8 @@ fxp_init_body(struct fxp_softc *sc)
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 */
+ cbp->tcp_udp_cksum = ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0 &&
+ (ifp->if_capenable & IFCAP_RXCSUM) != 0) ? 1 : 0;
cbp->vlan_tco = 0; /* (don't) enable vlan wakeup */
cbp->link_wake_en = 0; /* (don't) assert PME# on link change */
cbp->arp_wake_en = 0; /* (don't) assert PME# on arp */
@@ -2506,8 +2584,11 @@ fxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
ifp->if_hwassist &= ~FXP_CSUM_FEATURES;
}
if ((mask & IFCAP_RXCSUM) != 0 &&
- (ifp->if_capabilities & IFCAP_RXCSUM) != 0)
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
ifp->if_capenable ^= IFCAP_RXCSUM;
+ if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0)
+ reinit++;
+ }
if ((mask & IFCAP_VLAN_MTU) != 0 &&
(ifp->if_capabilities & IFCAP_VLAN_MTU) != 0) {
ifp->if_capenable ^= IFCAP_VLAN_MTU;
diff --git a/sys/dev/fxp/if_fxpvar.h b/sys/dev/fxp/if_fxpvar.h
index 1bab01e..fdaff2e 100644
--- a/sys/dev/fxp/if_fxpvar.h
+++ b/sys/dev/fxp/if_fxpvar.h
@@ -194,6 +194,7 @@ struct fxp_softc {
#define FXP_FLAG_DEFERRED_RNR 0x0200 /* DEVICE_POLLING deferred RNR */
#define FXP_FLAG_EXT_RFA 0x0400 /* extended RFDs for csum offload */
#define FXP_FLAG_SAVE_BAD 0x0800 /* save bad pkts: bad size, CRC, etc */
+#define FXP_FLAG_82559_RXCSUM 0x1000 /* 82559 compatible RX checksum */
/* Macros to ease CSR access. */
#define CSR_READ_1(sc, reg) bus_read_1(sc->fxp_res[0], reg)
OpenPOWER on IntegriCloud