p54: implement multicast filter
authorChristian Lamparter <chunkeey@googlemail.com>
Sun, 24 Apr 2011 15:22:59 +0000 (17:22 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 26 Apr 2011 19:50:30 +0000 (15:50 -0400)
"For best CPU usage and power consumption, having as few
frames as possible percolate through the stack is
desirable. Hence, the hardware should filter as much
as possible."

Note: Not all firmwares include the multicast filter
feature and the stack does not filter them either.
The ARP filter on the other hand was dropped from the
patch since it does not work correctly:

Quote from: Max Filippov <jcmvbkbc@gmail.com>
<http://www.spinics.net/lists/linux-wireless/msg67466.html>
"In the ARP case, when there's no other traffic on p54spi,
all ARP requests are dropped. But if there's some egress
traffic from p54spi, filter seems to work correctly:
only ARP requests that match filter pass through.

In the multicast case filter seems to work correctly, but
it treats broadcast as subject to that filtering too. By
default only 01:00:5e:00:00:01 gets into priv->mc_maclist,
so we miss all broadcasts.

These two filters seem to interfere:
- if we set ARP filter and multicast filter without bc
=>  we miss all ARPs if there's no egress traffic;
- if we set ARP filter and multicast filter with bc or
don't set mc filter at all => we get all ARPs.

This effect does not depend on filter setup order."

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Tested-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/p54/fwio.c
drivers/net/wireless/p54/lmac.h
drivers/net/wireless/p54/main.c
drivers/net/wireless/p54/p54.h

index 2fab7d20ffc2621bed540373ca05468c8c0669af..b6a061cbbdec1651f4fec04ecceac4539dd75ba7 100644 (file)
@@ -727,3 +727,34 @@ int p54_fetch_statistics(struct p54_common *priv)
        p54_tx(priv, skb);
        return 0;
 }
+
+int p54_set_groupfilter(struct p54_common *priv)
+{
+       struct p54_group_address_table *grp;
+       struct sk_buff *skb;
+       bool on = false;
+
+       skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp),
+                           P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp));
+
+       on = !(priv->filter_flags & FIF_ALLMULTI) &&
+            (priv->mc_maclist_num > 0 &&
+             priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM);
+
+       if (on) {
+               grp->filter_enable = cpu_to_le16(1);
+               grp->num_address = cpu_to_le16(priv->mc_maclist_num);
+               memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list));
+       } else {
+               grp->filter_enable = cpu_to_le16(0);
+               grp->num_address = cpu_to_le16(0);
+               memset(grp->mac_list, 0, sizeof(grp->mac_list));
+       }
+
+       p54_tx(priv, skb);
+       return 0;
+}
index eb581abc107906d6b3b9a9cd2317cec158c10801..3d8d622bec55d394543cf1a563b2163573e3b4f8 100644 (file)
@@ -540,6 +540,7 @@ int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set);
 int p54_setup_mac(struct p54_common *priv);
 int p54_set_ps(struct p54_common *priv);
 int p54_fetch_statistics(struct p54_common *priv);
+int p54_set_groupfilter(struct p54_common *priv);
 
 /* e/v DCF setup */
 int p54_set_edcf(struct p54_common *priv);
index 356e6bb443a63fe7ac4f4611b34025d13d6f2c4d..c5c1254ec0932cedeb0f9c8ba128daec8045d043 100644 (file)
@@ -308,6 +308,31 @@ out:
        return ret;
 }
 
+static u64 p54_prepare_multicast(struct ieee80211_hw *dev,
+                                struct netdev_hw_addr_list *mc_list)
+{
+       struct p54_common *priv = dev->priv;
+       struct netdev_hw_addr *ha;
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) !=
+               ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list));
+       /*
+        * The first entry is reserved for the global broadcast MAC.
+        * Otherwise the firmware will drop it and ARP will no longer work.
+        */
+       i = 1;
+       priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i;
+       netdev_hw_addr_list_for_each(ha, mc_list) {
+               memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN);
+               i++;
+               if (i >= ARRAY_SIZE(priv->mc_maclist))
+                       break;
+       }
+
+       return 1; /* update */
+}
+
 static void p54_configure_filter(struct ieee80211_hw *dev,
                                 unsigned int changed_flags,
                                 unsigned int *total_flags,
@@ -316,12 +341,16 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
        struct p54_common *priv = dev->priv;
 
        *total_flags &= FIF_PROMISC_IN_BSS |
+                       FIF_ALLMULTI |
                        FIF_OTHER_BSS;
 
        priv->filter_flags = *total_flags;
 
        if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS))
                p54_setup_mac(priv);
+
+       if (changed_flags & FIF_ALLMULTI || multicast)
+               p54_set_groupfilter(priv);
 }
 
 static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
@@ -591,6 +620,7 @@ static const struct ieee80211_ops p54_ops = {
        .config                 = p54_config,
        .flush                  = p54_flush,
        .bss_info_changed       = p54_bss_info_changed,
+       .prepare_multicast      = p54_prepare_multicast,
        .configure_filter       = p54_configure_filter,
        .conf_tx                = p54_conf_tx,
        .get_stats              = p54_get_stats,
@@ -660,6 +690,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
        init_completion(&priv->beacon_comp);
        INIT_DELAYED_WORK(&priv->work, p54_work);
 
+       memset(&priv->mc_maclist[0], ~0, ETH_ALEN);
        return dev;
 }
 EXPORT_SYMBOL_GPL(p54_init_common);
index 50730fc23fe57ba8c0cb6e849690a66859ae3c44..799d05e12595d0865c251ebe75383245253ea806 100644 (file)
@@ -211,8 +211,10 @@ struct p54_common {
        /* BBP/MAC state */
        u8 mac_addr[ETH_ALEN];
        u8 bssid[ETH_ALEN];
+       u8 mc_maclist[4][ETH_ALEN];
        u16 wakeup_timer;
        unsigned int filter_flags;
+       int mc_maclist_num;
        int mode;
        u32 tsf_low32, tsf_high32;
        u32 basic_rate_mask;