Module Name: src Committed By: mlelstv Date: Mon Jun 9 14:18:28 UTC 2014
Modified Files: src/sys/dev/usb: if_smsc.c if_smscreg.h if_smscvar.h Log Message: Fix receive loop, enable turbo mode, checksum offloading still needs correct handling of pseudo headers. The Raspberry PI now copies at 2MByte/s with scp and 4MByte/s with NFS. Based on work from nick@. To generate a diff of this commit: cvs rdiff -u -r1.12 -r1.13 src/sys/dev/usb/if_smsc.c cvs rdiff -u -r1.3 -r1.4 src/sys/dev/usb/if_smscreg.h cvs rdiff -u -r1.2 -r1.3 src/sys/dev/usb/if_smscvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/if_smsc.c diff -u src/sys/dev/usb/if_smsc.c:1.12 src/sys/dev/usb/if_smsc.c:1.13 --- src/sys/dev/usb/if_smsc.c:1.12 Fri Nov 1 14:24:03 2013 +++ src/sys/dev/usb/if_smsc.c Mon Jun 9 14:18:28 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if_smsc.c,v 1.12 2013/11/01 14:24:03 skrll Exp $ */ +/* $NetBSD: if_smsc.c,v 1.13 2014/06/09 14:18:28 mlelstv Exp $ */ /* $OpenBSD: if_smsc.c,v 1.4 2012/09/27 12:38:11 jsg Exp $ */ /* $FreeBSD: src/sys/dev/usb/net/if_smsc.c,v 1.1 2012/08/15 04:03:55 gonzo Exp $ */ @@ -108,6 +108,7 @@ int smsc_debug = 0; #endif +#define ETHER_ALIGN 2 /* * Various supported device vendors/products. */ @@ -481,17 +482,19 @@ smsc_sethwcsum(struct smsc_softc *sc) } /* Enable/disable the Rx checksum */ - if (ifp->if_capabilities & IFCAP_CSUM_IPv4_Rx) - val |= SMSC_COE_CTRL_RX_EN; + if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Rx|IFCAP_CSUM_UDPv4_Rx)) + val |= (SMSC_COE_CTRL_RX_EN | SMSC_COE_CTRL_RX_MODE); else - val &= ~SMSC_COE_CTRL_RX_EN; + val &= ~(SMSC_COE_CTRL_RX_EN | SMSC_COE_CTRL_RX_MODE); /* Enable/disable the Tx checksum (currently not supported) */ - if (ifp->if_capabilities & IFCAP_CSUM_IPv4_Tx) + if (ifp->if_capenable & (IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_UDPv4_Tx)) val |= SMSC_COE_CTRL_TX_EN; else val &= ~SMSC_COE_CTRL_TX_EN; + sc->sc_coe_ctrl = val; + err = smsc_write_reg(sc, SMSC_COE_CTRL, val); if (err != 0) { smsc_warn_printf(sc, "failed to write SMSC_COE_CTRL (err=%d)\n", @@ -573,6 +576,9 @@ smsc_init(struct ifnet *ifp) /* Load the multicast filter. */ smsc_setmulti(sc); + /* TCP/UDP checksum offload engines. */ + smsc_sethwcsum(sc); + /* Open RX and TX pipes. */ err = usbd_open_pipe(sc->sc_iface, sc->sc_ed[SMSC_ENDPT_RX], USBD_EXCLUSIVE_USE, &sc->sc_ep[SMSC_ENDPT_RX]); @@ -602,9 +608,6 @@ smsc_init(struct ifnet *ifp) usbd_transfer(c->sc_xfer); } - /* TCP/UDP checksum offload engines. */ - smsc_sethwcsum(sc); - /* Indicate we are up and running. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; @@ -808,14 +811,11 @@ smsc_chip_init(struct smsc_softc *sc) * Burst capability is the number of URBs that can be in a burst of * data/ethernet frames. */ -#ifdef SMSC_TURBO + if (sc->sc_udev->speed == USB_SPEED_HIGH) burst_cap = 37; else burst_cap = 128; -#else - burst_cap = 0; -#endif smsc_write_reg(sc, SMSC_BURST_CAP, burst_cap); @@ -835,9 +835,14 @@ smsc_chip_init(struct smsc_softc *sc) * The following settings are used for 'turbo mode', a.k.a multiple * frames per Rx transaction (again info taken form Linux driver). */ -#ifdef SMSC_TURBO - reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE); -#endif + if (burst_cap) + reg_val |= (SMSC_HW_CFG_MEF | SMSC_HW_CFG_BCE); + + /* + * set Rx data offset to ETHER_ALIGN which will make the IP header + * align on a word boundary. + */ + reg_val |= ETHER_ALIGN << SMSC_HW_CFG_RXDOFF_SHIFT; smsc_write_reg(sc, SMSC_HW_CFG, reg_val); @@ -868,6 +873,9 @@ smsc_chip_init(struct smsc_softc *sc) goto init_failed; } + /* disable pad stripping, collides with checksum offload */ + sc->sc_mac_csr &= ~SMSC_MAC_CSR_PADSTR; + /* Vlan */ smsc_write_reg(sc, SMSC_VLAN1, (uint32_t)ETHERTYPE_VLAN); @@ -1043,6 +1051,15 @@ smsc_attach(device_t parent, device_t se ifp->if_start = smsc_start; ifp->if_stop = smsc_stop; +#ifdef notyet + /* + * We can do TCPv4, and UDPv4 checksums in hardware. + */ + ifp->if_capabilities |= + /*IFCAP_CSUM_TCPv4_Tx |*/ IFCAP_CSUM_TCPv4_Rx | + /*IFCAP_CSUM_UDPv4_Tx |*/ IFCAP_CSUM_UDPv4_Rx; +#endif + sc->sc_ec.ec_capabilities = ETHERCAP_VLAN_MTU; /* Setup some of the basics */ @@ -1238,10 +1255,10 @@ smsc_rxeof(usbd_xfer_handle xfer, usbd_p struct ifnet *ifp = &sc->sc_ec.ec_if; u_char *buf = c->sc_buf; uint32_t total_len; - uint16_t pktlen = 0; + uint32_t rxhdr; + uint16_t pktlen; struct mbuf *m; int s; - uint32_t rxhdr; if (sc->sc_dying) return; @@ -1264,7 +1281,7 @@ smsc_rxeof(usbd_xfer_handle xfer, usbd_p usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); smsc_dbg_printf(sc, "xfer status total_len %d\n", total_len); - do { + while (total_len != 0) { if (total_len < sizeof(rxhdr)) { smsc_dbg_printf(sc, "total_len %d < sizeof(rxhdr) %zu\n", total_len, sizeof(rxhdr)); @@ -1272,10 +1289,9 @@ smsc_rxeof(usbd_xfer_handle xfer, usbd_p goto done; } - buf += pktlen; - memcpy(&rxhdr, buf, sizeof(rxhdr)); rxhdr = le32toh(rxhdr); + buf += sizeof(rxhdr); total_len -= sizeof(rxhdr); if (rxhdr & SMSC_RX_STAT_ERROR) { @@ -1287,6 +1303,9 @@ smsc_rxeof(usbd_xfer_handle xfer, usbd_p pktlen = (uint16_t)SMSC_RX_STAT_FRM_LENGTH(rxhdr); smsc_dbg_printf(sc, "rxeof total_len %d pktlen %d rxhdr " "0x%08x\n", total_len, pktlen, rxhdr); + + pktlen += ETHER_ALIGN; + if (pktlen > total_len) { smsc_dbg_printf(sc, "pktlen %d > total_len %d\n", pktlen, total_len); @@ -1294,9 +1313,6 @@ smsc_rxeof(usbd_xfer_handle xfer, usbd_p goto done; } - buf += sizeof(rxhdr); - total_len -= pktlen; - m = smsc_newbuf(); if (m == NULL) { smsc_dbg_printf(sc, "smc_newbuf returned NULL\n"); @@ -1306,25 +1322,71 @@ smsc_rxeof(usbd_xfer_handle xfer, usbd_p ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; - - pktlen -= 2; // JDM - m->m_pkthdr.len = m->m_len = pktlen; -#define ETHER_ALIGN 2 + m->m_flags |= M_HASFCS; m_adj(m, ETHER_ALIGN); + memcpy(mtod(m, char *), buf + ETHER_ALIGN, m->m_len); - memcpy(mtod(m, char *), buf, pktlen); + /* Check if RX TCP/UDP checksumming is being offloaded */ + if (sc->sc_coe_ctrl & SMSC_COE_CTRL_RX_EN) { + smsc_dbg_printf(sc,"RX checksum offload checking\n"); + struct ether_header *eh; + + eh = mtod(m, struct ether_header *); + + /* Remove the extra 2 bytes of the csum */ + m_adj(m, -2); + + /* + * The checksum appears to be simplistically calculated + * over the udp/tcp header and data up to the end of the + * eth frame. Which means if the eth frame is padded + * the csum calculation is incorrectly performed over + * the padding bytes as well. Therefore to be safe we + * ignore the H/W csum on frames less than or equal to + * 64 bytes. + * + * Ignore H/W csum for non-IPv4 packets. + */ + smsc_dbg_printf(sc,"Ethertype %02x pktlen %02x\n", + be16toh(eh->ether_type), pktlen); + if (be16toh(eh->ether_type) == ETHERTYPE_IP && + pktlen > ETHER_MIN_LEN) { + + m->m_pkthdr.csum_flags |= + (M_CSUM_TCPv4 | M_CSUM_UDPv4 | M_CSUM_DATA); + + /* + * Copy the TCP/UDP checksum from the last 2 + * bytes of the transfer and put in the + * csum_data field. + */ + memcpy(&m->m_pkthdr.csum_data, + buf + pktlen - 2, 2); + /* + * The data is copied in network order, but the + * csum algorithm in the kernel expects it to be + * in host network order. + */ + m->m_pkthdr.csum_data = + ntohs(m->m_pkthdr.csum_data); + smsc_dbg_printf(sc, + "RX checksum offloaded (0x%04x)\n", + m->m_pkthdr.csum_data); + } + } + + buf += pktlen; + total_len -= pktlen; /* push the packet up */ s = splnet(); bpf_mtap(ifp, m); ifp->if_input(ifp, m); splx(s); - } while (total_len > 0); + } done: - memset(c->sc_buf, 0, sc->sc_bufsz); - /* Setup new transfer. */ usbd_setup_xfer(xfer, sc->sc_ep[SMSC_ENDPT_RX], c, c->sc_buf, sc->sc_bufsz, Index: src/sys/dev/usb/if_smscreg.h diff -u src/sys/dev/usb/if_smscreg.h:1.3 src/sys/dev/usb/if_smscreg.h:1.4 --- src/sys/dev/usb/if_smscreg.h:1.3 Wed Apr 3 15:57:44 2013 +++ src/sys/dev/usb/if_smscreg.h Mon Jun 9 14:18:28 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if_smscreg.h,v 1.3 2013/04/03 15:57:44 skrll Exp $ */ +/* $NetBSD: if_smscreg.h,v 1.4 2014/06/09 14:18:28 mlelstv Exp $ */ /* $OpenBSD: if_smscreg.h,v 1.2 2012/09/27 12:38:11 jsg Exp $ */ /*- @@ -161,6 +161,7 @@ #define SMSC_HW_CFG_BIR (0x1UL << 12) #define SMSC_HW_CFG_LEDB (0x1UL << 11) +#define SMSC_HW_CFG_RXDOFF_SHIFT (9) #define SMSC_HW_CFG_RXDOFF (0x3UL << 9) /* RX pkt alignment */ #define SMSC_HW_CFG_DRP (0x1UL << 6) #define SMSC_HW_CFG_MEF (0x1UL << 5) @@ -200,6 +201,8 @@ #define SMSC_MAC_CSR_PASSBAD (0x1UL << 16) /* Pass on bad frames */ #define SMSC_MAC_CSR_HPFILT (0x1UL << 13) /* Hash filtering */ #define SMSC_MAC_CSR_BCAST (0x1UL << 11) /* Broadcast */ +#define SMSC_MAC_CSR_DISRTY (0x1UL << 10) /* Disable Retry */ +#define SMSC_MAC_CSR_PADSTR (0x1UL << 8) /* PAD stripping */ #define SMSC_MAC_CSR_TXEN (0x1UL << 3) /* TX enable */ #define SMSC_MAC_CSR_RXEN (0x1UL << 2) /* RX enable */ Index: src/sys/dev/usb/if_smscvar.h diff -u src/sys/dev/usb/if_smscvar.h:1.2 src/sys/dev/usb/if_smscvar.h:1.3 --- src/sys/dev/usb/if_smscvar.h:1.2 Sat Feb 9 16:42:45 2013 +++ src/sys/dev/usb/if_smscvar.h Mon Jun 9 14:18:28 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if_smscvar.h,v 1.2 2013/02/09 16:42:45 skrll Exp $ */ +/* $NetBSD: if_smscvar.h,v 1.3 2014/06/09 14:18:28 mlelstv Exp $ */ /* $OpenBSD: if_smscreg.h,v 1.2 2012/09/27 12:38:11 jsg Exp $ */ /*- @@ -69,6 +69,8 @@ struct smsc_softc { uint32_t sc_mac_csr; uint32_t sc_rev_id; + uint32_t sc_coe_ctrl; + int sc_if_flags; int sc_refcnt;