2009-06-05 Till Straumann <strauman@slac.stanford.edu>

* network/tsec.c, network/if_tsec_pub.h:
	implemented multicast support.
This commit is contained in:
Till Straumann
2009-06-06 00:49:41 +00:00
parent c30fcf522c
commit 5cb7096bde
3 changed files with 133 additions and 10 deletions

View File

@@ -1,3 +1,8 @@
2009-06-05 Till Straumann <strauman@slac.stanford.edu>
* network/tsec.c, network/if_tsec_pub.h:
implemented multicast support.
2009-04-28 Chris Johns <chrisj@rtems.org>
* start/start.S: Update for boot_card command line change.

View File

@@ -173,10 +173,44 @@ 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.
*/

View File

@@ -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;