aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas/main.c
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2008-05-20 08:32:45 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-05-21 21:47:38 -0400
commit75bf45a7b4ab81cfa5c5eab68b57bbfee8b8ede2 (patch)
treebfc740d4eda3a3389c4f4cc567f04609d5d055f5 /drivers/net/wireless/libertas/main.c
parente37fc6e11c79899451e394319cff18cc53d6448d (diff)
libertas: fix multicast filtering on eth and msh interfaces
We weren't properly handling multicast on the mesh interface. Fix that, which involves setting up the hardware to use the union of dev->mc_list for both eth%d and msh%d devices. This means we can't do it directly from ->set_multicast_list() because we'd need to lock the other device to read its list, and we can't do that because it might deadlock. So punt the actual work to keventd. Also, invoke the same when taking an interface down; for some reason the core calls ->set_multicast_list while IFF_UP is still set in dev->flags when we're taking it down, so its addresses don't get removed then. We also convert MAC_MULTICAST_ADR to a direct command while we're at it, removing one more entry from the big switch statement in the deprecated lbs_prepare_and_send_command() function. Signed-off-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/main.c')
-rw-r--r--drivers/net/wireless/libertas/main.c167
1 files changed, 101 insertions, 66 deletions
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index e333f14dce23..a87febad8c29 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -11,6 +11,7 @@
11#include <linux/if_arp.h> 11#include <linux/if_arp.h>
12#include <linux/kthread.h> 12#include <linux/kthread.h>
13#include <linux/kfifo.h> 13#include <linux/kfifo.h>
14#include <linux/stddef.h>
14 15
15#include <net/iw_handler.h> 16#include <net/iw_handler.h>
16#include <net/ieee80211.h> 17#include <net/ieee80211.h>
@@ -446,6 +447,8 @@ static int lbs_mesh_stop(struct net_device *dev)
446 447
447 spin_unlock_irq(&priv->driver_lock); 448 spin_unlock_irq(&priv->driver_lock);
448 449
450 schedule_work(&priv->mcast_work);
451
449 lbs_deb_leave(LBS_DEB_MESH); 452 lbs_deb_leave(LBS_DEB_MESH);
450 return 0; 453 return 0;
451} 454}
@@ -467,6 +470,8 @@ static int lbs_eth_stop(struct net_device *dev)
467 netif_stop_queue(dev); 470 netif_stop_queue(dev);
468 spin_unlock_irq(&priv->driver_lock); 471 spin_unlock_irq(&priv->driver_lock);
469 472
473 schedule_work(&priv->mcast_work);
474
470 lbs_deb_leave(LBS_DEB_NET); 475 lbs_deb_leave(LBS_DEB_NET);
471 return 0; 476 return 0;
472} 477}
@@ -563,89 +568,116 @@ done:
563 return ret; 568 return ret;
564} 569}
565 570
566static int lbs_copy_multicast_address(struct lbs_private *priv, 571
567 struct net_device *dev) 572static inline int mac_in_list(unsigned char *list, int list_len,
573 unsigned char *mac)
568{ 574{
569 int i = 0; 575 while (list_len) {
570 struct dev_mc_list *mcptr = dev->mc_list; 576 if (!memcmp(list, mac, ETH_ALEN))
577 return 1;
578 list += ETH_ALEN;
579 list_len--;
580 }
581 return 0;
582}
583
571 584
572 for (i = 0; i < dev->mc_count; i++) { 585static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd,
573 memcpy(&priv->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); 586 struct net_device *dev, int nr_addrs)
574 mcptr = mcptr->next; 587{
588 int i = nr_addrs;
589 struct dev_mc_list *mc_list;
590 DECLARE_MAC_BUF(mac);
591
592 if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST))
593 return nr_addrs;
594
595 netif_tx_lock_bh(dev);
596 for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {
597 if (mac_in_list(cmd->maclist, nr_addrs, mc_list->dmi_addr)) {
598 lbs_deb_net("mcast address %s:%s skipped\n", dev->name,
599 print_mac(mac, mc_list->dmi_addr));
600 continue;
601 }
602
603 if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE)
604 break;
605 memcpy(&cmd->maclist[6*i], mc_list->dmi_addr, ETH_ALEN);
606 lbs_deb_net("mcast address %s:%s added to filter\n", dev->name,
607 print_mac(mac, mc_list->dmi_addr));
608 i++;
575 } 609 }
610 netif_tx_unlock_bh(dev);
611 if (mc_list)
612 return -EOVERFLOW;
613
576 return i; 614 return i;
577} 615}
578 616
579static void lbs_set_multicast_list(struct net_device *dev) 617static void lbs_set_mcast_worker(struct work_struct *work)
580{ 618{
581 struct lbs_private *priv = dev->priv; 619 struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
582 int old_mac_control; 620 struct cmd_ds_mac_multicast_adr mcast_cmd;
583 DECLARE_MAC_BUF(mac); 621 int dev_flags;
622 int nr_addrs;
623 int old_mac_control = priv->mac_control;
584 624
585 lbs_deb_enter(LBS_DEB_NET); 625 lbs_deb_enter(LBS_DEB_NET);
586 626
587 old_mac_control = priv->mac_control; 627 dev_flags = priv->dev->flags;
588 628 if (priv->mesh_dev)
589 if (dev->flags & IFF_PROMISC) { 629 dev_flags |= priv->mesh_dev->flags;
590 lbs_deb_net("enable promiscuous mode\n"); 630
591 priv->mac_control |= 631 if (dev_flags & IFF_PROMISC) {
592 CMD_ACT_MAC_PROMISCUOUS_ENABLE; 632 priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE;
593 priv->mac_control &= 633 priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE |
594 ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | 634 CMD_ACT_MAC_MULTICAST_ENABLE);
595 CMD_ACT_MAC_MULTICAST_ENABLE); 635 goto out_set_mac_control;
596 } else { 636 } else if (dev_flags & IFF_ALLMULTI) {
597 /* Multicast */ 637 do_allmulti:
598 priv->mac_control &= 638 priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
599 ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; 639 priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE |
600 640 CMD_ACT_MAC_MULTICAST_ENABLE);
601 if (dev->flags & IFF_ALLMULTI || dev->mc_count > 641 goto out_set_mac_control;
602 MRVDRV_MAX_MULTICAST_LIST_SIZE) {
603 lbs_deb_net( "enabling all multicast\n");
604 priv->mac_control |=
605 CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
606 priv->mac_control &=
607 ~CMD_ACT_MAC_MULTICAST_ENABLE;
608 } else {
609 priv->mac_control &=
610 ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
611
612 if (!dev->mc_count) {
613 lbs_deb_net("no multicast addresses, "
614 "disabling multicast\n");
615 priv->mac_control &=
616 ~CMD_ACT_MAC_MULTICAST_ENABLE;
617 } else {
618 int i;
619
620 priv->mac_control |=
621 CMD_ACT_MAC_MULTICAST_ENABLE;
622
623 priv->nr_of_multicastmacaddr =
624 lbs_copy_multicast_address(priv, dev);
625
626 lbs_deb_net("multicast addresses: %d\n",
627 dev->mc_count);
628
629 for (i = 0; i < dev->mc_count; i++) {
630 lbs_deb_net("Multicast address %d: %s\n",
631 i, print_mac(mac,
632 priv->multicastlist[i]));
633 }
634 /* send multicast addresses to firmware */
635 lbs_prepare_and_send_command(priv,
636 CMD_MAC_MULTICAST_ADR,
637 CMD_ACT_SET, 0, 0,
638 NULL);
639 }
640 }
641 } 642 }
642 643
644 /* Once for priv->dev, again for priv->mesh_dev if it exists */
645 nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0);
646 if (nr_addrs >= 0 && priv->mesh_dev)
647 nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs);
648 if (nr_addrs < 0)
649 goto do_allmulti;
650
651 if (nr_addrs) {
652 int size = offsetof(struct cmd_ds_mac_multicast_adr,
653 maclist[6*nr_addrs]);
654
655 mcast_cmd.action = cpu_to_le16(CMD_ACT_SET);
656 mcast_cmd.hdr.size = cpu_to_le16(size);
657 mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs);
658
659 lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size);
660
661 priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
662 } else
663 priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
664
665 priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE |
666 CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
667 out_set_mac_control:
643 if (priv->mac_control != old_mac_control) 668 if (priv->mac_control != old_mac_control)
644 lbs_set_mac_control(priv); 669 lbs_set_mac_control(priv);
645 670
646 lbs_deb_leave(LBS_DEB_NET); 671 lbs_deb_leave(LBS_DEB_NET);
647} 672}
648 673
674static void lbs_set_multicast_list(struct net_device *dev)
675{
676 struct lbs_private *priv = dev->priv;
677
678 schedule_work(&priv->mcast_work);
679}
680
649/** 681/**
650 * @brief This function handles the major jobs in the LBS driver. 682 * @brief This function handles the major jobs in the LBS driver.
651 * It handles all events generated by firmware, RX data received 683 * It handles all events generated by firmware, RX data received
@@ -1122,6 +1154,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
1122 priv->work_thread = create_singlethread_workqueue("lbs_worker"); 1154 priv->work_thread = create_singlethread_workqueue("lbs_worker");
1123 INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker); 1155 INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker);
1124 INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); 1156 INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
1157 INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
1125 INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker); 1158 INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker);
1126 1159
1127 sprintf(priv->mesh_ssid, "mesh"); 1160 sprintf(priv->mesh_ssid, "mesh");
@@ -1158,6 +1191,7 @@ void lbs_remove_card(struct lbs_private *priv)
1158 1191
1159 cancel_delayed_work_sync(&priv->scan_work); 1192 cancel_delayed_work_sync(&priv->scan_work);
1160 cancel_delayed_work_sync(&priv->assoc_work); 1193 cancel_delayed_work_sync(&priv->assoc_work);
1194 cancel_work_sync(&priv->mcast_work);
1161 destroy_workqueue(priv->work_thread); 1195 destroy_workqueue(priv->work_thread);
1162 1196
1163 if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { 1197 if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
@@ -1322,6 +1356,8 @@ static int lbs_add_mesh(struct lbs_private *priv)
1322#ifdef WIRELESS_EXT 1356#ifdef WIRELESS_EXT
1323 mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def; 1357 mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def;
1324#endif 1358#endif
1359 mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
1360 mesh_dev->set_multicast_list = lbs_set_multicast_list;
1325 /* Register virtual mesh interface */ 1361 /* Register virtual mesh interface */
1326 ret = register_netdev(mesh_dev); 1362 ret = register_netdev(mesh_dev);
1327 if (ret) { 1363 if (ret) {
@@ -1554,7 +1590,6 @@ static int lbs_add_rtap(struct lbs_private *priv)
1554 rtap_dev->stop = lbs_rtap_stop; 1590 rtap_dev->stop = lbs_rtap_stop;
1555 rtap_dev->get_stats = lbs_rtap_get_stats; 1591 rtap_dev->get_stats = lbs_rtap_get_stats;
1556 rtap_dev->hard_start_xmit = lbs_rtap_hard_start_xmit; 1592 rtap_dev->hard_start_xmit = lbs_rtap_hard_start_xmit;
1557 rtap_dev->set_multicast_list = lbs_set_multicast_list;
1558 rtap_dev->priv = priv; 1593 rtap_dev->priv = priv;
1559 1594
1560 ret = register_netdev(rtap_dev); 1595 ret = register_netdev(rtap_dev);