summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c6
-rw-r--r--include/net/mac80211.h8
-rw-r--r--net/mac80211/ibss.c5
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c78
-rw-r--r--net/mac80211/mlme.c11
-rw-r--r--net/mac80211/scan.c17
7 files changed, 113 insertions, 14 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 8fa6e6ce5705..b1213b6a6b9f 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -553,9 +553,11 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
- printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d)\n",
+ printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d idle=%d ps=%d)\n",
wiphy_name(hw->wiphy), __func__,
- conf->channel->center_freq, conf->radio_enabled);
+ conf->channel->center_freq, conf->radio_enabled,
+ !!(conf->flags & IEEE80211_CONF_IDLE),
+ !!(conf->flags & IEEE80211_CONF_PS));
data->channel = conf->channel;
data->radio_enabled = conf->radio_enabled;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7806e22f4ace..38dc1cd10270 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -533,10 +533,16 @@ struct ieee80211_rx_status {
*
* @IEEE80211_CONF_RADIOTAP: add radiotap header at receive time (if supported)
* @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only)
+ * @IEEE80211_CONF_IDLE: The device is running, but idle; if the flag is set
+ * the driver should be prepared to handle configuration requests but
+ * may turn the device off as much as possible. Typically, this flag will
+ * be set when an interface is set UP but not associated or scanning, but
+ * it can also be unset in that case when monitor interfaces are active.
*/
enum ieee80211_conf_flags {
IEEE80211_CONF_RADIOTAP = (1<<0),
IEEE80211_CONF_PS = (1<<1),
+ IEEE80211_CONF_IDLE = (1<<2),
};
@@ -551,6 +557,7 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_POWER: the TX power changed
* @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
* @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
+ * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
*/
enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0),
@@ -561,6 +568,7 @@ enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_POWER = BIT(5),
IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
+ IEEE80211_CONF_CHANGE_IDLE = BIT(8),
};
static inline __deprecated enum ieee80211_conf_changed
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index a8e23232267e..aa537681f87c 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -862,6 +862,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.ssid_len = params->ssid_len;
+ ieee80211_recalc_idle(sdata->local);
+
set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
queue_work(sdata->local->hw.workqueue, &sdata->u.ibss.work);
@@ -889,6 +891,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
skb_queue_purge(&sdata->u.ibss.skb_queue);
memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
+ sdata->u.ibss.ssid_len = 0;
+
+ ieee80211_recalc_idle(sdata->local);
return 0;
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 236ea098bb6c..03e0d22603c8 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -985,6 +985,8 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype type);
void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
void ieee80211_remove_interfaces(struct ieee80211_local *local);
+u32 __ieee80211_recalc_idle(struct ieee80211_local *local);
+void ieee80211_recalc_idle(struct ieee80211_local *local);
/* tx handling */
void ieee80211_clear_tx_pending(struct ieee80211_local *local);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 256fa19e14ec..8b6daf0219f4 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -301,6 +301,8 @@ static int ieee80211_open(struct net_device *dev)
if (sdata->flags & IEEE80211_SDATA_PROMISC)
atomic_inc(&local->iff_promiscs);
+ hw_reconf_flags |= __ieee80211_recalc_idle(local);
+
local->open_count++;
if (hw_reconf_flags) {
ieee80211_hw_config(local, hw_reconf_flags);
@@ -548,6 +550,10 @@ static int ieee80211_stop(struct net_device *dev)
sdata->bss = NULL;
+ hw_reconf_flags |= __ieee80211_recalc_idle(local);
+
+ ieee80211_recalc_ps(local, -1);
+
if (local->open_count == 0) {
if (netif_running(local->mdev))
dev_close(local->mdev);
@@ -565,8 +571,6 @@ static int ieee80211_stop(struct net_device *dev)
hw_reconf_flags = 0;
}
- ieee80211_recalc_ps(local, -1);
-
/* do after stop to avoid reconfiguring when we stop anyway */
if (hw_reconf_flags)
ieee80211_hw_config(local, hw_reconf_flags);
@@ -892,3 +896,73 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
unregister_netdevice(sdata->dev);
}
}
+
+static u32 ieee80211_idle_off(struct ieee80211_local *local,
+ const char *reason)
+{
+ if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))
+ return 0;
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: device no longer idle - %s\n",
+ wiphy_name(local->hw.wiphy), reason);
+#endif
+
+ local->hw.conf.flags &= ~IEEE80211_CONF_IDLE;
+ return IEEE80211_CONF_CHANGE_IDLE;
+}
+
+static u32 ieee80211_idle_on(struct ieee80211_local *local)
+{
+ if (local->hw.conf.flags & IEEE80211_CONF_IDLE)
+ return 0;
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: device now idle\n",
+ wiphy_name(local->hw.wiphy));
+#endif
+
+ local->hw.conf.flags |= IEEE80211_CONF_IDLE;
+ return IEEE80211_CONF_CHANGE_IDLE;
+}
+
+u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+ int count = 0;
+
+ if (local->hw_scanning || local->sw_scanning)
+ return ieee80211_idle_off(local, "scanning");
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+ /* do not count disabled managed interfaces */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ sdata->u.mgd.state == IEEE80211_STA_MLME_DISABLED)
+ continue;
+ /* do not count unused IBSS interfaces */
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ !sdata->u.ibss.ssid_len)
+ continue;
+ /* count everything else */
+ count++;
+ }
+
+ if (!count)
+ return ieee80211_idle_on(local);
+ else
+ return ieee80211_idle_off(local, "in use");
+
+ return 0;
+}
+
+void ieee80211_recalc_idle(struct ieee80211_local *local)
+{
+ u32 chg;
+
+ mutex_lock(&local->iflist_mtx);
+ chg = __ieee80211_recalc_idle(local);
+ mutex_unlock(&local->iflist_mtx);
+ ieee80211_hw_config(local, chg);
+}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2ded4766d014..5509c5aa6beb 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -890,6 +890,7 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
sdata->dev->name, ifmgd->bssid);
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(local);
cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
/*
@@ -938,6 +939,7 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
" timed out\n",
sdata->dev->name, ifmgd->bssid);
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(local);
cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
@@ -1041,6 +1043,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
+ ieee80211_recalc_idle(local);
+
/* channel(_type) changes are handled by ieee80211_hw_config */
local->oper_channel_type = NL80211_CHAN_NO_HT;
@@ -1121,6 +1125,7 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
" timed out\n",
sdata->dev->name, ifmgd->bssid);
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(local);
cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid);
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
@@ -1141,6 +1146,7 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
"mixed-cell disabled - abort association\n", sdata->dev->name);
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(local);
return;
}
@@ -1279,6 +1285,7 @@ static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
/* Wait for SME to request association */
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(sdata->local);
} else
ieee80211_associate(sdata);
}
@@ -1515,6 +1522,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
/* Wait for SME to decide what to do next */
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(local);
}
return;
}
@@ -2083,6 +2091,7 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata)
} else {
ifmgd->assoc_scan_tries = 0;
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_recalc_idle(local);
}
}
return -1;
@@ -2126,6 +2135,8 @@ static void ieee80211_sta_work(struct work_struct *work)
} else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request))
return;
+ ieee80211_recalc_idle(local);
+
switch (ifmgd->state) {
case IEEE80211_STA_MLME_DISABLED:
break;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 127bd54e0e38..c99ef8d04d3d 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -302,17 +302,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
/* we only have to protect scan_req and hw/sw scan */
mutex_unlock(&local->scan_mtx);
- if (was_hw_scan) {
- /*
- * Somebody might have requested channel change during scan
- * that we won't have acted upon, try now. ieee80211_hw_config
- * will set the flag based on actual changes.
- */
- ieee80211_hw_config(local, 0);
- goto done;
- }
-
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ if (was_hw_scan)
+ goto done;
netif_tx_lock_bh(local->mdev);
netif_addr_lock(local->mdev);
@@ -351,6 +343,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
mutex_unlock(&local->iflist_mtx);
done:
+ ieee80211_recalc_idle(local);
ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
@@ -471,6 +464,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
* dependency, so that the scan completed calls
* have more locking freedom.
*/
+
+ ieee80211_recalc_idle(local);
mutex_unlock(&local->scan_mtx);
if (local->ops->hw_scan)
@@ -487,6 +482,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
} else
local->sw_scanning = false;
+ ieee80211_recalc_idle(local);
+
local->scan_req = NULL;
local->scan_sdata = NULL;
}