Commit eecc48000afe2ca6da22122d553b7cad294e42fc
Committed by
John W. Linville
1 parent
ff1b6e69ad
Exists in
master
and in
39 other branches
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>
Showing 9 changed files with 97 additions and 5 deletions Side-by-side Diff
include/net/mac80211.h
... | ... | @@ -1606,6 +1606,18 @@ |
1606 | 1606 | * you should ensure to cancel it on this callback. |
1607 | 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 | 1621 | * @add_interface: Called when a netdevice attached to the hardware is |
1610 | 1622 | * enabled. Because it is not called for monitor mode devices, @start |
1611 | 1623 | * and @stop must be implemented. |
... | ... | @@ -1831,6 +1843,10 @@ |
1831 | 1843 | void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); |
1832 | 1844 | int (*start)(struct ieee80211_hw *hw); |
1833 | 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 | 1850 | int (*add_interface)(struct ieee80211_hw *hw, |
1835 | 1851 | struct ieee80211_vif *vif); |
1836 | 1852 | int (*change_interface)(struct ieee80211_hw *hw, |
net/mac80211/cfg.c
... | ... | @@ -1300,7 +1300,7 @@ |
1300 | 1300 | static int ieee80211_suspend(struct wiphy *wiphy, |
1301 | 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 | 1306 | static int ieee80211_resume(struct wiphy *wiphy) |
net/mac80211/debugfs.c
net/mac80211/driver-ops.h
... | ... | @@ -41,6 +41,33 @@ |
41 | 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 | 71 | static inline int drv_add_interface(struct ieee80211_local *local, |
45 | 72 | struct ieee80211_vif *vif) |
46 | 73 | { |
net/mac80211/driver-trace.h
... | ... | @@ -108,6 +108,16 @@ |
108 | 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 | 121 | DEFINE_EVENT(local_only_evt, drv_stop, |
112 | 122 | TP_PROTO(struct ieee80211_local *local), |
113 | 123 | TP_ARGS(local) |
net/mac80211/ieee80211_i.h
... | ... | @@ -764,6 +764,9 @@ |
764 | 764 | /* device is started */ |
765 | 765 | bool started; |
766 | 766 | |
767 | + /* wowlan is enabled -- don't reconfig on resume */ | |
768 | + bool wowlan; | |
769 | + | |
767 | 770 | int tx_headroom; /* required headroom for hardware/radiotap */ |
768 | 771 | |
769 | 772 | /* count for keys needing tailroom space allocation */ |
... | ... | @@ -1250,7 +1253,8 @@ |
1250 | 1253 | void ieee80211_stop_device(struct ieee80211_local *local); |
1251 | 1254 | |
1252 | 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 | 1259 | static inline int __ieee80211_resume(struct ieee80211_hw *hw) |
1256 | 1260 | { |
... | ... | @@ -1263,7 +1267,8 @@ |
1263 | 1267 | return ieee80211_reconfig(hw_to_local(hw)); |
1264 | 1268 | } |
1265 | 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 | 1273 | return 0; |
1269 | 1274 | } |
net/mac80211/main.c
... | ... | @@ -696,6 +696,10 @@ |
696 | 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 | 703 | if (hw->max_report_rates == 0) |
700 | 704 | hw->max_report_rates = hw->max_rates; |
701 | 705 |
net/mac80211/pm.c
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | #include "driver-ops.h" |
7 | 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 | 11 | struct ieee80211_local *local = hw_to_local(hw); |
12 | 12 | struct ieee80211_sub_if_data *sdata; |
... | ... | @@ -47,6 +47,16 @@ |
47 | 47 | cancel_work_sync(&local->dynamic_ps_enable_work); |
48 | 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 | 60 | /* disable keys */ |
51 | 61 | list_for_each_entry(sdata, &local->interfaces, list) |
52 | 62 | ieee80211_disable_keys(sdata); |
... | ... | @@ -104,6 +114,7 @@ |
104 | 114 | if (local->open_count) |
105 | 115 | ieee80211_stop_device(local); |
106 | 116 | |
117 | + suspend: | |
107 | 118 | local->suspended = true; |
108 | 119 | /* need suspended to be visible before quiescing is false */ |
109 | 120 | barrier(); |
net/mac80211/util.c
... | ... | @@ -1125,9 +1125,27 @@ |
1125 | 1125 | struct sta_info *sta; |
1126 | 1126 | int res; |
1127 | 1127 | |
1128 | +#ifdef CONFIG_PM | |
1128 | 1129 | if (local->suspended) |
1129 | 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 | 1149 | /* restart hardware */ |
1132 | 1150 | if (local->open_count) { |
1133 | 1151 | /* |
... | ... | @@ -1258,6 +1276,7 @@ |
1258 | 1276 | if (ieee80211_sdata_running(sdata)) |
1259 | 1277 | ieee80211_enable_keys(sdata); |
1260 | 1278 | |
1279 | + wake_up: | |
1261 | 1280 | ieee80211_wake_queues_by_reason(hw, |
1262 | 1281 | IEEE80211_QUEUE_STOP_REASON_SUSPEND); |
1263 | 1282 |