Subject: changes to elinkxl.c:ex_set_mc()
To: None <tech-net@netbsd.org>
From: enami tsugutomo <enami@sm.sony.co.jp>
List: tech-net
Date: 04/30/2002 15:34:57
Hi, all.

In the function ex_set_mc() defined in elinkxl.c, there is two
possible bugs.

One is that it assumes someone set IFF_ALLMULTI for us, but actually
it's not true.  We need to set by our self.

Other is that for newer cards which supports multicast hash filtering,
we need to clear hash bit before programming them.

Appened diff is created by me and Frank.  But since I have only
pre-905B card, I can't test the latter.  So, I beg someone to test or
at least review this.

enami.

Index: elinkxl.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/elinkxl.c,v
retrieving revision 1.62
diff -u -r1.62 elinkxl.c
--- elinkxl.c	2002/04/06 19:28:01	1.62
+++ elinkxl.c	2002/04/30 06:28:17
@@ -711,7 +711,9 @@
 	return (error);
 }
 
-#define	ex_mchash(addr)	(ether_crc32_be((addr), ETHER_ADDR_LEN) & 0xff)
+#define	MCHASHSIZE		256
+#define	ex_mchash(addr)		(ether_crc32_be((addr), ETHER_ADDR_LEN) & \
+				    (MCHASHSIZE - 1))
 
 /*
  * Set multicast receive filter. Also take care of promiscuous mode
@@ -728,28 +730,44 @@
 	int i;
 	u_int16_t mask = FIL_INDIVIDUAL | FIL_BRDCST;
 
-	if (ifp->if_flags & IFF_PROMISC)
+	if (ifp->if_flags & IFF_PROMISC) {
 		mask |= FIL_PROMISC;
+		goto allmulti;
+	}
 	
-	if (!(ifp->if_flags & IFF_MULTICAST))
-		goto out;
+	ETHER_FIRST_MULTI(estep, ec, enm);
+	if (enm == NULL)
+		goto nomulti;
 
-	if (!(sc->ex_conf & EX_CONF_90XB) || ifp->if_flags & IFF_ALLMULTI) {
-		mask |= (ifp->if_flags & IFF_MULTICAST) ? FIL_MULTICAST : 0;
-	} else {
-		ETHER_FIRST_MULTI(estep, ec, enm);
-		while (enm != NULL) {
-			if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
-			    ETHER_ADDR_LEN) != 0)
-				goto out;
-			i = ex_mchash(enm->enm_addrlo);
-			bus_space_write_2(sc->sc_iot, sc->sc_ioh,
-			    ELINK_COMMAND, ELINK_SETHASHFILBIT | i);
-			ETHER_NEXT_MULTI(estep, enm);
-		}
-		mask |= FIL_MULTIHASH;
-	}
- out:
+	if ((sc->ex_conf & EX_CONF_90XB) == 0)
+		/* No multicast hash filtering. */
+		goto allmulti;
+
+	for (i = 0; i < MCHASHSIZE; i++)
+		bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+		    ELINK_COMMAND, ELINK_CLEARHASHFILBIT | i);
+
+	do {
+		if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
+		    ETHER_ADDR_LEN) != 0)
+			goto allmulti;
+
+		i = ex_mchash(enm->enm_addrlo);
+		bus_space_write_2(sc->sc_iot, sc->sc_ioh,
+		    ELINK_COMMAND, ELINK_SETHASHFILBIT | i);
+		ETHER_NEXT_MULTI(estep, enm);
+	} while (enm != NULL);
+	mask |= FIL_MULTIHASH;
+
+nomulti:
+	ifp->if_flags &= ~IFF_ALLMULTI;
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_COMMAND,
+	    SET_RX_FILTER | mask);
+	return;
+
+allmulti:
+	ifp->if_flags |= IFF_ALLMULTI;
+	mask |= FIL_MULTICAST;
 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_COMMAND,
 	    SET_RX_FILTER | mask);
 }
Index: elinkxlreg.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/elinkxlreg.h,v
retrieving revision 1.10
diff -u -r1.10 elinkxlreg.h
--- elinkxlreg.h	2001/12/28 20:35:47	1.10
+++ elinkxlreg.h	2002/04/30 06:28:17
@@ -164,6 +164,7 @@
 #define ELINK_DNUNSTALL		0x3003
 #define ELINK_TXRECLTHRESH	0xc000
 #define ELINK_TXSTARTTHRESH	0x9800
+#define ELINK_CLEARHASHFILBIT	0xc800
 #define ELINK_SETHASHFILBIT	0xcc00
 
 /*