Commit bfc65013 authored by Russell King - ARM Linux's avatar Russell King - ARM Linux Committed by David S. Miller

NET: am79c961: ensure multicast filter is correctly set at open

We were clearing out the multicast filter whenever the interface was
upped, and not setting the mode bits correctly.  This can cause
problems if there are any multicast addresses already set at this
point, or if ALLMULTI was set.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d814dee0
...@@ -196,6 +196,42 @@ am79c961_ramtest(struct net_device *dev, unsigned int val) ...@@ -196,6 +196,42 @@ am79c961_ramtest(struct net_device *dev, unsigned int val)
return errorcount; return errorcount;
} }
static void am79c961_mc_hash(char *addr, u16 *hash)
{
if (addr[0] & 0x01) {
int idx, bit;
u32 crc;
crc = ether_crc_le(ETH_ALEN, addr);
idx = crc >> 30;
bit = (crc >> 26) & 15;
hash[idx] |= 1 << bit;
}
}
static unsigned int am79c961_get_rx_mode(struct net_device *dev, u16 *hash)
{
unsigned int mode = MODE_PORT_10BT;
if (dev->flags & IFF_PROMISC) {
mode |= MODE_PROMISC;
memset(hash, 0xff, 4 * sizeof(*hash));
} else if (dev->flags & IFF_ALLMULTI) {
memset(hash, 0xff, 4 * sizeof(*hash));
} else {
struct netdev_hw_addr *ha;
memset(hash, 0, 4 * sizeof(*hash));
netdev_for_each_mc_addr(ha, dev)
am79c961_mc_hash(ha->addr, hash);
}
return mode;
}
static void static void
am79c961_init_for_open(struct net_device *dev) am79c961_init_for_open(struct net_device *dev)
{ {
...@@ -203,6 +239,7 @@ am79c961_init_for_open(struct net_device *dev) ...@@ -203,6 +239,7 @@ am79c961_init_for_open(struct net_device *dev)
unsigned long flags; unsigned long flags;
unsigned char *p; unsigned char *p;
u_int hdr_addr, first_free_addr; u_int hdr_addr, first_free_addr;
u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
int i; int i;
/* /*
...@@ -218,16 +255,12 @@ am79c961_init_for_open(struct net_device *dev) ...@@ -218,16 +255,12 @@ am79c961_init_for_open(struct net_device *dev)
write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */ write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
for (i = LADRL; i <= LADRH; i++) for (i = LADRL; i <= LADRH; i++)
write_rreg (dev->base_addr, i, 0); write_rreg (dev->base_addr, i, multi_hash[i - LADRL]);
for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2) for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
write_rreg (dev->base_addr, i, p[0] | (p[1] << 8)); write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
i = MODE_PORT_10BT; write_rreg (dev->base_addr, MODE, mode);
if (dev->flags & IFF_PROMISC)
i |= MODE_PROMISC;
write_rreg (dev->base_addr, MODE, i);
write_rreg (dev->base_addr, POLLINT, 0); write_rreg (dev->base_addr, POLLINT, 0);
write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
...@@ -340,21 +373,6 @@ am79c961_close(struct net_device *dev) ...@@ -340,21 +373,6 @@ am79c961_close(struct net_device *dev)
return 0; return 0;
} }
static void am79c961_mc_hash(char *addr, unsigned short *hash)
{
if (addr[0] & 0x01) {
int idx, bit;
u32 crc;
crc = ether_crc_le(ETH_ALEN, addr);
idx = crc >> 30;
bit = (crc >> 26) & 15;
hash[idx] |= 1 << bit;
}
}
/* /*
* Set or clear promiscuous/multicast mode filter for this adapter. * Set or clear promiscuous/multicast mode filter for this adapter.
*/ */
...@@ -362,24 +380,9 @@ static void am79c961_setmulticastlist (struct net_device *dev) ...@@ -362,24 +380,9 @@ static void am79c961_setmulticastlist (struct net_device *dev)
{ {
struct dev_priv *priv = netdev_priv(dev); struct dev_priv *priv = netdev_priv(dev);
unsigned long flags; unsigned long flags;
unsigned short multi_hash[4], mode; u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
int i, stopped; int i, stopped;
mode = MODE_PORT_10BT;
if (dev->flags & IFF_PROMISC) {
mode |= MODE_PROMISC;
} else if (dev->flags & IFF_ALLMULTI) {
memset(multi_hash, 0xff, sizeof(multi_hash));
} else {
struct netdev_hw_addr *ha;
memset(multi_hash, 0x00, sizeof(multi_hash));
netdev_for_each_mc_addr(ha, dev)
am79c961_mc_hash(ha->addr, multi_hash);
}
spin_lock_irqsave(&priv->chip_lock, flags); spin_lock_irqsave(&priv->chip_lock, flags);
stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment