aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/mac80211.h16
-rw-r--r--net/mac80211/cfg.c2
-rw-r--r--net/mac80211/debugfs.c2
-rw-r--r--net/mac80211/driver-ops.h27
-rw-r--r--net/mac80211/driver-trace.h10
-rw-r--r--net/mac80211/ieee80211_i.h9
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/pm.c13
-rw-r--r--net/mac80211/util.c19
9 files changed, 97 insertions, 5 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 63f75a06043b..9e5542794b95 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1606,6 +1606,18 @@ enum ieee80211_ampdu_mlme_action {
1606 * you should ensure to cancel it on this callback. 1606 * you should ensure to cancel it on this callback.
1607 * Must be implemented and can sleep. 1607 * Must be implemented and can sleep.
1608 * 1608 *
1609 * @suspend: Suspend the device; mac80211 itself will quiesce before and
1610 * stop transmitting and doing any other configuration, and then
1611 * ask the device to suspend. This is only invoked when WoWLAN is
1612 * configured, otherwise the device is deconfigured completely and
1613 * reconfigured at resume time.
1614 *
1615 * @resume: If WoWLAN was configured, this indicates that mac80211 is
1616 * now resuming its operation, after this the device must be fully
1617 * functional again. If this returns an error, the only way out is
1618 * to also unregister the device. If it returns 1, then mac80211
1619 * will also go through the regular complete restart on resume.
1620 *
1609 * @add_interface: Called when a netdevice attached to the hardware is 1621 * @add_interface: Called when a netdevice attached to the hardware is
1610 * enabled. Because it is not called for monitor mode devices, @start 1622 * enabled. Because it is not called for monitor mode devices, @start
1611 * and @stop must be implemented. 1623 * and @stop must be implemented.
@@ -1831,6 +1843,10 @@ struct ieee80211_ops {
1831 void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); 1843 void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
1832 int (*start)(struct ieee80211_hw *hw); 1844 int (*start)(struct ieee80211_hw *hw);
1833 void (*stop)(struct ieee80211_hw *hw); 1845 void (*stop)(struct ieee80211_hw *hw);
1846#ifdef CONFIG_PM
1847 int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
1848 int (*resume)(struct ieee80211_hw *hw);
1849#endif
1834 int (*add_interface)(struct ieee80211_hw *hw, 1850 int (*add_interface)(struct ieee80211_hw *hw,
1835 struct ieee80211_vif *vif); 1851 struct ieee80211_vif *vif);
1836 int (*change_interface)(struct ieee80211_hw *hw, 1852 int (*change_interface)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 321d598eb8cb..1ebc13383ae7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1300,7 +1300,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
1300static int ieee80211_suspend(struct wiphy *wiphy, 1300static int ieee80211_suspend(struct wiphy *wiphy,
1301 struct cfg80211_wowlan *wowlan) 1301 struct cfg80211_wowlan *wowlan)
1302{ 1302{
1303 return __ieee80211_suspend(wiphy_priv(wiphy)); 1303 return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
1304} 1304}
1305 1305
1306static int ieee80211_resume(struct wiphy *wiphy) 1306static int ieee80211_resume(struct wiphy *wiphy)
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 0a602dbfdb2b..186e02f7cc32 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -135,7 +135,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
135 struct ieee80211_local *local = file->private_data; 135 struct ieee80211_local *local = file->private_data;
136 136
137 rtnl_lock(); 137 rtnl_lock();
138 __ieee80211_suspend(&local->hw); 138 __ieee80211_suspend(&local->hw, NULL);
139 __ieee80211_resume(&local->hw); 139 __ieee80211_resume(&local->hw);
140 rtnl_unlock(); 140 rtnl_unlock();
141 141
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 2ddb56e5b51f..aa16bd8ef789 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -41,6 +41,33 @@ static inline void drv_stop(struct ieee80211_local *local)
41 local->started = false; 41 local->started = false;
42} 42}
43 43
44#ifdef CONFIG_PM
45static inline int drv_suspend(struct ieee80211_local *local,
46 struct cfg80211_wowlan *wowlan)
47{
48 int ret;
49
50 might_sleep();
51
52 trace_drv_suspend(local);
53 ret = local->ops->suspend(&local->hw, wowlan);
54 trace_drv_return_int(local, ret);
55 return ret;
56}
57
58static inline int drv_resume(struct ieee80211_local *local)
59{
60 int ret;
61
62 might_sleep();
63
64 trace_drv_resume(local);
65 ret = local->ops->resume(&local->hw);
66 trace_drv_return_int(local, ret);
67 return ret;
68}
69#endif
70
44static inline int drv_add_interface(struct ieee80211_local *local, 71static inline int drv_add_interface(struct ieee80211_local *local,
45 struct ieee80211_vif *vif) 72 struct ieee80211_vif *vif)
46{ 73{
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 191e834ec46b..11e1ea5111ea 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -108,6 +108,16 @@ DEFINE_EVENT(local_only_evt, drv_start,
108 TP_ARGS(local) 108 TP_ARGS(local)
109); 109);
110 110
111DEFINE_EVENT(local_only_evt, drv_suspend,
112 TP_PROTO(struct ieee80211_local *local),
113 TP_ARGS(local)
114);
115
116DEFINE_EVENT(local_only_evt, drv_resume,
117 TP_PROTO(struct ieee80211_local *local),
118 TP_ARGS(local)
119);
120
111DEFINE_EVENT(local_only_evt, drv_stop, 121DEFINE_EVENT(local_only_evt, drv_stop,
112 TP_PROTO(struct ieee80211_local *local), 122 TP_PROTO(struct ieee80211_local *local),
113 TP_ARGS(local) 123 TP_ARGS(local)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9e3b4f0f31bd..e89bc27f8dc3 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -764,6 +764,9 @@ struct ieee80211_local {
764 /* device is started */ 764 /* device is started */
765 bool started; 765 bool started;
766 766
767 /* wowlan is enabled -- don't reconfig on resume */
768 bool wowlan;
769
767 int tx_headroom; /* required headroom for hardware/radiotap */ 770 int tx_headroom; /* required headroom for hardware/radiotap */
768 771
769 /* count for keys needing tailroom space allocation */ 772 /* count for keys needing tailroom space allocation */
@@ -1250,7 +1253,8 @@ int ieee80211_reconfig(struct ieee80211_local *local);
1250void ieee80211_stop_device(struct ieee80211_local *local); 1253void ieee80211_stop_device(struct ieee80211_local *local);
1251 1254
1252#ifdef CONFIG_PM 1255#ifdef CONFIG_PM
1253int __ieee80211_suspend(struct ieee80211_hw *hw); 1256int __ieee80211_suspend(struct ieee80211_hw *hw,
1257 struct cfg80211_wowlan *wowlan);
1254 1258
1255static inline int __ieee80211_resume(struct ieee80211_hw *hw) 1259static inline int __ieee80211_resume(struct ieee80211_hw *hw)
1256{ 1260{
@@ -1263,7 +1267,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
1263 return ieee80211_reconfig(hw_to_local(hw)); 1267 return ieee80211_reconfig(hw_to_local(hw));
1264} 1268}
1265#else 1269#else
1266static inline int __ieee80211_suspend(struct ieee80211_hw *hw) 1270static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
1271 struct cfg80211_wowlan *wowlan)
1267{ 1272{
1268 return 0; 1273 return 0;
1269} 1274}
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index d8be1986a108..cb326d36be9c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -696,6 +696,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
696 WLAN_CIPHER_SUITE_AES_CMAC 696 WLAN_CIPHER_SUITE_AES_CMAC
697 }; 697 };
698 698
699 if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) &&
700 (!local->ops->suspend || !local->ops->resume))
701 return -EINVAL;
702
699 if (hw->max_report_rates == 0) 703 if (hw->max_report_rates == 0)
700 hw->max_report_rates = hw->max_rates; 704 hw->max_report_rates = hw->max_rates;
701 705
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 042461710880..730778a2c90c 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,7 +6,7 @@
6#include "driver-ops.h" 6#include "driver-ops.h"
7#include "led.h" 7#include "led.h"
8 8
9int __ieee80211_suspend(struct ieee80211_hw *hw) 9int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
10{ 10{
11 struct ieee80211_local *local = hw_to_local(hw); 11 struct ieee80211_local *local = hw_to_local(hw);
12 struct ieee80211_sub_if_data *sdata; 12 struct ieee80211_sub_if_data *sdata;
@@ -47,6 +47,16 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
47 cancel_work_sync(&local->dynamic_ps_enable_work); 47 cancel_work_sync(&local->dynamic_ps_enable_work);
48 del_timer_sync(&local->dynamic_ps_timer); 48 del_timer_sync(&local->dynamic_ps_timer);
49 49
50 local->wowlan = wowlan && local->open_count;
51 if (local->wowlan) {
52 int err = drv_suspend(local, wowlan);
53 if (err) {
54 local->quiescing = false;
55 return err;
56 }
57 goto suspend;
58 }
59
50 /* disable keys */ 60 /* disable keys */
51 list_for_each_entry(sdata, &local->interfaces, list) 61 list_for_each_entry(sdata, &local->interfaces, list)
52 ieee80211_disable_keys(sdata); 62 ieee80211_disable_keys(sdata);
@@ -104,6 +114,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
104 if (local->open_count) 114 if (local->open_count)
105 ieee80211_stop_device(local); 115 ieee80211_stop_device(local);
106 116
117 suspend:
107 local->suspended = true; 118 local->suspended = true;
108 /* need suspended to be visible before quiescing is false */ 119 /* need suspended to be visible before quiescing is false */
109 barrier(); 120 barrier();
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index ef0560a2346a..d3fe2d237485 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1125,9 +1125,27 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1125 struct sta_info *sta; 1125 struct sta_info *sta;
1126 int res; 1126 int res;
1127 1127
1128#ifdef CONFIG_PM
1128 if (local->suspended) 1129 if (local->suspended)
1129 local->resuming = true; 1130 local->resuming = true;
1130 1131
1132 if (local->wowlan) {
1133 local->wowlan = false;
1134 res = drv_resume(local);
1135 if (res < 0) {
1136 local->resuming = false;
1137 return res;
1138 }
1139 if (res == 0)
1140 goto wake_up;
1141 WARN_ON(res > 1);
1142 /*
1143 * res is 1, which means the driver requested
1144 * to go through a regular reset on wakeup.
1145 */
1146 }
1147#endif
1148
1131 /* restart hardware */ 1149 /* restart hardware */
1132 if (local->open_count) { 1150 if (local->open_count) {
1133 /* 1151 /*
@@ -1258,6 +1276,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1258 if (ieee80211_sdata_running(sdata)) 1276 if (ieee80211_sdata_running(sdata))
1259 ieee80211_enable_keys(sdata); 1277 ieee80211_enable_keys(sdata);
1260 1278
1279 wake_up:
1261 ieee80211_wake_queues_by_reason(hw, 1280 ieee80211_wake_queues_by_reason(hw,
1262 IEEE80211_QUEUE_STOP_REASON_SUSPEND); 1281 IEEE80211_QUEUE_STOP_REASON_SUSPEND);
1263 1282