Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 2 Jan 2012 23:50:33 +0000 (UTC)
From:      Pyun YongHyeon <yongari@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r229345 - in stable/8/sys: conf dev/usb/net
Message-ID:  <201201022350.q02NoXH4094544@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: yongari
Date: Mon Jan  2 23:50:32 2012
New Revision: 229345
URL: http://svn.freebsd.org/changeset/base/229345

Log:
  MFC r226743:
    Implement TX/RX checksum offloading support for ASIX AX88772B
    controller.
  
    AX88772B data sheet does not show detailed information about
    checksum offloading related things. It seems the controller has
    lots of options to support checksum offloading but I failed to
    understand why this feature requires so much complex controller
    configuration and status bits.
    One of major difference between AX88772B and its predecessor is
    AX88772B uses a new RX header format when RX checksum offloading is
    enabled.  It also requires the received length of a frame should be
    multiple of 4.  Controller will pad necessary bytes to make the
    length of received frame to be multiple of 4.  It is driver's
    responsibility to offset this pad bytes.
    Note, AX88772B could be configured to get partial checksum value in
    in RX header. This mode uses different RX header format and
    currently we don't use that fature.
  
    This change makes axe(4) use driver specific MII attach handler to
    override uether(9)'s default MII attach and announce flow-control
    capability for AX88178/AX88772A/AX88772B to PHY drivers.  It seems
    original AX88772 also supports flow-control but I didn't enable it
    due to lack of test/access to the controller.  The flow-control
    threshold parameter is loaded from EEPROM and there is no way to
    override this value without reprogramming EEPROM. For AX88772B,
    TX/RX IP/TCP/UDP checksum offloading is announced to network stack.
    IPv6 and PPPoE checksum offloading is also supported by controller
    but we have no way to take advantage of these features.
    Driver already knows PHY address so make PHY driver know that
    information and remove unnecessary PHY address check used in
    miibus_readreg/miibus_writereg callbacks.  Also announce AX88178,
    AX88772A and AX88772B support VLAN over-sized frame.
  
    While I'm here clean up headers and remove axe_start() in
    axe_init() because the link wouldn't be available right after media
    change.

Modified:
  stable/8/sys/dev/usb/net/if_axe.c
  stable/8/sys/dev/usb/net/if_axereg.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/conf/ldscript.mips.octeon1.32   (props changed)
  stable/8/sys/conf/ldscript.mips.octeon1.64   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/sys/dev/usb/net/if_axe.c
==============================================================================
--- stable/8/sys/dev/usb/net/if_axe.c	Mon Jan  2 23:47:51 2012	(r229344)
+++ stable/8/sys/dev/usb/net/if_axe.c	Mon Jan  2 23:50:32 2012	(r229345)
@@ -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] = {
 
@@ -266,6 +280,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,
@@ -304,9 +319,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);
@@ -338,10 +350,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);
@@ -372,7 +380,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)) {
@@ -390,14 +398,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)
@@ -845,6 +862,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.
  */
@@ -930,52 +994,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:
@@ -998,6 +1025,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
@@ -1040,6 +1192,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);
@@ -1131,6 +1298,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);
@@ -1147,27 +1342,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);
@@ -1175,11 +1383,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
@@ -1208,7 +1422,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
@@ -1250,3 +1463,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/8/sys/dev/usb/net/if_axereg.h
==============================================================================
--- stable/8/sys/dev/usb/net/if_axereg.h	Mon Jan  2 23:47:51 2012	(r229344)
+++ stable/8/sys/dev/usb/net/if_axereg.h	Mon Jan  2 23:50:32 2012	(r229345)
@@ -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?201201022350.q02NoXH4094544>