Author: yongari
Date: Tue Oct 25 18:36:18 2011
New Revision: 226743
URL: http://svn.freebsd.org/changeset/base/226743

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

Modified:
  head/sys/dev/usb/net/if_axe.c
  head/sys/dev/usb/net/if_axereg.h

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

Modified: head/sys/dev/usb/net/if_axereg.h
==============================================================================
--- head/sys/dev/usb/net/if_axereg.h    Tue Oct 25 17:10:15 2011        
(r226742)
+++ head/sys/dev/usb/net/if_axereg.h    Tue Oct 25 18:36:18 2011        
(r226743)
@@ -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;
 };
 
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to