summaryrefslogtreecommitdiffstats
path: root/c/src
diff options
context:
space:
mode:
authorTill Straumann <strauman@slac.stanford.edu>2009-06-06 00:45:57 +0000
committerTill Straumann <strauman@slac.stanford.edu>2009-06-06 00:45:57 +0000
commit4488b8853040a901425ceb390f71e3a1942066e1 (patch)
treeeef5d0618638c16dac9d8d754bce1ccdefe2f932 /c/src
parentAdd PR 1420. (diff)
downloadrtems-4488b8853040a901425ceb390f71e3a1942066e1.tar.bz2
2009-06-05 Till Straumann <strauman@slac.stanford.edu>
* network/tsec.c, network/if_tsec_pub.h: added multicast support.
Diffstat (limited to 'c/src')
-rw-r--r--c/src/lib/libbsp/powerpc/mvme3100/ChangeLog4
-rw-r--r--c/src/lib/libbsp/powerpc/mvme3100/network/if_tsec_pub.h34
-rw-r--r--c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c106
3 files changed, 133 insertions, 11 deletions
diff --git a/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog b/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog
index 78ad7499ff..bba0bf74ea 100644
--- a/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog
+++ b/c/src/lib/libbsp/powerpc/mvme3100/ChangeLog
@@ -1,3 +1,7 @@
+2009-06-05 Till Straumann <strauman@slac.stanford.edu>
+ * network/tsec.c, network/if_tsec_pub.h:
+ added multicast support.
+
2009-03-05 Till Straumann <strauman@slac.stanford.edu>
* startup/bspstart.c: removed legacy code (inherited
diff --git a/c/src/lib/libbsp/powerpc/mvme3100/network/if_tsec_pub.h b/c/src/lib/libbsp/powerpc/mvme3100/network/if_tsec_pub.h
index da3f2bc1a7..0a7760d0c1 100644
--- a/c/src/lib/libbsp/powerpc/mvme3100/network/if_tsec_pub.h
+++ b/c/src/lib/libbsp/powerpc/mvme3100/network/if_tsec_pub.h
@@ -173,11 +173,45 @@ BSP_tsec_reset_stats(struct tsec_private *mp);
* 'promisc' whether to set promiscuous flag.
* 'enaddr' pointer to six bytes with MAC address. Read
* from the device if NULL.
+ * NOTE: multicast filter is cleared by this routine.
*/
void
BSP_tsec_init_hw(struct tsec_private *mp, int promisc, unsigned char *enaddr);
/*
+ * Clear multicast hash filter. No multicast frames are accepted
+ * after executing this routine (unless the hardware was initialized
+ * in 'promiscuous' mode).
+ */
+void
+BSP_tsec_mcast_filter_clear(struct tsec_private *mp);
+
+/*
+ * Program multicast filter to accept all multicast frames.
+ */
+void
+BSP_tsec_mcast_filter_accept_all(struct tsec_private *mp);
+
+/*
+ * Add a MAC address to the multicast filter.
+ * Existing entries are not changed but note that
+ * the filter is imperfect, i.e., multiple MAC addresses
+ * may alias to a single filter entry. Hence software
+ * filtering must still be performed.
+ *
+ * NOTE: Deletion of an address is not possible. This is
+ * usually accomplished by a higher-level driver
+ * maintaining a list/database of multicast addresses
+ * and going through a sequence:
+ *
+ * BSP_tsec_mcast_filter_clear()
+ * forall mcast addresses do
+ * BSP_tsec_mcast_filter_accept_add()
+ */
+void
+BSP_tsec_mcast_filter_accept_add(struct tsec_private *mp, unsigned char *enaddr);
+
+/*
* Dump statistics to FILE 'f'. If NULL, stdout is used.
*/
void
diff --git a/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c b/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c
index 993a4ee306..3127466b4a 100644
--- a/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c
+++ b/c/src/lib/libbsp/powerpc/mvme3100/network/tsec.c
@@ -50,6 +50,7 @@
#include <libcpu/byteorder.h>
#include <inttypes.h>
#include <stdio.h>
+#include <errno.h>
#include <assert.h>
#include <bsp.h>
@@ -113,6 +114,9 @@ phy_irq_pending(struct tsec_private *mp);
static uint32_t
phy_ack_irq(struct tsec_private *mp);
+static void
+tsec_update_mcast(struct ifnet *ifp);
+
#if defined(PARANOIA) || defined(DEBUG)
void tsec_dump_tring(struct tsec_private *mp);
void tsec_dump_rring(struct tsec_private *mp);
@@ -1339,9 +1343,10 @@ rtems_interrupt_level l;
for ( i=0; i<8*4; i+=4 ) {
fec_wr( b, TSEC_IADDR0 + i, 0 );
- fec_wr( b, TSEC_GADDR0 + i, 0 );
}
+ BSP_tsec_mcast_filter_clear(mp);
+
BSP_tsec_reset_stats(mp);
fec_wr( b, TSEC_ATTR, (TSEC_ATTR_RDSEN | TSEC_ATTR_RBDSEN) );
@@ -1396,6 +1401,54 @@ rtems_interrupt_level l;
rtems_interrupt_enable( l );
}
+static uint8_t
+hash_accept(struct tsec_private *mp, uint32_t tble, const uint8_t *enaddr)
+{
+uint8_t s;
+
+ s = ether_crc32_le(enaddr, ETHER_ADDR_LEN);
+
+ /* bit-reverse */
+ s = ((s&0x0f) << 4) | ((s&0xf0) >> 4);
+ s = ((s&0x33) << 2) | ((s&0xcc) >> 2);
+ s = ((s&0x55) << 1) | ((s&0xaa) >> 1);
+
+ fec_wr( mp->base, tble + (s >> (5-2)), (1 << (31 - (s & 31))) );
+}
+
+void
+BSP_tsec_mcast_filter_clear(struct tsec_private *mp)
+{
+int i;
+ for ( i=0; i<8*4; i+=4 ) {
+ fec_wr( mp->base, TSEC_GADDR0 + i, 0 );
+ }
+}
+
+void
+BSP_tsec_mcast_filter_accept_all(struct tsec_private *mp)
+{
+int i;
+ for ( i=0; i<8*4; i+=4 ) {
+ fec_wr( mp->base, TSEC_GADDR0 + i, 0xffffffff );
+ }
+}
+
+void
+BSP_tsec_mcast_filter_accept_add(struct tsec_private *mp, uint8_t *enaddr)
+{
+static const uint8_t bcst={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ if ( ! (enaddr[0] & 0x01) ) {
+ /* not a multicast address; ignore */
+ return;
+ }
+ if ( 0 == memcmp( enaddr, bcst, sizeof(bcst) ) ) {
+ /* broadcast; ignore */
+ return;
+ }
+ hash_accept(mp, TSEC_GADDR0, enaddr);
+}
+
void
BSP_tsec_dump_stats(struct tsec_private *mp, FILE *f)
{
@@ -2310,6 +2363,8 @@ tsec_init(void *arg)
struct tsec_softc *sc = arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
BSP_tsec_init_hw(&sc->pvt, ifp->if_flags & IFF_PROMISC, sc->arpcom.ac_enaddr);
+
+ tsec_update_mcast(ifp);
ifp->if_flags |= IFF_RUNNING;
sc->arpcom.ac_if.if_timer = 0;
}
@@ -2348,6 +2403,31 @@ struct tsec_softc *sc = ifp->if_softc;
tsec_start(ifp);
}
+static void
+tsec_update_mcast(struct ifnet *ifp)
+{
+struct tsec_softc *sc = ifp->if_softc;
+struct ether_multi *enm;
+struct ether_multistep step;
+
+ if ( IFF_ALLMULTI & ifp->if_flags ) {
+ BSP_tsec_mcast_filter_accept_all( &sc->pvt );
+ } else {
+ BSP_tsec_mcast_filter_clear( &sc->pvt );
+
+ ETHER_FIRST_MULTI(step, (struct arpcom *)ifp, enm);
+
+ while ( enm ) {
+ if ( memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) )
+ assert( !"Should never get here; IFF_ALLMULTI should be set!" );
+
+ BSP_tsec_mcast_filter_accept_add(&sc->pvt, enm->enm_addrlo);
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ }
+}
+
/* bsdnet driver ioctl entry */
static int
tsec_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
@@ -2395,15 +2475,19 @@ int f;
error = BSP_tsec_media_ioctl(&sc->pvt, cmd, &ifr->ifr_media);
break;
-/*
- * TODO
- *
- * case SIOCADDMULTI:
- * case SIOCDELMULTI:
- *
- * break;
- */
-
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = (cmd == SIOCADDMULTI)
+ ? ether_addmulti(ifr, &sc->arpcom)
+ : ether_delmulti(ifr, &sc->arpcom);
+
+ if (error == ENETRESET) {
+ if (ifp->if_flags & IFF_RUNNING) {
+ tsec_update_mcast(ifp);
+ }
+ error = 0;
+ }
+ break;
case SIO_RTEMS_SHOW_STATS:
BSP_tsec_dump_stats( &sc->pvt, stdout );
@@ -2570,7 +2654,7 @@ struct ifnet *ifp;
ifp->if_timer = 0;
sc->bsd.oif_flags = /* ... */
- ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
/*
* if unset, this set to 10Mbps by ether_ifattach; seems to be unused by bsdnet stack;