summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/net/if_axe.c
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2011-10-25 18:36:18 +0000
committeryongari <yongari@FreeBSD.org>2011-10-25 18:36:18 +0000
commit74a618ae31597582c6defe59202866adb6c421e5 (patch)
treec56a0dab1c7e5eef07ba34c9e1551a1a0c49b623 /sys/dev/usb/net/if_axe.c
parentf1b63119c2457e4abefb1aa803be96e11707f401 (diff)
downloadFreeBSD-src-74a618ae31597582c6defe59202866adb6c421e5.zip
FreeBSD-src-74a618ae31597582c6defe59202866adb6c421e5.tar.gz
Implement TX/RX checksum offloading support for ASIX AX88772B
controller. AX88772B data sheet does not show detailed information about checksum offloading related things. It seems the controller has lots of options to support checksum offloading but I failed to understand why this feature requires so much complex controller configuration and status bits. One of major difference between AX88772B and its predecessor is AX88772B uses a new RX header format when RX checksum offloading is enabled. It also requires the received length of a frame should be multiple of 4. Controller will pad necessary bytes to make the length of received frame to be multiple of 4. It is driver's responsibility to offset this pad bytes. Note, AX88772B could be configured to get partial checksum value in in RX header. This mode uses different RX header format and currently we don't use that fature. This change makes axe(4) use driver specific MII attach handler to override uether(9)'s default MII attach and announce flow-control capability for AX88178/AX88772A/AX88772B to PHY drivers. It seems original AX88772 also supports flow-control but I didn't enable it due to lack of test/access to the controller. The flow-control threshold parameter is loaded from EEPROM and there is no way to override this value without reprogramming EEPROM. For AX88772B, TX/RX IP/TCP/UDP checksum offloading is announced to network stack. IPv6 and PPPoE checksum offloading is also supported by controller but we have no way to take advantage of these features. Driver already knows PHY address so make PHY driver know that information and remove unnecessary PHY address check used in miibus_readreg/miibus_writereg callbacks. Also announce AX88178, AX88772A and AX88772B support VLAN over-sized frame. While I'm here clean up headers and remove axe_start() in axe_init() because the link wouldn't be available right after media change.
Diffstat (limited to 'sys/dev/usb/net/if_axe.c')
-rw-r--r--sys/dev/usb/net/if_axe.c401
1 files changed, 328 insertions, 73 deletions
diff --git a/sys/dev/usb/net/if_axe.c b/sys/dev/usb/net/if_axe.c
index b2aef68..62adda4 100644
--- a/sys/dev/usb/net/if_axe.c
+++ b/sys/dev/usb/net/if_axe.c
@@ -76,24 +76,30 @@ __FBSDID("$FreeBSD$");
* http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf
*/
-#include <sys/stdint.h>
-#include <sys/stddef.h>
#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/types.h>
#include <sys/systm.h>
-#include <sys/kernel.h>
#include <sys/bus.h>
-#include <sys/module.h>
+#include <sys/condvar.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
#include <sys/mutex.h>
-#include <sys/condvar.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
-#include <sys/unistd.h>
-#include <sys/callout.h>
-#include <sys/malloc.h>
-#include <sys/priv.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_types.h>
+#include <net/if_media.h>
+#include <net/if_vlan_var.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
@@ -122,6 +128,8 @@ __FBSDID("$FreeBSD$");
*/
#define AXE_178_MAX_FRAME_BURST 1
+#define AXE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
+
#ifdef USB_DEBUG
static int axe_debug = 0;
@@ -186,6 +194,7 @@ static uether_fn_t axe_tick;
static uether_fn_t axe_setmulti;
static uether_fn_t axe_setpromisc;
+static int axe_attach_post_sub(struct usb_ether *);
static int axe_ifmedia_upd(struct ifnet *);
static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
static int axe_cmd(struct axe_softc *, int, int, int, void *);
@@ -195,6 +204,11 @@ static void axe_ax88772_phywake(struct axe_softc *);
static void axe_ax88772a_init(struct axe_softc *);
static void axe_ax88772b_init(struct axe_softc *);
static int axe_get_phyno(struct axe_softc *, int);
+static int axe_ioctl(struct ifnet *, u_long, caddr_t);
+static int axe_rx_frame(struct usb_ether *, struct usb_page_cache *, int);
+static int axe_rxeof(struct usb_ether *, struct usb_page_cache *,
+ unsigned int offset, unsigned int, struct axe_csum_hdr *);
+static void axe_csum_cfg(struct usb_ether *);
static const struct usb_config axe_config[AXE_N_TRANSFER] = {
@@ -266,6 +280,7 @@ MODULE_VERSION(axe, 1);
static const struct usb_ether_methods axe_ue_methods = {
.ue_attach_post = axe_attach_post,
+ .ue_attach_post_sub = axe_attach_post_sub,
.ue_start = axe_start,
.ue_init = axe_init,
.ue_stop = axe_stop,
@@ -304,9 +319,6 @@ axe_miibus_readreg(device_t dev, int phy, int reg)
uint16_t val;
int locked;
- if (sc->sc_phyno != phy)
- return (0);
-
locked = mtx_owned(&sc->sc_mtx);
if (!locked)
AXE_LOCK(sc);
@@ -338,10 +350,6 @@ axe_miibus_writereg(device_t dev, int phy, int reg, int val)
int locked;
val = htole32(val);
-
- if (sc->sc_phyno != phy)
- return (0);
-
locked = mtx_owned(&sc->sc_mtx);
if (!locked)
AXE_LOCK(sc);
@@ -372,7 +380,7 @@ axe_miibus_statchg(device_t dev)
if (mii == NULL || ifp == NULL ||
(ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
goto done;
-
+
sc->sc_flags &= ~AXE_FLAG_LINK;
if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
(IFM_ACTIVE | IFM_AVALID)) {
@@ -390,14 +398,23 @@ axe_miibus_statchg(device_t dev)
break;
}
}
-
+
/* Lost link, do nothing. */
if ((sc->sc_flags & AXE_FLAG_LINK) == 0)
goto done;
-
+
val = 0;
- if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
val |= AXE_MEDIA_FULL_DUPLEX;
+ if (AXE_IS_178_FAMILY(sc)) {
+ if ((IFM_OPTIONS(mii->mii_media_active) &
+ IFM_ETH_TXPAUSE) != 0)
+ val |= AXE_178_MEDIA_TXFLOW_CONTROL_EN;
+ if ((IFM_OPTIONS(mii->mii_media_active) &
+ IFM_ETH_RXPAUSE) != 0)
+ val |= AXE_178_MEDIA_RXFLOW_CONTROL_EN;
+ }
+ }
if (AXE_IS_178_FAMILY(sc)) {
val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC;
if ((sc->sc_flags & AXE_FLAG_178) != 0)
@@ -845,6 +862,53 @@ axe_attach_post(struct usb_ether *ue)
axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs);
}
+static int
+axe_attach_post_sub(struct usb_ether *ue)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+ u_int adv_pause;
+ int error;
+
+ sc = uether_getsc(ue);
+ ifp = ue->ue_ifp;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_start = uether_start;
+ ifp->if_ioctl = axe_ioctl;
+ ifp->if_init = uether_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
+ ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ if (AXE_IS_178_FAMILY(sc))
+ ifp->if_capabilities |= IFCAP_VLAN_MTU;
+ if (sc->sc_flags & AXE_FLAG_772B) {
+ ifp->if_capabilities |= IFCAP_TXCSUM | IFCAP_RXCSUM;
+ ifp->if_hwassist = AXE_CSUM_FEATURES;
+ /*
+ * Checksum offloading of AX88772B also works with VLAN
+ * tagged frames but there is no way to take advantage
+ * of the feature because vlan(4) assumes
+ * IFCAP_VLAN_HWTAGGING is prerequisite condition to
+ * support checksum offloading with VLAN. VLAN hardware
+ * tagging support of AX88772B is very limited so it's
+ * not possible to announce IFCAP_VLAN_HWTAGGING.
+ */
+ }
+ ifp->if_capenable = ifp->if_capabilities;
+ if (sc->sc_flags & (AXE_FLAG_772A | AXE_FLAG_772B | AXE_FLAG_178))
+ adv_pause = MIIF_DOPAUSE;
+ else
+ adv_pause = 0;
+ mtx_lock(&Giant);
+ error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp,
+ uether_ifmedia_upd, ue->ue_methods->ue_mii_sts,
+ BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, adv_pause);
+ mtx_unlock(&Giant);
+
+ return (error);
+}
+
/*
* Probe for a AX88172 chip.
*/
@@ -930,52 +994,15 @@ axe_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct axe_softc *sc = usbd_xfer_softc(xfer);
struct usb_ether *ue = &sc->sc_ue;
- struct ifnet *ifp = uether_getifp(ue);
- struct axe_sframe_hdr hdr;
struct usb_page_cache *pc;
- int err, pos, len;
int actlen;
usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
- pos = 0;
- len = 0;
- err = 0;
-
pc = usbd_xfer_get_frame(xfer, 0);
- if (AXE_IS_178_FAMILY(sc)) {
- while (pos < actlen) {
- if ((pos + sizeof(hdr)) > actlen) {
- /* too little data */
- err = EINVAL;
- break;
- }
- usbd_copy_out(pc, pos, &hdr, sizeof(hdr));
-
- if ((hdr.len ^ hdr.ilen) != 0xFFFF) {
- /* we lost sync */
- err = EINVAL;
- break;
- }
- pos += sizeof(hdr);
-
- len = le16toh(hdr.len);
- if ((pos + len) > actlen) {
- /* invalid length */
- err = EINVAL;
- break;
- }
- uether_rxbuf(ue, pc, pos, len);
-
- pos += len + (len % 2);
- }
- } else
- uether_rxbuf(ue, pc, 0, actlen);
-
- if (err != 0)
- ifp->if_ierrors++;
+ axe_rx_frame(ue, pc, actlen);
/* FALLTHROUGH */
case USB_ST_SETUP:
@@ -998,6 +1025,131 @@ tr_setup:
}
}
+static int
+axe_rx_frame(struct usb_ether *ue, struct usb_page_cache *pc, int actlen)
+{
+ struct axe_softc *sc;
+ struct axe_sframe_hdr hdr;
+ struct axe_csum_hdr csum_hdr;
+ int error, len, pos;
+
+ sc = uether_getsc(ue);
+ pos = 0;
+ len = 0;
+ error = 0;
+ if ((sc->sc_flags & AXE_FLAG_STD_FRAME) != 0) {
+ while (pos < actlen) {
+ if ((pos + sizeof(hdr)) > actlen) {
+ /* too little data */
+ error = EINVAL;
+ break;
+ }
+ usbd_copy_out(pc, pos, &hdr, sizeof(hdr));
+
+ if ((hdr.len ^ hdr.ilen) != sc->sc_lenmask) {
+ /* we lost sync */
+ error = EINVAL;
+ break;
+ }
+ pos += sizeof(hdr);
+ len = le16toh(hdr.len);
+ if (pos + len > actlen) {
+ /* invalid length */
+ error = EINVAL;
+ break;
+ }
+ axe_rxeof(ue, pc, pos, len, NULL);
+ pos += len + (len % 2);
+ }
+ } else if ((sc->sc_flags & AXE_FLAG_CSUM_FRAME) != 0) {
+ while (pos < actlen) {
+ if ((pos + sizeof(csum_hdr)) > actlen) {
+ /* too little data */
+ error = EINVAL;
+ break;
+ }
+ usbd_copy_out(pc, pos, &csum_hdr, sizeof(csum_hdr));
+
+ csum_hdr.len = le16toh(csum_hdr.len);
+ csum_hdr.ilen = le16toh(csum_hdr.ilen);
+ csum_hdr.cstatus = le16toh(csum_hdr.cstatus);
+ if ((AXE_CSUM_RXBYTES(csum_hdr.len) ^
+ AXE_CSUM_RXBYTES(csum_hdr.ilen)) !=
+ sc->sc_lenmask) {
+ /* we lost sync */
+ error = EINVAL;
+ break;
+ }
+ /*
+ * Get total transferred frame length including
+ * checksum header. The length should be multiple
+ * of 4.
+ */
+ len = sizeof(csum_hdr) + AXE_CSUM_RXBYTES(csum_hdr.len);
+ len = (len + 3) & ~3;
+ if (pos + len > actlen) {
+ /* invalid length */
+ error = EINVAL;
+ break;
+ }
+ axe_rxeof(ue, pc, pos + sizeof(csum_hdr),
+ AXE_CSUM_RXBYTES(csum_hdr.len), &csum_hdr);
+ pos += len;
+ }
+ } else
+ axe_rxeof(ue, pc, 0, actlen, NULL);
+
+ if (error != 0)
+ ue->ue_ifp->if_ierrors++;
+ return (error);
+}
+
+static int
+axe_rxeof(struct usb_ether *ue, struct usb_page_cache *pc, unsigned int offset,
+ unsigned int len, struct axe_csum_hdr *csum_hdr)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+ struct mbuf *m;
+
+ if (len < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN) {
+ ifp->if_ierrors++;
+ return (EINVAL);
+ }
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ ifp->if_iqdrops++;
+ return (ENOMEM);
+ }
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+ m_adj(m, ETHER_ALIGN);
+
+ usbd_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ if (csum_hdr != NULL && csum_hdr->cstatus & AXE_CSUM_HDR_L3_TYPE_IPV4) {
+ if ((csum_hdr->cstatus & (AXE_CSUM_HDR_L4_CSUM_ERR |
+ AXE_CSUM_HDR_L3_CSUM_ERR)) == 0) {
+ m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED |
+ CSUM_IP_VALID;
+ if ((csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) ==
+ AXE_CSUM_HDR_L4_TYPE_TCP ||
+ (csum_hdr->cstatus & AXE_CSUM_HDR_L4_TYPE_MASK) ==
+ AXE_CSUM_HDR_L4_TYPE_UDP) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ }
+ }
+
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+ return (0);
+}
+
#if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4)))
#error "Please update axe_bulk_write_callback()!"
#endif
@@ -1040,6 +1192,21 @@ tr_setup:
if (AXE_IS_178_FAMILY(sc)) {
hdr.len = htole16(m->m_pkthdr.len);
hdr.ilen = ~hdr.len;
+ /*
+ * If upper stack computed checksum, driver
+ * should tell controller not to insert
+ * computed checksum for checksum offloading
+ * enabled controller.
+ */
+ if (ifp->if_capabilities & IFCAP_TXCSUM) {
+ if ((m->m_pkthdr.csum_flags &
+ AXE_CSUM_FEATURES) != 0)
+ hdr.len |= htole16(
+ AXE_TX_CSUM_PSEUDO_HDR);
+ else
+ hdr.len |= htole16(
+ AXE_TX_CSUM_DIS);
+ }
usbd_copy_in(pc, pos, &hdr, sizeof(hdr));
pos += sizeof(hdr);
usbd_m_copy_in(pc, pos, m, 0, m->m_pkthdr.len);
@@ -1131,6 +1298,34 @@ axe_start(struct usb_ether *ue)
}
static void
+axe_csum_cfg(struct usb_ether *ue)
+{
+ struct axe_softc *sc;
+ struct ifnet *ifp;
+ uint16_t csum1, csum2;
+
+ sc = uether_getsc(ue);
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((sc->sc_flags & AXE_FLAG_772B) != 0) {
+ ifp = uether_getifp(ue);
+ csum1 = 0;
+ csum2 = 0;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ csum1 |= AXE_TXCSUM_IP | AXE_TXCSUM_TCP |
+ AXE_TXCSUM_UDP;
+ axe_cmd(sc, AXE_772B_CMD_WRITE_TXCSUM, csum2, csum1, NULL);
+ csum1 = 0;
+ csum2 = 0;
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ csum1 |= AXE_RXCSUM_IP | AXE_RXCSUM_IPVE |
+ AXE_RXCSUM_TCP | AXE_RXCSUM_UDP | AXE_RXCSUM_ICMP |
+ AXE_RXCSUM_IGMP;
+ axe_cmd(sc, AXE_772B_CMD_WRITE_RXCSUM, csum2, csum1, NULL);
+ }
+}
+
+static void
axe_init(struct usb_ether *ue)
{
struct axe_softc *sc = uether_getsc(ue);
@@ -1147,27 +1342,40 @@ axe_init(struct usb_ether *ue)
axe_reset(sc);
- /* Set MAC address. */
- if (AXE_IS_178_FAMILY(sc))
+ /* Set MAC address and transmitter IPG values. */
+ if (AXE_IS_178_FAMILY(sc)) {
axe_cmd(sc, AXE_178_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
- else
- axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
-
- /* Set transmitter IPG values */
- if (AXE_IS_178_FAMILY(sc))
axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2],
(sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL);
- else {
+ } else {
+ axe_cmd(sc, AXE_172_CMD_WRITE_NODEID, 0, 0, IF_LLADDR(ifp));
axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL);
axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL);
axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL);
}
- /* AX88772B uses different maximum frame burst configuration. */
- if (sc->sc_flags & AXE_FLAG_772B)
+ if (AXE_IS_178_FAMILY(sc)) {
+ sc->sc_flags &= ~(AXE_FLAG_STD_FRAME | AXE_FLAG_CSUM_FRAME);
+ if ((sc->sc_flags & AXE_FLAG_772B) != 0)
+ sc->sc_lenmask = AXE_CSUM_HDR_LEN_MASK;
+ else
+ sc->sc_lenmask = AXE_HDR_LEN_MASK;
+ if ((sc->sc_flags & AXE_FLAG_772B) != 0 &&
+ (ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ sc->sc_flags |= AXE_FLAG_CSUM_FRAME;
+ else
+ sc->sc_flags |= AXE_FLAG_STD_FRAME;
+ }
+
+ /* Configure TX/RX checksum offloading. */
+ axe_csum_cfg(ue);
+
+ if (sc->sc_flags & AXE_FLAG_772B) {
+ /* AX88772B uses different maximum frame burst configuration. */
axe_cmd(sc, AXE_772B_CMD_RXCTL_WRITE_CFG,
ax88772b_mfb_table[AX88772B_MFB_16K].threshold,
ax88772b_mfb_table[AX88772B_MFB_16K].byte_cnt, NULL);
+ }
/* Enable receiver, set RX mode. */
rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
@@ -1175,11 +1383,17 @@ axe_init(struct usb_ether *ue)
if (sc->sc_flags & AXE_FLAG_772B) {
/*
* Select RX header format type 1. Aligning IP
- * header on 4 byte boundary is not needed
+ * header on 4 byte boundary is not needed when
+ * checksum offloading feature is not used
* because we always copy the received frame in
- * RX handler.
+ * RX handler. When RX checksum offloading is
+ * active, aligning IP header is required to
+ * reflect actual frame length including RX
+ * header size.
*/
rxmode |= AXE_772B_RXCMD_HDR_TYPE_1;
+ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
+ rxmode |= AXE_772B_RXCMD_IPHDR_ALIGN;
} else {
/*
* Default Rx buffer size is too small to get
@@ -1208,7 +1422,6 @@ axe_init(struct usb_ether *ue)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
/* Switch to selected media. */
axe_ifmedia_upd(ifp);
- axe_start(ue);
}
static void
@@ -1250,3 +1463,45 @@ axe_stop(struct usb_ether *ue)
usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]);
usbd_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]);
}
+
+static int
+axe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct usb_ether *ue = ifp->if_softc;
+ struct axe_softc *sc;
+ struct ifreq *ifr;
+ int error, mask, reinit;
+
+ sc = uether_getsc(ue);
+ ifr = (struct ifreq *)data;
+ error = 0;
+ reinit = 0;
+ if (cmd == SIOCSIFCAP) {
+ AXE_LOCK(sc);
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+ if ((mask & IFCAP_TXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_TXCSUM;
+ if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+ ifp->if_hwassist |= AXE_CSUM_FEATURES;
+ else
+ ifp->if_hwassist &= ~AXE_CSUM_FEATURES;
+ reinit++;
+ }
+ if ((mask & IFCAP_RXCSUM) != 0 &&
+ (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
+ ifp->if_capenable ^= IFCAP_RXCSUM;
+ reinit++;
+ }
+ if (reinit > 0 && ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ else
+ reinit = 0;
+ AXE_UNLOCK(sc);
+ if (reinit > 0)
+ uether_init(ue);
+ } else
+ error = uether_ioctl(ifp, cmd, data);
+
+ return (error);
+}
OpenPOWER on IntegriCloud