On 2018/08/19 09:40, Stefan Sperling wrote:
> On Sun, Aug 19, 2018 at 11:05:04AM +0200, Stefan Sperling wrote:
>> On Sun, Aug 19, 2018 at 09:56:33AM +0200, Remi Locherer wrote:
>>> It would help if you could send a clean version that applies to -current.
>>
>> One of the attachments was in fact clean but yes, this
>> thread has been much too noisy to follow easily.
>>
>> Try this.
> 
> Unfortunately, while this diff does indeed work on xhci(4), I've just
> found that this diff breaks axen(4) attached to ehci(4) completely.
> 
> I see several "axen0: rxeof: too short transfer" in dmesg and
> almost all packets are lost. Even my Ethernet switch gives up
> eventually and disables the port.
> 
> So this diff is not ready to be committed.

I didn't check if axen works on ehci.
On my ehci (intel PCH) that bug is reproduced, and
I found that it works on ehci with 16kB RX buffer.
I preserve the original bufsz decision code.



 - rxeof: Fix mbuf leak if checksum errors are detected.
 - rxeof: Avoid loop to extract packets if pkt_count is 0.
 - rxeof: Add more sanity checks.
 - rxeof: Increament if_ierror in some error paths.
 - qctrl: Apply queuing control parameters from FreeBSD axge(4).
 - qctrl: Set qctrl in miireg_statchg dynamically, not in ax88179_init.
 - init: Fix dhclient failure on xhci by calling set_config in init.

--- sys/dev/usb/if_axen.c.orig  Wed Aug  8 16:30:40 2018
+++ sys/dev/usb/if_axen.c       Wed Aug 22 12:38:18 2018
@@ -121,6 +121,13 @@ void       axen_unlock_mii(struct axen_softc *sc);
 
 void   axen_ax88179_init(struct axen_softc *);
 
+struct axen_qctrl axen_bulk_size[] = {
+       { 7, 0x4f, 0x00, 0x12, 0xff },
+       { 7, 0x20, 0x03, 0x16, 0xff },
+       { 7, 0xae, 0x07, 0x18, 0xff },
+       { 7, 0xcc, 0x4c, 0x18, 0x08 }
+};
+
 /* Get exclusive access to the MII registers */
 void
 axen_lock_mii(struct axen_softc *sc)
@@ -238,6 +245,8 @@ axen_miibus_statchg(struct device *dev)
        int                     err;
        uint16_t                val;
        uWord                   wval;
+       uint8_t                 linkstat = 0;
+       int                     qctrl;
 
        ifp = GET_IFP(sc);
        if (mii == NULL || ifp == NULL ||
@@ -265,27 +274,49 @@ axen_miibus_statchg(struct device *dev)
                return;
 
        val = 0;
-       if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+       if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
                val |= AXEN_MEDIUM_FDX;
+               if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+                       val |= AXEN_MEDIUM_TXFLOW_CTRL_EN;
+               if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+                       val |= AXEN_MEDIUM_RXFLOW_CTRL_EN;
+       }
 
-       val |= (AXEN_MEDIUM_RECV_EN | AXEN_MEDIUM_ALWAYS_ONE);
-       val |= (AXEN_MEDIUM_RXFLOW_CTRL_EN | AXEN_MEDIUM_TXFLOW_CTRL_EN);
+       val |= AXEN_MEDIUM_RECV_EN;
 
+       /* bulkin queue setting */
+       axen_lock_mii(sc);
+       axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_USB_UPLINK, &linkstat);
+       axen_unlock_mii(sc);
+
        switch (IFM_SUBTYPE(mii->mii_media_active)) {
        case IFM_1000_T:
                val |= AXEN_MEDIUM_GIGA | AXEN_MEDIUM_EN_125MHZ;
+               if (linkstat & AXEN_USB_SS)
+                       qctrl = 0;
+               else if (linkstat & AXEN_USB_HS)
+                       qctrl = 1;
+               else
+                       qctrl = 3;
                break;
        case IFM_100_TX:
                val |= AXEN_MEDIUM_PS;
+               if (linkstat & (AXEN_USB_SS | AXEN_USB_HS))
+                       qctrl = 2;
+               else
+                       qctrl = 3;
                break;
        case IFM_10_T:
-               /* doesn't need to be handled */
+       default:
+               qctrl = 3;
                break;
        }
 
        DPRINTF(("axen_miibus_statchg: val=0x%x\n", val));
        USETW(wval, val);
        axen_lock_mii(sc);
+       axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL,
+           &axen_bulk_size[qctrl]);
        err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval);
        axen_unlock_mii(sc);
        if (err) {
@@ -408,7 +439,6 @@ axen_ax88179_init(struct axen_softc *sc)
        uWord           wval;
        uByte           val;
        u_int16_t       ctl, temp;
-       struct axen_qctrl qctrl;
 
        axen_lock_mii(sc);
 
@@ -471,27 +501,12 @@ axen_ax88179_init(struct axen_softc *sc)
        switch (val) {
        case AXEN_USB_FS:
                DPRINTF(("uplink: USB1.1\n"));
-               qctrl.ctrl      = 0x07;
-               qctrl.timer_low = 0xcc;
-               qctrl.timer_high= 0x4c;
-               qctrl.bufsize   = AXEN_BUFSZ_LS - 1;
-               qctrl.ifg       = 0x08;
                break;
        case AXEN_USB_HS:
                DPRINTF(("uplink: USB2.0\n"));
-               qctrl.ctrl      = 0x07;
-               qctrl.timer_low = 0x02;
-               qctrl.timer_high= 0xa0;
-               qctrl.bufsize   = AXEN_BUFSZ_HS - 1;
-               qctrl.ifg       = 0xff;
                break;
        case AXEN_USB_SS:
                DPRINTF(("uplink: USB3.0\n"));
-               qctrl.ctrl      = 0x07;
-               qctrl.timer_low = 0x4f;
-               qctrl.timer_high= 0x00;
-               qctrl.bufsize   = AXEN_BUFSZ_SS - 1;
-               qctrl.ifg       = 0xff;
                break;
        default:
                printf("%s: unknown uplink bus:0x%02x\n",
@@ -499,7 +514,6 @@ axen_ax88179_init(struct axen_softc *sc)
                axen_unlock_mii(sc);
                return;
        }
-       axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl);
 
        /* Set MAC address. */
        axen_cmd(sc, AXEN_CMD_MAC_WRITE_ETHER, ETHER_ADDR_LEN,
@@ -710,7 +724,8 @@ axen_attach(struct device *parent, struct device *self
        mii->mii_flags = MIIF_AUTOTSLEEP;
 
        ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, axen_ifmedia_sts);
-       mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+       mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY,
+           MIIF_DOPAUSE);
 
        if (LIST_FIRST(&mii->mii_phys) == NULL) {
                ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
@@ -888,7 +903,6 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        u_int32_t               *hdr_p;
        u_int16_t               hdr_offset, pkt_count;
        size_t                  pkt_len;
-       size_t                  temp;
        int                     s;
 
        DPRINTFN(10,("%s: %s: enter\n", sc->axen_dev.dv_xname,__func__));
@@ -914,10 +928,19 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
 
        if (total_len < sizeof(pkt_hdr)) {
+               printf("%s: rxeof: too short (%#x) transfer %d\n",
+                   sc->axen_dev.dv_xname, total_len, status);
                ifp->if_ierrors++;
                goto done;
        }
 
+       if (total_len > sc->axen_bufsz) {
+               printf("%s: rxeof: too large transfer\n",
+                   sc->axen_dev.dv_xname);
+               ifp->if_ierrors++;
+               goto done;
+       }
+
        /* 
         * buffer map
         * [packet #0]...[packet #n][pkt hdr#0]..[pkt hdr#n][recv_hdr]
@@ -928,16 +951,10 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        hdr_offset = (u_int16_t)(rx_hdr >> 16);
        pkt_count  = (u_int16_t)(rx_hdr & 0xffff);
 
-       if (total_len > sc->axen_bufsz) {
-               printf("%s: rxeof: too large transfer\n",
-                   sc->axen_dev.dv_xname);
-               goto done;
-       }
-
        /* sanity check */
        if (hdr_offset > total_len) {
-               ifp->if_ierrors++;
                usbd_delay_ms(sc->axen_udev, 100);
+               ifp->if_ierrors++;
                goto done;
        }
 
@@ -954,11 +971,14 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        if (pkt_count > AXEN_MAX_PACKED_PACKET) {
                DPRINTF(("Too many packets (%d) in a transaction, discard.\n", 
                    pkt_count));
+               ifp->if_ierrors++;
                goto done;
        }
 #endif
 
-       do {
+       for (; pkt_count > 0; pkt_count--) {
+               uint16_t csum_flags = 0;
+
                if ((buf[0] != 0xee) || (buf[1] != 0xee)){
                        printf("%s: invalid buffer(pkt#%d), continue\n",
                            sc->axen_dev.dv_xname, pkt_count);
@@ -972,34 +992,44 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
                DPRINTFN(10,("rxeof: packet#%d, pkt_hdr 0x%08x, pkt_len %zu\n",
                   pkt_count, pkt_hdr, pkt_len));
 
+               if (pkt_len > MCLBYTES || pkt_len < ETHER_MIN_LEN) {
+                       printf("%s: invalid pkt_len %zu\n",
+                           sc->axen_dev.dv_xname,pkt_len);
+                       ifp->if_ierrors++;
+                       goto nextpkt;
+               }
+
                if ((pkt_hdr & AXEN_RXHDR_CRC_ERR) ||
                    (pkt_hdr & AXEN_RXHDR_DROP_ERR)) {
-                       ifp->if_ierrors++;
                        /* move to next pkt header */
                        DPRINTF(("crc err(pkt#%d)\n", pkt_count));
+                       ifp->if_ierrors++;
                        goto nextpkt;
                }
 
-               /* process each packet */
-               /* allocate mbuf */
-               m = axen_newbuf();
-               if (m == NULL) {
-                       ifp->if_ierrors++;
-                       goto nextpkt;
-               }
-
-               /* skip pseudo header (2byte) and trailer padding (4Byte) */
-               m->m_pkthdr.len = m->m_len = pkt_len - 6;
-
 #ifdef AXEN_TOE
                /* cheksum err */
                if ((pkt_hdr & AXEN_RXHDR_L3CSUM_ERR) || 
                    (pkt_hdr & AXEN_RXHDR_L4CSUM_ERR)) {
-                       printf("%s: checksum err (pkt#%d)\n",
-                           sc->axen_dev.dv_xname, pkt_count);
+                       printf("%s: checksum err (pkt#%hu) len %#zx total %#x"
+                           " pkt_hdr %#x\n", sc->axen_dev.dv_xname,
+                           pkt_count, pkt_len, total_len, pkt_hdr);
+#if 0
+                       int i;
+                       for (i = 0; i < min(pkt_len, 2+14+40); i++) {
+                               printf("%02x ", buf[i]);
+                               if (i % 16 == 7)
+                                       printf(" ");
+                               if (i % 16 == 15)
+                                       printf("\n");
+                       }
+                       if (i % 16 != 15)
+                               printf("\n");
+#endif
+                       ifp->if_ierrors++;
                        goto nextpkt;
                } else {
-                       m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
+                       csum_flags |= M_IPV4_CSUM_IN_OK;
                }
 
                int l4_type;
@@ -1008,12 +1038,23 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
 
                if ((l4_type == AXEN_RXHDR_L4_TYPE_TCP) ||
                    (l4_type == AXEN_RXHDR_L4_TYPE_UDP)) 
-                       m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
-                           M_UDP_CSUM_IN_OK;
+                       csum_flags |= M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK;
 #endif
 
-               memcpy(mtod(m, char *), buf + 2, pkt_len - 6);
+               /* process each packet */
+               /* allocate mbuf */
+               m = axen_newbuf();
+               if (m == NULL) {
+                       ifp->if_ierrors++;
+                       goto nextpkt;
+               }
 
+               /* skip pseudo header (2byte) and trailer padding (4Byte) */
+               m->m_pkthdr.len = m->m_len = pkt_len - 6;
+               m->m_pkthdr.csum_flags |= csum_flags;
+
+               memcpy(mtod(m, char *), buf + 2, m->m_len);
+
                ml_enqueue(&ml, m);
 
 nextpkt:
@@ -1022,11 +1063,9 @@ nextpkt:
                 * as each packet will be aligned 8byte boundary,
                 * need to fix up the start point of the buffer.
                 */
-               temp = ((pkt_len + 7) & 0xfff8);
-               buf = buf + temp;
+               buf += roundup(pkt_len, 8);
                hdr_p++;
-               pkt_count--;
-       } while( pkt_count > 0);
+       }
 
 done:
        /* push the packet up */
@@ -1260,6 +1299,14 @@ axen_init(void *xsc)
         * Cancel pending I/O and free all RX/TX buffers.
         */
        axen_reset(sc);
+
+#define AXEN_CONFIG_NO 1
+#define AXEN_IFACE_IDX 0
+       if (usbd_set_config_no(sc->axen_udev, AXEN_CONFIG_NO, 1) ||
+           usbd_device2interface_handle(sc->axen_udev, AXEN_IFACE_IDX,
+                                        &sc->axen_iface))
+               printf("%s: set_config failed\n", sc->axen_dev.dv_xname);
+       usbd_delay_ms(sc->axen_udev, 10);
 
        /* XXX: ? */
        bval = 0x01;



--- sys/dev/usb/if_axen.c.orig  Sun Jan 22 10:17:39 2017
+++ sys/dev/usb/if_axen.c       Wed Aug 22 14:13:02 2018
@@ -121,6 +121,13 @@ void       axen_unlock_mii(struct axen_softc *sc);
 
 void   axen_ax88179_init(struct axen_softc *);
 
+struct axen_qctrl axen_bulk_size[] = {
+       { 7, 0x4f, 0x00, 0x12, 0xff },
+       { 7, 0x20, 0x03, 0x16, 0xff },
+       { 7, 0xae, 0x07, 0x18, 0xff },
+       { 7, 0xcc, 0x4c, 0x18, 0x08 }
+};
+
 /* Get exclusive access to the MII registers */
 void
 axen_lock_mii(struct axen_softc *sc)
@@ -238,6 +245,8 @@ axen_miibus_statchg(struct device *dev)
        int                     err;
        uint16_t                val;
        uWord                   wval;
+       uint8_t                 linkstat = 0;
+       int                     qctrl;
 
        ifp = GET_IFP(sc);
        if (mii == NULL || ifp == NULL ||
@@ -265,27 +274,49 @@ axen_miibus_statchg(struct device *dev)
                return;
 
        val = 0;
-       if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+       if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
                val |= AXEN_MEDIUM_FDX;
+               if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
+                       val |= AXEN_MEDIUM_TXFLOW_CTRL_EN;
+               if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
+                       val |= AXEN_MEDIUM_RXFLOW_CTRL_EN;
+       }
 
-       val |= (AXEN_MEDIUM_RECV_EN | AXEN_MEDIUM_ALWAYS_ONE);
-       val |= (AXEN_MEDIUM_RXFLOW_CTRL_EN | AXEN_MEDIUM_TXFLOW_CTRL_EN);
+       val |= AXEN_MEDIUM_RECV_EN;
 
+       /* bulkin queue setting */
+       axen_lock_mii(sc);
+       axen_cmd(sc, AXEN_CMD_MAC_READ, 1, AXEN_USB_UPLINK, &linkstat);
+       axen_unlock_mii(sc);
+
        switch (IFM_SUBTYPE(mii->mii_media_active)) {
        case IFM_1000_T:
                val |= AXEN_MEDIUM_GIGA | AXEN_MEDIUM_EN_125MHZ;
+               if (linkstat & AXEN_USB_SS)
+                       qctrl = 0;
+               else if (linkstat & AXEN_USB_HS)
+                       qctrl = 1;
+               else
+                       qctrl = 3;
                break;
        case IFM_100_TX:
                val |= AXEN_MEDIUM_PS;
+               if (linkstat & (AXEN_USB_SS | AXEN_USB_HS))
+                       qctrl = 2;
+               else
+                       qctrl = 3;
                break;
        case IFM_10_T:
-               /* doesn't need to be handled */
+       default:
+               qctrl = 3;
                break;
        }
 
        DPRINTF(("axen_miibus_statchg: val=0x%x\n", val));
        USETW(wval, val);
        axen_lock_mii(sc);
+       axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL,
+           &axen_bulk_size[qctrl]);
        err = axen_cmd(sc, AXEN_CMD_MAC_WRITE2, 2, AXEN_MEDIUM_STATUS, &wval);
        axen_unlock_mii(sc);
        if (err) {
@@ -408,7 +439,6 @@ axen_ax88179_init(struct axen_softc *sc)
        uWord           wval;
        uByte           val;
        u_int16_t       ctl, temp;
-       struct axen_qctrl qctrl;
 
        axen_lock_mii(sc);
 
@@ -470,34 +500,18 @@ axen_ax88179_init(struct axen_softc *sc)
        switch (val) {
        case AXEN_USB_FS:
                DPRINTF(("uplink: USB1.1\n"));
-               qctrl.ctrl      = 0x07;
-               qctrl.timer_low = 0xcc;
-               qctrl.timer_high= 0x4c;
-               qctrl.bufsize   = AXEN_BUFSZ_LS - 1;
-               qctrl.ifg       = 0x08;
                break;
        case AXEN_USB_HS:
                DPRINTF(("uplink: USB2.0\n"));
-               qctrl.ctrl      = 0x07;
-               qctrl.timer_low = 0x02;
-               qctrl.timer_high= 0xa0;
-               qctrl.bufsize   = AXEN_BUFSZ_HS - 1;
-               qctrl.ifg       = 0xff;
                break;
        case AXEN_USB_SS:
                DPRINTF(("uplink: USB3.0\n"));
-               qctrl.ctrl      = 0x07;
-               qctrl.timer_low = 0x4f;
-               qctrl.timer_high= 0x00;
-               qctrl.bufsize   = AXEN_BUFSZ_SS - 1;
-               qctrl.ifg       = 0xff;
                break;
        default:
                printf("unknown uplink bus:0x%02x\n", val);
                axen_unlock_mii(sc);
                return;
        }
-       axen_cmd(sc, AXEN_CMD_MAC_SET_RXSR, 5, AXEN_RX_BULKIN_QCTRL, &qctrl);
 
        /* Set MAC address. */
        axen_cmd(sc, AXEN_CMD_MAC_WRITE_ETHER, ETHER_ADDR_LEN,
@@ -708,7 +722,8 @@ axen_attach(struct device *parent, struct device *self
        mii->mii_flags = MIIF_AUTOTSLEEP;
 
        ifmedia_init(&mii->mii_media, 0, axen_ifmedia_upd, axen_ifmedia_sts);
-       mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+       mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY,
+           MIIF_DOPAUSE);
 
        if (LIST_FIRST(&mii->mii_phys) == NULL) {
                ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
@@ -886,7 +901,6 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        u_int32_t               *hdr_p;
        u_int16_t               hdr_offset, pkt_count;
        size_t                  pkt_len;
-       size_t                  temp;
        int                     s;
 
        DPRINTFN(10,("%s: %s: enter\n", sc->axen_dev.dv_xname,__func__));
@@ -912,10 +926,19 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
 
        if (total_len < sizeof(pkt_hdr)) {
+               printf("%s: rxeof: too short (%#x) transfer %d\n",
+                   sc->axen_dev.dv_xname, total_len, status);
                ifp->if_ierrors++;
                goto done;
        }
 
+       if (total_len > sc->axen_bufsz) {
+               printf("%s: rxeof: too large transfer\n",
+                   sc->axen_dev.dv_xname);
+               ifp->if_ierrors++;
+               goto done;
+       }
+
        /* 
         * buffer map
         * [packet #0]...[packet #n][pkt hdr#0]..[pkt hdr#n][recv_hdr]
@@ -926,15 +949,10 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        hdr_offset = (u_int16_t)(rx_hdr >> 16);
        pkt_count  = (u_int16_t)(rx_hdr & 0xffff);
 
-       if (total_len > sc->axen_bufsz) {
-               printf("rxeof: too large transfer\n");
-               goto done;
-       }
-
        /* sanity check */
        if (hdr_offset > total_len) {
-               ifp->if_ierrors++;
                usbd_delay_ms(sc->axen_udev, 100);
+               ifp->if_ierrors++;
                goto done;
        }
 
@@ -951,11 +969,14 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
        if (pkt_count > AXEN_MAX_PACKED_PACKET) {
                DPRINTF(("Too many packets (%d) in a transaction, discard.\n", 
                    pkt_count));
+               ifp->if_ierrors++;
                goto done;
        }
 #endif
 
-       do {
+       for (; pkt_count > 0; pkt_count--) {
+               uint16_t csum_flags = 0;
+
                if ((buf[0] != 0xee) || (buf[1] != 0xee)){
                        printf("invalid buffer(pkt#%d), continue\n", pkt_count);
                        ifp->if_ierrors += pkt_count;
@@ -968,33 +989,44 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
                DPRINTFN(10,("rxeof: packet#%d, pkt_hdr 0x%08x, pkt_len %zu\n",
                   pkt_count, pkt_hdr, pkt_len));
 
-               if ((pkt_hdr & AXEN_RXHDR_CRC_ERR) ||
-                   (pkt_hdr & AXEN_RXHDR_DROP_ERR)) {
-                       ifp->if_ierrors++;
-                       /* move to next pkt header */
-                       DPRINTF(("crc err(pkt#%d)\n", pkt_count));
+               if (pkt_len > MCLBYTES || pkt_len < ETHER_MIN_LEN) {
+                       printf("%s: invalid pkt_len %zu\n",
+                           sc->axen_dev.dv_xname,pkt_len);
+                       ifp->if_ierrors++;
                        goto nextpkt;
                }
 
-               /* process each packet */
-               /* allocate mbuf */
-               m = axen_newbuf();
-               if (m == NULL) {
+               if ((pkt_hdr & AXEN_RXHDR_CRC_ERR) ||
+                   (pkt_hdr & AXEN_RXHDR_DROP_ERR)) {
+                       /* move to next pkt header */
+                       DPRINTF(("crc err(pkt#%d)\n", pkt_count));
                        ifp->if_ierrors++;
                        goto nextpkt;
                }
 
-               /* skip pseudo header (2byte) and trailer padding (4Byte) */
-               m->m_pkthdr.len = m->m_len = pkt_len - 6;
-
 #ifdef AXEN_TOE
                /* cheksum err */
                if ((pkt_hdr & AXEN_RXHDR_L3CSUM_ERR) || 
                    (pkt_hdr & AXEN_RXHDR_L4CSUM_ERR)) {
-                       printf("checksum err (pkt#%d)\n", pkt_count);
+                       printf("%s: checksum err (pkt#%hu) len %#zx total %#x"
+                           " pkt_hdr %#x\n", sc->axen_dev.dv_xname,
+                           pkt_count, pkt_len, total_len, pkt_hdr);
+#if 0
+                       int i;
+                       for (i = 0; i < min(pkt_len, 2+14+40); i++) {
+                               printf("%02x ", buf[i]);
+                               if (i % 16 == 7)
+                                       printf(" ");
+                               if (i % 16 == 15)
+                                       printf("\n");
+                       }
+                       if (i % 16 != 15)
+                               printf("\n");
+#endif
+                       ifp->if_ierrors++;
                        goto nextpkt;
                } else {
-                       m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
+                       csum_flags |= M_IPV4_CSUM_IN_OK;
                }
 
                int l4_type;
@@ -1003,12 +1035,23 @@ axen_rxeof(struct usbd_xfer *xfer, void *priv, usbd_st
 
                if ((l4_type == AXEN_RXHDR_L4_TYPE_TCP) ||
                    (l4_type == AXEN_RXHDR_L4_TYPE_UDP)) 
-                       m->m_pkthdr.csum_flags |= M_TCP_CSUM_IN_OK |
-                           M_UDP_CSUM_IN_OK;
+                       csum_flags |= M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK;
 #endif
 
-               memcpy(mtod(m, char *), buf + 2, pkt_len - 6);
+               /* process each packet */
+               /* allocate mbuf */
+               m = axen_newbuf();
+               if (m == NULL) {
+                       ifp->if_ierrors++;
+                       goto nextpkt;
+               }
 
+               /* skip pseudo header (2byte) and trailer padding (4Byte) */
+               m->m_pkthdr.len = m->m_len = pkt_len - 6;
+               m->m_pkthdr.csum_flags |= csum_flags;
+
+               memcpy(mtod(m, char *), buf + 2, m->m_len);
+
                ml_enqueue(&ml, m);
 
 nextpkt:
@@ -1017,11 +1060,9 @@ nextpkt:
                 * as each packet will be aligned 8byte boundary,
                 * need to fix up the start point of the buffer.
                 */
-               temp = ((pkt_len + 7) & 0xfff8);
-               buf = buf + temp;
+               buf += roundup(pkt_len, 8);
                hdr_p++;
-               pkt_count--;
-       } while( pkt_count > 0);
+       }
 
 done:
        /* push the packet up */
@@ -1255,6 +1296,14 @@ axen_init(void *xsc)
         * Cancel pending I/O and free all RX/TX buffers.
         */
        axen_reset(sc);
+
+#define AXEN_CONFIG_NO 1
+#define AXEN_IFACE_IDX 0
+       if (usbd_set_config_no(sc->axen_udev, AXEN_CONFIG_NO, 1) ||
+           usbd_device2interface_handle(sc->axen_udev, AXEN_IFACE_IDX,
+                                        &sc->axen_iface))
+               printf("%s: set_config failed\n", sc->axen_dev.dv_xname);
+       usbd_delay_ms(sc->axen_udev, 10);
 
        /* XXX: ? */
        bval = 0x01;



 - Fix the bug of actlen calcucation if a TD is splitted.
    A TD Transfer Length is sum of transferred TRB length, that is,
    the originating TRB length minus the remain length.
 - Set chain bit on LINK TRB if the TD crosses LINK TRB.
    When the TD consists of multiple TRBs and the TD crosses LINK TRB,
    the last TRB does not generate the interrupt immediately if Link TRB
    does not have chain bit.

--- sys/dev/usb/xhci.c.orig     Tue Jul 17 19:44:49 2018
+++ sys/dev/usb/xhci.c  Fri Aug 24 08:42:12 2018
@@ -748,25 +748,47 @@ xhci_event_xfer(struct xhci_softc *sc, uint64_t paddr,
 
        switch (code) {
        case XHCI_CODE_SUCCESS:
+       case XHCI_CODE_SHORT_XFER:
                /*
-                * This might be the last TRB of a TD that ended up
-                * with a Short Transfer condition, see below.
+                * This might or might not be the last TRB of a TD.
                 */
-               if (xfer->actlen == 0)
-                       xfer->actlen = xfer->length - remain;
+               xx = (struct xhci_xfer *)xfer;
+               if (xfer->actlen == 0) {
+                       int i;
+                       int start;
+                       uint32_t orig_trb_len;
 
-               xfer->status = USBD_NORMAL_COMPLETION;
-               break;
-       case XHCI_CODE_SHORT_XFER:
-               xfer->actlen = xfer->length - remain;
+                       if (xx->index + 1 < xx->ntrb)
+                               start = xx->index + 1 + (xp->ring.ntrb - 1) -
+                                   xx->ntrb;
+                       else
+                               start = xx->index + 1 - xx->ntrb;
 
+                       for (i = trb_idx;; i--) {
+                               uint32_t type = XHCI_TRB_TYPE_MASK &
+                                   le32toh(xp->ring.trbs[i].trb_flags);
+                               orig_trb_len = XHCI_TRB_LEN(le32toh(
+                                   xp->ring.trbs[i].trb_status));
+
+                               if (i == trb_idx)
+                                       xfer->actlen += orig_trb_len - remain;
+                               else if (type != XHCI_TRB_TYPE_SETUP)
+                                       xfer->actlen += orig_trb_len;
+
+                               if (i == start)
+                                       break;
+
+                               if (i == 0)
+                                       i = (xp->ring.ntrb - 1);
+                       }
+               }
+
                /*
                 * If this is not the last TRB of a transfer, we should
                 * theoretically clear the IOC at the end of the chain
                 * but the HC might have already processed it before we
                 * had a chance to schedule the softinterrupt.
                 */
-               xx = (struct xhci_xfer *)xfer;
                if (xx->index != trb_idx) {
                        DPRINTF(("%s: short xfer %p for %u\n", DEVNAME(sc),
                            xfer, xx->index));
@@ -2712,6 +2734,16 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
        if ((xfer->flags & USBD_FORCE_SHORT_XFER || xfer->length == 0) &&
            (xfer->length % UE_GET_SIZE(mps) == 0))
                ntrb++;
+
+       /* If the TD crosses LINK TRB, set chain bit on LINK TRB. */
+       struct xhci_trb *lnk = &xp->ring.trbs[xp->ring.ntrb - 1];
+       if (xp->ring.ntrb - 1 < xp->ring.index + ntrb)
+               lnk->trb_flags |= htole32(XHCI_TRB_CHAIN);
+       else
+               lnk->trb_flags &= ~htole32(XHCI_TRB_CHAIN);
+       bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
+           TRBOFF(&xp->ring, lnk), sizeof(struct xhci_trb),
+           BUS_DMASYNC_PREWRITE);
 
        if (xp->free_trbs < ntrb)
                return (USBD_NOMEM);


--- sys/dev/usb/xhci.c.orig     Fri Sep  8 10:25:19 2017
+++ sys/dev/usb/xhci.c  Fri Aug 24 03:26:39 2018
@@ -748,25 +748,47 @@ xhci_event_xfer(struct xhci_softc *sc, uint64_t paddr,
 
        switch (code) {
        case XHCI_CODE_SUCCESS:
+       case XHCI_CODE_SHORT_XFER:
                /*
-                * This might be the last TRB of a TD that ended up
-                * with a Short Transfer condition, see below.
+                * This might or might not be the last TRB of a TD.
                 */
-               if (xfer->actlen == 0)
-                       xfer->actlen = xfer->length - remain;
+               xx = (struct xhci_xfer *)xfer;
+               if (xfer->actlen == 0) {
+                       int i;
+                       int start;
+                       uint32_t orig_trb_len;
 
-               xfer->status = USBD_NORMAL_COMPLETION;
-               break;
-       case XHCI_CODE_SHORT_XFER:
-               xfer->actlen = xfer->length - remain;
+                       if (xx->index + 1 < xx->ntrb)
+                               start = xx->index + 1 + (xp->ring.ntrb - 1) -
+                                   xx->ntrb;
+                       else
+                               start = xx->index + 1 - xx->ntrb;
 
+                       for (i = trb_idx;; i--) {
+                               uint32_t type = XHCI_TRB_TYPE_MASK &
+                                   le32toh(xp->ring.trbs[i].trb_flags);
+                               orig_trb_len = XHCI_TRB_LEN(le32toh(
+                                   xp->ring.trbs[i].trb_status));
+
+                               if (i == trb_idx)
+                                       xfer->actlen += orig_trb_len - remain;
+                               else if (type != XHCI_TRB_TYPE_SETUP)
+                                       xfer->actlen += orig_trb_len;
+
+                               if (i == start)
+                                       break;
+
+                               if (i == 0)
+                                       i = (xp->ring.ntrb - 1);
+                       }
+               }
+
                /*
                 * If this is not the last TRB of a transfer, we should
                 * theoretically clear the IOC at the end of the chain
                 * but the HC might have already processed it before we
                 * had a change to schedule the softinterrupt.
                 */
-               xx = (struct xhci_xfer *)xfer;
                if (xx->index != trb_idx)
                        return;
 
@@ -2618,6 +2640,16 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
        if ((xfer->flags & USBD_FORCE_SHORT_XFER || xfer->length == 0) &&
            (xfer->length % mps == 0))
                ntrb++;
+
+       /* If the TD crosses LINK TRB, set chain bit on LINK TRB. */
+       struct xhci_trb *lnk = &xp->ring.trbs[xp->ring.ntrb - 1];
+       if (xp->ring.ntrb - 1 < xp->ring.index + ntrb)
+               lnk->trb_flags |= htole32(XHCI_TRB_CHAIN);
+       else
+               lnk->trb_flags &= ~htole32(XHCI_TRB_CHAIN);
+       bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
+           TRBOFF(&xp->ring, lnk), sizeof(struct xhci_trb),
+           BUS_DMASYNC_PREWRITE);
 
        if (xp->free_trbs < ntrb)
                return (USBD_NOMEM);


Reply via email to