aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJanusz Dziedzic <janusz.dziedzic@tieto.com>2015-03-23 11:32:53 -0400
committerKalle Valo <kvalo@qca.qualcomm.com>2015-03-30 02:09:30 -0400
commit5fd3ac3c36f8b28f54ce3094e69bfc1d54ca0be9 (patch)
treef24ee44ff64844d3e3d7b6c5ebc646a8a02d269a
parentf5431e87ae0357bd72cfbfbe9552aa3d555c3800 (diff)
ath10k: add WOW disconnect/magic-packet support
Add support for WOW disconnect and magic-packet. Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath10k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h8
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c75
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c14
-rw-r--r--drivers/net/wireless/ath/ath10k/wow.c276
-rw-r--r--drivers/net/wireless/ath/ath10k/wow.h38
7 files changed, 346 insertions, 67 deletions
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index 92a1e097e436..9729e6941635 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -18,6 +18,7 @@ ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
18ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o 18ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
19ath10k_core-$(CONFIG_THERMAL) += thermal.o 19ath10k_core-$(CONFIG_THERMAL) += thermal.o
20ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o 20ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o
21ath10k_core-$(CONFIG_PM) += wow.o
21 22
22obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o 23obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
23ath10k_pci-y += pci.o \ 24ath10k_pci-y += pci.o \
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index c0e454bb6a8d..e7fc531993ea 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1386,6 +1386,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
1386 init_completion(&ar->scan.completed); 1386 init_completion(&ar->scan.completed);
1387 init_completion(&ar->scan.on_channel); 1387 init_completion(&ar->scan.on_channel);
1388 init_completion(&ar->target_suspend); 1388 init_completion(&ar->target_suspend);
1389 init_completion(&ar->wow.wakeup_completed);
1389 1390
1390 init_completion(&ar->install_key_done); 1391 init_completion(&ar->install_key_done);
1391 init_completion(&ar->vdev_setup_done); 1392 init_completion(&ar->vdev_setup_done);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index cfd37a6e8931..348efd88bccf 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -35,6 +35,7 @@
35#include "../dfs_pattern_detector.h" 35#include "../dfs_pattern_detector.h"
36#include "spectral.h" 36#include "spectral.h"
37#include "thermal.h" 37#include "thermal.h"
38#include "wow.h"
38 39
39#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) 40#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB)
40#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) 41#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -443,6 +444,12 @@ enum ath10k_fw_features {
443 */ 444 */
444 ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5, 445 ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT = 5,
445 446
447 /* Some firmware revisions have an incomplete WoWLAN implementation
448 * despite WMI service bit being advertised. This feature flag is used
449 * to distinguish whether WoWLAN is really supported or not.
450 */
451 ATH10K_FW_FEATURE_WOWLAN_SUPPORT = 6,
452
446 /* keep last */ 453 /* keep last */
447 ATH10K_FW_FEATURE_COUNT, 454 ATH10K_FW_FEATURE_COUNT,
448}; 455};
@@ -691,6 +698,7 @@ struct ath10k {
691 } stats; 698 } stats;
692 699
693 struct ath10k_thermal thermal; 700 struct ath10k_thermal thermal;
701 struct ath10k_wow wow;
694 702
695 /* must be last */ 703 /* must be last */
696 u8 drv_priv[0] __aligned(sizeof(void *)); 704 u8 drv_priv[0] __aligned(sizeof(void *));
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 73a9d3843e91..755ea6114079 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -29,6 +29,7 @@
29#include "testmode.h" 29#include "testmode.h"
30#include "wmi.h" 30#include "wmi.h"
31#include "wmi-ops.h" 31#include "wmi-ops.h"
32#include "wow.h"
32 33
33/**********/ 34/**********/
34/* Crypto */ 35/* Crypto */
@@ -4810,70 +4811,6 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
4810 return 1; 4811 return 1;
4811} 4812}
4812 4813
4813#ifdef CONFIG_PM
4814static int ath10k_suspend(struct ieee80211_hw *hw,
4815 struct cfg80211_wowlan *wowlan)
4816{
4817 struct ath10k *ar = hw->priv;
4818 int ret;
4819
4820 mutex_lock(&ar->conf_mutex);
4821
4822 ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND);
4823 if (ret) {
4824 if (ret == -ETIMEDOUT)
4825 goto resume;
4826 ret = 1;
4827 goto exit;
4828 }
4829
4830 ret = ath10k_hif_suspend(ar);
4831 if (ret) {
4832 ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
4833 goto resume;
4834 }
4835
4836 ret = 0;
4837 goto exit;
4838resume:
4839 ret = ath10k_wmi_pdev_resume_target(ar);
4840 if (ret)
4841 ath10k_warn(ar, "failed to resume target: %d\n", ret);
4842
4843 ret = 1;
4844exit:
4845 mutex_unlock(&ar->conf_mutex);
4846 return ret;
4847}
4848
4849static int ath10k_resume(struct ieee80211_hw *hw)
4850{
4851 struct ath10k *ar = hw->priv;
4852 int ret;
4853
4854 mutex_lock(&ar->conf_mutex);
4855
4856 ret = ath10k_hif_resume(ar);
4857 if (ret) {
4858 ath10k_warn(ar, "failed to resume hif: %d\n", ret);
4859 ret = 1;
4860 goto exit;
4861 }
4862
4863 ret = ath10k_wmi_pdev_resume_target(ar);
4864 if (ret) {
4865 ath10k_warn(ar, "failed to resume target: %d\n", ret);
4866 ret = 1;
4867 goto exit;
4868 }
4869
4870 ret = 0;
4871exit:
4872 mutex_unlock(&ar->conf_mutex);
4873 return ret;
4874}
4875#endif
4876
4877static void ath10k_reconfig_complete(struct ieee80211_hw *hw, 4814static void ath10k_reconfig_complete(struct ieee80211_hw *hw,
4878 enum ieee80211_reconfig_type reconfig_type) 4815 enum ieee80211_reconfig_type reconfig_type)
4879{ 4816{
@@ -5423,8 +5360,8 @@ static const struct ieee80211_ops ath10k_ops = {
5423 CFG80211_TESTMODE_CMD(ath10k_tm_cmd) 5360 CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
5424 5361
5425#ifdef CONFIG_PM 5362#ifdef CONFIG_PM
5426 .suspend = ath10k_suspend, 5363 .suspend = ath10k_wow_op_suspend,
5427 .resume = ath10k_resume, 5364 .resume = ath10k_wow_op_resume,
5428#endif 5365#endif
5429#ifdef CONFIG_MAC80211_DEBUGFS 5366#ifdef CONFIG_MAC80211_DEBUGFS
5430 .sta_add_debugfs = ath10k_sta_add_debugfs, 5367 .sta_add_debugfs = ath10k_sta_add_debugfs,
@@ -5861,6 +5798,12 @@ int ath10k_mac_register(struct ath10k *ar)
5861 5798
5862 ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations; 5799 ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations;
5863 5800
5801 ret = ath10k_wow_init(ar);
5802 if (ret) {
5803 ath10k_warn(ar, "failed to init wow: %d\n", ret);
5804 goto err_free;
5805 }
5806
5864 /* 5807 /*
5865 * on LL hardware queues are managed entirely by the FW 5808 * on LL hardware queues are managed entirely by the FW
5866 * so we only advertise to mac we can do the queues thing 5809 * so we only advertise to mac we can do the queues thing
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 54430a1a3f73..a5ed54959a10 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -2898,7 +2898,19 @@ void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb)
2898 2898
2899void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) 2899void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb)
2900{ 2900{
2901 ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); 2901 struct wmi_wow_ev_arg ev = {};
2902 int ret;
2903
2904 complete(&ar->wow.wakeup_completed);
2905
2906 ret = ath10k_wmi_pull_wow_event(ar, skb, &ev);
2907 if (ret) {
2908 ath10k_warn(ar, "failed to parse wow wakeup event: %d\n", ret);
2909 return;
2910 }
2911
2912 ath10k_dbg(ar, ATH10K_DBG_WMI, "wow wakeup host reason %s\n",
2913 wow_reason(ev.wake_reason));
2902} 2914}
2903 2915
2904void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) 2916void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb)
diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c
new file mode 100644
index 000000000000..dc1b02580e79
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wow.c
@@ -0,0 +1,276 @@
1/*
2 * Copyright (c) 2015 Qualcomm Atheros, Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "mac.h"
18
19#include <net/mac80211.h>
20#include "hif.h"
21#include "core.h"
22#include "debug.h"
23#include "wmi.h"
24#include "wmi-ops.h"
25
26static const struct wiphy_wowlan_support ath10k_wowlan_support = {
27 .flags = WIPHY_WOWLAN_DISCONNECT |
28 WIPHY_WOWLAN_MAGIC_PKT,
29};
30
31static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
32{
33 struct ath10k *ar = arvif->ar;
34 int i, ret;
35
36 for (i = 0; i < WOW_EVENT_MAX; i++) {
37 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
38 if (ret) {
39 ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
40 wow_wakeup_event(i), arvif->vdev_id, ret);
41 return ret;
42 }
43 }
44
45 return 0;
46}
47
48static int ath10k_wow_cleanup(struct ath10k *ar)
49{
50 struct ath10k_vif *arvif;
51 int ret;
52
53 lockdep_assert_held(&ar->conf_mutex);
54
55 list_for_each_entry(arvif, &ar->arvifs, list) {
56 ret = ath10k_wow_vif_cleanup(arvif);
57 if (ret) {
58 ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
59 arvif->vdev_id, ret);
60 return ret;
61 }
62 }
63
64 return 0;
65}
66
67static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
68 struct cfg80211_wowlan *wowlan)
69{
70 int ret, i;
71 unsigned long wow_mask = 0;
72 struct ath10k *ar = arvif->ar;
73
74 /* Setup requested WOW features */
75 switch (arvif->vdev_type) {
76 case WMI_VDEV_TYPE_IBSS:
77 __set_bit(WOW_BEACON_EVENT, &wow_mask);
78 /* fall through */
79 case WMI_VDEV_TYPE_AP:
80 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
81 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
82 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
83 __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
84 __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
85 __set_bit(WOW_HTT_EVENT, &wow_mask);
86 __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
87 break;
88 case WMI_VDEV_TYPE_STA:
89 if (wowlan->disconnect) {
90 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
91 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
92 __set_bit(WOW_BMISS_EVENT, &wow_mask);
93 __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
94 }
95
96 if (wowlan->magic_pkt)
97 __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
98 break;
99 default:
100 break;
101 }
102
103 for (i = 0; i < WOW_EVENT_MAX; i++) {
104 if (!test_bit(i, &wow_mask))
105 continue;
106 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
107 if (ret) {
108 ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
109 wow_wakeup_event(i), arvif->vdev_id, ret);
110 return ret;
111 }
112 }
113
114 return 0;
115}
116
117static int ath10k_wow_set_wakeups(struct ath10k *ar,
118 struct cfg80211_wowlan *wowlan)
119{
120 struct ath10k_vif *arvif;
121 int ret;
122
123 lockdep_assert_held(&ar->conf_mutex);
124
125 list_for_each_entry(arvif, &ar->arvifs, list) {
126 ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
127 if (ret) {
128 ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
129 arvif->vdev_id, ret);
130 return ret;
131 }
132 }
133
134 return 0;
135}
136
137static int ath10k_wow_enable(struct ath10k *ar)
138{
139 int ret;
140
141 lockdep_assert_held(&ar->conf_mutex);
142
143 reinit_completion(&ar->target_suspend);
144
145 ret = ath10k_wmi_wow_enable(ar);
146 if (ret) {
147 ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
148 return ret;
149 }
150
151 ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
152 if (ret == 0) {
153 ath10k_warn(ar, "timed out while waiting for suspend completion\n");
154 return -ETIMEDOUT;
155 }
156
157 return 0;
158}
159
160static int ath10k_wow_wakeup(struct ath10k *ar)
161{
162 int ret;
163
164 lockdep_assert_held(&ar->conf_mutex);
165
166 reinit_completion(&ar->wow.wakeup_completed);
167
168 ret = ath10k_wmi_wow_host_wakeup_ind(ar);
169 if (ret) {
170 ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
171 ret);
172 return ret;
173 }
174
175 ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
176 if (ret == 0) {
177 ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
178 return -ETIMEDOUT;
179 }
180
181 return 0;
182}
183
184int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
185 struct cfg80211_wowlan *wowlan)
186{
187 struct ath10k *ar = hw->priv;
188 int ret;
189
190 mutex_lock(&ar->conf_mutex);
191
192 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
193 ar->fw_features))) {
194 ret = 1;
195 goto exit;
196 }
197
198 ret = ath10k_wow_cleanup(ar);
199 if (ret) {
200 ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
201 ret);
202 goto exit;
203 }
204
205 ret = ath10k_wow_set_wakeups(ar, wowlan);
206 if (ret) {
207 ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
208 ret);
209 goto cleanup;
210 }
211
212 ret = ath10k_wow_enable(ar);
213 if (ret) {
214 ath10k_warn(ar, "failed to start wow: %d\n", ret);
215 goto cleanup;
216 }
217
218 ret = ath10k_hif_suspend(ar);
219 if (ret) {
220 ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
221 goto wakeup;
222 }
223
224 goto exit;
225
226wakeup:
227 ath10k_wow_wakeup(ar);
228
229cleanup:
230 ath10k_wow_cleanup(ar);
231
232exit:
233 mutex_unlock(&ar->conf_mutex);
234 return ret ? 1 : 0;
235}
236
237int ath10k_wow_op_resume(struct ieee80211_hw *hw)
238{
239 struct ath10k *ar = hw->priv;
240 int ret;
241
242 mutex_lock(&ar->conf_mutex);
243
244 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
245 ar->fw_features))) {
246 ret = 1;
247 goto exit;
248 }
249
250 ret = ath10k_hif_resume(ar);
251 if (ret) {
252 ath10k_warn(ar, "failed to resume hif: %d\n", ret);
253 goto exit;
254 }
255
256 ret = ath10k_wow_wakeup(ar);
257 if (ret)
258 ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
259
260exit:
261 mutex_unlock(&ar->conf_mutex);
262 return ret ? 1 : 0;
263}
264
265int ath10k_wow_init(struct ath10k *ar)
266{
267 if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->fw_features))
268 return 0;
269
270 if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
271 return -EINVAL;
272
273 ar->hw->wiphy->wowlan = &ath10k_wowlan_support;
274
275 return 0;
276}
diff --git a/drivers/net/wireless/ath/ath10k/wow.h b/drivers/net/wireless/ath/ath10k/wow.h
new file mode 100644
index 000000000000..21c43c9c91e0
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wow.h
@@ -0,0 +1,38 @@
1/*
2 * Copyright (c) 2015 Qualcomm Atheros, Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16#ifndef _WOW_H_
17#define _WOW_H_
18
19struct ath10k_wow {
20 struct completion wakeup_completed;
21};
22
23#ifdef CONFIG_PM
24
25int ath10k_wow_init(struct ath10k *ar);
26int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
27 struct cfg80211_wowlan *wowlan);
28int ath10k_wow_op_resume(struct ieee80211_hw *hw);
29
30#else
31
32static inline int ath10k_wow_init(struct ath10k *ar)
33{
34 return 0;
35}
36
37#endif /* CONFIG_PM */
38#endif /* _WOW_H_ */