Commit eecc48000afe2ca6da22122d553b7cad294e42fc

Authored by Johannes Berg
Committed by John W. Linville
1 parent ff1b6e69ad

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,
... ... @@ -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
... ... @@ -135,7 +135,7 @@
135 135 struct ieee80211_local *local = file->private_data;
136 136  
137 137 rtnl_lock();
138   - __ieee80211_suspend(&local->hw);
  138 + __ieee80211_suspend(&local->hw, NULL);
139 139 __ieee80211_resume(&local->hw);
140 140 rtnl_unlock();
141 141  
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 }
... ... @@ -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  
... ... @@ -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();
... ... @@ -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