aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas
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
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')
-rw-r--r--drivers/net/wireless/libertas/cmd.c29
-rw-r--r--drivers/net/wireless/libertas/cmdresp.c1
-rw-r--r--drivers/net/wireless/libertas/dev.h2
-rw-r--r--drivers/net/wireless/libertas/hostcmd.h2
-rw-r--r--drivers/net/wireless/libertas/main.c167
5 files changed, 105 insertions, 96 deletions
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index bed68e9add02..b494aba869c5 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -746,28 +746,6 @@ out:
746 return ret; 746 return ret;
747} 747}
748 748
749static int lbs_cmd_mac_multicast_adr(struct lbs_private *priv,
750 struct cmd_ds_command *cmd,
751 u16 cmd_action)
752{
753 struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr;
754
755 lbs_deb_enter(LBS_DEB_CMD);
756 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) +
757 S_DS_GEN);
758 cmd->command = cpu_to_le16(CMD_MAC_MULTICAST_ADR);
759
760 lbs_deb_cmd("MULTICAST_ADR: setting %d addresses\n", pMCastAdr->nr_of_adrs);
761 pMCastAdr->action = cpu_to_le16(cmd_action);
762 pMCastAdr->nr_of_adrs =
763 cpu_to_le16((u16) priv->nr_of_multicastmacaddr);
764 memcpy(pMCastAdr->maclist, priv->multicastlist,
765 priv->nr_of_multicastmacaddr * ETH_ALEN);
766
767 lbs_deb_leave(LBS_DEB_CMD);
768 return 0;
769}
770
771/** 749/**
772 * @brief Get the radio channel 750 * @brief Get the radio channel
773 * 751 *
@@ -1247,8 +1225,7 @@ void lbs_set_mac_control(struct lbs_private *priv)
1247 cmd.action = cpu_to_le16(priv->mac_control); 1225 cmd.action = cpu_to_le16(priv->mac_control);
1248 cmd.reserved = 0; 1226 cmd.reserved = 0;
1249 1227
1250 lbs_cmd_async(priv, CMD_MAC_CONTROL, 1228 lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd));
1251 &cmd.hdr, sizeof(cmd));
1252 1229
1253 lbs_deb_leave(LBS_DEB_CMD); 1230 lbs_deb_leave(LBS_DEB_CMD);
1254} 1231}
@@ -1360,10 +1337,6 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
1360 cmdptr, cmd_action); 1337 cmdptr, cmd_action);
1361 break; 1338 break;
1362 1339
1363 case CMD_MAC_MULTICAST_ADR:
1364 ret = lbs_cmd_mac_multicast_adr(priv, cmdptr, cmd_action);
1365 break;
1366
1367 case CMD_802_11_MONITOR_MODE: 1340 case CMD_802_11_MONITOR_MODE:
1368 ret = lbs_cmd_802_11_monitor_mode(cmdptr, 1341 ret = lbs_cmd_802_11_monitor_mode(cmdptr,
1369 cmd_action, pdata_buf); 1342 cmd_action, pdata_buf);
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index 9e50fdd94810..4c3c5ec16f98 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -316,7 +316,6 @@ static inline int handle_cmd_response(struct lbs_private *priv,
316 316
317 break; 317 break;
318 318
319 case CMD_RET(CMD_MAC_MULTICAST_ADR):
320 case CMD_RET(CMD_802_11_RESET): 319 case CMD_RET(CMD_802_11_RESET):
321 case CMD_RET(CMD_802_11_AUTHENTICATE): 320 case CMD_RET(CMD_802_11_AUTHENTICATE):
322 case CMD_RET(CMD_802_11_BEACON_STOP): 321 case CMD_RET(CMD_802_11_BEACON_STOP):
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 0d9edb9b11f5..e12ce6506729 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -140,6 +140,8 @@ struct lbs_private {
140 wait_queue_head_t waitq; 140 wait_queue_head_t waitq;
141 struct workqueue_struct *work_thread; 141 struct workqueue_struct *work_thread;
142 142
143 struct work_struct mcast_work;
144
143 /** Scanning */ 145 /** Scanning */
144 struct delayed_work scan_work; 146 struct delayed_work scan_work;
145 struct delayed_work assoc_work; 147 struct delayed_work assoc_work;
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
index f29bc5bbda3e..c36ab3162238 100644
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -219,6 +219,7 @@ struct cmd_ds_mac_control {
219}; 219};
220 220
221struct cmd_ds_mac_multicast_adr { 221struct cmd_ds_mac_multicast_adr {
222 struct cmd_header hdr;
222 __le16 action; 223 __le16 action;
223 __le16 nr_of_adrs; 224 __le16 nr_of_adrs;
224 u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; 225 u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
@@ -703,7 +704,6 @@ struct cmd_ds_command {
703 struct cmd_ds_802_11_rf_antenna rant; 704 struct cmd_ds_802_11_rf_antenna rant;
704 struct cmd_ds_802_11_monitor_mode monitor; 705 struct cmd_ds_802_11_monitor_mode monitor;
705 struct cmd_ds_802_11_rate_adapt_rateset rateset; 706 struct cmd_ds_802_11_rate_adapt_rateset rateset;
706 struct cmd_ds_mac_multicast_adr madr;
707 struct cmd_ds_802_11_ad_hoc_join adj; 707 struct cmd_ds_802_11_ad_hoc_join adj;
708 struct cmd_ds_802_11_rssi rssi; 708 struct cmd_ds_802_11_rssi rssi;
709 struct cmd_ds_802_11_rssi_rsp rssirsp; 709 struct cmd_ds_802_11_rssi_rsp rssirsp;
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);