summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphilip <philip@FreeBSD.org>2006-03-10 23:50:53 +0000
committerphilip <philip@FreeBSD.org>2006-03-10 23:50:53 +0000
commitdffb931651deee52af7d1976b0f14baf193bba39 (patch)
tree061302cb3f07370992df22ff5f7032b70980c839
parent87f2ab82b24d08a85bd8bb6bf1978ecf9999164f (diff)
downloadFreeBSD-src-dffb931651deee52af7d1976b0f14baf193bba39.zip
FreeBSD-src-dffb931651deee52af7d1976b0f14baf193bba39.tar.gz
Fix multicast support for cs89x0 chips. Just setting the RX_MULTCAST_ACCEPT
flag isn't enough - the filter needs to be set up too, or no multicast frames are accepted. Sponsored by: Philips Industrial Applications (indirectly) MFC after: 3 days
-rw-r--r--sys/dev/cs/if_cs.c74
1 files changed, 56 insertions, 18 deletions
diff --git a/sys/dev/cs/if_cs.c b/sys/dev/cs/if_cs.c
index a55c4eb..1417c4d 100644
--- a/sys/dev/cs/if_cs.c
+++ b/sys/dev/cs/if_cs.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/if_arp.h>
+#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/ethernet.h>
@@ -1051,11 +1052,28 @@ cs_reset(struct cs_softc *sc)
cs_init(sc);
}
+static uint16_t
+cs_hash_index(struct sockaddr_dl *addr)
+{
+ uint32_t crc;
+ uint16_t idx;
+ caddr_t lla;
+
+ lla = LLADDR(addr);
+ crc = ether_crc32_le(lla, ETHER_ADDR_LEN);
+ idx = crc >> 26;
+
+ return (idx);
+}
+
static void
cs_setmode(struct cs_softc *sc)
{
- struct ifnet *ifp = sc->ifp;
int rx_ctl;
+ uint16_t af[4];
+ uint16_t port, mask, index;
+ struct ifnet *ifp = sc->ifp;
+ struct ifmultiaddr *ifma;
/* Stop the receiver while changing filters */
cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) & ~SERIAL_RX_ON);
@@ -1063,26 +1081,46 @@ cs_setmode(struct cs_softc *sc)
if (ifp->if_flags & IFF_PROMISC) {
/* Turn on promiscuous mode. */
rx_ctl = RX_OK_ACCEPT | RX_PROM_ACCEPT;
- } else {
- if (ifp->if_flags & IFF_MULTICAST) {
- /* Allow receiving frames with multicast addresses */
- rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
- RX_OK_ACCEPT | RX_MULTCAST_ACCEPT;
- /*
- * Here the reconfiguration of chip's multicast
- * filters should be done but I've no idea about
- * hash transformation in this chip. If you can
- * add this code or describe me the transformation
- * I'd be very glad.
- */
+ } else if (ifp->if_flags & IFF_MULTICAST) {
+ /* Allow receiving frames with multicast addresses */
+ rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
+ RX_OK_ACCEPT | RX_MULTCAST_ACCEPT;
+
+ /* Start with an empty filter */
+ af[0] = af[1] = af[2] = af[3] = 0x0000;
+
+ if (ifp->if_flags & IFF_ALLMULTI) {
+ /* Accept all multicast frames */
+ af[0] = af[1] = af[2] = af[3] = 0xffff;
} else {
- /*
- * Receive only good frames addressed for us and
- * good broadcasts.
+ /*
+ * Set up the filter to only accept multicast
+ * frames we're interested in.
*/
- rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
- RX_OK_ACCEPT;
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ struct sockaddr_dl *dl =
+ (struct sockaddr_dl *)ifma->ifma_addr;
+
+ index = cs_hash_index(dl);
+ port = (u_int16_t) (index >> 4);
+ mask = (u_int16_t) (1 << (index & 0xf));
+ af[port] |= mask;
+ }
+ IF_ADDR_UNLOCK(ifp);
}
+
+ cs_writereg(sc, PP_LAF + 0, af[0]);
+ cs_writereg(sc, PP_LAF + 2, af[1]);
+ cs_writereg(sc, PP_LAF + 4, af[2]);
+ cs_writereg(sc, PP_LAF + 6, af[3]);
+ } else {
+ /*
+ * Receive only good frames addressed for us and
+ * good broadcasts.
+ */
+ rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
+ RX_OK_ACCEPT;
}
/* Set up the filter */
OpenPOWER on IntegriCloud