Author: hselasky
Date: Sat Dec 31 14:47:35 2011
New Revision: 229106
URL: http://svn.freebsd.org/changeset/base/229106

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

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

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

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

Reply via email to