Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 31 Dec 2011 14:47:35 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r229106 - stable/9/sys/dev/usb/net
Message-ID:  <201112311447.pBVElZ0W068970@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Sat Dec 31 14:47:35 2011
New Revision: 229106
URL: http://svn.freebsd.org/changeset/base/229106

Log:
  MFC r226743:
  Implement TX/RX checksum offloading support for ASIX AX88772B
  controller.

Modified:
  stable/9/sys/dev/usb/net/if_axe.c
  stable/9/sys/dev/usb/net/if_axereg.h
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/amd64/include/xen/   (props changed)
  stable/9/sys/boot/   (props changed)
  stable/9/sys/boot/i386/efi/   (props changed)
  stable/9/sys/boot/ia64/efi/   (props changed)
  stable/9/sys/boot/ia64/ski/   (props changed)
  stable/9/sys/boot/powerpc/boot1.chrp/   (props changed)
  stable/9/sys/boot/powerpc/ofw/   (props changed)
  stable/9/sys/cddl/contrib/opensolaris/   (props changed)
  stable/9/sys/conf/   (props changed)
  stable/9/sys/contrib/dev/acpica/   (props changed)
  stable/9/sys/contrib/octeon-sdk/   (props changed)
  stable/9/sys/contrib/pf/   (props changed)
  stable/9/sys/contrib/x86emu/   (props changed)

Modified: stable/9/sys/dev/usb/net/if_axe.c
==============================================================================
--- stable/9/sys/dev/usb/net/if_axe.c	Sat Dec 31 14:45:43 2011	(r229105)
+++ stable/9/sys/dev/usb/net/if_axe.c	Sat Dec 31 14:47:35 2011	(r229106)
@@ -76,24 +76,30 @@ __FBSDID("$FreeBSD$");
  * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf
  */
 
-#include <sys/stdint.h>
-#include <sys/stddef.h>
 #include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/types.h>
 #include <sys/systm.h>
-#include <sys/kernel.h>
 #include <sys/bus.h>
-#include <sys/module.h>
+#include <sys/condvar.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
 #include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
 #include <sys/mutex.h>
-#include <sys/condvar.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
 #include <sys/sysctl.h>
 #include <sys/sx.h>
-#include <sys/unistd.h>
-#include <sys/callout.h>
-#include <sys/malloc.h>
-#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/if_vlan_var.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
@@ -122,6 +128,8 @@ __FBSDID("$FreeBSD$");
  */
 #define AXE_178_MAX_FRAME_BURST	1
 
+#define	AXE_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP)
+
 #ifdef USB_DEBUG
 static int axe_debug = 0;
 
@@ -186,6 +194,7 @@ static uether_fn_t axe_tick;
 static uether_fn_t axe_setmulti;
 static uether_fn_t axe_setpromisc;
 
+static int	axe_attach_post_sub(struct usb_ether *);
 static int	axe_ifmedia_upd(struct ifnet *);
 static void	axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
 static int	axe_cmd(struct axe_softc *, int, int, int, void *);
@@ -195,6 +204,11 @@ static void	axe_ax88772_phywake(struct a
 static void	axe_ax88772a_init(struct axe_softc *);
 static void	axe_ax88772b_init(struct axe_softc *);
 static int	axe_get_phyno(struct axe_softc *, int);
+static int	axe_ioctl(struct ifnet *, u_long, caddr_t);
+static int	axe_rx_frame(struct usb_ether *, struct usb_page_cache *, int);
+static int	axe_rxeof(struct usb_ether *, struct usb_page_cache *,
+		    unsigned int offset, unsigned int, struct axe_csum_hdr *);
+static void	axe_csum_cfg(struct usb_ether *);
 
 static const struct usb_config axe_config[AXE_N_TRANSFER] = {
 
@@ -263,6 +277,7 @@ MODULE_VERSION(axe, 1);
 
 static const struct usb_ether_methods axe_ue_methods = {
 	.ue_attach_post = axe_attach_post,
+	.ue_attach_post_sub = axe_attach_post_sub,
 	.ue_start = axe_start,
 	.ue_init = axe_init,
 	.ue_stop = axe_stop,
@@ -301,9 +316,6 @@ axe_miibus_readreg(device_t dev, int phy
 	uint16_t val;
 	int locked;
 
-	if (sc->sc_phyno != phy)
-		return (0);
-
 	locked = mtx_owned(&sc->sc_mtx);
 	if (!locked)
 		AXE_LOCK(sc);
@@ -335,10 +347,6 @@ axe_miibus_writereg(device_t dev, int ph
 	int locked;
 
 	val = htole32(val);
-
-	if (sc->sc_phyno != phy)
-		return (0);
-
 	locked = mtx_owned(&sc->sc_mtx);
 	if (!locked)
 		AXE_LOCK(sc);
@@ -369,7 +377,7 @@ axe_miibus_statchg(device_t dev)
 	if (mii == NULL || ifp == NULL ||
 	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
 		goto done;
- 
+
 	sc->sc_flags &= ~AXE_FLAG_LINK;
 	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
 	    (IFM_ACTIVE | IFM_AVALID)) {
@@ -387,14 +395,23 @@ axe_miibus_statchg(device_t dev)
 			break;
 		}
 	}
- 
+
 	/* Lost link, do nothing. */
 	if ((sc->sc_flags & AXE_FLAG_LINK) == 0)
 		goto done;
- 
+
 	val = 0;
-	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
 		val |= AXE_MEDIA_FULL_DUPLEX;
+		if (AXE_IS_178_FAMILY(sc)) {
+			if ((IFM_OPTIONS(mii->mii_media_active) &
+			    IFM_ETH_TXPAUSE) != 0)
+				val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN;
+			if ((IFM_OPTIONS(mii->mii_media_active) &
+			    IFM_ETH_RXPAUSE) != 0)
+				val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN;
+		}
+	}
 	if (AXE_IS_178_FAMILY(sc)) {
 		val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC;
 		if ((sc->sc_flags & AXE_FLAG_178) != 0)
@@ -842,6 +859,53 @@ axe_attach_post(struct usb_ether *ue)
 		axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs);
 }
 
+static int
+axe_attach_post_sub(struct usb_ether *ue)
+{
+	struct axe_softc *sc;
+	struct ifnet *ifp;
+	u_int adv_pause;
+	int error;
+
+	sc = uether_getsc(ue);
+	ifp = ue->ue_ifp;
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_start = uether_start;
+	ifp->if_ioctl = axe_ioctl;
+	ifp->if_init = uether_init;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+	IFQ_SET_READY(&ifp->if_snd);
+
+	if (AXE_IS_178_FAMILY(sc))
+		ifp->if_capabilities |= IFCAP_VLAN_MTU;
+	if (sc->sc_flags & AXE_FLAG_772B) {
+		ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_RXCSUM;
+		ifp->if_hwassist = AXE_CSUM_FEATURES;
+		/*
+		 * Checksum offloading of AX88772B also works with VLAN
+		 * tagged frames but there is no way to take advantage
+		 * of the feature because vlan(4) assumes
+		 * IFCAP_VLAN_HWTAGGING is prerequisite condition to
+		 * support checksum offloading with VLAN. VLAN hardware
+		 * tagging support of AX88772B is very limited so it's
+		 * not possible to announce IFCAP_VLAN_HWTAGGING.
+		 */
+	}
+	ifp->if_capenable = ifp->if_capabilities;
+	if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B | AXE_FLAG_178))
+		adv_pause = MIIF_DOPAUSE;
+	else
+		adv_pause = 0;
+	mtx_lock(&Giant);
+	error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+	    uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+	    BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, adv_pause);
+	mtx_unlock(&Giant);
+
+	return (error);
+}
+
 /*
  * Probe for a AX88172 chip.
  */
@@ -927,52 +991,15 @@ axe_bulk_read_callback(struct usb_xfer *
 {
 	struct axe_softc *sc = usbd_xfer_softc(xfer);
 	struct usb_ether *ue = &sc->sc_ue;
-	struct ifnet *ifp = uether_getifp(ue);
-	struct axe_sframe_hdr hdr;
 	struct usb_page_cache *pc;
-	int err, pos, len;
 	int actlen;
 
 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
 
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
-		pos = 0;
-		len = 0;
-		err = 0;
-
 		pc = usbd_xfer_get_frame(xfer, 0);
-		if (AXE_IS_178_FAMILY(sc)) {
-			while (pos < actlen) {
-				if ((pos + sizeof(hdr)) > actlen) {
-					/* too little data */
-					err = EINVAL;
-					break;
-				}
-				usbd_copy_out(pc, pos, &hdr, sizeof(hdr));
-
-				if ((hdr.len ^ hdr.ilen) != 0xFFFF) {
-					/* we lost sync */
-					err = EINVAL;
-					break;
-				}
-				pos += sizeof(hdr);
-
-				len = le16toh(hdr.len);
-				if ((pos + len) > actlen) {
-					/* invalid length */
-					err = EINVAL;
-					break;
-				}
-				uether_rxbuf(ue, pc, pos, len);
-
-				pos += len + (len % 2);
-			}
-		} else
-			uether_rxbuf(ue, pc, 0, actlen);
-
-		if (err != 0)
-			ifp->if_ierrors++;
+		axe_rx_frame(ue, pc, actlen);
 
 		/* FALLTHROUGH */
 	case USB_ST_SETUP:
@@ -995,6 +1022,131 @@ tr_setup:
 	}
 }
 
+static int
+axe_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen)
+{
+	struct axe_softc *sc;
+	struct axe_sframe_hdr hdr;
+	struct axe_csum_hdr csum_hdr;
+	int error, len, pos;
+
+	sc = uether_getsc(ue);
+	pos = 0;
+	len = 0;
+	error = 0;
+	if ((sc->sc_flags & AXE_FLAG_STD_FRAME) != 0) {
+		while (pos < actlen) {
+			if ((pos + sizeof(hdr)) > actlen) {
+				/* too little data */
+				error = EINVAL;
+				break;
+			}
+			usbd_copy_out(pc, pos, &hdr, sizeof(hdr));
+
+			if ((hdr.len ^ hdr.ilen) != sc->sc_lenmask) {
+				/* we lost sync */
+				error = EINVAL;
+				break;
+			}
+			pos += sizeof(hdr);
+			len = le16toh(hdr.len);
+			if (pos + len > actlen) {
+				/* invalid length */
+				error = EINVAL;
+				break;
+			}
+			axe_rxeof(ue, pc, pos, len, NULL);
+			pos += len + (len % 2);
+		}
+	} else if ((sc->sc_flags & AXE_FLAG_CSUM_FRAME) != 0) {
+		while (pos < actlen) {
+			if ((pos + sizeof(csum_hdr)) > actlen) {
+				/* too little data */
+				error = EINVAL;
+				break;
+			}
+			usbd_copy_out(pc, pos, &csum_hdr, sizeof(csum_hdr));
+
+			csum_hdr.len = le16toh(csum_hdr.len);
+			csum_hdr.ilen = le16toh(csum_hdr.ilen);
+			csum_hdr.cstatus = le16toh(csum_hdr.cstatus);
+			if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^
+			    AXE_CSUM_RXBYTES(csum_hdr.ilen)) !=
+			    sc->sc_lenmask) {
+				/* we lost sync */
+				error = EINVAL;
+				break;
+			}
+			/*
+			 * Get total transferred frame length including
+			 * checksum header.  The length should be multiple
+			 * of 4.
+			 */
+			len = sizeof(csum_hdr) + AXE_CSUM_RXBYTES(csum_hdr.len);
+			len = (len + 3) & ~3;
+			if (pos + len > actlen) {
+				/* invalid length */
+				error = EINVAL;
+				break;
+			}
+			axe_rxeof(ue, pc, pos + sizeof(csum_hdr),
+			    AXE_CSUM_RXBYTES(csum_hdr.len), &csum_hdr);
+			pos += len;
+		}
+	} else
+		axe_rxeof(ue, pc, 0, actlen, NULL);
+
+	if (error != 0)
+		ue->ue_ifp->if_ierrors++;
+	return (error);
+}
+
+static int
+axe_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset,
+    unsigned int len, struct axe_csum_hdr *csum_hdr)
+{
+	struct ifnet *ifp = ue->ue_ifp;
+	struct mbuf *m;
+
+	if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) {
+		ifp->if_ierrors++;
+		return (EINVAL);
+	}
+
+	m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+	if (m == NULL) {
+		ifp->if_iqdrops++;
+		return (ENOMEM);
+	}
+	m->m_len = m->m_pkthdr.len = MCLBYTES;
+	m_adj(m, ETHER_ALIGN);
+
+	usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+	ifp->if_ipackets++;
+	m->m_pkthdr.rcvif = ifp;
+	m->m_pkthdr.len = m->m_len = len;
+
+	if (csum_hdr != NULL && csum_hdr->cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) {
+		if ((csum_hdr->cstatus & (AXE_CSUM_HDR_L4_CSUM_ERR |
+		    AXE_CSUM_HDR_L3_CSUM_ERR)) == 0) {
+			m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED |
+			    CSUM_IP_VALID;
+			if ((csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) ==
+			    AXE_CSUM_HDR_L4_TYPE_TCP ||
+			    (csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) ==
+			    AXE_CSUM_HDR_L4_TYPE_UDP) {
+				m->m_pkthdr.csum_flags |=
+				    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+				m->m_pkthdr.csum_data = 0xffff;
+			}
+		}
+	}
+
+	_IF_ENQUEUE(&ue->ue_rxq, m);
+	return (0);
+}
+
 #if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4)))
 #error "Please update axe_bulk_write_callback()!"
 #endif
@@ -1037,6 +1189,21 @@ tr_setup:
 			if (AXE_IS_178_FAMILY(sc)) {
 				hdr.len = htole16(m->m_pkthdr.len);
 				hdr.ilen = ~hdr.len;
+				/*
+				 * If upper stack computed checksum, driver
+				 * should tell controller not to insert
+				 * computed checksum for checksum offloading
+				 * enabled controller.
+				 */
+				if (ifp->if_capabilities & IFCAP_TXCSUM) {
+					if ((m->m_pkthdr.csum_flags &
+					    AXE_CSUM_FEATURES) != 0)
+						hdr.len |= htole16(
+						    AXE_TX_CSUM_PSEUDO_HDR);
+					else
+						hdr.len |= htole16(
+						    AXE_TX_CSUM_DIS);
+				}
 				usbd_copy_in(pc, pos, &hdr, sizeof(hdr));
 				pos += sizeof(hdr);
 				usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
@@ -1128,6 +1295,34 @@ axe_start(struct usb_ether *ue)
 }
 
 static void
+axe_csum_cfg(struct usb_ether *ue)
+{
+	struct axe_softc *sc;
+	struct ifnet *ifp;
+	uint16_t csum1, csum2;
+
+	sc = uether_getsc(ue);
+	AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+	if ((sc->sc_flags & AXE_FLAG_772B) != 0) {
+		ifp = uether_getifp(ue);
+		csum1 = 0;
+		csum2 = 0;
+		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+			csum1 |= AXE_TXCSUM_IP | AXE_TXCSUM_TCP |
+			    AXE_TXCSUM_UDP;
+		axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL);
+		csum1 = 0;
+		csum2 = 0;
+		if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+			csum1 |= AXE_RXCSUM_IP | AXE_RXCSUM_IPVE |
+			    AXE_RXCSUM_TCP | AXE_RXCSUM_UDP | AXE_RXCSUM_ICMP |
+			    AXE_RXCSUM_IGMP;
+		axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL);
+	}
+}
+
+static void
 axe_init(struct usb_ether *ue)
 {
 	struct axe_softc *sc = uether_getsc(ue);
@@ -1144,27 +1339,40 @@ axe_init(struct usb_ether *ue)
 
 	axe_reset(sc);
 
-	/* Set MAC address. */
-	if (AXE_IS_178_FAMILY(sc))
+	/* Set MAC address and transmitter IPG values. */
+	if (AXE_IS_178_FAMILY(sc)) {
 		axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
-	else
-		axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
-
-	/* Set transmitter IPG values */
-	if (AXE_IS_178_FAMILY(sc))
 		axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2],
 		    (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL);
-	else {
+	} else {
+		axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
 		axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL);
 		axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL);
 		axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL);
 	}
 
-	/* AX88772B uses different maximum frame burst configuration. */
-	if (sc->sc_flags & AXE_FLAG_772B)
+	if (AXE_IS_178_FAMILY(sc)) {
+		sc->sc_flags &= ~(AXE_FLAG_STD_FRAME | AXE_FLAG_CSUM_FRAME);
+		if ((sc->sc_flags & AXE_FLAG_772B) != 0)
+			sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK;
+		else
+			sc->sc_lenmask = AXE_HDR_LEN_MASK;
+		if ((sc->sc_flags & AXE_FLAG_772B) != 0 &&
+		    (ifp->if_capenable & IFCAP_RXCSUM) != 0)
+			sc->sc_flags |= AXE_FLAG_CSUM_FRAME;
+		else
+			sc->sc_flags |= AXE_FLAG_STD_FRAME;
+	}
+
+	/* Configure TX/RX checksum offloading. */
+	axe_csum_cfg(ue);
+
+	if (sc->sc_flags & AXE_FLAG_772B) {
+		/* AX88772B uses different maximum frame burst configuration. */
 		axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG,
 		    ax88772b_mfb_table[AX88772B_MFB_16K].threshold,
 		    ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL);
+	}
 
 	/* Enable receiver, set RX mode. */
 	rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
@@ -1172,11 +1380,17 @@ axe_init(struct usb_ether *ue)
 		if (sc->sc_flags & AXE_FLAG_772B) {
 			/*
 			 * Select RX header format type 1.  Aligning IP
-			 * header on 4 byte boundary is not needed
+			 * header on 4 byte boundary is not needed when
+			 * checksum offloading feature is not used
 			 * because we always copy the received frame in
-			 * RX handler.
+			 * RX handler.  When RX checksum offloading is
+			 * active, aligning IP header is required to
+			 * reflect actual frame length including RX
+			 * header size.
 			 */
 			rxmode |= AXE_772B_RXCMD_HDR_TYPE_1;
+			if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+				rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN;
 		} else {
 			/*
 			 * Default Rx buffer size is too small to get
@@ -1205,7 +1419,6 @@ axe_init(struct usb_ether *ue)
 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
 	/* Switch to selected media. */
 	axe_ifmedia_upd(ifp);
-	axe_start(ue);
 }
 
 static void
@@ -1247,3 +1460,45 @@ axe_stop(struct usb_ether *ue)
 	usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]);
 	usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]);
 }
+
+static int
+axe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+	struct usb_ether *ue = ifp->if_softc;
+	struct axe_softc *sc;
+	struct ifreq *ifr;
+	int error, mask, reinit;
+
+	sc = uether_getsc(ue);
+	ifr = (struct ifreq *)data;
+	error = 0;
+	reinit = 0;
+	if (cmd == SIOCSIFCAP) {
+		AXE_LOCK(sc);
+		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+		if ((mask & IFCAP_TXCSUM) != 0 &&
+		    (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
+			ifp->if_capenable ^= IFCAP_TXCSUM;
+			if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+				ifp->if_hwassist |= AXE_CSUM_FEATURES;
+			else
+				ifp->if_hwassist &= ~AXE_CSUM_FEATURES;
+			reinit++;
+		}
+		if ((mask & IFCAP_RXCSUM) != 0 &&
+		    (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
+			ifp->if_capenable ^= IFCAP_RXCSUM;
+			reinit++;
+		}
+		if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING)
+			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+		else
+			reinit = 0;
+		AXE_UNLOCK(sc);
+		if (reinit > 0)
+			uether_init(ue);
+	} else
+		error = uether_ioctl(ifp, cmd, data);
+
+	return (error);
+}

Modified: stable/9/sys/dev/usb/net/if_axereg.h
==============================================================================
--- stable/9/sys/dev/usb/net/if_axereg.h	Sat Dec 31 14:45:43 2011	(r229105)
+++ stable/9/sys/dev/usb/net/if_axereg.h	Sat Dec 31 14:47:35 2011	(r229106)
@@ -97,6 +97,10 @@
 #define	AXE_CMD_WRITE_VLAN_CTRL			0x4028
 
 #define	AXE_772B_CMD_RXCTL_WRITE_CFG		0x012A
+#define	AXE_772B_CMD_READ_RXCSUM		0x002B
+#define	AXE_772B_CMD_WRITE_RXCSUM		0x012C
+#define	AXE_772B_CMD_READ_TXCSUM		0x002D
+#define	AXE_772B_CMD_WRITE_TXCSUM		0x012E
 
 #define	AXE_SW_RESET_CLEAR			0x00
 #define	AXE_SW_RESET_RR				0x01
@@ -199,6 +203,40 @@
 #define	AXE_VLAN_CTRL_VID1_MASK		0x00000FFF
 #define	AXE_VLAN_CTRL_VID2_MASK		0x0FFF0000
 
+#define	AXE_RXCSUM_IP			0x0001
+#define	AXE_RXCSUM_IPVE			0x0002
+#define	AXE_RXCSUM_IPV6E		0x0004
+#define	AXE_RXCSUM_TCP			0x0008
+#define	AXE_RXCSUM_UDP			0x0010
+#define	AXE_RXCSUM_ICMP			0x0020
+#define	AXE_RXCSUM_IGMP			0x0040
+#define	AXE_RXCSUM_ICMP6		0x0080
+#define	AXE_RXCSUM_TCPV6		0x0100
+#define	AXE_RXCSUM_UDPV6		0x0200
+#define	AXE_RXCSUM_ICMPV6		0x0400
+#define	AXE_RXCSUM_IGMPV6		0x0800
+#define	AXE_RXCSUM_ICMP6V6		0x1000
+#define	AXE_RXCSUM_FOPC			0x8000
+
+#define	AXE_RXCSUM_64TE			0x0100
+#define	AXE_RXCSUM_PPPOE		0x0200
+#define	AXE_RXCSUM_RPCE			0x8000
+
+#define	AXE_TXCSUM_IP			0x0001
+#define	AXE_TXCSUM_TCP			0x0002
+#define	AXE_TXCSUM_UDP			0x0004
+#define	AXE_TXCSUM_ICMP			0x0008
+#define	AXE_TXCSUM_IGMP			0x0010
+#define	AXE_TXCSUM_ICMP6		0x0020
+#define	AXE_TXCSUM_TCPV6		0x0100
+#define	AXE_TXCSUM_UDPV6		0x0200
+#define	AXE_TXCSUM_ICMPV6		0x0400
+#define	AXE_TXCSUM_IGMPV6		0x0800
+#define	AXE_TXCSUM_ICMP6V6		0x1000
+
+#define	AXE_TXCSUM_64TE			0x0001
+#define	AXE_TXCSUM_PPPOE		0x0002
+
 #define	AXE_BULK_BUF_SIZE	16384	/* bytes */
 
 #define	AXE_CTL_READ		0x01
@@ -227,9 +265,62 @@ struct ax88772b_mfb {
 
 struct axe_sframe_hdr {
 	uint16_t len;
+#define	AXE_HDR_LEN_MASK	0xFFFF
+	uint16_t ilen;
+} __packed;
+
+#define	AXE_TX_CSUM_PSEUDO_HDR	0x4000
+#define	AXE_TX_CSUM_DIS		0x8000
+
+/*
+ * When RX checksum offloading is enabled, AX88772B uses new RX header
+ * format and it's not compatible with previous RX header format.  In
+ * addition, IP header align option should be enabled to get correct
+ * frame size including RX header.  Total transferred size including
+ * the RX header is multiple of 4 and controller will pad necessary
+ * bytes if the length is not multiple of 4.
+ * This driver does not enable partial checksum feature which will
+ * compute 16bit checksum from 14th byte to the end of the frame.  If
+ * this feature is enabled, computed checksum value is embedded into
+ * RX header which in turn means it uses different RX header format.
+ */
+struct axe_csum_hdr {
+	uint16_t len;
+#define	AXE_CSUM_HDR_LEN_MASK		0x07FF
+#define	AXE_CSUM_HDR_CRC_ERR		0x1000
+#define	AXE_CSUM_HDR_MII_ERR		0x2000
+#define	AXE_CSUM_HDR_RUNT		0x4000
+#define	AXE_CSUM_HDR_BMCAST		0x8000
 	uint16_t ilen;
+	uint16_t cstatus;
+#define	AXE_CSUM_HDR_VLAN_MASK		0x0007
+#define	AXE_CSUM_HDR_VLAN_STRIP		0x0008
+#define	AXE_CSUM_HDR_VLAN_PRI_MASK	0x0070
+#define	AXE_CSUM_HDR_L4_CSUM_ERR	0x0100
+#define	AXE_CSUM_HDR_L3_CSUM_ERR	0x0200
+#define	AXE_CSUM_HDR_L4_TYPE_UDP	0x0400
+#define	AXE_CSUM_HDR_L4_TYPE_ICMP	0x0800
+#define	AXE_CSUM_HDR_L4_TYPE_IGMP	0x0C00
+#define	AXE_CSUM_HDR_L4_TYPE_TCP	0x1000
+#define	AXE_CSUM_HDR_L4_TYPE_TCPV6	0x1400
+#define	AXE_CSUM_HDR_L4_TYPE_MASK	0x1C00
+#define	AXE_CSUM_HDR_L3_TYPE_IPV4	0x2000
+#define	AXE_CSUM_HDR_L3_TYPE_IPV6	0x4000
+
+#ifdef AXE_APPEND_PARTIAL_CSUM
+	/*
+	 * These members present only when partial checksum
+	 * offloading is enabled.  The checksum value is simple
+	 * 16bit sum of received frame starting at offset 14 of
+	 * the frame to the end of the frame excluding FCS bytes.
+	 */
+	uint16_t csum_value;
+	uint16_t dummy;
+#endif
 } __packed;
 
+#define	AXE_CSUM_RXBYTES(x)	((x) & AXE_CSUM_HDR_LEN_MASK)
+
 #define	GET_MII(sc)		uether_getmii(&(sc)->sc_ue)
 
 /* The interrupt endpoint is currently unused by the ASIX part. */
@@ -247,6 +338,8 @@ struct axe_softc {
 
 	int			sc_flags;
 #define	AXE_FLAG_LINK		0x0001
+#define	AXE_FLAG_STD_FRAME	0x0010
+#define	AXE_FLAG_CSUM_FRAME	0x0020
 #define	AXE_FLAG_772		0x1000	/* AX88772 */
 #define	AXE_FLAG_772A		0x2000	/* AX88772A */
 #define	AXE_FLAG_772B		0x4000	/* AX88772B */
@@ -255,6 +348,7 @@ struct axe_softc {
 	uint8_t			sc_ipgs[3];
 	uint8_t			sc_phyaddrs[2];
 	uint16_t		sc_pwrcfg;
+	uint16_t		sc_lenmask;
 	int			sc_tx_bufsz;
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201112311447.pBVElZ0W068970>