Module Name:    src
Committed By:   skrll
Date:           Sun Dec  4 10:07:06 UTC 2016

Modified Files:
        src/sys/dev/usb: if_axe.c if_axereg.h

Log Message:
Sync with FreeBSD/OpenBSD and add support for 88772B devices.

While I'm here convert to USB_DEBUG


To generate a diff of this commit:
cvs rdiff -u -r1.75 -r1.76 src/sys/dev/usb/if_axe.c
cvs rdiff -u -r1.18 -r1.19 src/sys/dev/usb/if_axereg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/if_axe.c
diff -u src/sys/dev/usb/if_axe.c:1.75 src/sys/dev/usb/if_axe.c:1.76
--- src/sys/dev/usb/if_axe.c:1.75	Fri Nov 25 12:56:29 2016
+++ src/sys/dev/usb/if_axe.c	Sun Dec  4 10:07:06 2016
@@ -1,5 +1,5 @@
-/*	$NetBSD: if_axe.c,v 1.75 2016/11/25 12:56:29 skrll Exp $	*/
-/*	$OpenBSD: if_axe.c,v 1.96 2010/01/09 05:33:08 jsg Exp $ */
+/*	$NetBSD: if_axe.c,v 1.76 2016/12/04 10:07:06 skrll Exp $	*/
+/*	$OpenBSD: if_axe.c,v 1.137 2016/04/13 11:03:37 mpi Exp $ */
 
 /*
  * Copyright (c) 2005, 2006, 2007 Jonathan Gray <j...@openbsd.org>
@@ -50,14 +50,8 @@
  */
 
 /*
- * ASIX Electronics AX88172 USB 2.0 ethernet driver. Used in the
- * LinkSys USB200M and various other adapters.
- *
- * Manuals available from:
- * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
- * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
- * controller) to find the definitions for the RX control register.
- * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
+ * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver.
+ * Used in the LinkSys USB200M and various other adapters.
  *
  * Written by Bill Paul <wp...@windriver.com>
  * Senior Engineer
@@ -77,19 +71,23 @@
  *   to send any packets.
  *
  * Note that this device appears to only support loading the station
- * address via autoload from the EEPROM (i.e. there's no way to manaully
+ * address via autoload from the EEPROM (i.e. there's no way to manually
  * set it).
  *
  * (Adam Weinberger wanted me to name this driver if_gir.c.)
  */
 
 /*
- * Ported to OpenBSD 3/28/2004 by Greg Taleck <tal...@oz.net>
- * with bits and pieces from the aue and url drivers.
+ * Ax88178 and Ax88772 support backported from the OpenBSD driver.
+ * 2007/02/12, J.R. Oldroyd, f...@opal.com
+ *
+ * Manual here:
+ * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf
+ * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.75 2016/11/25 12:56:29 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1.76 2016/12/04 10:07:06 skrll Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -120,6 +118,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1
 #include <dev/mii/miivar.h>
 
 #include <dev/usb/usb.h>
+#include <dev/usb/usbhist.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
 #include <dev/usb/usbdivar.h>
@@ -127,14 +126,62 @@ __KERNEL_RCSID(0, "$NetBSD: if_axe.c,v 1
 
 #include <dev/usb/if_axereg.h>
 
-#ifdef	AXE_DEBUG
-#define DPRINTF(x)	do { if (axedebug) printf x; } while (0)
-#define DPRINTFN(n,x)	do { if (axedebug >= (n)) printf x; } while (0)
-int	axedebug = 0;
+/*
+ * AXE_178_MAX_FRAME_BURST
+ * max frame burst size for Ax88178 and Ax88772
+ *	0	2048 bytes
+ *	1	4096 bytes
+ *	2	8192 bytes
+ *	3	16384 bytes
+ * use the largest your system can handle without USB stalling.
+ *
+ * NB: 88772 parts appear to generate lots of input errors with
+ * a 2K rx buffer and 8K is only slightly faster than 4K on an
+ * EHCI port on a T42 so change at your own risk.
+ */
+#define AXE_178_MAX_FRAME_BURST	1
+
+
+#ifdef USB_DEBUG
+#ifndef AXE_DEBUG
+#define axedebug 0
 #else
-#define DPRINTF(x)
-#define DPRINTFN(n,x)
-#endif
+static int axedebug = 20;
+
+SYSCTL_SETUP(sysctl_hw_axe_setup, "sysctl hw.axe setup")
+{
+	int err;
+	const struct sysctlnode *rnode;
+	const struct sysctlnode *cnode;
+
+	err = sysctl_createv(clog, 0, NULL, &rnode,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "axe",
+	    SYSCTL_DESCR("axe global controls"),
+	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
+
+	if (err)
+		goto fail;
+
+	/* control debugging printfs */
+	err = sysctl_createv(clog, 0, &rnode, &cnode,
+	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
+	    "debug", SYSCTL_DESCR("Enable debugging output"),
+	    NULL, 0, &axedebug, sizeof(axedebug), CTL_CREATE, CTL_EOL);
+	if (err)
+		goto fail;
+
+	return;
+fail:
+	aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
+}
+
+#endif /* AXE_DEBUG */
+#endif /* USB_DEBUG */
+
+#define DPRINTF(FMT,A,B,C,D)	USBHIST_LOGN(axedebug,1,FMT,A,B,C,D)
+#define DPRINTFN(N,FMT,A,B,C,D)	USBHIST_LOGN(axedebug,N,FMT,A,B,C,D)
+#define AXEHIST_FUNC()		USBHIST_FUNC()
+#define AXEHIST_CALLED(name)	USBHIST_CALLED(axedebug)
 
 /*
  * Various supported device vendors/products.
@@ -146,22 +193,23 @@ static const struct axe_type axe_devs[] 
 	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88172}, 0 },
 	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88772}, AX772 },
 	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88772A}, AX772 },
-	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88772B}, AX772 | AX772B },
-	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88772B_1}, AX772 | AX772B },
+	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88772B}, AX772B },
+	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88772B_1}, AX772B },
 	{ { USB_VENDOR_ASIX,		USB_PRODUCT_ASIX_AX88178}, AX178 },
 	{ { USB_VENDOR_ATEN,		USB_PRODUCT_ATEN_UC210T}, 0 },
 	{ { USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_F5D5055 }, AX178 },
 	{ { USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USB2AR}, 0},
-	{ { USB_VENDOR_CISCOLINKSYS,	USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772 },
+	{ { USB_VENDOR_CISCOLINKSYS,	USB_PRODUCT_CISCOLINKSYS_USB200MV2}, AX772A },
 	{ { USB_VENDOR_COREGA,		USB_PRODUCT_COREGA_FETHER_USB2_TX }, 0},
 	{ { USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DUBE100}, 0 },
 	{ { USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DUBE100B1 }, AX772 },
 	{ { USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_DUBE100B1 }, AX772 },
-	{ { USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DUBE100C1 }, AX772 | AX772B },
+	{ { USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DUBE100C1 }, AX772B },
 	{ { USB_VENDOR_GOODWAY,		USB_PRODUCT_GOODWAY_GWUSB2E}, 0 },
 	{ { USB_VENDOR_IODATA,		USB_PRODUCT_IODATA_ETGUS2 }, AX178 },
 	{ { USB_VENDOR_JVC,		USB_PRODUCT_JVC_MP_PRX1}, 0 },
-	{ { USB_VENDOR_LENOVO,		USB_PRODUCT_LENOVO_ETHERNET }, AX772 | AX772B },
+	{ { USB_VENDOR_LENOVO,		USB_PRODUCT_LENOVO_ETHERNET }, AX772B },
+ 	{ { USB_VENDOR_LINKSYS, 	USB_PRODUCT_LINKSYS_HG20F9}, AX772B },
 	{ { USB_VENDOR_LINKSYS2,	USB_PRODUCT_LINKSYS2_USB200M}, 0 },
 	{ { USB_VENDOR_LINKSYS4,	USB_PRODUCT_LINKSYS4_USB1000 }, AX178 },
 	{ { USB_VENDOR_LOGITEC,		USB_PRODUCT_LOGITEC_LAN_GTJU2}, AX178 },
@@ -171,12 +219,24 @@ static const struct axe_type axe_devs[] 
 	{ { USB_VENDOR_NETGEAR,		USB_PRODUCT_NETGEAR_FA120}, 0 },
 	{ { USB_VENDOR_OQO,		USB_PRODUCT_OQO_ETHER01PLUS }, AX772 },
 	{ { USB_VENDOR_PLANEX3,		USB_PRODUCT_PLANEX3_GU1000T }, AX178 },
-	{ { USB_VENDOR_SYSTEMTALKS,	USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 },
 	{ { USB_VENDOR_SITECOM,		USB_PRODUCT_SITECOM_LN029}, 0 },
-	{ { USB_VENDOR_SITECOMEU,	USB_PRODUCT_SITECOMEU_LN028 }, AX178 }
+	{ { USB_VENDOR_SITECOMEU,	USB_PRODUCT_SITECOMEU_LN028 }, AX178 },
+	{ { USB_VENDOR_SITECOMEU,	USB_PRODUCT_SITECOMEU_LN031 }, AX178 },
+	{ { USB_VENDOR_SYSTEMTALKS,	USB_PRODUCT_SYSTEMTALKS_SGCX2UL}, 0 },
 };
 #define axe_lookup(v, p) ((const struct axe_type *)usb_lookup(axe_devs, v, p))
 
+static const struct ax88772b_mfb ax88772b_mfb_table[] = {
+	{ 0x8000, 0x8001, 2048 },
+	{ 0x8100, 0x8147, 4096 },
+	{ 0x8200, 0x81EB, 6144 },
+	{ 0x8300, 0x83D7, 8192 },
+	{ 0x8400, 0x851E, 16384 },
+	{ 0x8500, 0x8666, 20480 },
+	{ 0x8600, 0x87AE, 24576 },
+	{ 0x8700, 0x8A3D, 32768 }
+};
+
 int	axe_match(device_t, cfdata_t, void *);
 void	axe_attach(device_t, device_t, void *);
 int	axe_detach(device_t, int);
@@ -204,8 +264,6 @@ static void	axe_miibus_writereg(device_t
 static void	axe_miibus_statchg(struct ifnet *);
 static int	axe_cmd(struct axe_softc *, int, int, int, void *);
 static void	axe_reset(struct axe_softc *);
-static int	axe_ifmedia_upd(struct ifnet *);
-static void	axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
 
 static void	axe_setmulti(struct axe_softc *);
 static void	axe_lock_mii(struct axe_softc *);
@@ -235,6 +293,7 @@ axe_unlock_mii(struct axe_softc *sc)
 static int
 axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	usb_device_request_t req;
 	usbd_status err;
 
@@ -243,6 +302,8 @@ axe_cmd(struct axe_softc *sc, int cmd, i
 	if (sc->axe_dying)
 		return 0;
 
+	DPRINTFN(20, "cmd %#x index %#x val %#x", cmd, index, val, 0);
+
 	if (AXE_CMD_DIR(cmd))
 		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
 	else
@@ -255,7 +316,7 @@ axe_cmd(struct axe_softc *sc, int cmd, i
 	err = usbd_do_request(sc->axe_udev, &req, buf);
 
 	if (err) {
-		DPRINTF(("axe_cmd err: cmd %d err %d\n", cmd, err));
+		DPRINTF("cmd %d err %d", cmd, err, 0, 0);
 		return -1;
 	}
 	return 0;
@@ -264,11 +325,15 @@ axe_cmd(struct axe_softc *sc, int cmd, i
 static int
 axe_miibus_readreg_locked(device_t dev, int phy, int reg)
 {
+    	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc = device_private(dev);
 	usbd_status err;
 	uint16_t val;
 
+	DPRINTFN(30, "phy 0x%x reg 0x%x\n", phy, reg, 0, 0);
+
 	axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+
 	err = axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, (void *)&val);
 	axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
 	if (err) {
@@ -277,18 +342,17 @@ axe_miibus_readreg_locked(device_t dev, 
 	}
 
 	val = le16toh(val);
-	if (sc->axe_flags & AX772 && reg == MII_BMSR) {
+	if (AXE_IS_772(sc) && reg == MII_BMSR) {
 		/*
-		 * BMSR of AX88772 indicates it supports extended
+		 * BMSR of AX88772 indicates that it supports extended
 		 * capability but the extended status register is
-		 * reserverd for embedded ethernet PHY. So clear the
+		 * reserved for embedded ethernet PHY. So clear the
 		 * extended capability bit of BMSR.
 		 */
 		 val &= ~BMSR_EXTCAP;
 	}
 
-	DPRINTF(("axe_miibus_readreg: phy 0x%x reg 0x%x val 0x%x\n",
-	    phy, reg, val));
+	DPRINTFN(30, "phy 0x%x reg 0x%x val %#x", phy, reg, val, 0);
 
 	return val;
 }
@@ -350,17 +414,26 @@ axe_miibus_writereg(device_t dev, int ph
 static void
 axe_miibus_statchg(struct ifnet *ifp)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
+
 	struct axe_softc *sc = ifp->if_softc;
 	struct mii_data *mii = &sc->axe_mii;
 	int val, err;
 
-	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
-		val = AXE_MEDIA_FULL_DUPLEX;
-	else
-		val = 0;
-
-	if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
-		val |= (AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC);
+	val = 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->axe_flags & AX178)
 			val |= AXE_178_MEDIA_ENCK;
 		switch (IFM_SUBTYPE(mii->mii_media_active)) {
@@ -376,7 +449,7 @@ axe_miibus_statchg(struct ifnet *ifp)
 		}
 	}
 
-	DPRINTF(("axe_miibus_statchg: val=0x%x\n", val));
+	DPRINTF("val=0x%x", val, 0, 0, 0);
 	axe_lock_mii(sc);
 	err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL);
 	axe_unlock_mii(sc);
@@ -386,47 +459,10 @@ axe_miibus_statchg(struct ifnet *ifp)
 	}
 }
 
-/*
- * Set media options
- */
-static int
-axe_ifmedia_upd(struct ifnet *ifp)
-{
-	struct axe_softc *sc = ifp->if_softc;
-	struct mii_data *mii = &sc->axe_mii;
-	int rc;
-
-	sc->axe_link = 0;
-
-	if (mii->mii_instance) {
-		struct mii_softc *miisc;
-
-		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
-			mii_phy_reset(miisc);
-	}
-
-	if ((rc = mii_mediachg(mii)) == ENXIO)
-		return 0;
-	return rc;
-}
-
-/*
- * Report current media status
- */
-static void
-axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
-{
-	struct axe_softc	*sc = ifp->if_softc;
-	struct mii_data		*mii = &sc->axe_mii;
-
-	mii_pollstat(mii);
-	ifmr->ifm_active = mii->mii_media_active;
-	ifmr->ifm_status = mii->mii_media_status;
-}
-
 static void
 axe_setmulti(struct axe_softc *sc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct ifnet *ifp = &sc->sc_if;
 	struct ether_multi *enm;
 	struct ether_multistep step;
@@ -441,11 +477,16 @@ axe_setmulti(struct axe_softc *sc)
 	axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, (void *)&rxmode);
 	rxmode = le16toh(rxmode);
 
-	rxmode &= ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC);
-
-	/* If we want promiscuous mode, set the allframes bit */
-	if (ifp->if_flags & IFF_PROMISC) {
-		rxmode |= AXE_RXCMD_PROMISC;
+	rxmode &=
+	    ~(AXE_RXCMD_ALLMULTI | AXE_RXCMD_PROMISC |
+	    AXE_RXCMD_BROADCAST | AXE_RXCMD_MULTICAST);
+
+	rxmode |=
+	    (ifp->if_flags & IFF_BROADCAST) ? AXE_RXCMD_BROADCAST : 0;
+
+	if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+		if (ifp->if_flags & IFF_PROMISC)
+			rxmode |= AXE_RXCMD_PROMISC;
 		goto allmulti;
 	}
 
@@ -461,6 +502,8 @@ axe_setmulti(struct axe_softc *sc)
 		ETHER_NEXT_MULTI(step, enm);
 	}
 	ifp->if_flags &= ~IFF_ALLMULTI;
+	rxmode |= AXE_RXCMD_MULTICAST;
+
 	axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
 	axe_unlock_mii(sc);
@@ -473,16 +516,40 @@ axe_setmulti(struct axe_softc *sc)
 	axe_unlock_mii(sc);
 }
 
+
 static void
 axe_reset(struct axe_softc *sc)
 {
 
 	if (sc->axe_dying)
 		return;
+
+	/*
+	 * softnet_lock can be taken when NET_MPAFE is not defined when calling
+	 * if_addr_init -> if_init.  This doesn't mixe well with the
+	 * usbd_delay_ms calls in the init routines as things like nd6_slowtimo
+	 * can fire during the wait and attempt to take softnet_lock and then
+	 * block the softclk thread meaing the wait never ends.
+	 */
+#ifndef NET_MPSAFE
 	/* XXX What to reset? */
 
 	/* Wait a little while for the chip to get its brains in order. */
 	DELAY(1000);
+#else
+	axe_lock_mii(sc);
+
+	if (sc->axe_flags & AX178) {
+		axe_ax88178_init(sc);
+	} else if (sc->axe_flags & AX772) {
+		axe_ax88772_init(sc);
+	} else if (sc->axe_flags & AX772A) {
+		axe_ax88772a_init(sc);
+	} else if (sc->axe_flags & AX772B) {
+		axe_ax88772b_init(sc);
+	}
+	axe_unlock_mii(sc);
+#endif
 }
 
 static int
@@ -518,6 +585,7 @@ axe_get_phyno(struct axe_softc *sc, int 
 static void
 axe_ax88178_init(struct axe_softc *sc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	int gpio0, ledmode, phymode;
 	uint16_t eeprom, val;
 
@@ -528,7 +596,7 @@ axe_ax88178_init(struct axe_softc *sc)
 
 	eeprom = le16toh(eeprom);
 
-	DPRINTF((" EEPROM is 0x%x\n", eeprom));
+	DPRINTF("EEPROM is 0x%x", eeprom, 0, 0, 0);
 
 	/* if EEPROM is invalid we have to use to GPIO0 */
 	if (eeprom == 0xffff) {
@@ -541,7 +609,7 @@ axe_ax88178_init(struct axe_softc *sc)
 		ledmode = eeprom >> 8;
 	}
 
-	DPRINTF(("use gpio0: %d, phymode %d\n", gpio0, phymode));
+	DPRINTF("use gpio0: %d, phymode %d", gpio0, phymode, 0, 0);
 
 	/* Program GPIOs depending on PHY hardware. */
 	switch (phymode) {
@@ -623,7 +691,7 @@ axe_ax88178_init(struct axe_softc *sc)
 	axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
 	    AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
 	usbd_delay_ms(sc->axe_udev, 150);
-	/* Enable MII/GMII/RGMII for external PHY */
+	/* Enable MII/GMII/RGMII interface to work with external PHY. */
 	axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL);
 	usbd_delay_ms(sc->axe_udev, 10);
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
@@ -632,13 +700,15 @@ axe_ax88178_init(struct axe_softc *sc)
 static void
 axe_ax88772_init(struct axe_softc *sc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 
 	axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL);
 	usbd_delay_ms(sc->axe_udev, 40);
 
 	if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) {
 		/* ask for the embedded PHY */
-		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL);
+		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0,
+		    AXE_SW_PHY_SELECT_EMBEDDED, NULL);
 		usbd_delay_ms(sc->axe_udev, 10);
 
 		/* power down and reset state, pin reset state */
@@ -658,7 +728,8 @@ axe_ax88772_init(struct axe_softc *sc)
 		    AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL);
 	} else {
 		/* ask for external PHY */
-		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL);
+		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_EXT,
+		    NULL);
 		usbd_delay_ms(sc->axe_udev, 10);
 
 		/* power down internal PHY */
@@ -670,6 +741,101 @@ axe_ax88772_init(struct axe_softc *sc)
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
 }
 
+static void
+axe_ax88772_phywake(struct axe_softc *sc)
+{
+	AXEHIST_FUNC(); AXEHIST_CALLED();
+
+	if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) {
+		/* Manually select internal(embedded) PHY - MAC mode. */
+		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0,
+		    AXE_SW_PHY_SELECT_EMBEDDED,
+		    NULL);
+		usbd_delay_ms(sc->axe_udev, hztoms(hz / 32));
+	} else {
+		/*
+		 * Manually select external PHY - MAC mode.
+		 * Reverse MII/RMII is for AX88772A PHY mode.
+		 */
+		axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, AXE_SW_PHY_SELECT_SS_ENB |
+		    AXE_SW_PHY_SELECT_EXT | AXE_SW_PHY_SELECT_SS_MII, NULL);
+		usbd_delay_ms(sc->axe_udev, hztoms(hz / 32));
+	}
+
+	axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD |
+	    AXE_SW_RESET_IPRL, NULL);
+
+	/* T1 = min 500ns everywhere */
+	usbd_delay_ms(sc->axe_udev, 150);
+
+	/* Take PHY out of power down. */
+	if (sc->axe_phyno == AXE_772_PHY_NO_EPHY) {
+		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL);
+	} else {
+		axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRTE, NULL);
+	}
+
+	/* 772 T2 is 60ms. 772A T2 is 160ms, 772B T2 is 600ms */
+	usbd_delay_ms(sc->axe_udev, 600);
+
+	axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
+
+	/* T3 = 500ns everywhere */
+	usbd_delay_ms(sc->axe_udev, hztoms(hz / 32));
+	axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL, NULL);
+	usbd_delay_ms(sc->axe_udev, hztoms(hz / 32));
+}
+
+static void
+axe_ax88772a_init(struct axe_softc *sc)
+{
+	AXEHIST_FUNC(); AXEHIST_CALLED();
+
+	/* Reload EEPROM. */
+	AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM, hz / 32);
+	axe_ax88772_phywake(sc);
+	/* Stop MAC. */
+	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_ax88772b_init(struct axe_softc *sc)
+{
+	AXEHIST_FUNC(); AXEHIST_CALLED();
+	uint16_t eeprom;
+	int i;
+
+	/* Reload EEPROM. */
+	AXE_GPIO_WRITE(AXE_GPIO_RELOAD_EEPROM , hz / 32);
+
+	/*
+	 * Save PHY power saving configuration(high byte) and
+	 * clear EEPROM checksum value(low byte).
+	 */
+	axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_PHY_PWRCFG, &eeprom);
+	sc->sc_pwrcfg = le16toh(eeprom) & 0xFF00;
+
+	/*
+	 * Auto-loaded default station address from internal ROM is
+	 * 00:00:00:00:00:00 such that an explicit access to EEPROM
+	 * is required to get real station address.
+	 */
+	uint8_t *eaddr = sc->axe_enaddr;
+	for (i = 0; i < ETHER_ADDR_LEN / 2; i++) {
+		axe_cmd(sc, AXE_CMD_SROM_READ, 0, AXE_EEPROM_772B_NODE_ID + i,
+		    &eeprom);
+		eeprom = le16toh(eeprom);
+		*eaddr++ = (uint8_t)(eeprom & 0xFF);
+		*eaddr++ = (uint8_t)((eeprom >> 8) & 0xFF);
+	}
+	/* Wakeup PHY. */
+	axe_ax88772_phywake(sc);
+	/* Stop MAC. */
+	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+#undef	AXE_GPIO_WRITE
+
 /*
  * Probe for a AX88172 chip.
  */
@@ -689,6 +855,7 @@ axe_match(device_t parent, cfdata_t matc
 void
 axe_attach(device_t parent, device_t self, void *aux)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc = device_private(self);
 	struct usb_attach_arg *uaa = aux;
 	struct usbd_device *dev = uaa->uaa_device;
@@ -696,7 +863,6 @@ axe_attach(device_t parent, device_t sel
 	usb_interface_descriptor_t *id;
 	usb_endpoint_descriptor_t *ed;
 	struct mii_data	*mii;
-	uint8_t eaddr[ETHER_ADDR_LEN];
 	char *devinfop;
 	const char *devname = device_xname(self);
 	struct ifnet *ifp;
@@ -736,12 +902,16 @@ axe_attach(device_t parent, device_t sel
 	id = usbd_get_interface_descriptor(sc->axe_iface);
 
 	/* decide on what our bufsize will be */
-	if (sc->axe_flags & AX178 || sc->axe_flags & AX772)
+	if (AXE_IS_178_FAMILY(sc))
 		sc->axe_bufsz = (sc->axe_udev->ud_speed == USB_SPEED_HIGH) ?
 		    AXE_178_MAX_BUFSZ : AXE_178_MIN_BUFSZ;
 	else
 		sc->axe_bufsz = AXE_172_BUFSZ;
 
+	sc->axe_ed[AXE_ENDPT_RX] = -1;
+	sc->axe_ed[AXE_ENDPT_TX] = -1;
+	sc->axe_ed[AXE_ENDPT_INTR] = -1;
+
 	/* Find endpoints. */
 	for (i = 0; i < id->bNumEndpoints; i++) {
 		ed = usbd_interface2endpoint_descriptor(sc->axe_iface, i);
@@ -749,14 +919,16 @@ axe_attach(device_t parent, device_t sel
 			aprint_error_dev(self, "couldn't get ep %d\n", i);
 			return;
 		}
-		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
-		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+		const uint8_t xt = UE_GET_XFERTYPE(ed->bmAttributes);
+		const uint8_t dir = UE_GET_DIR(ed->bEndpointAddress);
+
+		if (dir == UE_DIR_IN && xt == UE_BULK &&
+		    sc->axe_ed[AXE_ENDPT_RX] == -1) {
 			sc->axe_ed[AXE_ENDPT_RX] = ed->bEndpointAddress;
-		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
-			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+		} else if (dir == UE_DIR_OUT && xt == UE_BULK &&
+		    sc->axe_ed[AXE_ENDPT_TX] == -1) {
 			sc->axe_ed[AXE_ENDPT_TX] = ed->bEndpointAddress;
-		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
-			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+		} else if (dir == UE_DIR_IN && xt == UE_INTERRUPT) {
 			sc->axe_ed[AXE_ENDPT_INTR] = ed->bEndpointAddress;
 		}
 	}
@@ -767,39 +939,51 @@ axe_attach(device_t parent, device_t sel
 	axe_lock_mii(sc);
 	axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, (void *)&sc->axe_phyaddrs);
 
-	DPRINTF((" phyaddrs[0]: %x phyaddrs[1]: %x\n",
-	    sc->axe_phyaddrs[0], sc->axe_phyaddrs[1]));
+	DPRINTF(" phyaddrs[0]: %x phyaddrs[1]: %x",
+	    sc->axe_phyaddrs[0], sc->axe_phyaddrs[1], 0, 0);
 	sc->axe_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI);
 	if (sc->axe_phyno == -1)
 		sc->axe_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC);
 	if (sc->axe_phyno == -1) {
-		DPRINTF((" no valid PHY address found, assuming PHY address 0\n"));
+		DPRINTF(" no valid PHY address found, assuming PHY address 0",
+		    0, 0, 0, 0);
 		sc->axe_phyno = 0;
 	}
 
-	if (sc->axe_flags & AX178)
+	/* Initialize controller and get station address. */
+
+	if (sc->axe_flags & AX178) {
 		axe_ax88178_init(sc);
-	else if (sc->axe_flags & AX772)
+		axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->axe_enaddr);
+	} else if (sc->axe_flags & AX772) {
 		axe_ax88772_init(sc);
+		axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->axe_enaddr);
+	} else if (sc->axe_flags & AX772A) {
+		axe_ax88772a_init(sc);
+		axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, sc->axe_enaddr);
+	} else if (sc->axe_flags & AX772B) {
+		axe_ax88772b_init(sc);
+	} else
+		axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, sc->axe_enaddr);
 
 	/*
-	 * Get station address.
+	 * Fetch IPG values.
 	 */
-	if (sc->axe_flags & AX178 || sc->axe_flags & AX772)
-		axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, &eaddr);
-	else
-		axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, &eaddr);
+	if (sc->axe_flags & (AX772A | AX772B)) {
+		/* Set IPG values. */
+		sc->axe_ipgs[0] = AXE_IPG0_DEFAULT;
+		sc->axe_ipgs[1] = AXE_IPG1_DEFAULT;
+		sc->axe_ipgs[2] = AXE_IPG2_DEFAULT;
+	} else
+		axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->axe_ipgs);
 
-	/*
-	 * Load IPG values
-	 */
-	axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, (void *)&sc->axe_ipgs);
 	axe_unlock_mii(sc);
 
 	/*
 	 * An ASIX chip was detected. Inform the world.
 	 */
-	aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(eaddr));
+	aprint_normal_dev(self, "Ethernet address %s\n",
+	    ether_sprintf(sc->axe_enaddr));
 
 	/* Initialize interface info.*/
 	ifp = &sc->sc_if;
@@ -814,7 +998,29 @@ axe_attach(device_t parent, device_t sel
 
 	IFQ_SET_READY(&ifp->if_snd);
 
-	sc->axe_ec.ec_capabilities = ETHERCAP_VLAN_MTU;
+	if (AXE_IS_178_FAMILY(sc))
+		sc->axe_ec.ec_capabilities = ETHERCAP_VLAN_MTU;
+	if (sc->axe_flags & AX772B) {
+		ifp->if_capabilities =
+		    IFCAP_CSUM_IPv4_Rx |
+		    IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx |
+		    IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx;
+		/*
+		 * 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.
+		 */
+	}
+	u_int adv_pause;
+	if (sc->axe_flags & (AX772A | AX772B | AX178))
+		adv_pause = MIIF_DOPAUSE;
+	else
+		adv_pause = 0;
+	adv_pause = 0;
 
 	/* Initialize MII/media info. */
 	mii = &sc->axe_mii;
@@ -825,15 +1031,10 @@ axe_attach(device_t parent, device_t sel
 	mii->mii_flags = MIIF_AUTOTSLEEP;
 
 	sc->axe_ec.ec_mii = mii;
-	if (sc->axe_flags & AXE_MII)
-		ifmedia_init(&mii->mii_media, 0, axe_ifmedia_upd,
-		    axe_ifmedia_sts);
-	else
-		ifmedia_init(&mii->mii_media, 0, ether_mediachange,
-		    ether_mediastatus);
+	ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus);
 
 	mii_attach(sc->axe_dev, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY,
-	    0);
+	    adv_pause);
 
 	if (LIST_EMPTY(&mii->mii_phys)) {
 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
@@ -843,7 +1044,7 @@ axe_attach(device_t parent, device_t sel
 
 	/* Attach the interface. */
 	if_attach(ifp);
-	ether_ifattach(ifp, eaddr);
+	ether_ifattach(ifp, sc->axe_enaddr);
 	rnd_attach_source(&sc->rnd_source, device_xname(sc->axe_dev),
 	    RND_TYPE_NET, RND_FLAG_DEFAULT);
 
@@ -862,12 +1063,11 @@ axe_attach(device_t parent, device_t sel
 int
 axe_detach(device_t self, int flags)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc = device_private(self);
 	int s;
 	struct ifnet *ifp = &sc->sc_if;
 
-	DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axe_dev), __func__));
-
 	/* Detached before attached finished, so just bail out. */
 	if (!sc->axe_attached)
 		return 0;
@@ -876,6 +1076,13 @@ axe_detach(device_t self, int flags)
 
 	sc->axe_dying = true;
 
+	if (sc->axe_ep[AXE_ENDPT_TX] != NULL)
+		usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_TX]);
+	if (sc->axe_ep[AXE_ENDPT_RX] != NULL)
+		usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_RX]);
+	if (sc->axe_ep[AXE_ENDPT_INTR] != NULL)
+		usbd_abort_pipe(sc->axe_ep[AXE_ENDPT_INTR]);
+
 	/*
 	 * Remove any pending tasks.  They cannot be executing because they run
 	 * in the same thread as detach.
@@ -887,6 +1094,12 @@ axe_detach(device_t self, int flags)
 	if (ifp->if_flags & IFF_RUNNING)
 		axe_stop(ifp, 1);
 
+
+	if (--sc->axe_refcnt >= 0) {
+		/* Wait for processes to go away. */
+		usb_detach_waitold(sc->axe_dev);
+	}
+
 	callout_destroy(&sc->axe_stat_ch);
 	mutex_destroy(&sc->axe_mii_lock);
 	rnd_detach_source(&sc->rnd_source);
@@ -904,10 +1117,6 @@ axe_detach(device_t self, int flags)
 
 	sc->axe_attached = false;
 
-	if (--sc->axe_refcnt >= 0) {
-		/* Wait for processes to go away. */
-		usb_detach_waitold(sc->axe_dev);
-	}
 	splx(s);
 
 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->axe_udev, sc->axe_dev);
@@ -918,10 +1127,9 @@ axe_detach(device_t self, int flags)
 int
 axe_activate(device_t self, devact_t act)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc = device_private(self);
 
-	DPRINTFN(2,("%s: %s: enter\n", device_xname(sc->axe_dev), __func__));
-
 	switch (act) {
 	case DVACT_DEACTIVATE:
 		if_deactivate(&sc->axe_ec.ec_if);
@@ -935,12 +1143,12 @@ axe_activate(device_t self, devact_t act
 static int
 axe_rx_list_init(struct axe_softc *sc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
+
 	struct axe_cdata *cd;
 	struct axe_chain *c;
 	int i;
 
-	DPRINTF(("%s: %s: enter\n", device_xname(sc->axe_dev), __func__));
-
 	cd = &sc->axe_cdata;
 	for (i = 0; i < AXE_RX_LIST_CNT; i++) {
 		c = &cd->axe_rx_chain[i];
@@ -961,12 +1169,11 @@ axe_rx_list_init(struct axe_softc *sc)
 static int
 axe_tx_list_init(struct axe_softc *sc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_cdata *cd;
 	struct axe_chain *c;
 	int i;
 
-	DPRINTF(("%s: %s: enter\n", device_xname(sc->axe_dev), __func__));
-
 	cd = &sc->axe_cdata;
 	for (i = 0; i < AXE_TX_LIST_CNT; i++) {
 		c = &cd->axe_tx_chain[i];
@@ -992,14 +1199,13 @@ axe_tx_list_init(struct axe_softc *sc)
 static void
 axe_rxeof(struct usbd_xfer *xfer, void * priv, usbd_status status)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc;
 	struct axe_chain *c;
 	struct ifnet *ifp;
 	uint8_t *buf;
 	uint32_t total_len;
-	u_int rxlen, pktlen;
 	struct mbuf *m;
-	struct axe_sframe_hdr hdr;
 	int s;
 
 	c = (struct axe_chain *)priv;
@@ -1007,8 +1213,6 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 	buf = c->axe_buf;
 	ifp = &sc->sc_if;
 
-	DPRINTFN(10,("%s: %s: enter\n", device_xname(sc->axe_dev),__func__));
-
 	if (sc->axe_dying)
 		return;
 
@@ -1018,9 +1222,10 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 	if (status != USBD_NORMAL_COMPLETION) {
 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
 			return;
-		if (usbd_ratecheck(&sc->axe_rx_notice))
+		if (usbd_ratecheck(&sc->axe_rx_notice)) {
 			aprint_error_dev(sc->axe_dev, "usb errors on rx: %s\n",
 			    usbd_errstr(status));
+		}
 		if (status == USBD_STALLED)
 			usbd_clear_endpoint_stall_async(sc->axe_ep[AXE_ENDPT_RX]);
 		goto done;
@@ -1029,13 +1234,24 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 	usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
 
 	do {
-		if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
+		u_int pktlen = 0;
+		u_int rxlen = 0;
+		int flags = 0;
+		if ((sc->axe_flags & AXSTD_FRAME) != 0) {
+			struct axe_sframe_hdr hdr;
+
 			if (total_len < sizeof(hdr)) {
 				ifp->if_ierrors++;
 				goto done;
 			}
 
 			memcpy(&hdr, buf, sizeof(hdr));
+
+			DPRINTFN(20, "total_len %#x len %x ilen %#x",
+			    total_len,
+			    (le16toh(hdr.len) & AXE_RH1M_RXLEN_MASK),
+			    (le16toh(hdr.ilen) & AXE_RH1M_RXLEN_MASK), 0);
+
 			total_len -= sizeof(hdr);
 			buf += sizeof(hdr);
 
@@ -1056,6 +1272,77 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 				total_len -= rxlen;
 			}
 
+		} else if ((sc->axe_flags & AXCSUM_FRAME) != 0) {
+			struct axe_csum_hdr csum_hdr;
+
+			if (total_len <  sizeof(csum_hdr)) {
+				ifp->if_ierrors++;
+				goto done;
+			}
+
+			memcpy(&csum_hdr, buf, sizeof(csum_hdr));
+
+			csum_hdr.len = le16toh(csum_hdr.len);
+			csum_hdr.ilen = le16toh(csum_hdr.ilen);
+			csum_hdr.cstatus = le16toh(csum_hdr.cstatus);
+
+			DPRINTFN(20, "total_len %#x len %#x ilen %#x"
+			    " cstatus %#x", total_len,
+			    csum_hdr.len, csum_hdr.ilen, csum_hdr.cstatus);
+
+			if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^
+			    AXE_CSUM_RXBYTES(csum_hdr.ilen)) !=
+			    sc->sc_lenmask) {
+				/* we lost sync */
+				ifp->if_ierrors++;
+				DPRINTFN(20, "len %#x ilen %#x lenmask %#x err",
+				    AXE_CSUM_RXBYTES(csum_hdr.len),
+				    AXE_CSUM_RXBYTES(csum_hdr.ilen),
+				    sc->sc_lenmask, 0);
+				goto done;
+			}
+			/*
+			 * Get total transferred frame length including
+			 * checksum header.  The length should be multiple
+			 * of 4.
+			 */
+			pktlen = AXE_CSUM_RXBYTES(csum_hdr.len);
+			int len = sizeof(csum_hdr) + pktlen;
+			len = (len + 3) & ~3;
+			if (total_len < len) {
+				DPRINTFN(20, "total_len %#x < len %#x",
+				    total_len, len, 0, 0);
+				/* invalid length */
+				ifp->if_ierrors++;
+				goto done;
+			}
+			buf += sizeof(csum_hdr);
+
+			const uint16_t cstatus = csum_hdr.cstatus;
+
+			if (cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) {
+				if (cstatus & AXE_CSUM_HDR_L4_CSUM_ERR)
+					flags |= M_CSUM_TCP_UDP_BAD;
+				if (cstatus & AXE_CSUM_HDR_L3_CSUM_ERR)
+					flags |= M_CSUM_IPv4_BAD;
+
+				const uint16_t l4type =
+				    cstatus & AXE_CSUM_HDR_L4_TYPE_MASK;
+
+				if (l4type == AXE_CSUM_HDR_L4_TYPE_TCP)
+					flags |= M_CSUM_TCPv4;
+				if (l4type == AXE_CSUM_HDR_L4_TYPE_UDP)
+					flags |= M_CSUM_UDPv4;
+			}
+			if (total_len < len) {
+				pktlen = total_len;
+				total_len = 0;
+			} else {
+				total_len -= len;
+				rxlen = len - sizeof(csum_hdr);
+			}
+			DPRINTFN(20, "total_len %#x len %#x pktlen %#x"
+			    " rxlen %#x", total_len, len, pktlen, rxlen);
 		} else { /* AX172 */
 			pktlen = rxlen = total_len;
 			total_len = 0;
@@ -1080,16 +1367,17 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 		ifp->if_ipackets++;
 		m_set_rcvif(m, ifp);
 		m->m_pkthdr.len = m->m_len = pktlen;
+		m->m_pkthdr.csum_flags = flags;
 
 		memcpy(mtod(m, uint8_t *), buf, pktlen);
 		buf += rxlen;
 
+		DPRINTFN(10, "deliver %d (%#x)", m->m_len, m->m_len, 0, 0);
+
 		s = splnet();
 
 		bpf_mtap(ifp, m);
 
-		DPRINTFN(10,("%s: %s: deliver %d\n", device_xname(sc->axe_dev),
-		    __func__, m->m_len));
 		if_percpuq_enqueue((ifp)->if_percpuq, (m));
 
 		splx(s);
@@ -1103,7 +1391,7 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 	    USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, axe_rxeof);
 	usbd_transfer(xfer);
 
-	DPRINTFN(10,("%s: %s: start rx\n", device_xname(sc->axe_dev), __func__));
+	DPRINTFN(10, "start rx", 0, 0, 0, 0);
 }
 
 /*
@@ -1114,14 +1402,12 @@ axe_rxeof(struct usbd_xfer *xfer, void *
 static void
 axe_txeof(struct usbd_xfer *xfer, void * priv, usbd_status status)
 {
-	struct axe_softc *sc;
-	struct axe_chain *c;
-	struct ifnet *ifp;
+	AXEHIST_FUNC(); AXEHIST_CALLED();
+	struct axe_chain *c = priv;
+	struct axe_softc *sc = c->axe_sc;
+	struct ifnet *ifp = &sc->sc_if;
 	int s;
 
-	c = priv;
-	sc = c->axe_sc;
-	ifp = &sc->sc_if;
 
 	if (sc->axe_dying)
 		return;
@@ -1155,13 +1441,12 @@ axe_txeof(struct usbd_xfer *xfer, void *
 static void
 axe_tick(void *xsc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc = xsc;
 
 	if (sc == NULL)
 		return;
 
-	DPRINTFN(0xff, ("%s: %s: enter\n", device_xname(sc->axe_dev), __func__));
-
 	if (sc->axe_dying)
 		return;
 
@@ -1172,13 +1457,12 @@ axe_tick(void *xsc)
 static void
 axe_tick_task(void *xsc)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	int s;
-	struct axe_softc *sc;
+	struct axe_softc *sc = xsc;
 	struct ifnet *ifp;
 	struct mii_data *mii;
 
-	sc = xsc;
-
 	if (sc == NULL)
 		return;
 
@@ -1197,8 +1481,7 @@ axe_tick_task(void *xsc)
 	if (sc->axe_link == 0 &&
 	    (mii->mii_media_status & IFM_ACTIVE) != 0 &&
 	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
-		DPRINTF(("%s: %s: got link\n", device_xname(sc->axe_dev),
-		    __func__));
+		DPRINTF("got link", 0, 0, 0, 0);
 		sc->axe_link++;
 		if (!IFQ_IS_EMPTY(&ifp->if_snd))
 			axe_start(ifp);
@@ -1215,7 +1498,6 @@ axe_encap(struct axe_softc *sc, struct m
 	struct ifnet *ifp = &sc->sc_if;
 	struct axe_chain *c;
 	usbd_status err;
-	struct axe_sframe_hdr hdr;
 	int length, boundary;
 
 	c = &sc->axe_cdata.axe_tx_chain[idx];
@@ -1224,7 +1506,9 @@ axe_encap(struct axe_softc *sc, struct m
 	 * Copy the mbuf data into a contiguous buffer, leaving two
 	 * bytes at the beginning to hold the frame length.
 	 */
-	if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
+	if (AXE_IS_178_FAMILY(sc)) {
+	    	struct axe_sframe_hdr hdr;
+
 		boundary = (sc->axe_udev->ud_speed == USB_SPEED_HIGH) ? 512 : 64;
 
 		hdr.len = htole16(m->m_pkthdr.len);
@@ -1262,6 +1546,44 @@ axe_encap(struct axe_softc *sc, struct m
 	return 0;
 }
 
+
+static void
+axe_csum_cfg(struct axe_softc *sc)
+{
+	struct ifnet *ifp = &sc->sc_if;
+	uint16_t csum1, csum2;
+
+	if ((sc->axe_flags & AX772B) != 0) {
+		csum1 = 0;
+		csum2 = 0;
+		if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Tx) != 0)
+			csum1 |= AXE_TXCSUM_IP;
+		if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Tx) != 0)
+			csum1 |= AXE_TXCSUM_TCP;
+		if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Tx) != 0)
+			csum1 |= AXE_TXCSUM_UDP;
+		if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Tx) != 0)
+			csum1 |= AXE_TXCSUM_TCPV6;
+		if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Tx) != 0)
+			csum1 |= AXE_TXCSUM_UDPV6;
+		axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL);
+		csum1 = 0;
+		csum2 = 0;
+
+		if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Rx) != 0)
+			csum1 |= AXE_RXCSUM_IP;
+		if ((ifp->if_capenable & IFCAP_CSUM_TCPv4_Rx) != 0)
+			csum1 |= AXE_RXCSUM_TCP;
+		if ((ifp->if_capenable & IFCAP_CSUM_UDPv4_Rx) != 0)
+			csum1 |= AXE_RXCSUM_UDP;
+		if ((ifp->if_capenable & IFCAP_CSUM_TCPv6_Rx) != 0)
+			csum1 |= AXE_RXCSUM_TCPV6;
+		if ((ifp->if_capenable & IFCAP_CSUM_UDPv6_Rx) != 0)
+			csum1 |= AXE_RXCSUM_UDPV6;
+		axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL);
+	}
+}
+
 static void
 axe_start(struct ifnet *ifp)
 {
@@ -1270,9 +1592,6 @@ axe_start(struct ifnet *ifp)
 
 	sc = ifp->if_softc;
 
-	if ((sc->axe_flags & AXE_MII) != 0 && sc->axe_link == 0)
-		return;
-
 	if ((ifp->if_flags & (IFF_OACTIVE|IFF_RUNNING)) != IFF_RUNNING)
 		return;
 
@@ -1307,12 +1626,12 @@ axe_start(struct ifnet *ifp)
 static int
 axe_init(struct ifnet *ifp)
 {
+	AXEHIST_FUNC(); AXEHIST_CALLED();
 	struct axe_softc *sc = ifp->if_softc;
 	struct axe_chain *c;
 	usbd_status err;
 	int rxmode;
 	int i, s;
-	uint8_t eaddr[ETHER_ADDR_LEN];
 
 	s = splnet();
 
@@ -1324,36 +1643,76 @@ axe_init(struct ifnet *ifp)
 	 */
 	axe_reset(sc);
 
-	/* Set MAC address */
-	if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
-		memcpy(eaddr, CLLADDR(ifp->if_sadl), sizeof(eaddr));
-		axe_lock_mii(sc);
-		axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, eaddr);
-		axe_unlock_mii(sc);
-	}
-
-	/* Set transmitter IPG values */
 	axe_lock_mii(sc);
-	if (sc->axe_flags & AX178 || sc->axe_flags & AX772)
+
+#if 0
+	ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
+			      AX_GPIO_GPO2EN, 5, in_pm);
+#endif
+	/* Set MAC address and transmitter IPG values. */
+	if (AXE_IS_178_FAMILY(sc)) {
+		axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, sc->axe_enaddr);
 		axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->axe_ipgs[2],
 		    (sc->axe_ipgs[1] << 8) | (sc->axe_ipgs[0]), NULL);
-	else {
+	} else {
+		axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, sc->axe_enaddr);
 		axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->axe_ipgs[0], NULL);
 		axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->axe_ipgs[1], NULL);
 		axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->axe_ipgs[2], NULL);
 	}
+	if (AXE_IS_178_FAMILY(sc)) {
+		sc->axe_flags &= ~(AXSTD_FRAME | AXCSUM_FRAME);
+		if ((sc->axe_flags & AX772B) != 0 &&
+		    (ifp->if_capenable & AX_RXCSUM) != 0) {
+			sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK;
+			sc->axe_flags |= AXCSUM_FRAME;
+		} else {
+			sc->sc_lenmask = AXE_HDR_LEN_MASK;
+			sc->axe_flags |= AXSTD_FRAME;
+		}
+	}
+
+	/* Configure TX/RX checksum offloading. */
+	axe_csum_cfg(sc);
 
+	if (sc->axe_flags & AX772B) {
+		/* 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_BROADCAST | AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE;
-	if (sc->axe_flags & AX772B)
-		rxmode |= AXE_772B_RXCMD_RH1M;
-	else if (sc->axe_flags & AX178 || sc->axe_flags & AX772) {
-		if (sc->axe_udev->ud_speed == USB_SPEED_HIGH) {
-			/* Largest possible USB buffer size for AX88178 */
-			rxmode |= AXE_178_RXCMD_MFB;
+	rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
+	if (AXE_IS_178_FAMILY(sc)) {
+		if (sc->axe_flags & AX772B) {
+			/*
+			 * Select RX header format type 1.  Aligning IP
+			 * 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.  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 (sc->axe_flags & AXCSUM_FRAME)
+				rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN;
+		} else {
+			/*
+			 * Default Rx buffer size is too small to get
+			 * maximum performance.
+			 */
+#if 0
+			if (sc->axe_udev->ud_speed == USB_SPEED_HIGH) {
+				/* Largest possible USB buffer size for AX88178 */
+#endif
+			rxmode |= AXE_178_RXCMD_MFB_16384;
 		}
-	} else
+	} else {
 		rxmode |= AXE_172_RXCMD_UNICAST;
+	}
+
 
 	/* If we want promiscuous mode, set the allframes bit. */
 	if (ifp->if_flags & IFF_PROMISC)
@@ -1362,6 +1721,8 @@ axe_init(struct ifnet *ifp)
 	if (ifp->if_flags & IFF_BROADCAST)
 		rxmode |= AXE_RXCMD_BROADCAST;
 
+	DPRINTF("rxmode 0x%#x", rxmode, 0, 0, 0);
+
 	axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
 	axe_unlock_mii(sc);
 
@@ -1498,8 +1859,6 @@ axe_stop(struct ifnet *ifp, int disable)
 	usbd_status err;
 	int i;
 
-	axe_reset(sc);
-
 	ifp->if_timer = 0;
 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 
@@ -1530,6 +1889,8 @@ axe_stop(struct ifnet *ifp, int disable)
 		}
 	}
 
+	axe_reset(sc);
+
 	/* Free RX resources. */
 	for (i = 0; i < AXE_RX_LIST_CNT; i++) {
 		if (sc->axe_cdata.axe_rx_chain[i].axe_xfer != NULL) {

Index: src/sys/dev/usb/if_axereg.h
diff -u src/sys/dev/usb/if_axereg.h:1.18 src/sys/dev/usb/if_axereg.h:1.19
--- src/sys/dev/usb/if_axereg.h:1.18	Sat Apr 23 10:15:31 2016
+++ src/sys/dev/usb/if_axereg.h	Sun Dec  4 10:07:06 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_axereg.h,v 1.18 2016/04/23 10:15:31 skrll Exp $	*/
+/*	$NetBSD: if_axereg.h,v 1.19 2016/12/04 10:07:06 skrll Exp $	*/
 
 /*
  * Copyright (c) 1997, 1998, 1999, 2000-2003
@@ -57,10 +57,11 @@
 #define AXE_CMD_CMD(x)	((x) & 0x00FF)
 
 #define AXE_172_CMD_READ_RXTX_SRAM		0x2002
-#define AXE_182_CMD_READ_RXTX_SRAM		0x6002
+#define AXE_182_CMD_READ_RXTX_SRAM		0x8002
 #define AXE_172_CMD_WRITE_RX_SRAM		0x0103
-#define AXE_172_CMD_WRITE_TX_SRAM		0x0104
 #define AXE_182_CMD_WRITE_RXTX_SRAM		0x8103
+#define AXE_172_CMD_WRITE_TX_SRAM		0x0104
+
 #define AXE_CMD_MII_OPMODE_SW			0x0106
 #define AXE_CMD_MII_READ_REG			0x2007
 #define AXE_CMD_MII_WRITE_REG			0x2108
@@ -95,6 +96,16 @@
 #define AXE_CMD_SW_PHY_STATUS			0x0021
 #define AXE_CMD_SW_PHY_SELECT			0x0122
 
+/* AX88772A and AX88772B only. */
+#define AXE_CMD_READ_VLAN_CTRL			0x4027
+#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
 #define AXE_SW_RESET_RT				0x02
@@ -110,8 +121,10 @@
 #define AXE_178_MEDIA_GMII			0x0001
 #define AXE_MEDIA_FULL_DUPLEX			0x0002
 #define AXE_172_MEDIA_TX_ABORT_ALLOW		0x0004
-/* AX88178 documentation says to always write 1 to reserved bit... */
+
+/* AX88178/88772 documentation says to always write 1 to bit 2 */
 #define AXE_178_MEDIA_MAGIC			0x0004
+/* AX88772 documentation says to always write 0 to bit 3 */
 #define AXE_178_MEDIA_ENCK			0x0008
 #define AXE_172_MEDIA_FLOW_CONTROL_EN		0x0010
 #define AXE_178_MEDIA_RXFLOW_CONTROL_EN		0x0010
@@ -123,6 +136,25 @@
 #define AXE_178_MEDIA_SBP			0x0800
 #define AXE_178_MEDIA_SUPERMAC			0x1000
 
+#define	AXE_RXCMD_PROMISC			0x0001
+#define	AXE_RXCMD_ALLMULTI			0x0002
+#define	AXE_172_RXCMD_UNICAST			0x0004
+#define	AXE_178_RXCMD_KEEP_INVALID_CRC		0x0004
+#define	AXE_RXCMD_BROADCAST			0x0008
+#define	AXE_RXCMD_MULTICAST			0x0010
+#define	AXE_RXCMD_ACCEPT_RUNT			0x0040	/* AX88772B */
+#define	AXE_RXCMD_ENABLE			0x0080
+#define	AXE_178_RXCMD_MFB_MASK			0x0300
+#define	AXE_178_RXCMD_MFB_2048			0x0000
+#define	AXE_178_RXCMD_MFB_4096			0x0100
+#define	AXE_178_RXCMD_MFB_8192			0x0200
+#define	AXE_178_RXCMD_MFB_16384			0x0300
+#define	AXE_772B_RXCMD_HDR_TYPE_0		0x0000
+#define	AXE_772B_RXCMD_HDR_TYPE_1		0x0100
+#define	AXE_772B_RXCMD_IPHDR_ALIGN		0x0200
+#define	AXE_772B_RXCMD_ADD_CHKSUM		0x0400
+#define	AXE_RXCMD_LOOPBACK			0x1000	/* AX88772A/AX88772B */
+
 #define AXE_PHY_SEL_PRI		1
 #define AXE_PHY_SEL_SEC		0
 #define AXE_PHY_TYPE_MASK	0xE0
@@ -141,6 +173,12 @@
 
 #define AXE_772_PHY_NO_EPHY	0x10	/* Embedded 10/100 PHY of AX88772 */
 
+
+/* 178, 772, 772A, 172A, 772B */
+#define AXE_IPG0_DEFAULT	0x15
+#define AXE_IPG1_DEFAULT	0x0c
+#define AXE_IPG2_DEFAULT	0x12
+
 #define	AXE_GPIO0_EN		0x01
 #define	AXE_GPIO0		0x02
 #define	AXE_GPIO1_EN		0x04
@@ -160,21 +198,76 @@
 #define	AXE_PHY_MODE_REALTEK_8251CL	0x0E
 #define	AXE_PHY_MODE_ATTANSIC		0x40
 
-#define AXE_RXCMD_PROMISC			0x0001
-#define AXE_RXCMD_ALLMULTI			0x0002
-#define AXE_172_RXCMD_UNICAST			0x0004
-#define AXE_178_RXCMD_KEEP_INVALID_CRC		0x0004
-#define AXE_RXCMD_BROADCAST			0x0008
-#define AXE_RXCMD_MULTICAST			0x0010
-#define AXE_RXCMD_ENABLE			0x0080
-#define AXE_178_RXCMD_MFB			0x0300
-
-#define AXE_NOPHY				0xE0
-#define AXE_INTPHY				0x10
-
-#define AXE_772B_RXCMD_RH1M	0x0100
-#define AXE_772B_RXCMD_RH2M	0x0200
-#define AXE_772B_RXCMD_RH3M	0x0400
+/* AX88772A/AX88772B only. */
+#define	AXE_SW_PHY_SELECT_EXT		0x0000
+#define	AXE_SW_PHY_SELECT_EMBEDDED	0x0001
+#define	AXE_SW_PHY_SELECT_AUTO		0x0002
+#define	AXE_SW_PHY_SELECT_SS_MII	0x0004
+#define	AXE_SW_PHY_SELECT_SS_RVRS_MII	0x0008
+#define	AXE_SW_PHY_SELECT_SS_RVRS_RMII	0x000C
+#define	AXE_SW_PHY_SELECT_SS_ENB	0x0010
+
+#define	AXE_SW_RESET_CLEAR		0x00
+#define	AXE_SW_RESET_RR			0x01
+#define	AXE_SW_RESET_RT			0x02
+#define	AXE_SW_RESET_PRTE		0x04	/* not 772b */
+#define	AXE_SW_RESET_PRL		0x08	/* not 772b */
+#define	AXE_SW_RESET_BZ			0x10
+#define	AXE_SW_RESET_IPRL		0x20
+#define	AXE_SW_RESET_IPPD		0x40
+#define	AXE_SW_RESET_IPOSC		__BIT(7)
+/* 772B only */
+#define	AXE_SW_RESET_IPPSL_MASK		__BITS(9,8)
+#define	 AXE_SW_RESET_IPPSL_0		0
+#define	 AXE_SW_RESET_IPPSL_1		1
+#define	AXE_SW_RESET_IPCOPS		__BIT(10)
+#define	AXE_SW_RESET_IPCOPSC		__BIT(11)
+#define	AXE_SW_RESET_AD			__BIT(12)
+#define	AXE_SW_RESET_IPFPS		__BIT(13)
+#define	AXE_SW_RESET_WOLLP		__BIT(14)
+
+/* AX88772A/AX88772B VLAN control. */
+#define	AXE_VLAN_CTRL_ENB		0x00001000
+#define	AXE_VLAN_CTRL_STRIP		0x00002000
+#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_NOPHY			0xE0
+#define AXE_INTPHY			0x10
 
 #define AXE_RH1M_RXLEN_MASK	0x07ff
 
@@ -197,23 +290,97 @@
 #define AXE_CONFIG_NO		1
 #define AXE_IFACE_IDX		0
 
+/* EEPROM Map. */
+#define	AXE_EEPROM_772B_NODE_ID		0x04
+#define	AXE_EEPROM_772B_PHY_PWRCFG	0x18
+
+struct ax88772b_mfb {
+	int	byte_cnt;
+	int	threshold;
+	int	size;
+};
+#define	AX88772B_MFB_2K		0
+#define	AX88772B_MFB_4K		1
+#define	AX88772B_MFB_6K		2
+#define	AX88772B_MFB_8K		3
+#define	AX88772B_MFB_16K	4
+#define	AX88772B_MFB_20K	5
+#define	AX88772B_MFB_24K	6
+#define	AX88772B_MFB_32K	7
+
+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
+
 /*
- * The interrupt endpoint is currently unused
- * by the ASIX part.
+ * 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.
  */
-#define AXE_ENDPT_RX		0x0
-#define AXE_ENDPT_TX		0x1
-#define AXE_ENDPT_INTR		0x2
-#define AXE_ENDPT_MAX		0x3
+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)
+
+/*
+ * The interrupt  and CBW endpoints are currently unused byt the driver.
+ */
+#define AXE_ENDPT_CTRL		0x0
+#define AXE_ENDPT_INTR		0x1
+#define AXE_ENDPT_RX		0x2
+#define AXE_ENDPT_TX		0x3
+#define AXx72A_ENDPT_RXCBW	0x4	/* AX88172A, and AX88772A */
+#define AXx72A_ENDPT_TXCBW	0x5	/* AX88172A, and AX88772A */
+#define AX772B_ENDPT_BOTM	0x5	/* AX88772B */
+#define AXE_ENDPT_MAX		0x6
 
 struct axe_type {
 	struct usb_devno	axe_dev;
 	uint16_t		axe_flags;
-#define AX178		0x0001		/* AX88178 */
-#define AX772		0x0002		/* AX88772 */
-#define AX772B		0x0004		/* AX88772B */
-#define AXE_ANY_PHY	0x1000		/* Chip lies about valid phys */
-#define AXE_MII		0x2000		/* Chip-specific MII handling */
 };
 
 struct axe_softc;
@@ -235,11 +402,6 @@ struct axe_cdata {
 	int			axe_rx_prod;
 };
 
-struct axe_sframe_hdr {
-	uint16_t		len;
-	uint16_t		ilen;
-} __packed;
-
 struct axe_softc {
 	device_t axe_dev;
 	struct ethercom		axe_ec;
@@ -250,14 +412,23 @@ struct axe_softc {
 
 	uint16_t		axe_vendor;
 	uint16_t		axe_product;
-	uint16_t		axe_flags;
+	uint32_t		axe_flags;	/* copied from axe_type */
+#define AX178		__BIT(0)	/* AX88178 */
+#define AX772		__BIT(1)	/* AX88772 */
+#define AX772A		__BIT(2)	/* AX88772A */
+#define AX772B		__BIT(3)	/* AX88772B */
+#define	AXSTD_FRAME	__BIT(12)
+#define	AXCSUM_FRAME	__BIT(13)
 
 	int			axe_ed[AXE_ENDPT_MAX];
 	struct usbd_pipe *	axe_ep[AXE_ENDPT_MAX];
 	int			axe_if_flags;
+	int			axe_phyno;
 	struct axe_cdata	axe_cdata;
 	struct callout axe_stat_ch;
 
+	uint8_t			axe_enaddr[ETHER_ADDR_LEN];
+
 	int			axe_refcnt;
 	bool			axe_dying;
 	bool			axe_attached;
@@ -270,7 +441,9 @@ struct axe_softc {
 
 	uint8_t			axe_ipgs[3];
 	uint8_t 		axe_phyaddrs[2];
-	int			axe_phyno;
+	uint16_t		sc_pwrcfg;
+	uint16_t		sc_lenmask;
+
 	struct timeval		axe_rx_notice;
 	int			axe_bufsz;
 
@@ -278,3 +451,19 @@ struct axe_softc {
 };
 
 #define ETHER_ALIGN		2
+
+#define	AXE_IS_178_FAMILY(sc)						  \
+	((sc)->axe_flags & (AX772 | AX772A | AX772B | AX178))
+
+#define	AXE_IS_772(sc)							  \
+	((sc)->axe_flags & (AX772 | AX772A | AX772B))
+
+#define AX_RXCSUM					\
+    (IFCAP_CSUM_IPv4_Rx | 				\
+     IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx |	\
+     IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx)
+
+#define AX_TXCSUM					\
+    (IFCAP_CSUM_IPv4_Tx | 				\
+     IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_UDPv4_Tx |	\
+     IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_UDPv6_Tx)

Reply via email to