diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-05-04 09:37:29 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-05-05 14:59:20 -0400 |
commit | eecc48000afe2ca6da22122d553b7cad294e42fc (patch) | |
tree | ade8a18351be5ca63b14d24f3f4db47909486fba | |
parent | ff1b6e69ad4f31fb3c9c6da2665655f2e798dd70 (diff) |
mac80211: add basic support for WoWLAN
This adds basic support for the new WoWLAN
configuration in mac80211. The behaviour is
completely offloaded to the driver though,
with two new callbacks (suspend/resume).
Options for the driver include a complete
reconfiguration after wakeup, and exposing
all the triggers it wants to support.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/net/mac80211.h | 16 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 2 | ||||
-rw-r--r-- | net/mac80211/debugfs.c | 2 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 27 | ||||
-rw-r--r-- | net/mac80211/driver-trace.h | 10 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 9 | ||||
-rw-r--r-- | net/mac80211/main.c | 4 | ||||
-rw-r--r-- | net/mac80211/pm.c | 13 | ||||
-rw-r--r-- | net/mac80211/util.c | 19 |
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, | |||
1300 | static int ieee80211_suspend(struct wiphy *wiphy, | 1300 | static 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 | ||
1306 | static int ieee80211_resume(struct wiphy *wiphy) | 1306 | static 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 | ||
45 | static 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 | |||
58 | static 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 | |||
44 | static inline int drv_add_interface(struct ieee80211_local *local, | 71 | static 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 | ||
111 | DEFINE_EVENT(local_only_evt, drv_suspend, | ||
112 | TP_PROTO(struct ieee80211_local *local), | ||
113 | TP_ARGS(local) | ||
114 | ); | ||
115 | |||
116 | DEFINE_EVENT(local_only_evt, drv_resume, | ||
117 | TP_PROTO(struct ieee80211_local *local), | ||
118 | TP_ARGS(local) | ||
119 | ); | ||
120 | |||
111 | DEFINE_EVENT(local_only_evt, drv_stop, | 121 | DEFINE_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); | |||
1250 | void ieee80211_stop_device(struct ieee80211_local *local); | 1253 | void ieee80211_stop_device(struct ieee80211_local *local); |
1251 | 1254 | ||
1252 | #ifdef CONFIG_PM | 1255 | #ifdef CONFIG_PM |
1253 | int __ieee80211_suspend(struct ieee80211_hw *hw); | 1256 | int __ieee80211_suspend(struct ieee80211_hw *hw, |
1257 | struct cfg80211_wowlan *wowlan); | ||
1254 | 1258 | ||
1255 | static inline int __ieee80211_resume(struct ieee80211_hw *hw) | 1259 | static 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 |
1266 | static inline int __ieee80211_suspend(struct ieee80211_hw *hw) | 1270 | static 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 | ||
9 | int __ieee80211_suspend(struct ieee80211_hw *hw) | 9 | int __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 | ||