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 .pattern_min_len = WOW_MIN_PATTERN_SIZE,
30 .pattern_max_len = WOW_MAX_PATTERN_SIZE,
31 .max_pkt_offset = WOW_MAX_PKT_OFFSET,
32 };
33
ath10k_wow_vif_cleanup(struct ath10k_vif * arvif)34 static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
35 {
36 struct ath10k *ar = arvif->ar;
37 int i, ret;
38
39 for (i = 0; i < WOW_EVENT_MAX; i++) {
40 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
41 if (ret) {
42 ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
43 wow_wakeup_event(i), arvif->vdev_id, ret);
44 return ret;
45 }
46 }
47
48 for (i = 0; i < ar->wow.max_num_patterns; i++) {
49 ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
50 if (ret) {
51 ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
52 i, arvif->vdev_id, ret);
53 return ret;
54 }
55 }
56
57 return 0;
58 }
59
ath10k_wow_cleanup(struct ath10k * ar)60 static int ath10k_wow_cleanup(struct ath10k *ar)
61 {
62 struct ath10k_vif *arvif;
63 int ret;
64
65 lockdep_assert_held(&ar->conf_mutex);
66
67 list_for_each_entry(arvif, &ar->arvifs, list) {
68 ret = ath10k_wow_vif_cleanup(arvif);
69 if (ret) {
70 ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
71 arvif->vdev_id, ret);
72 return ret;
73 }
74 }
75
76 return 0;
77 }
78
ath10k_vif_wow_set_wakeups(struct ath10k_vif * arvif,struct cfg80211_wowlan * wowlan)79 static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
80 struct cfg80211_wowlan *wowlan)
81 {
82 int ret, i;
83 unsigned long wow_mask = 0;
84 struct ath10k *ar = arvif->ar;
85 const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
86 int pattern_id = 0;
87
88 /* Setup requested WOW features */
89 switch (arvif->vdev_type) {
90 case WMI_VDEV_TYPE_IBSS:
91 __set_bit(WOW_BEACON_EVENT, &wow_mask);
92 /* fall through */
93 case WMI_VDEV_TYPE_AP:
94 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
95 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
96 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
97 __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
98 __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
99 __set_bit(WOW_HTT_EVENT, &wow_mask);
100 __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
101 break;
102 case WMI_VDEV_TYPE_STA:
103 if (wowlan->disconnect) {
104 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
105 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
106 __set_bit(WOW_BMISS_EVENT, &wow_mask);
107 __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
108 }
109
110 if (wowlan->magic_pkt)
111 __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
112 break;
113 default:
114 break;
115 }
116
117 for (i = 0; i < wowlan->n_patterns; i++) {
118 u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
119 int j;
120
121 if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
122 continue;
123
124 /* convert bytemask to bitmask */
125 for (j = 0; j < patterns[i].pattern_len; j++)
126 if (patterns[i].mask[j / 8] & BIT(j % 8))
127 bitmask[j] = 0xff;
128
129 ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
130 pattern_id,
131 patterns[i].pattern,
132 bitmask,
133 patterns[i].pattern_len,
134 patterns[i].pkt_offset);
135 if (ret) {
136 ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
137 pattern_id,
138 arvif->vdev_id, ret);
139 return ret;
140 }
141
142 pattern_id++;
143 __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
144 }
145
146 for (i = 0; i < WOW_EVENT_MAX; i++) {
147 if (!test_bit(i, &wow_mask))
148 continue;
149 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
150 if (ret) {
151 ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
152 wow_wakeup_event(i), arvif->vdev_id, ret);
153 return ret;
154 }
155 }
156
157 return 0;
158 }
159
ath10k_wow_set_wakeups(struct ath10k * ar,struct cfg80211_wowlan * wowlan)160 static int ath10k_wow_set_wakeups(struct ath10k *ar,
161 struct cfg80211_wowlan *wowlan)
162 {
163 struct ath10k_vif *arvif;
164 int ret;
165
166 lockdep_assert_held(&ar->conf_mutex);
167
168 list_for_each_entry(arvif, &ar->arvifs, list) {
169 ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
170 if (ret) {
171 ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
172 arvif->vdev_id, ret);
173 return ret;
174 }
175 }
176
177 return 0;
178 }
179
ath10k_wow_enable(struct ath10k * ar)180 static int ath10k_wow_enable(struct ath10k *ar)
181 {
182 int ret;
183
184 lockdep_assert_held(&ar->conf_mutex);
185
186 reinit_completion(&ar->target_suspend);
187
188 ret = ath10k_wmi_wow_enable(ar);
189 if (ret) {
190 ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
191 return ret;
192 }
193
194 ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
195 if (ret == 0) {
196 ath10k_warn(ar, "timed out while waiting for suspend completion\n");
197 return -ETIMEDOUT;
198 }
199
200 return 0;
201 }
202
ath10k_wow_wakeup(struct ath10k * ar)203 static int ath10k_wow_wakeup(struct ath10k *ar)
204 {
205 int ret;
206
207 lockdep_assert_held(&ar->conf_mutex);
208
209 reinit_completion(&ar->wow.wakeup_completed);
210
211 ret = ath10k_wmi_wow_host_wakeup_ind(ar);
212 if (ret) {
213 ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
214 ret);
215 return ret;
216 }
217
218 ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
219 if (ret == 0) {
220 ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
221 return -ETIMEDOUT;
222 }
223
224 return 0;
225 }
226
ath10k_wow_op_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)227 int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
228 struct cfg80211_wowlan *wowlan)
229 {
230 struct ath10k *ar = hw->priv;
231 int ret;
232
233 mutex_lock(&ar->conf_mutex);
234
235 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
236 ar->fw_features))) {
237 ret = 1;
238 goto exit;
239 }
240
241 ret = ath10k_wow_cleanup(ar);
242 if (ret) {
243 ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
244 ret);
245 goto exit;
246 }
247
248 ret = ath10k_wow_set_wakeups(ar, wowlan);
249 if (ret) {
250 ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
251 ret);
252 goto cleanup;
253 }
254
255 ret = ath10k_wow_enable(ar);
256 if (ret) {
257 ath10k_warn(ar, "failed to start wow: %d\n", ret);
258 goto cleanup;
259 }
260
261 ret = ath10k_hif_suspend(ar);
262 if (ret) {
263 ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
264 goto wakeup;
265 }
266
267 goto exit;
268
269 wakeup:
270 ath10k_wow_wakeup(ar);
271
272 cleanup:
273 ath10k_wow_cleanup(ar);
274
275 exit:
276 mutex_unlock(&ar->conf_mutex);
277 return ret ? 1 : 0;
278 }
279
ath10k_wow_op_resume(struct ieee80211_hw * hw)280 int ath10k_wow_op_resume(struct ieee80211_hw *hw)
281 {
282 struct ath10k *ar = hw->priv;
283 int ret;
284
285 mutex_lock(&ar->conf_mutex);
286
287 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
288 ar->fw_features))) {
289 ret = 1;
290 goto exit;
291 }
292
293 ret = ath10k_hif_resume(ar);
294 if (ret) {
295 ath10k_warn(ar, "failed to resume hif: %d\n", ret);
296 goto exit;
297 }
298
299 ret = ath10k_wow_wakeup(ar);
300 if (ret)
301 ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
302
303 exit:
304 if (ret) {
305 switch (ar->state) {
306 case ATH10K_STATE_ON:
307 ar->state = ATH10K_STATE_RESTARTING;
308 ret = 1;
309 break;
310 case ATH10K_STATE_OFF:
311 case ATH10K_STATE_RESTARTING:
312 case ATH10K_STATE_RESTARTED:
313 case ATH10K_STATE_UTF:
314 case ATH10K_STATE_WEDGED:
315 ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
316 ar->state);
317 ret = -EIO;
318 break;
319 }
320 }
321
322 mutex_unlock(&ar->conf_mutex);
323 return ret;
324 }
325
ath10k_wow_init(struct ath10k * ar)326 int ath10k_wow_init(struct ath10k *ar)
327 {
328 if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->fw_features))
329 return 0;
330
331 if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
332 return -EINVAL;
333
334 ar->wow.wowlan_support = ath10k_wowlan_support;
335 ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
336 ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
337
338 return 0;
339 }
340