wow.c (cdd38c5f1ce4398ec58fec95904b75824daab7b5) wow.c (ba9177fcef21fa98406e73c472b5ac2eb4ec5f31)
1// SPDX-License-Identifier: BSD-3-Clause-Clear
2/*
3 * Copyright (c) 2020 The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/delay.h>
7
8#include "mac.h"
1// SPDX-License-Identifier: BSD-3-Clause-Clear
2/*
3 * Copyright (c) 2020 The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/delay.h>
7
8#include "mac.h"
9
10#include <net/mac80211.h>
9#include "core.h"
10#include "hif.h"
11#include "debug.h"
12#include "wmi.h"
13#include "wow.h"
14
11#include "core.h"
12#include "hif.h"
13#include "debug.h"
14#include "wmi.h"
15#include "wow.h"
16
17static const struct wiphy_wowlan_support ath11k_wowlan_support = {
18 .flags = WIPHY_WOWLAN_DISCONNECT |
19 WIPHY_WOWLAN_MAGIC_PKT,
20 .pattern_min_len = WOW_MIN_PATTERN_SIZE,
21 .pattern_max_len = WOW_MAX_PATTERN_SIZE,
22 .max_pkt_offset = WOW_MAX_PKT_OFFSET,
23};
24
15int ath11k_wow_enable(struct ath11k_base *ab)
16{
17 struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
18 int i, ret;
19
20 clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
21
22 for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) {

--- 43 unchanged lines hidden (view full) ---

66 ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ);
67 if (ret == 0) {
68 ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n");
69 return -ETIMEDOUT;
70 }
71
72 return 0;
73}
25int ath11k_wow_enable(struct ath11k_base *ab)
26{
27 struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
28 int i, ret;
29
30 clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
31
32 for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) {

--- 43 unchanged lines hidden (view full) ---

76 ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ);
77 if (ret == 0) {
78 ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n");
79 return -ETIMEDOUT;
80 }
81
82 return 0;
83}
84
85static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif)
86{
87 struct ath11k *ar = arvif->ar;
88 int i, ret;
89
90 for (i = 0; i < WOW_EVENT_MAX; i++) {
91 ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
92 if (ret) {
93 ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
94 wow_wakeup_event(i), arvif->vdev_id, ret);
95 return ret;
96 }
97 }
98
99 for (i = 0; i < ar->wow.max_num_patterns; i++) {
100 ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
101 if (ret) {
102 ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n",
103 i, arvif->vdev_id, ret);
104 return ret;
105 }
106 }
107
108 return 0;
109}
110
111static int ath11k_wow_cleanup(struct ath11k *ar)
112{
113 struct ath11k_vif *arvif;
114 int ret;
115
116 lockdep_assert_held(&ar->conf_mutex);
117
118 list_for_each_entry(arvif, &ar->arvifs, list) {
119 ret = ath11k_wow_vif_cleanup(arvif);
120 if (ret) {
121 ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n",
122 arvif->vdev_id, ret);
123 return ret;
124 }
125 }
126
127 return 0;
128}
129
130/* Convert a 802.3 format to a 802.11 format.
131 * +------------+-----------+--------+----------------+
132 * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... |
133 * +------------+-----------+--------+----------------+
134 * |__ |_______ |____________ |________
135 * | | | |
136 * +--+------------+----+-----------+---------------+-----------+
137 * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... |
138 * +--+------------+----+-----------+---------------+-----------+
139 */
140static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
141 const struct cfg80211_pkt_pattern *old)
142{
143 u8 hdr_8023_pattern[ETH_HLEN] = {};
144 u8 hdr_8023_bit_mask[ETH_HLEN] = {};
145 u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
146 u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
147
148 int total_len = old->pkt_offset + old->pattern_len;
149 int hdr_80211_end_offset;
150
151 struct ieee80211_hdr_3addr *new_hdr_pattern =
152 (struct ieee80211_hdr_3addr *)hdr_80211_pattern;
153 struct ieee80211_hdr_3addr *new_hdr_mask =
154 (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
155 struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
156 struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
157 int hdr_len = sizeof(*new_hdr_pattern);
158
159 struct rfc1042_hdr *new_rfc_pattern =
160 (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
161 struct rfc1042_hdr *new_rfc_mask =
162 (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
163 int rfc_len = sizeof(*new_rfc_pattern);
164
165 memcpy(hdr_8023_pattern + old->pkt_offset,
166 old->pattern, ETH_HLEN - old->pkt_offset);
167 memcpy(hdr_8023_bit_mask + old->pkt_offset,
168 old->mask, ETH_HLEN - old->pkt_offset);
169
170 /* Copy destination address */
171 memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
172 memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
173
174 /* Copy source address */
175 memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
176 memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
177
178 /* Copy logic link type */
179 memcpy(&new_rfc_pattern->snap_type,
180 &old_hdr_pattern->h_proto,
181 sizeof(old_hdr_pattern->h_proto));
182 memcpy(&new_rfc_mask->snap_type,
183 &old_hdr_mask->h_proto,
184 sizeof(old_hdr_mask->h_proto));
185
186 /* Compute new pkt_offset */
187 if (old->pkt_offset < ETH_ALEN)
188 new->pkt_offset = old->pkt_offset +
189 offsetof(struct ieee80211_hdr_3addr, addr1);
190 else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
191 new->pkt_offset = old->pkt_offset +
192 offsetof(struct ieee80211_hdr_3addr, addr3) -
193 offsetof(struct ethhdr, h_source);
194 else
195 new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
196
197 /* Compute new hdr end offset */
198 if (total_len > ETH_HLEN)
199 hdr_80211_end_offset = hdr_len + rfc_len;
200 else if (total_len > offsetof(struct ethhdr, h_proto))
201 hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
202 else if (total_len > ETH_ALEN)
203 hdr_80211_end_offset = total_len - ETH_ALEN +
204 offsetof(struct ieee80211_hdr_3addr, addr3);
205 else
206 hdr_80211_end_offset = total_len +
207 offsetof(struct ieee80211_hdr_3addr, addr1);
208
209 new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
210
211 memcpy((u8 *)new->pattern,
212 hdr_80211_pattern + new->pkt_offset,
213 new->pattern_len);
214 memcpy((u8 *)new->mask,
215 hdr_80211_bit_mask + new->pkt_offset,
216 new->pattern_len);
217
218 if (total_len > ETH_HLEN) {
219 /* Copy frame body */
220 memcpy((u8 *)new->pattern + new->pattern_len,
221 (void *)old->pattern + ETH_HLEN - old->pkt_offset,
222 total_len - ETH_HLEN);
223 memcpy((u8 *)new->mask + new->pattern_len,
224 (void *)old->mask + ETH_HLEN - old->pkt_offset,
225 total_len - ETH_HLEN);
226
227 new->pattern_len += total_len - ETH_HLEN;
228 }
229}
230
231static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif,
232 struct cfg80211_wowlan *wowlan)
233{
234 int ret, i;
235 unsigned long wow_mask = 0;
236 struct ath11k *ar = arvif->ar;
237 const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
238 int pattern_id = 0;
239
240 /* Setup requested WOW features */
241 switch (arvif->vdev_type) {
242 case WMI_VDEV_TYPE_IBSS:
243 __set_bit(WOW_BEACON_EVENT, &wow_mask);
244 fallthrough;
245 case WMI_VDEV_TYPE_AP:
246 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
247 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
248 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
249 __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
250 __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
251 __set_bit(WOW_HTT_EVENT, &wow_mask);
252 __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
253 break;
254 case WMI_VDEV_TYPE_STA:
255 if (wowlan->disconnect) {
256 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
257 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
258 __set_bit(WOW_BMISS_EVENT, &wow_mask);
259 __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
260 }
261
262 if (wowlan->magic_pkt)
263 __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
264 break;
265 default:
266 break;
267 }
268
269 for (i = 0; i < wowlan->n_patterns; i++) {
270 u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
271 u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
272 u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
273 struct cfg80211_pkt_pattern new_pattern = {};
274 struct cfg80211_pkt_pattern old_pattern = patterns[i];
275 int j;
276
277 new_pattern.pattern = ath_pattern;
278 new_pattern.mask = ath_bitmask;
279 if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
280 continue;
281 /* convert bytemask to bitmask */
282 for (j = 0; j < patterns[i].pattern_len; j++)
283 if (patterns[i].mask[j / 8] & BIT(j % 8))
284 bitmask[j] = 0xff;
285 old_pattern.mask = bitmask;
286
287 if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
288 ATH11K_HW_TXRX_NATIVE_WIFI) {
289 if (patterns[i].pkt_offset < ETH_HLEN) {
290 u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {};
291
292 memcpy(pattern_ext, old_pattern.pattern,
293 old_pattern.pattern_len);
294 old_pattern.pattern = pattern_ext;
295 ath11k_wow_convert_8023_to_80211(&new_pattern,
296 &old_pattern);
297 } else {
298 new_pattern = old_pattern;
299 new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
300 }
301 }
302
303 if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
304 return -EINVAL;
305
306 ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id,
307 pattern_id,
308 new_pattern.pattern,
309 new_pattern.mask,
310 new_pattern.pattern_len,
311 new_pattern.pkt_offset);
312 if (ret) {
313 ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n",
314 pattern_id,
315 arvif->vdev_id, ret);
316 return ret;
317 }
318
319 pattern_id++;
320 __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
321 }
322
323 for (i = 0; i < WOW_EVENT_MAX; i++) {
324 if (!test_bit(i, &wow_mask))
325 continue;
326 ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
327 if (ret) {
328 ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n",
329 wow_wakeup_event(i), arvif->vdev_id, ret);
330 return ret;
331 }
332 }
333
334 return 0;
335}
336
337static int ath11k_wow_set_wakeups(struct ath11k *ar,
338 struct cfg80211_wowlan *wowlan)
339{
340 struct ath11k_vif *arvif;
341 int ret;
342
343 lockdep_assert_held(&ar->conf_mutex);
344
345 list_for_each_entry(arvif, &ar->arvifs, list) {
346 ret = ath11k_vif_wow_set_wakeups(arvif, wowlan);
347 if (ret) {
348 ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n",
349 arvif->vdev_id, ret);
350 return ret;
351 }
352 }
353
354 return 0;
355}
356
357int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
358 struct cfg80211_wowlan *wowlan)
359{
360 struct ath11k *ar = hw->priv;
361 int ret;
362
363 mutex_lock(&ar->conf_mutex);
364
365 ret = ath11k_wow_cleanup(ar);
366 if (ret) {
367 ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n",
368 ret);
369 goto exit;
370 }
371
372 ret = ath11k_wow_set_wakeups(ar, wowlan);
373 if (ret) {
374 ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n",
375 ret);
376 goto cleanup;
377 }
378
379 ret = ath11k_mac_wait_tx_complete(ar);
380 if (ret) {
381 ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret);
382 goto cleanup;
383 }
384
385 ret = ath11k_wow_enable(ar->ab);
386 if (ret) {
387 ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
388 goto cleanup;
389 }
390
391 ath11k_ce_stop_shadow_timers(ar->ab);
392 ath11k_dp_stop_shadow_timers(ar->ab);
393
394 ath11k_hif_irq_disable(ar->ab);
395 ath11k_hif_ce_irq_disable(ar->ab);
396
397 ret = ath11k_hif_suspend(ar->ab);
398 if (ret) {
399 ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret);
400 goto wakeup;
401 }
402
403 goto exit;
404
405wakeup:
406 ath11k_wow_wakeup(ar->ab);
407
408cleanup:
409 ath11k_wow_cleanup(ar);
410
411exit:
412 mutex_unlock(&ar->conf_mutex);
413 return ret ? 1 : 0;
414}
415
416void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
417{
418 struct ath11k *ar = hw->priv;
419
420 mutex_lock(&ar->conf_mutex);
421 device_set_wakeup_enable(ar->ab->dev, enabled);
422 mutex_unlock(&ar->conf_mutex);
423}
424
425int ath11k_wow_op_resume(struct ieee80211_hw *hw)
426{
427 struct ath11k *ar = hw->priv;
428 int ret;
429
430 mutex_lock(&ar->conf_mutex);
431
432 ret = ath11k_hif_resume(ar->ab);
433 if (ret) {
434 ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret);
435 goto exit;
436 }
437
438 ath11k_hif_ce_irq_enable(ar->ab);
439 ath11k_hif_irq_enable(ar->ab);
440
441 ret = ath11k_wow_wakeup(ar->ab);
442 if (ret)
443 ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret);
444
445exit:
446 if (ret) {
447 switch (ar->state) {
448 case ATH11K_STATE_ON:
449 ar->state = ATH11K_STATE_RESTARTING;
450 ret = 1;
451 break;
452 case ATH11K_STATE_OFF:
453 case ATH11K_STATE_RESTARTING:
454 case ATH11K_STATE_RESTARTED:
455 case ATH11K_STATE_WEDGED:
456 ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
457 ar->state);
458 ret = -EIO;
459 break;
460 }
461 }
462
463 mutex_unlock(&ar->conf_mutex);
464 return ret;
465}
466
467int ath11k_wow_init(struct ath11k *ar)
468{
469 if (WARN_ON(!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map)))
470 return -EINVAL;
471
472 ar->wow.wowlan_support = ath11k_wowlan_support;
473
474 if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
475 ATH11K_HW_TXRX_NATIVE_WIFI) {
476 ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
477 ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
478 }
479
480 ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS;
481 ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
482 ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
483
484 device_set_wakeup_capable(ar->ab->dev, true);
485
486 return 0;
487}