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);