]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/net/wireless/ath/ath10k/wow.c
ath10k: convert wow pattern from 802.3 to 802.11
[linux.git] / drivers / net / wireless / ath / ath10k / wow.c
1 /*
2  * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
3  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include "mac.h"
19
20 #include <net/mac80211.h>
21 #include "hif.h"
22 #include "core.h"
23 #include "debug.h"
24 #include "wmi.h"
25 #include "wmi-ops.h"
26
27 static const struct wiphy_wowlan_support ath10k_wowlan_support = {
28         .flags = WIPHY_WOWLAN_DISCONNECT |
29                  WIPHY_WOWLAN_MAGIC_PKT,
30         .pattern_min_len = WOW_MIN_PATTERN_SIZE,
31         .pattern_max_len = WOW_MAX_PATTERN_SIZE,
32         .max_pkt_offset = WOW_MAX_PKT_OFFSET,
33 };
34
35 static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
36 {
37         struct ath10k *ar = arvif->ar;
38         int i, ret;
39
40         for (i = 0; i < WOW_EVENT_MAX; i++) {
41                 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
42                 if (ret) {
43                         ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
44                                     wow_wakeup_event(i), arvif->vdev_id, ret);
45                         return ret;
46                 }
47         }
48
49         for (i = 0; i < ar->wow.max_num_patterns; i++) {
50                 ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
51                 if (ret) {
52                         ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
53                                     i, arvif->vdev_id, ret);
54                         return ret;
55                 }
56         }
57
58         return 0;
59 }
60
61 static int ath10k_wow_cleanup(struct ath10k *ar)
62 {
63         struct ath10k_vif *arvif;
64         int ret;
65
66         lockdep_assert_held(&ar->conf_mutex);
67
68         list_for_each_entry(arvif, &ar->arvifs, list) {
69                 ret = ath10k_wow_vif_cleanup(arvif);
70                 if (ret) {
71                         ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
72                                     arvif->vdev_id, ret);
73                         return ret;
74                 }
75         }
76
77         return 0;
78 }
79
80 /**
81  * Convert a 802.3 format to a 802.11 format.
82  *         +------------+-----------+--------+----------------+
83  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
84  *         +------------+-----------+--------+----------------+
85  *                |__         |_______    |____________  |________
86  *                   |                |                |          |
87  *         +--+------------+----+-----------+---------------+-----------+
88  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
89  *         +--+------------+----+-----------+---------------+-----------+
90  */
91 static void ath10k_wow_convert_8023_to_80211
92                                         (struct cfg80211_pkt_pattern *new,
93                                         const struct cfg80211_pkt_pattern *old)
94 {
95         u8 hdr_8023_pattern[ETH_HLEN] = {};
96         u8 hdr_8023_bit_mask[ETH_HLEN] = {};
97         u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
98         u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
99
100         int total_len = old->pkt_offset + old->pattern_len;
101         int hdr_80211_end_offset;
102
103         struct ieee80211_hdr_3addr *new_hdr_pattern =
104                 (struct ieee80211_hdr_3addr *)hdr_80211_pattern;
105         struct ieee80211_hdr_3addr *new_hdr_mask =
106                 (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
107         struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
108         struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
109         int hdr_len = sizeof(*new_hdr_pattern);
110
111         struct rfc1042_hdr *new_rfc_pattern =
112                 (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
113         struct rfc1042_hdr *new_rfc_mask =
114                 (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
115         int rfc_len = sizeof(*new_rfc_pattern);
116
117         memcpy(hdr_8023_pattern + old->pkt_offset,
118                old->pattern, ETH_HLEN - old->pkt_offset);
119         memcpy(hdr_8023_bit_mask + old->pkt_offset,
120                old->mask, ETH_HLEN - old->pkt_offset);
121
122         /* Copy destination address */
123         memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
124         memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
125
126         /* Copy source address */
127         memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
128         memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
129
130         /* Copy logic link type */
131         memcpy(&new_rfc_pattern->snap_type,
132                &old_hdr_pattern->h_proto,
133                sizeof(old_hdr_pattern->h_proto));
134         memcpy(&new_rfc_mask->snap_type,
135                &old_hdr_mask->h_proto,
136                sizeof(old_hdr_mask->h_proto));
137
138         /* Caculate new pkt_offset */
139         if (old->pkt_offset < ETH_ALEN)
140                 new->pkt_offset = old->pkt_offset +
141                         offsetof(struct ieee80211_hdr_3addr, addr1);
142         else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
143                 new->pkt_offset = old->pkt_offset +
144                         offsetof(struct ieee80211_hdr_3addr, addr3) -
145                         offsetof(struct ethhdr, h_source);
146         else
147                 new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
148
149         /* Caculate new hdr end offset */
150         if (total_len > ETH_HLEN)
151                 hdr_80211_end_offset = hdr_len + rfc_len;
152         else if (total_len > offsetof(struct ethhdr, h_proto))
153                 hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
154         else if (total_len > ETH_ALEN)
155                 hdr_80211_end_offset = total_len - ETH_ALEN +
156                         offsetof(struct ieee80211_hdr_3addr, addr3);
157         else
158                 hdr_80211_end_offset = total_len +
159                         offsetof(struct ieee80211_hdr_3addr, addr1);
160
161         new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
162
163         memcpy((u8 *)new->pattern,
164                hdr_80211_pattern + new->pkt_offset,
165                new->pattern_len);
166         memcpy((u8 *)new->mask,
167                hdr_80211_bit_mask + new->pkt_offset,
168                new->pattern_len);
169
170         if (total_len > ETH_HLEN) {
171                 /* Copy frame body */
172                 memcpy((u8 *)new->pattern + new->pattern_len,
173                        (void *)old->pattern + ETH_HLEN - old->pkt_offset,
174                        total_len - ETH_HLEN);
175                 memcpy((u8 *)new->mask + new->pattern_len,
176                        (void *)old->mask + ETH_HLEN - old->pkt_offset,
177                        total_len - ETH_HLEN);
178
179                 new->pattern_len += total_len - ETH_HLEN;
180         }
181 }
182
183 static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
184                                       struct cfg80211_wowlan *wowlan)
185 {
186         int ret, i;
187         unsigned long wow_mask = 0;
188         struct ath10k *ar = arvif->ar;
189         const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
190         int pattern_id = 0;
191
192         /* Setup requested WOW features */
193         switch (arvif->vdev_type) {
194         case WMI_VDEV_TYPE_IBSS:
195                 __set_bit(WOW_BEACON_EVENT, &wow_mask);
196                  /* fall through */
197         case WMI_VDEV_TYPE_AP:
198                 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
199                 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
200                 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
201                 __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
202                 __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
203                 __set_bit(WOW_HTT_EVENT, &wow_mask);
204                 __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
205                 break;
206         case WMI_VDEV_TYPE_STA:
207                 if (wowlan->disconnect) {
208                         __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
209                         __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
210                         __set_bit(WOW_BMISS_EVENT, &wow_mask);
211                         __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
212                 }
213
214                 if (wowlan->magic_pkt)
215                         __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
216                 break;
217         default:
218                 break;
219         }
220
221         for (i = 0; i < wowlan->n_patterns; i++) {
222                 u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
223                 u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
224                 u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
225                 struct cfg80211_pkt_pattern new_pattern = {};
226                 struct cfg80211_pkt_pattern old_pattern = patterns[i];
227                 int j;
228
229                 new_pattern.pattern = ath_pattern;
230                 new_pattern.mask = ath_bitmask;
231                 if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
232                         continue;
233                 /* convert bytemask to bitmask */
234                 for (j = 0; j < patterns[i].pattern_len; j++)
235                         if (patterns[i].mask[j / 8] & BIT(j % 8))
236                                 bitmask[j] = 0xff;
237                 old_pattern.mask = bitmask;
238                 new_pattern = old_pattern;
239
240                 if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
241                         if (patterns[i].pkt_offset < ETH_HLEN)
242                                 ath10k_wow_convert_8023_to_80211(&new_pattern,
243                                                                  &old_pattern);
244                         else
245                                 new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
246                 }
247
248                 if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
249                         return -EINVAL;
250
251                 ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
252                                                  pattern_id,
253                                                  new_pattern.pattern,
254                                                  new_pattern.mask,
255                                                  new_pattern.pattern_len,
256                                                  new_pattern.pkt_offset);
257                 if (ret) {
258                         ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
259                                     pattern_id,
260                                     arvif->vdev_id, ret);
261                         return ret;
262                 }
263
264                 pattern_id++;
265                 __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
266         }
267
268         for (i = 0; i < WOW_EVENT_MAX; i++) {
269                 if (!test_bit(i, &wow_mask))
270                         continue;
271                 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
272                 if (ret) {
273                         ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
274                                     wow_wakeup_event(i), arvif->vdev_id, ret);
275                         return ret;
276                 }
277         }
278
279         return 0;
280 }
281
282 static int ath10k_wow_set_wakeups(struct ath10k *ar,
283                                   struct cfg80211_wowlan *wowlan)
284 {
285         struct ath10k_vif *arvif;
286         int ret;
287
288         lockdep_assert_held(&ar->conf_mutex);
289
290         list_for_each_entry(arvif, &ar->arvifs, list) {
291                 ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
292                 if (ret) {
293                         ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
294                                     arvif->vdev_id, ret);
295                         return ret;
296                 }
297         }
298
299         return 0;
300 }
301
302 static int ath10k_wow_enable(struct ath10k *ar)
303 {
304         int ret;
305
306         lockdep_assert_held(&ar->conf_mutex);
307
308         reinit_completion(&ar->target_suspend);
309
310         ret = ath10k_wmi_wow_enable(ar);
311         if (ret) {
312                 ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
313                 return ret;
314         }
315
316         ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
317         if (ret == 0) {
318                 ath10k_warn(ar, "timed out while waiting for suspend completion\n");
319                 return -ETIMEDOUT;
320         }
321
322         return 0;
323 }
324
325 static int ath10k_wow_wakeup(struct ath10k *ar)
326 {
327         int ret;
328
329         lockdep_assert_held(&ar->conf_mutex);
330
331         reinit_completion(&ar->wow.wakeup_completed);
332
333         ret = ath10k_wmi_wow_host_wakeup_ind(ar);
334         if (ret) {
335                 ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
336                             ret);
337                 return ret;
338         }
339
340         ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
341         if (ret == 0) {
342                 ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
343                 return -ETIMEDOUT;
344         }
345
346         return 0;
347 }
348
349 int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
350                           struct cfg80211_wowlan *wowlan)
351 {
352         struct ath10k *ar = hw->priv;
353         int ret;
354
355         mutex_lock(&ar->conf_mutex);
356
357         if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
358                               ar->running_fw->fw_file.fw_features))) {
359                 ret = 1;
360                 goto exit;
361         }
362
363         ret =  ath10k_wow_cleanup(ar);
364         if (ret) {
365                 ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
366                             ret);
367                 goto exit;
368         }
369
370         ret = ath10k_wow_set_wakeups(ar, wowlan);
371         if (ret) {
372                 ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
373                             ret);
374                 goto cleanup;
375         }
376
377         ret = ath10k_wow_enable(ar);
378         if (ret) {
379                 ath10k_warn(ar, "failed to start wow: %d\n", ret);
380                 goto cleanup;
381         }
382
383         ret = ath10k_hif_suspend(ar);
384         if (ret) {
385                 ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
386                 goto wakeup;
387         }
388
389         goto exit;
390
391 wakeup:
392         ath10k_wow_wakeup(ar);
393
394 cleanup:
395         ath10k_wow_cleanup(ar);
396
397 exit:
398         mutex_unlock(&ar->conf_mutex);
399         return ret ? 1 : 0;
400 }
401
402 void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
403 {
404         struct ath10k *ar = hw->priv;
405
406         mutex_lock(&ar->conf_mutex);
407         if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
408                      ar->running_fw->fw_file.fw_features)) {
409                 device_set_wakeup_enable(ar->dev, enabled);
410         }
411         mutex_unlock(&ar->conf_mutex);
412 }
413
414 int ath10k_wow_op_resume(struct ieee80211_hw *hw)
415 {
416         struct ath10k *ar = hw->priv;
417         int ret;
418
419         mutex_lock(&ar->conf_mutex);
420
421         if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
422                               ar->running_fw->fw_file.fw_features))) {
423                 ret = 1;
424                 goto exit;
425         }
426
427         ret = ath10k_hif_resume(ar);
428         if (ret) {
429                 ath10k_warn(ar, "failed to resume hif: %d\n", ret);
430                 goto exit;
431         }
432
433         ret = ath10k_wow_wakeup(ar);
434         if (ret)
435                 ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
436
437 exit:
438         if (ret) {
439                 switch (ar->state) {
440                 case ATH10K_STATE_ON:
441                         ar->state = ATH10K_STATE_RESTARTING;
442                         ret = 1;
443                         break;
444                 case ATH10K_STATE_OFF:
445                 case ATH10K_STATE_RESTARTING:
446                 case ATH10K_STATE_RESTARTED:
447                 case ATH10K_STATE_UTF:
448                 case ATH10K_STATE_WEDGED:
449                         ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
450                                     ar->state);
451                         ret = -EIO;
452                         break;
453                 }
454         }
455
456         mutex_unlock(&ar->conf_mutex);
457         return ret;
458 }
459
460 int ath10k_wow_init(struct ath10k *ar)
461 {
462         if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
463                       ar->running_fw->fw_file.fw_features))
464                 return 0;
465
466         if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
467                 return -EINVAL;
468
469         ar->wow.wowlan_support = ath10k_wowlan_support;
470
471         if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
472                 ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
473                 ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
474         }
475
476         ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
477         ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
478
479         device_set_wakeup_capable(ar->dev, true);
480
481         return 0;
482 }