aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/mac80211/Makefile2
-rw-r--r--net/mac80211/cfg.c17
-rw-r--r--net/mac80211/ieee80211_i.h4
-rw-r--r--net/mac80211/pm.c114
4 files changed, 137 insertions, 0 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 5c6fadfb6a0..58c94bb38e8 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -38,6 +38,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
mesh_plink.o \
mesh_hwmp.o
+mac80211-$(CONFIG_PM) += pm.o
+
# objects for PID algorithm
rc80211_pid-y := rc80211_pid_algo.o
rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index d1ac3ab2c51..3527de22caf 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1256,6 +1256,21 @@ static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy,
return ret;
}
+#ifdef CONFIG_PM
+static int ieee80211_suspend(struct wiphy *wiphy)
+{
+ return __ieee80211_suspend(wiphy_priv(wiphy));
+}
+
+static int ieee80211_resume(struct wiphy *wiphy)
+{
+ return __ieee80211_resume(wiphy_priv(wiphy));
+}
+#else
+#define ieee80211_suspend NULL
+#define ieee80211_resume NULL
+#endif
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1286,4 +1301,6 @@ struct cfg80211_ops mac80211_config_ops = {
.set_txq_params = ieee80211_set_txq_params,
.set_channel = ieee80211_set_channel,
.set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
+ .suspend = ieee80211_suspend,
+ .resume = ieee80211_resume,
};
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5eafd3affe2..faa2476a245 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1006,6 +1006,10 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
u16 capab_info, u8 *pwr_constr_elem,
u8 pwr_constr_elem_len);
+/* Suspend/resume */
+int __ieee80211_suspend(struct ieee80211_hw *hw);
+int __ieee80211_resume(struct ieee80211_hw *hw);
+
/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
extern const unsigned char rfc1042_header[6];
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
new file mode 100644
index 00000000000..6d17ed7fd49
--- /dev/null
+++ b/net/mac80211/pm.c
@@ -0,0 +1,114 @@
+#include <net/mac80211.h>
+#include <net/rtnetlink.h>
+
+#include "ieee80211_i.h"
+#include "led.h"
+
+int __ieee80211_suspend(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_init_conf conf;
+ struct sta_info *sta;
+
+ flush_workqueue(local->hw.workqueue);
+
+ /* disable keys */
+ list_for_each_entry(sdata, &local->interfaces, list)
+ ieee80211_disable_keys(sdata);
+
+ /* remove STAs */
+ list_for_each_entry(sta, &local->sta_list, list) {
+
+ if (local->ops->sta_notify) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data,
+ u.ap);
+
+ local->ops->sta_notify(hw, &sdata->vif,
+ STA_NOTIFY_REMOVE, &sta->sta);
+ }
+ }
+
+ /* remove all interfaces */
+ list_for_each_entry(sdata, &local->interfaces, list) {
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ netif_running(sdata->dev)) {
+ conf.vif = &sdata->vif;
+ conf.type = sdata->vif.type;
+ conf.mac_addr = sdata->dev->dev_addr;
+ local->ops->remove_interface(hw, &conf);
+ }
+ }
+
+ /* stop hardware */
+ if (local->open_count) {
+ ieee80211_led_radio(local, false);
+ local->ops->stop(hw);
+ }
+ return 0;
+}
+
+int __ieee80211_resume(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_init_conf conf;
+ struct sta_info *sta;
+ int res;
+
+ /* restart hardware */
+ if (local->open_count) {
+ res = local->ops->start(hw);
+
+ ieee80211_led_radio(local, hw->conf.radio_enabled);
+ }
+
+ /* add interfaces */
+ list_for_each_entry(sdata, &local->interfaces, list) {
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ netif_running(sdata->dev)) {
+ conf.vif = &sdata->vif;
+ conf.type = sdata->vif.type;
+ conf.mac_addr = sdata->dev->dev_addr;
+ res = local->ops->add_interface(hw, &conf);
+ }
+ }
+
+ /* add STAs back */
+ list_for_each_entry(sta, &local->sta_list, list) {
+
+ if (local->ops->sta_notify) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data,
+ u.ap);
+
+ local->ops->sta_notify(hw, &sdata->vif,
+ STA_NOTIFY_ADD, &sta->sta);
+ }
+ }
+
+ /* add back keys */
+ list_for_each_entry(sdata, &local->interfaces, list)
+ if (netif_running(sdata->dev))
+ ieee80211_enable_keys(sdata);
+
+ /* setup RTS threshold */
+ if (local->ops->set_rts_threshold)
+ local->ops->set_rts_threshold(hw, local->rts_threshold);
+
+ /* reconfigure hardware */
+ ieee80211_hw_config(local, ~0);
+
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
+ return 0;
+}