diff options
author | Janusz Dziedzic <janusz.dziedzic@tieto.com> | 2015-03-23 11:32:53 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2015-03-30 02:09:30 -0400 |
commit | 5fd3ac3c36f8b28f54ce3094e69bfc1d54ca0be9 (patch) | |
tree | f24ee44ff64844d3e3d7b6c5ebc646a8a02d269a /drivers/net/wireless/ath/ath10k/wow.c | |
parent | f5431e87ae0357bd72cfbfbe9552aa3d555c3800 (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>
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/wow.c')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wow.c | 276 |
1 files changed, 276 insertions, 0 deletions
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 | |||
26 | static const struct wiphy_wowlan_support ath10k_wowlan_support = { | ||
27 | .flags = WIPHY_WOWLAN_DISCONNECT | | ||
28 | WIPHY_WOWLAN_MAGIC_PKT, | ||
29 | }; | ||
30 | |||
31 | static 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 | |||
48 | static 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 | |||
67 | static 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 | |||
117 | static 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 | |||
137 | static 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 | |||
160 | static 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 | |||
184 | int 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 | |||
226 | wakeup: | ||
227 | ath10k_wow_wakeup(ar); | ||
228 | |||
229 | cleanup: | ||
230 | ath10k_wow_cleanup(ar); | ||
231 | |||
232 | exit: | ||
233 | mutex_unlock(&ar->conf_mutex); | ||
234 | return ret ? 1 : 0; | ||
235 | } | ||
236 | |||
237 | int 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 | |||
260 | exit: | ||
261 | mutex_unlock(&ar->conf_mutex); | ||
262 | return ret ? 1 : 0; | ||
263 | } | ||
264 | |||
265 | int 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 | } | ||