summaryrefslogtreecommitdiff
path: root/sys/dev/pci/if_re.c
diff options
context:
space:
mode:
authorPeter Valchev <pvalchev@cvs.openbsd.org>2004-06-05 07:39:56 +0000
committerPeter Valchev <pvalchev@cvs.openbsd.org>2004-06-05 07:39:56 +0000
commit31fa11de5b3256af5c700900141ee07fc8ee81b0 (patch)
tree2e5300e28c4b4ac7c308b2bf3be9ed784f471bd1 /sys/dev/pci/if_re.c
parent060beeae40038712271f1727749e794d32428cb0 (diff)
add multicast support
Diffstat (limited to 'sys/dev/pci/if_re.c')
-rw-r--r--sys/dev/pci/if_re.c81
1 files changed, 79 insertions, 2 deletions
diff --git a/sys/dev/pci/if_re.c b/sys/dev/pci/if_re.c
index 9c8a39b8e78..6416bb65388 100644
--- a/sys/dev/pci/if_re.c
+++ b/sys/dev/pci/if_re.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_re.c,v 1.1 2004/06/05 06:13:06 pvalchev Exp $ */
+/* $OpenBSD: if_re.c,v 1.2 2004/06/05 07:39:55 pvalchev Exp $ */
/*
* Copyright (c) 1997, 1998-2003
* Bill Paul <wpaul@windriver.com>. All rights reserved.
@@ -201,6 +201,7 @@ int re_miibus_readreg (struct device *, int, int);
void re_miibus_writereg (struct device *, int, int, int);
void re_miibus_statchg (struct device *);
+void re_setmulti (struct rl_softc *);
void re_reset (struct rl_softc *);
int re_diag (struct rl_softc *);
@@ -490,6 +491,68 @@ re_miibus_statchg(struct device *dev)
return;
}
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+void
+re_setmulti(sc)
+ struct rl_softc *sc;
+{
+ struct ifnet *ifp;
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ u_int32_t rxfilt;
+ int mcnt = 0;
+ struct arpcom *ac = &sc->sc_arpcom;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+
+ ifp = &sc->sc_arpcom.ac_if;
+
+ rxfilt = CSR_READ_4(sc, RL_RXCFG);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxfilt |= RL_RXCFG_RX_MULTI;
+ CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
+ CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF);
+ CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ CSR_WRITE_4(sc, RL_MAR0, 0);
+ CSR_WRITE_4(sc, RL_MAR4, 0);
+
+ /* now program new ones */
+ ETHER_FIRST_MULTI(step, ac, enm);
+ while (enm != NULL) {
+ if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
+ ifp->if_flags |= IFF_ALLMULTI;
+ mcnt = MAX_NUM_MULTICAST_ADDRESSES;
+ }
+ if (mcnt == MAX_NUM_MULTICAST_ADDRESSES)
+ break;
+
+ h = (ether_crc32_be(enm->enm_addrlo,
+ ETHER_ADDR_LEN) >> 26) & 0x0000003F;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ if (mcnt)
+ rxfilt |= RL_RXCFG_RX_MULTI;
+ else
+ rxfilt &= ~RL_RXCFG_RX_MULTI;
+
+ CSR_WRITE_4(sc, RL_RXCFG, rxfilt);
+ CSR_WRITE_4(sc, RL_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, RL_MAR4, hashes[1]);
+}
+
void
re_reset(sc)
struct rl_softc *sc;
@@ -1801,7 +1864,7 @@ re_init(struct ifnet *ifp)
/*
* Program the multicast filter, if necessary.
*/
- rl_setmulti(sc);
+ re_setmulti(sc);
#ifdef DEVICE_POLLING
/*
@@ -1957,6 +2020,20 @@ re_ioctl(ifp, command, data)
}
error = 0;
break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->sc_arpcom) :
+ ether_delmulti(ifr, &sc->sc_arpcom);
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware
+ * filter accordingly.
+ */
+ re_setmulti(sc);
+ error = 0;
+ }
+ break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, command);