aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Lamparter <chunkeey@googlemail.com>2011-04-24 11:22:59 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-26 15:50:30 -0400
commitbe8d98eab81d1f6445461a1631513f7091805e53 (patch)
treef543f18ff00ca8890b3d99afc733071be898c74a
parent04ad1fb2640a4f23e99ccb705c179d64abac03f2 (diff)
p54: implement multicast filter
"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>
-rw-r--r--drivers/net/wireless/p54/fwio.c31
-rw-r--r--drivers/net/wireless/p54/lmac.h1
-rw-r--r--drivers/net/wireless/p54/main.c31
-rw-r--r--drivers/net/wireless/p54/p54.h2
4 files changed, 65 insertions, 0 deletions
diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c
index 2fab7d20ffc2..b6a061cbbdec 100644
--- a/drivers/net/wireless/p54/fwio.c
+++ b/drivers/net/wireless/p54/fwio.c
@@ -727,3 +727,34 @@ int p54_fetch_statistics(struct p54_common *priv)
727 p54_tx(priv, skb); 727 p54_tx(priv, skb);
728 return 0; 728 return 0;
729} 729}
730
731int p54_set_groupfilter(struct p54_common *priv)
732{
733 struct p54_group_address_table *grp;
734 struct sk_buff *skb;
735 bool on = false;
736
737 skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp),
738 P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL);
739 if (!skb)
740 return -ENOMEM;
741
742 grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp));
743
744 on = !(priv->filter_flags & FIF_ALLMULTI) &&
745 (priv->mc_maclist_num > 0 &&
746 priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM);
747
748 if (on) {
749 grp->filter_enable = cpu_to_le16(1);
750 grp->num_address = cpu_to_le16(priv->mc_maclist_num);
751 memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list));
752 } else {
753 grp->filter_enable = cpu_to_le16(0);
754 grp->num_address = cpu_to_le16(0);
755 memset(grp->mac_list, 0, sizeof(grp->mac_list));
756 }
757
758 p54_tx(priv, skb);
759 return 0;
760}
diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h
index eb581abc1079..3d8d622bec55 100644
--- a/drivers/net/wireless/p54/lmac.h
+++ b/drivers/net/wireless/p54/lmac.h
@@ -540,6 +540,7 @@ int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set);
540int p54_setup_mac(struct p54_common *priv); 540int p54_setup_mac(struct p54_common *priv);
541int p54_set_ps(struct p54_common *priv); 541int p54_set_ps(struct p54_common *priv);
542int p54_fetch_statistics(struct p54_common *priv); 542int p54_fetch_statistics(struct p54_common *priv);
543int p54_set_groupfilter(struct p54_common *priv);
543 544
544/* e/v DCF setup */ 545/* e/v DCF setup */
545int p54_set_edcf(struct p54_common *priv); 546int p54_set_edcf(struct p54_common *priv);
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index 356e6bb443a6..c5c1254ec093 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -308,6 +308,31 @@ out:
308 return ret; 308 return ret;
309} 309}
310 310
311static u64 p54_prepare_multicast(struct ieee80211_hw *dev,
312 struct netdev_hw_addr_list *mc_list)
313{
314 struct p54_common *priv = dev->priv;
315 struct netdev_hw_addr *ha;
316 int i;
317
318 BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) !=
319 ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list));
320 /*
321 * The first entry is reserved for the global broadcast MAC.
322 * Otherwise the firmware will drop it and ARP will no longer work.
323 */
324 i = 1;
325 priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i;
326 netdev_hw_addr_list_for_each(ha, mc_list) {
327 memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN);
328 i++;
329 if (i >= ARRAY_SIZE(priv->mc_maclist))
330 break;
331 }
332
333 return 1; /* update */
334}
335
311static void p54_configure_filter(struct ieee80211_hw *dev, 336static void p54_configure_filter(struct ieee80211_hw *dev,
312 unsigned int changed_flags, 337 unsigned int changed_flags,
313 unsigned int *total_flags, 338 unsigned int *total_flags,
@@ -316,12 +341,16 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
316 struct p54_common *priv = dev->priv; 341 struct p54_common *priv = dev->priv;
317 342
318 *total_flags &= FIF_PROMISC_IN_BSS | 343 *total_flags &= FIF_PROMISC_IN_BSS |
344 FIF_ALLMULTI |
319 FIF_OTHER_BSS; 345 FIF_OTHER_BSS;
320 346
321 priv->filter_flags = *total_flags; 347 priv->filter_flags = *total_flags;
322 348
323 if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) 349 if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS))
324 p54_setup_mac(priv); 350 p54_setup_mac(priv);
351
352 if (changed_flags & FIF_ALLMULTI || multicast)
353 p54_set_groupfilter(priv);
325} 354}
326 355
327static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, 356static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
@@ -591,6 +620,7 @@ static const struct ieee80211_ops p54_ops = {
591 .config = p54_config, 620 .config = p54_config,
592 .flush = p54_flush, 621 .flush = p54_flush,
593 .bss_info_changed = p54_bss_info_changed, 622 .bss_info_changed = p54_bss_info_changed,
623 .prepare_multicast = p54_prepare_multicast,
594 .configure_filter = p54_configure_filter, 624 .configure_filter = p54_configure_filter,
595 .conf_tx = p54_conf_tx, 625 .conf_tx = p54_conf_tx,
596 .get_stats = p54_get_stats, 626 .get_stats = p54_get_stats,
@@ -660,6 +690,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
660 init_completion(&priv->beacon_comp); 690 init_completion(&priv->beacon_comp);
661 INIT_DELAYED_WORK(&priv->work, p54_work); 691 INIT_DELAYED_WORK(&priv->work, p54_work);
662 692
693 memset(&priv->mc_maclist[0], ~0, ETH_ALEN);
663 return dev; 694 return dev;
664} 695}
665EXPORT_SYMBOL_GPL(p54_init_common); 696EXPORT_SYMBOL_GPL(p54_init_common);
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index 50730fc23fe5..799d05e12595 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -211,8 +211,10 @@ struct p54_common {
211 /* BBP/MAC state */ 211 /* BBP/MAC state */
212 u8 mac_addr[ETH_ALEN]; 212 u8 mac_addr[ETH_ALEN];
213 u8 bssid[ETH_ALEN]; 213 u8 bssid[ETH_ALEN];
214 u8 mc_maclist[4][ETH_ALEN];
214 u16 wakeup_timer; 215 u16 wakeup_timer;
215 unsigned int filter_flags; 216 unsigned int filter_flags;
217 int mc_maclist_num;
216 int mode; 218 int mode;
217 u32 tsf_low32, tsf_high32; 219 u32 tsf_low32, tsf_high32;
218 u32 basic_rate_mask; 220 u32 basic_rate_mask;