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)