mac80211: Add capability to enable/disable beaconing
authorJohannes Berg <johannes@sipsolutions.net>
Thu, 22 Jan 2009 17:07:31 +0000 (18:07 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 29 Jan 2009 21:01:13 +0000 (16:01 -0500)
This patch adds a flag to notify drivers to start and stop
beaconing when needed, for example, during a scan run. Based
on Sujith's first patch to do the same, but now disables
beaconing for all virtual interfaces while scanning, has a
separate change flag and tracks user-space requests.

Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/scan.c

index 634c08d..35643c5 100644 (file)
@@ -646,10 +646,12 @@ struct ieee80211_if_init_conf {
  * @IEEE80211_IFCC_BSSID: The BSSID changed.
  * @IEEE80211_IFCC_BEACON: The beacon for this interface changed
  *     (currently AP and MESH only), use ieee80211_beacon_get().
+ * @IEEE80211_IFCC_BEACON_ENABLED: The enable_beacon value changed.
  */
 enum ieee80211_if_conf_change {
-       IEEE80211_IFCC_BSSID    = BIT(0),
-       IEEE80211_IFCC_BEACON   = BIT(1),
+       IEEE80211_IFCC_BSSID            = BIT(0),
+       IEEE80211_IFCC_BEACON           = BIT(1),
+       IEEE80211_IFCC_BEACON_ENABLED   = BIT(2),
 };
 
 /**
@@ -657,6 +659,8 @@ enum ieee80211_if_conf_change {
  *
  * @changed: parameters that have changed, see &enum ieee80211_if_conf_change.
  * @bssid: BSSID of the network we are associated to/creating.
+ * @enable_beacon: Indicates whether beacons can be sent.
+ *     This is valid only for AP/IBSS/MESH modes.
  *
  * This structure is passed to the config_interface() callback of
  * &struct ieee80211_hw.
@@ -664,6 +668,7 @@ enum ieee80211_if_conf_change {
 struct ieee80211_if_conf {
        u32 changed;
        const u8 *bssid;
+       bool enable_beacon;
 };
 
 /**
index 3527de2..a1a1344 100644 (file)
@@ -523,7 +523,8 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
 
        kfree(old);
 
-       return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+       return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON |
+                                         IEEE80211_IFCC_BEACON_ENABLED);
 }
 
 static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
@@ -583,7 +584,7 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
        synchronize_rcu();
        kfree(old);
 
-       return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+       return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED);
 }
 
 /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
index 6f0fe35..8d5c19e 100644 (file)
@@ -168,7 +168,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
                return 0;
 
        memset(&conf, 0, sizeof(conf));
-       conf.changed = changed;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION ||
            sdata->vif.type == NL80211_IFTYPE_ADHOC)
@@ -183,9 +182,50 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
                return -EINVAL;
        }
 
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+               break;
+       default:
+               /* do not warn to simplify caller in scan.c */
+               changed &= ~IEEE80211_IFCC_BEACON_ENABLED;
+               if (WARN_ON(changed & IEEE80211_IFCC_BEACON))
+                       return -EINVAL;
+               changed &= ~IEEE80211_IFCC_BEACON;
+               break;
+       }
+
+       if (changed & IEEE80211_IFCC_BEACON_ENABLED) {
+               if (local->sw_scanning) {
+                       conf.enable_beacon = false;
+               } else {
+                       /*
+                        * Beacon should be enabled, but AP mode must
+                        * check whether there is a beacon configured.
+                        */
+                       switch (sdata->vif.type) {
+                       case NL80211_IFTYPE_AP:
+                               conf.enable_beacon =
+                                       !!rcu_dereference(sdata->u.ap.beacon);
+                               break;
+                       case NL80211_IFTYPE_ADHOC:
+                       case NL80211_IFTYPE_MESH_POINT:
+                               conf.enable_beacon = true;
+                               break;
+                       default:
+                               /* not reached */
+                               WARN_ON(1);
+                               break;
+                       }
+               }
+       }
+
        if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
                return -EINVAL;
 
+       conf.changed = changed;
+
        return local->ops->config_interface(local_to_hw(local),
                                            &sdata->vif, &conf);
 }
index 2d573f8..8a1fcae 100644 (file)
@@ -442,7 +442,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
 
        ifmsh->housekeeping = true;
        queue_work(local->hw.workqueue, &ifmsh->work);
-       ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+       ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON |
+                                  IEEE80211_IFCC_BEACON_ENABLED);
 }
 
 void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
index 9852da5..ec40047 100644 (file)
@@ -1599,7 +1599,8 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        ifsta->probe_resp = skb;
 
-       ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+       ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON |
+                                  IEEE80211_IFCC_BEACON_ENABLED);
 
 
        rates = 0;
index a2caeed..8248d7b 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/wireless.h>
 #include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
 #include <net/mac80211.h>
 #include <net/iw_handler.h>
 
@@ -472,8 +473,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
        netif_addr_unlock(local->mdev);
        netif_tx_unlock_bh(local->mdev);
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list) {
                /* Tell AP we're back */
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                        if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
@@ -482,8 +483,10 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
                        }
                } else
                        netif_tx_wake_all_queues(sdata->dev);
+
+               ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED);
        }
-       rcu_read_unlock();
+       mutex_unlock(&local->iflist_mtx);
 
  done:
        ieee80211_mlme_notify_scan_completed(local);
@@ -491,7 +494,6 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_scan_completed);
 
-
 void ieee80211_scan_work(struct work_struct *work)
 {
        struct ieee80211_local *local =
@@ -633,8 +635,10 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
 
        local->sw_scanning = true;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED);
+
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                        if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
                                netif_tx_stop_all_queues(sdata->dev);
@@ -643,7 +647,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
                } else
                        netif_tx_stop_all_queues(sdata->dev);
        }
-       rcu_read_unlock();
+       mutex_unlock(&local->iflist_mtx);
 
        if (ssid) {
                local->scan_ssid_len = ssid_len;