diff options
author | David Woodhouse <dwmw2@infradead.org> | 2008-05-20 08:32:45 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-05-21 21:47:38 -0400 |
commit | 75bf45a7b4ab81cfa5c5eab68b57bbfee8b8ede2 (patch) | |
tree | bfc740d4eda3a3389c4f4cc567f04609d5d055f5 /drivers/net/wireless/libertas/main.c | |
parent | e37fc6e11c79899451e394319cff18cc53d6448d (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.c | 167 |
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 | ||
566 | static int lbs_copy_multicast_address(struct lbs_private *priv, | 571 | |
567 | struct net_device *dev) | 572 | static 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++) { | 585 | static 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 | ||
579 | static void lbs_set_multicast_list(struct net_device *dev) | 617 | static 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 | ||
674 | static 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); |