Module Name: src Committed By: martin Date: Sun Oct 26 17:39:16 UTC 2014
Modified Files: src/sys/dev/ic: dwc_gmac.c dwc_gmac_var.h Log Message: Greatly simplify ioctl handling by fully relying on ether_ioctl and a ifflags-change callback, together with a shadow copy of the last active interface flags. Fix ALLMULTI vs. PROMISC handling. Fix mac hash filter calculation by reversing the crc bits (pointed out by Jared). To generate a diff of this commit: cvs rdiff -u -r1.21 -r1.22 src/sys/dev/ic/dwc_gmac.c cvs rdiff -u -r1.4 -r1.5 src/sys/dev/ic/dwc_gmac_var.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/ic/dwc_gmac.c diff -u src/sys/dev/ic/dwc_gmac.c:1.21 src/sys/dev/ic/dwc_gmac.c:1.22 --- src/sys/dev/ic/dwc_gmac.c:1.21 Sat Oct 25 18:00:25 2014 +++ src/sys/dev/ic/dwc_gmac.c Sun Oct 26 17:39:16 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_gmac.c,v 1.21 2014/10/25 18:00:25 joerg Exp $ */ +/* $NetBSD: dwc_gmac.c,v 1.22 2014/10/26 17:39:16 martin Exp $ */ /*- * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc. @@ -41,7 +41,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.21 2014/10/25 18:00:25 joerg Exp $"); +__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.22 2014/10/26 17:39:16 martin Exp $"); /* #define DWC_GMAC_DEBUG 1 */ @@ -91,6 +91,8 @@ static int dwc_gmac_ioctl(struct ifnet * static void dwc_gmac_tx_intr(struct dwc_gmac_softc *sc); static void dwc_gmac_rx_intr(struct dwc_gmac_softc *sc); static void dwc_gmac_setmulti(struct dwc_gmac_softc *sc); +static int dwc_gmac_ifflags_cb(struct ethercom *); +static uint32_t bitrev32(uint32_t x); #define TX_DESC_OFFSET(N) ((AWGE_RX_RING_COUNT+(N)) \ *sizeof(struct dwc_gmac_dev_dmadesc)) @@ -122,6 +124,7 @@ static void dwc_gmac_dump_tx_desc(struct static void dwc_gmac_dump_rx_desc(struct dwc_gmac_softc *sc); static void dwc_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg); static void dwc_dump_status(struct dwc_gmac_softc *sc); +static void dwc_gmac_dump_ffilt(struct dwc_gmac_softc *sc, uint32_t ffilt); #endif void @@ -237,6 +240,7 @@ dwc_gmac_attach(struct dwc_gmac_softc *s */ if_attach(ifp); ether_ifattach(ifp, enaddr); + ether_set_ifflags_cb(&sc->sc_ec, dwc_gmac_ifflags_cb); /* * Enable interrupts @@ -897,78 +901,51 @@ dwc_gmac_queue(struct dwc_gmac_softc *sc return 0; } +/* + * If the interface is up and running, only modify the receive + * filter when setting promiscuous or debug mode. Otherwise fall + * through to ether_ioctl, which will reset the chip. + */ +static int +dwc_gmac_ifflags_cb(struct ethercom *ec) +{ + struct ifnet *ifp = &ec->ec_if; + struct dwc_gmac_softc *sc = ifp->if_softc; + int change = ifp->if_flags ^ sc->sc_if_flags; + + if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0) + return ENETRESET; + if ((change & IFF_PROMISC) != 0) + dwc_gmac_setmulti(sc); + return 0; +} + static int dwc_gmac_ioctl(struct ifnet *ifp, u_long cmd, void *data) { struct dwc_gmac_softc *sc = ifp->if_softc; - struct ifaddr *ifa = (struct ifaddr *)data; int s, error = 0; s = splnet(); - switch (cmd) { - case SIOCINITIFADDR: - ifp->if_flags |= IFF_UP; - dwc_gmac_init(ifp); - switch (ifa->ifa_addr->sa_family) { -#ifdef INET - case AF_INET: - arp_ifinit(ifp, ifa); - break; -#endif - default: - break; - } - break; - - case SIOCSIFFLAGS: - if ((error = ifioctl_common(ifp, cmd, data)) != 0) - break; - - switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { - case IFF_RUNNING: - /* - * If interface is marked down and it is running, then - * stop it. - */ - dwc_gmac_stop(ifp, 0); - ifp->if_flags &= ~IFF_RUNNING; - break; - case IFF_UP: - /* - * If interface is marked up and it is stopped, then - * start it. - */ - error = dwc_gmac_init(ifp); - break; - case IFF_UP|IFF_RUNNING: - /* - * If setting debug or promiscuous mode, do not reset - * the chip; for everything else, call dwc_gmac_init() - * which will trigger a reset. - */ - /* XXX - for now allways init */ - error = dwc_gmac_init(ifp); - break; - case 0: - break; - } - - break; - - default: - if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) - break; + if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) { error = 0; if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI) ; - else if (ifp->if_flags & IFF_RUNNING) + else if (ifp->if_flags & IFF_RUNNING) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ dwc_gmac_setmulti(sc); - break; + } } + /* Try to get things going again */ + if (ifp->if_flags & IFF_UP) + dwc_gmac_start(ifp); + sc->sc_if_flags = sc->sc_ec.ec_if.if_flags; splx(s); - return error; } @@ -1134,6 +1111,20 @@ skip: } +/* + * Revers order of bits - http://aggregate.org/MAGIC/#Bit%20Reversal + */ +static uint32_t +bitrev32(uint32_t x) +{ + x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); + x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); + x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); + x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); + + return (x >> 16) | (x << 16); +} + static void dwc_gmac_setmulti(struct dwc_gmac_softc *sc) { @@ -1141,26 +1132,20 @@ dwc_gmac_setmulti(struct dwc_gmac_softc struct ether_multi *enm; struct ether_multistep step; uint32_t hashes[2] = { 0, 0 }; - uint32_t ffilt; - int h, mcnt; + uint32_t ffilt, h; + int mcnt, s; + + s = splnet(); ffilt = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT); if (ifp->if_flags & IFF_PROMISC) { -allmulti: - ifp->if_flags |= IFF_ALLMULTI; - ffilt |= AWIN_GMAC_MAC_FFILT_PM; - bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT, - ffilt); - bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTLOW, - 0xffffffff); - bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH, - 0xffffffff); - return; + ffilt |= AWIN_GMAC_MAC_FFILT_PR; + goto special_filter; } ifp->if_flags &= ~IFF_ALLMULTI; - ffilt &= ~AWIN_GMAC_MAC_FFILT_PM; + ffilt &= ~(AWIN_GMAC_MAC_FFILT_PM|AWIN_GMAC_MAC_FFILT_PR); bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTLOW, 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH, 0); @@ -1169,10 +1154,15 @@ allmulti: mcnt = 0; while (enm != NULL) { if (memcmp(enm->enm_addrlo, enm->enm_addrhi, - ETHER_ADDR_LEN) != 0) - goto allmulti; + ETHER_ADDR_LEN) != 0) { + ffilt |= AWIN_GMAC_MAC_FFILT_PM; + ifp->if_flags |= IFF_ALLMULTI; + goto special_filter; + } - h = (~ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN)) >> 26; + h = bitrev32( + ~ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN) + ) >> 26; hashes[h >> 5] |= (1 << (h & 0x1f)); mcnt++; @@ -1189,6 +1179,28 @@ allmulti: hashes[0]); bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH, hashes[1]); + sc->sc_if_flags = sc->sc_ec.ec_if.if_flags; + + splx(s); + +#ifdef DWC_GMAC_DEBUG + dwc_gmac_dump_ffilt(sc, ffilt); +#endif + return; + +special_filter: +#ifdef DWC_GMAC_DEBUG + dwc_gmac_dump_ffilt(sc, ffilt); +#endif + /* no MAC hashes, ALLMULTI or PROMISC */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT, + ffilt); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTLOW, + 0xffffffff); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH, + 0xffffffff); + sc->sc_if_flags = sc->sc_ec.ec_if.if_flags; + splx(s); } int @@ -1338,10 +1350,32 @@ static void dwc_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg) { dwc_dump_status(sc); + dwc_gmac_dump_ffilt(sc, + bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT)); dwc_gmac_dump_dma(sc); dwc_gmac_dump_tx_desc(sc); dwc_gmac_dump_rx_desc(sc); panic("%s", msg); } + +static void dwc_gmac_dump_ffilt(struct dwc_gmac_softc *sc, uint32_t ffilt) +{ + char buf[200]; + + /* print filter setup */ + snprintb(buf, sizeof(buf), "\177\20" + "b\x1f""RA\0" + "b\x0a""HPF\0" + "b\x09""SAF\0" + "b\x08""SAIF\0" + "b\x05""DBF\0" + "b\x04""PM\0" + "b\x03""DAIF\0" + "b\x02""HMC\0" + "b\x01""HUC\0" + "b\x00""PR\0" + "\0", ffilt); + aprint_normal_dev(sc->sc_dev, "FFILT: %s\n", buf); +} #endif Index: src/sys/dev/ic/dwc_gmac_var.h diff -u src/sys/dev/ic/dwc_gmac_var.h:1.4 src/sys/dev/ic/dwc_gmac_var.h:1.5 --- src/sys/dev/ic/dwc_gmac_var.h:1.4 Mon Oct 20 20:10:05 2014 +++ src/sys/dev/ic/dwc_gmac_var.h Sun Oct 26 17:39:16 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_gmac_var.h,v 1.4 2014/10/20 20:10:05 jmcneill Exp $ */ +/* $NetBSD: dwc_gmac_var.h,v 1.5 2014/10/26 17:39:16 martin Exp $ */ /*- * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc. @@ -86,6 +86,7 @@ struct dwc_gmac_softc { bus_dma_segment_t sc_dma_ring_seg; /* and TX ring */ struct dwc_gmac_rx_ring sc_rxq; struct dwc_gmac_tx_ring sc_txq; + short sc_if_flags; /* shadow of ether flags */ uint16_t sc_mii_clk; };