diff options
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); |