1*da8fa4e3SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2*da8fa4e3SBjoern A. Zeeb /*
3*da8fa4e3SBjoern A. Zeeb * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
4*da8fa4e3SBjoern A. Zeeb * Copyright (c) 2018, The Linux Foundation. All rights reserved.
5*da8fa4e3SBjoern A. Zeeb */
6*da8fa4e3SBjoern A. Zeeb
7*da8fa4e3SBjoern A. Zeeb #include "mac.h"
8*da8fa4e3SBjoern A. Zeeb
9*da8fa4e3SBjoern A. Zeeb #include <net/mac80211.h>
10*da8fa4e3SBjoern A. Zeeb #include "hif.h"
11*da8fa4e3SBjoern A. Zeeb #include "core.h"
12*da8fa4e3SBjoern A. Zeeb #include "debug.h"
13*da8fa4e3SBjoern A. Zeeb #include "wmi.h"
14*da8fa4e3SBjoern A. Zeeb #include "wmi-ops.h"
15*da8fa4e3SBjoern A. Zeeb
16*da8fa4e3SBjoern A. Zeeb static const struct wiphy_wowlan_support ath10k_wowlan_support = {
17*da8fa4e3SBjoern A. Zeeb .flags = WIPHY_WOWLAN_DISCONNECT |
18*da8fa4e3SBjoern A. Zeeb WIPHY_WOWLAN_MAGIC_PKT,
19*da8fa4e3SBjoern A. Zeeb .pattern_min_len = WOW_MIN_PATTERN_SIZE,
20*da8fa4e3SBjoern A. Zeeb .pattern_max_len = WOW_MAX_PATTERN_SIZE,
21*da8fa4e3SBjoern A. Zeeb .max_pkt_offset = WOW_MAX_PKT_OFFSET,
22*da8fa4e3SBjoern A. Zeeb };
23*da8fa4e3SBjoern A. Zeeb
ath10k_wow_vif_cleanup(struct ath10k_vif * arvif)24*da8fa4e3SBjoern A. Zeeb static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
25*da8fa4e3SBjoern A. Zeeb {
26*da8fa4e3SBjoern A. Zeeb struct ath10k *ar = arvif->ar;
27*da8fa4e3SBjoern A. Zeeb int i, ret;
28*da8fa4e3SBjoern A. Zeeb
29*da8fa4e3SBjoern A. Zeeb for (i = 0; i < WOW_EVENT_MAX; i++) {
30*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
31*da8fa4e3SBjoern A. Zeeb if (ret) {
32*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
33*da8fa4e3SBjoern A. Zeeb wow_wakeup_event(i), arvif->vdev_id, ret);
34*da8fa4e3SBjoern A. Zeeb return ret;
35*da8fa4e3SBjoern A. Zeeb }
36*da8fa4e3SBjoern A. Zeeb }
37*da8fa4e3SBjoern A. Zeeb
38*da8fa4e3SBjoern A. Zeeb for (i = 0; i < ar->wow.max_num_patterns; i++) {
39*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
40*da8fa4e3SBjoern A. Zeeb if (ret) {
41*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
42*da8fa4e3SBjoern A. Zeeb i, arvif->vdev_id, ret);
43*da8fa4e3SBjoern A. Zeeb return ret;
44*da8fa4e3SBjoern A. Zeeb }
45*da8fa4e3SBjoern A. Zeeb }
46*da8fa4e3SBjoern A. Zeeb
47*da8fa4e3SBjoern A. Zeeb return 0;
48*da8fa4e3SBjoern A. Zeeb }
49*da8fa4e3SBjoern A. Zeeb
ath10k_wow_cleanup(struct ath10k * ar)50*da8fa4e3SBjoern A. Zeeb static int ath10k_wow_cleanup(struct ath10k *ar)
51*da8fa4e3SBjoern A. Zeeb {
52*da8fa4e3SBjoern A. Zeeb struct ath10k_vif *arvif;
53*da8fa4e3SBjoern A. Zeeb int ret;
54*da8fa4e3SBjoern A. Zeeb
55*da8fa4e3SBjoern A. Zeeb lockdep_assert_held(&ar->conf_mutex);
56*da8fa4e3SBjoern A. Zeeb
57*da8fa4e3SBjoern A. Zeeb list_for_each_entry(arvif, &ar->arvifs, list) {
58*da8fa4e3SBjoern A. Zeeb ret = ath10k_wow_vif_cleanup(arvif);
59*da8fa4e3SBjoern A. Zeeb if (ret) {
60*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
61*da8fa4e3SBjoern A. Zeeb arvif->vdev_id, ret);
62*da8fa4e3SBjoern A. Zeeb return ret;
63*da8fa4e3SBjoern A. Zeeb }
64*da8fa4e3SBjoern A. Zeeb }
65*da8fa4e3SBjoern A. Zeeb
66*da8fa4e3SBjoern A. Zeeb return 0;
67*da8fa4e3SBjoern A. Zeeb }
68*da8fa4e3SBjoern A. Zeeb
69*da8fa4e3SBjoern A. Zeeb /*
70*da8fa4e3SBjoern A. Zeeb * Convert a 802.3 format to a 802.11 format.
71*da8fa4e3SBjoern A. Zeeb * +------------+-----------+--------+----------------+
72*da8fa4e3SBjoern A. Zeeb * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... |
73*da8fa4e3SBjoern A. Zeeb * +------------+-----------+--------+----------------+
74*da8fa4e3SBjoern A. Zeeb * |__ |_______ |____________ |________
75*da8fa4e3SBjoern A. Zeeb * | | | |
76*da8fa4e3SBjoern A. Zeeb * +--+------------+----+-----------+---------------+-----------+
77*da8fa4e3SBjoern A. Zeeb * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... |
78*da8fa4e3SBjoern A. Zeeb * +--+------------+----+-----------+---------------+-----------+
79*da8fa4e3SBjoern A. Zeeb */
ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern * new,const struct cfg80211_pkt_pattern * old)80*da8fa4e3SBjoern A. Zeeb static void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
81*da8fa4e3SBjoern A. Zeeb const struct cfg80211_pkt_pattern *old)
82*da8fa4e3SBjoern A. Zeeb {
83*da8fa4e3SBjoern A. Zeeb u8 hdr_8023_pattern[ETH_HLEN] = {};
84*da8fa4e3SBjoern A. Zeeb u8 hdr_8023_bit_mask[ETH_HLEN] = {};
85*da8fa4e3SBjoern A. Zeeb u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
86*da8fa4e3SBjoern A. Zeeb u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
87*da8fa4e3SBjoern A. Zeeb
88*da8fa4e3SBjoern A. Zeeb int total_len = old->pkt_offset + old->pattern_len;
89*da8fa4e3SBjoern A. Zeeb int hdr_80211_end_offset;
90*da8fa4e3SBjoern A. Zeeb
91*da8fa4e3SBjoern A. Zeeb struct ieee80211_hdr_3addr *new_hdr_pattern =
92*da8fa4e3SBjoern A. Zeeb (struct ieee80211_hdr_3addr *)hdr_80211_pattern;
93*da8fa4e3SBjoern A. Zeeb struct ieee80211_hdr_3addr *new_hdr_mask =
94*da8fa4e3SBjoern A. Zeeb (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
95*da8fa4e3SBjoern A. Zeeb struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
96*da8fa4e3SBjoern A. Zeeb struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
97*da8fa4e3SBjoern A. Zeeb int hdr_len = sizeof(*new_hdr_pattern);
98*da8fa4e3SBjoern A. Zeeb
99*da8fa4e3SBjoern A. Zeeb struct rfc1042_hdr *new_rfc_pattern =
100*da8fa4e3SBjoern A. Zeeb (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
101*da8fa4e3SBjoern A. Zeeb struct rfc1042_hdr *new_rfc_mask =
102*da8fa4e3SBjoern A. Zeeb (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
103*da8fa4e3SBjoern A. Zeeb int rfc_len = sizeof(*new_rfc_pattern);
104*da8fa4e3SBjoern A. Zeeb
105*da8fa4e3SBjoern A. Zeeb memcpy(hdr_8023_pattern + old->pkt_offset,
106*da8fa4e3SBjoern A. Zeeb old->pattern, ETH_HLEN - old->pkt_offset);
107*da8fa4e3SBjoern A. Zeeb memcpy(hdr_8023_bit_mask + old->pkt_offset,
108*da8fa4e3SBjoern A. Zeeb old->mask, ETH_HLEN - old->pkt_offset);
109*da8fa4e3SBjoern A. Zeeb
110*da8fa4e3SBjoern A. Zeeb /* Copy destination address */
111*da8fa4e3SBjoern A. Zeeb memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
112*da8fa4e3SBjoern A. Zeeb memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
113*da8fa4e3SBjoern A. Zeeb
114*da8fa4e3SBjoern A. Zeeb /* Copy source address */
115*da8fa4e3SBjoern A. Zeeb memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
116*da8fa4e3SBjoern A. Zeeb memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
117*da8fa4e3SBjoern A. Zeeb
118*da8fa4e3SBjoern A. Zeeb /* Copy logic link type */
119*da8fa4e3SBjoern A. Zeeb memcpy(&new_rfc_pattern->snap_type,
120*da8fa4e3SBjoern A. Zeeb &old_hdr_pattern->h_proto,
121*da8fa4e3SBjoern A. Zeeb sizeof(old_hdr_pattern->h_proto));
122*da8fa4e3SBjoern A. Zeeb memcpy(&new_rfc_mask->snap_type,
123*da8fa4e3SBjoern A. Zeeb &old_hdr_mask->h_proto,
124*da8fa4e3SBjoern A. Zeeb sizeof(old_hdr_mask->h_proto));
125*da8fa4e3SBjoern A. Zeeb
126*da8fa4e3SBjoern A. Zeeb /* Calculate new pkt_offset */
127*da8fa4e3SBjoern A. Zeeb if (old->pkt_offset < ETH_ALEN)
128*da8fa4e3SBjoern A. Zeeb new->pkt_offset = old->pkt_offset +
129*da8fa4e3SBjoern A. Zeeb offsetof(struct ieee80211_hdr_3addr, addr1);
130*da8fa4e3SBjoern A. Zeeb else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
131*da8fa4e3SBjoern A. Zeeb new->pkt_offset = old->pkt_offset +
132*da8fa4e3SBjoern A. Zeeb offsetof(struct ieee80211_hdr_3addr, addr3) -
133*da8fa4e3SBjoern A. Zeeb offsetof(struct ethhdr, h_source);
134*da8fa4e3SBjoern A. Zeeb else
135*da8fa4e3SBjoern A. Zeeb new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
136*da8fa4e3SBjoern A. Zeeb
137*da8fa4e3SBjoern A. Zeeb /* Calculate new hdr end offset */
138*da8fa4e3SBjoern A. Zeeb if (total_len > ETH_HLEN)
139*da8fa4e3SBjoern A. Zeeb hdr_80211_end_offset = hdr_len + rfc_len;
140*da8fa4e3SBjoern A. Zeeb else if (total_len > offsetof(struct ethhdr, h_proto))
141*da8fa4e3SBjoern A. Zeeb hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
142*da8fa4e3SBjoern A. Zeeb else if (total_len > ETH_ALEN)
143*da8fa4e3SBjoern A. Zeeb hdr_80211_end_offset = total_len - ETH_ALEN +
144*da8fa4e3SBjoern A. Zeeb offsetof(struct ieee80211_hdr_3addr, addr3);
145*da8fa4e3SBjoern A. Zeeb else
146*da8fa4e3SBjoern A. Zeeb hdr_80211_end_offset = total_len +
147*da8fa4e3SBjoern A. Zeeb offsetof(struct ieee80211_hdr_3addr, addr1);
148*da8fa4e3SBjoern A. Zeeb
149*da8fa4e3SBjoern A. Zeeb new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
150*da8fa4e3SBjoern A. Zeeb
151*da8fa4e3SBjoern A. Zeeb memcpy((u8 *)new->pattern,
152*da8fa4e3SBjoern A. Zeeb hdr_80211_pattern + new->pkt_offset,
153*da8fa4e3SBjoern A. Zeeb new->pattern_len);
154*da8fa4e3SBjoern A. Zeeb memcpy((u8 *)new->mask,
155*da8fa4e3SBjoern A. Zeeb hdr_80211_bit_mask + new->pkt_offset,
156*da8fa4e3SBjoern A. Zeeb new->pattern_len);
157*da8fa4e3SBjoern A. Zeeb
158*da8fa4e3SBjoern A. Zeeb if (total_len > ETH_HLEN) {
159*da8fa4e3SBjoern A. Zeeb /* Copy frame body */
160*da8fa4e3SBjoern A. Zeeb memcpy((u8 *)new->pattern + new->pattern_len,
161*da8fa4e3SBjoern A. Zeeb (void *)old->pattern + ETH_HLEN - old->pkt_offset,
162*da8fa4e3SBjoern A. Zeeb total_len - ETH_HLEN);
163*da8fa4e3SBjoern A. Zeeb memcpy((u8 *)new->mask + new->pattern_len,
164*da8fa4e3SBjoern A. Zeeb (void *)old->mask + ETH_HLEN - old->pkt_offset,
165*da8fa4e3SBjoern A. Zeeb total_len - ETH_HLEN);
166*da8fa4e3SBjoern A. Zeeb
167*da8fa4e3SBjoern A. Zeeb new->pattern_len += total_len - ETH_HLEN;
168*da8fa4e3SBjoern A. Zeeb }
169*da8fa4e3SBjoern A. Zeeb }
170*da8fa4e3SBjoern A. Zeeb
ath10k_wmi_pno_check(struct ath10k * ar,u32 vdev_id,struct cfg80211_sched_scan_request * nd_config,struct wmi_pno_scan_req * pno)171*da8fa4e3SBjoern A. Zeeb static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id,
172*da8fa4e3SBjoern A. Zeeb struct cfg80211_sched_scan_request *nd_config,
173*da8fa4e3SBjoern A. Zeeb struct wmi_pno_scan_req *pno)
174*da8fa4e3SBjoern A. Zeeb {
175*da8fa4e3SBjoern A. Zeeb int i, j, ret = 0;
176*da8fa4e3SBjoern A. Zeeb u8 ssid_len;
177*da8fa4e3SBjoern A. Zeeb
178*da8fa4e3SBjoern A. Zeeb pno->enable = 1;
179*da8fa4e3SBjoern A. Zeeb pno->vdev_id = vdev_id;
180*da8fa4e3SBjoern A. Zeeb pno->uc_networks_count = nd_config->n_match_sets;
181*da8fa4e3SBjoern A. Zeeb
182*da8fa4e3SBjoern A. Zeeb if (!pno->uc_networks_count ||
183*da8fa4e3SBjoern A. Zeeb pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
184*da8fa4e3SBjoern A. Zeeb return -EINVAL;
185*da8fa4e3SBjoern A. Zeeb
186*da8fa4e3SBjoern A. Zeeb if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
187*da8fa4e3SBjoern A. Zeeb return -EINVAL;
188*da8fa4e3SBjoern A. Zeeb
189*da8fa4e3SBjoern A. Zeeb /* Filling per profile params */
190*da8fa4e3SBjoern A. Zeeb for (i = 0; i < pno->uc_networks_count; i++) {
191*da8fa4e3SBjoern A. Zeeb ssid_len = nd_config->match_sets[i].ssid.ssid_len;
192*da8fa4e3SBjoern A. Zeeb
193*da8fa4e3SBjoern A. Zeeb if (ssid_len == 0 || ssid_len > 32)
194*da8fa4e3SBjoern A. Zeeb return -EINVAL;
195*da8fa4e3SBjoern A. Zeeb
196*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len);
197*da8fa4e3SBjoern A. Zeeb
198*da8fa4e3SBjoern A. Zeeb memcpy(pno->a_networks[i].ssid.ssid,
199*da8fa4e3SBjoern A. Zeeb nd_config->match_sets[i].ssid.ssid,
200*da8fa4e3SBjoern A. Zeeb nd_config->match_sets[i].ssid.ssid_len);
201*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].authentication = 0;
202*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].encryption = 0;
203*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].bcast_nw_type = 0;
204*da8fa4e3SBjoern A. Zeeb
205*da8fa4e3SBjoern A. Zeeb /*Copying list of valid channel into request */
206*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].channel_count = nd_config->n_channels;
207*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
208*da8fa4e3SBjoern A. Zeeb
209*da8fa4e3SBjoern A. Zeeb for (j = 0; j < nd_config->n_channels; j++) {
210*da8fa4e3SBjoern A. Zeeb pno->a_networks[i].channels[j] =
211*da8fa4e3SBjoern A. Zeeb nd_config->channels[j]->center_freq;
212*da8fa4e3SBjoern A. Zeeb }
213*da8fa4e3SBjoern A. Zeeb }
214*da8fa4e3SBjoern A. Zeeb
215*da8fa4e3SBjoern A. Zeeb /* set scan to passive if no SSIDs are specified in the request */
216*da8fa4e3SBjoern A. Zeeb if (nd_config->n_ssids == 0)
217*da8fa4e3SBjoern A. Zeeb pno->do_passive_scan = true;
218*da8fa4e3SBjoern A. Zeeb else
219*da8fa4e3SBjoern A. Zeeb pno->do_passive_scan = false;
220*da8fa4e3SBjoern A. Zeeb
221*da8fa4e3SBjoern A. Zeeb for (i = 0; i < nd_config->n_ssids; i++) {
222*da8fa4e3SBjoern A. Zeeb j = 0;
223*da8fa4e3SBjoern A. Zeeb while (j < pno->uc_networks_count) {
224*da8fa4e3SBjoern A. Zeeb if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) ==
225*da8fa4e3SBjoern A. Zeeb nd_config->ssids[i].ssid_len &&
226*da8fa4e3SBjoern A. Zeeb (memcmp(pno->a_networks[j].ssid.ssid,
227*da8fa4e3SBjoern A. Zeeb nd_config->ssids[i].ssid,
228*da8fa4e3SBjoern A. Zeeb __le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) {
229*da8fa4e3SBjoern A. Zeeb pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
230*da8fa4e3SBjoern A. Zeeb break;
231*da8fa4e3SBjoern A. Zeeb }
232*da8fa4e3SBjoern A. Zeeb j++;
233*da8fa4e3SBjoern A. Zeeb }
234*da8fa4e3SBjoern A. Zeeb }
235*da8fa4e3SBjoern A. Zeeb
236*da8fa4e3SBjoern A. Zeeb if (nd_config->n_scan_plans == 2) {
237*da8fa4e3SBjoern A. Zeeb pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
238*da8fa4e3SBjoern A. Zeeb pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
239*da8fa4e3SBjoern A. Zeeb pno->slow_scan_period =
240*da8fa4e3SBjoern A. Zeeb nd_config->scan_plans[1].interval * MSEC_PER_SEC;
241*da8fa4e3SBjoern A. Zeeb } else if (nd_config->n_scan_plans == 1) {
242*da8fa4e3SBjoern A. Zeeb pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
243*da8fa4e3SBjoern A. Zeeb pno->fast_scan_max_cycles = 1;
244*da8fa4e3SBjoern A. Zeeb pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
245*da8fa4e3SBjoern A. Zeeb } else {
246*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "Invalid number of scan plans %d !!",
247*da8fa4e3SBjoern A. Zeeb nd_config->n_scan_plans);
248*da8fa4e3SBjoern A. Zeeb }
249*da8fa4e3SBjoern A. Zeeb
250*da8fa4e3SBjoern A. Zeeb if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
251*da8fa4e3SBjoern A. Zeeb /* enable mac randomization */
252*da8fa4e3SBjoern A. Zeeb pno->enable_pno_scan_randomization = 1;
253*da8fa4e3SBjoern A. Zeeb memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
254*da8fa4e3SBjoern A. Zeeb memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
255*da8fa4e3SBjoern A. Zeeb }
256*da8fa4e3SBjoern A. Zeeb
257*da8fa4e3SBjoern A. Zeeb pno->delay_start_time = nd_config->delay;
258*da8fa4e3SBjoern A. Zeeb
259*da8fa4e3SBjoern A. Zeeb /* Current FW does not support min-max range for dwell time */
260*da8fa4e3SBjoern A. Zeeb pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
261*da8fa4e3SBjoern A. Zeeb pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
262*da8fa4e3SBjoern A. Zeeb return ret;
263*da8fa4e3SBjoern A. Zeeb }
264*da8fa4e3SBjoern A. Zeeb
ath10k_vif_wow_set_wakeups(struct ath10k_vif * arvif,struct cfg80211_wowlan * wowlan)265*da8fa4e3SBjoern A. Zeeb static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
266*da8fa4e3SBjoern A. Zeeb struct cfg80211_wowlan *wowlan)
267*da8fa4e3SBjoern A. Zeeb {
268*da8fa4e3SBjoern A. Zeeb int ret, i;
269*da8fa4e3SBjoern A. Zeeb unsigned long wow_mask = 0;
270*da8fa4e3SBjoern A. Zeeb struct ath10k *ar = arvif->ar;
271*da8fa4e3SBjoern A. Zeeb const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
272*da8fa4e3SBjoern A. Zeeb int pattern_id = 0;
273*da8fa4e3SBjoern A. Zeeb
274*da8fa4e3SBjoern A. Zeeb /* Setup requested WOW features */
275*da8fa4e3SBjoern A. Zeeb switch (arvif->vdev_type) {
276*da8fa4e3SBjoern A. Zeeb case WMI_VDEV_TYPE_IBSS:
277*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_BEACON_EVENT, &wow_mask);
278*da8fa4e3SBjoern A. Zeeb fallthrough;
279*da8fa4e3SBjoern A. Zeeb case WMI_VDEV_TYPE_AP:
280*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
281*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
282*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
283*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
284*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
285*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_HTT_EVENT, &wow_mask);
286*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
287*da8fa4e3SBjoern A. Zeeb break;
288*da8fa4e3SBjoern A. Zeeb case WMI_VDEV_TYPE_STA:
289*da8fa4e3SBjoern A. Zeeb if (wowlan->disconnect) {
290*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
291*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
292*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_BMISS_EVENT, &wow_mask);
293*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
294*da8fa4e3SBjoern A. Zeeb }
295*da8fa4e3SBjoern A. Zeeb
296*da8fa4e3SBjoern A. Zeeb if (wowlan->magic_pkt)
297*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
298*da8fa4e3SBjoern A. Zeeb
299*da8fa4e3SBjoern A. Zeeb if (wowlan->nd_config) {
300*da8fa4e3SBjoern A. Zeeb struct wmi_pno_scan_req *pno;
301*da8fa4e3SBjoern A. Zeeb int ret;
302*da8fa4e3SBjoern A. Zeeb
303*da8fa4e3SBjoern A. Zeeb pno = kzalloc(sizeof(*pno), GFP_KERNEL);
304*da8fa4e3SBjoern A. Zeeb if (!pno)
305*da8fa4e3SBjoern A. Zeeb return -ENOMEM;
306*da8fa4e3SBjoern A. Zeeb
307*da8fa4e3SBjoern A. Zeeb ar->nlo_enabled = true;
308*da8fa4e3SBjoern A. Zeeb
309*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_pno_check(ar, arvif->vdev_id,
310*da8fa4e3SBjoern A. Zeeb wowlan->nd_config, pno);
311*da8fa4e3SBjoern A. Zeeb if (!ret) {
312*da8fa4e3SBjoern A. Zeeb ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
313*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
314*da8fa4e3SBjoern A. Zeeb }
315*da8fa4e3SBjoern A. Zeeb
316*da8fa4e3SBjoern A. Zeeb kfree(pno);
317*da8fa4e3SBjoern A. Zeeb }
318*da8fa4e3SBjoern A. Zeeb break;
319*da8fa4e3SBjoern A. Zeeb default:
320*da8fa4e3SBjoern A. Zeeb break;
321*da8fa4e3SBjoern A. Zeeb }
322*da8fa4e3SBjoern A. Zeeb
323*da8fa4e3SBjoern A. Zeeb for (i = 0; i < wowlan->n_patterns; i++) {
324*da8fa4e3SBjoern A. Zeeb u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
325*da8fa4e3SBjoern A. Zeeb u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
326*da8fa4e3SBjoern A. Zeeb u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
327*da8fa4e3SBjoern A. Zeeb struct cfg80211_pkt_pattern new_pattern = {};
328*da8fa4e3SBjoern A. Zeeb struct cfg80211_pkt_pattern old_pattern = patterns[i];
329*da8fa4e3SBjoern A. Zeeb int j;
330*da8fa4e3SBjoern A. Zeeb
331*da8fa4e3SBjoern A. Zeeb new_pattern.pattern = ath_pattern;
332*da8fa4e3SBjoern A. Zeeb new_pattern.mask = ath_bitmask;
333*da8fa4e3SBjoern A. Zeeb if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
334*da8fa4e3SBjoern A. Zeeb continue;
335*da8fa4e3SBjoern A. Zeeb /* convert bytemask to bitmask */
336*da8fa4e3SBjoern A. Zeeb for (j = 0; j < patterns[i].pattern_len; j++)
337*da8fa4e3SBjoern A. Zeeb if (patterns[i].mask[j / 8] & BIT(j % 8))
338*da8fa4e3SBjoern A. Zeeb bitmask[j] = 0xff;
339*da8fa4e3SBjoern A. Zeeb old_pattern.mask = bitmask;
340*da8fa4e3SBjoern A. Zeeb
341*da8fa4e3SBjoern A. Zeeb if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
342*da8fa4e3SBjoern A. Zeeb if (patterns[i].pkt_offset < ETH_HLEN) {
343*da8fa4e3SBjoern A. Zeeb ath10k_wow_convert_8023_to_80211(&new_pattern,
344*da8fa4e3SBjoern A. Zeeb &old_pattern);
345*da8fa4e3SBjoern A. Zeeb } else {
346*da8fa4e3SBjoern A. Zeeb new_pattern = old_pattern;
347*da8fa4e3SBjoern A. Zeeb new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
348*da8fa4e3SBjoern A. Zeeb }
349*da8fa4e3SBjoern A. Zeeb }
350*da8fa4e3SBjoern A. Zeeb
351*da8fa4e3SBjoern A. Zeeb if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
352*da8fa4e3SBjoern A. Zeeb return -EINVAL;
353*da8fa4e3SBjoern A. Zeeb
354*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
355*da8fa4e3SBjoern A. Zeeb pattern_id,
356*da8fa4e3SBjoern A. Zeeb new_pattern.pattern,
357*da8fa4e3SBjoern A. Zeeb new_pattern.mask,
358*da8fa4e3SBjoern A. Zeeb new_pattern.pattern_len,
359*da8fa4e3SBjoern A. Zeeb new_pattern.pkt_offset);
360*da8fa4e3SBjoern A. Zeeb if (ret) {
361*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
362*da8fa4e3SBjoern A. Zeeb pattern_id,
363*da8fa4e3SBjoern A. Zeeb arvif->vdev_id, ret);
364*da8fa4e3SBjoern A. Zeeb return ret;
365*da8fa4e3SBjoern A. Zeeb }
366*da8fa4e3SBjoern A. Zeeb
367*da8fa4e3SBjoern A. Zeeb pattern_id++;
368*da8fa4e3SBjoern A. Zeeb __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
369*da8fa4e3SBjoern A. Zeeb }
370*da8fa4e3SBjoern A. Zeeb
371*da8fa4e3SBjoern A. Zeeb for (i = 0; i < WOW_EVENT_MAX; i++) {
372*da8fa4e3SBjoern A. Zeeb if (!test_bit(i, &wow_mask))
373*da8fa4e3SBjoern A. Zeeb continue;
374*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
375*da8fa4e3SBjoern A. Zeeb if (ret) {
376*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
377*da8fa4e3SBjoern A. Zeeb wow_wakeup_event(i), arvif->vdev_id, ret);
378*da8fa4e3SBjoern A. Zeeb return ret;
379*da8fa4e3SBjoern A. Zeeb }
380*da8fa4e3SBjoern A. Zeeb }
381*da8fa4e3SBjoern A. Zeeb
382*da8fa4e3SBjoern A. Zeeb return 0;
383*da8fa4e3SBjoern A. Zeeb }
384*da8fa4e3SBjoern A. Zeeb
ath10k_wow_set_wakeups(struct ath10k * ar,struct cfg80211_wowlan * wowlan)385*da8fa4e3SBjoern A. Zeeb static int ath10k_wow_set_wakeups(struct ath10k *ar,
386*da8fa4e3SBjoern A. Zeeb struct cfg80211_wowlan *wowlan)
387*da8fa4e3SBjoern A. Zeeb {
388*da8fa4e3SBjoern A. Zeeb struct ath10k_vif *arvif;
389*da8fa4e3SBjoern A. Zeeb int ret;
390*da8fa4e3SBjoern A. Zeeb
391*da8fa4e3SBjoern A. Zeeb lockdep_assert_held(&ar->conf_mutex);
392*da8fa4e3SBjoern A. Zeeb
393*da8fa4e3SBjoern A. Zeeb list_for_each_entry(arvif, &ar->arvifs, list) {
394*da8fa4e3SBjoern A. Zeeb ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
395*da8fa4e3SBjoern A. Zeeb if (ret) {
396*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
397*da8fa4e3SBjoern A. Zeeb arvif->vdev_id, ret);
398*da8fa4e3SBjoern A. Zeeb return ret;
399*da8fa4e3SBjoern A. Zeeb }
400*da8fa4e3SBjoern A. Zeeb }
401*da8fa4e3SBjoern A. Zeeb
402*da8fa4e3SBjoern A. Zeeb return 0;
403*da8fa4e3SBjoern A. Zeeb }
404*da8fa4e3SBjoern A. Zeeb
ath10k_vif_wow_clean_nlo(struct ath10k_vif * arvif)405*da8fa4e3SBjoern A. Zeeb static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif)
406*da8fa4e3SBjoern A. Zeeb {
407*da8fa4e3SBjoern A. Zeeb int ret = 0;
408*da8fa4e3SBjoern A. Zeeb struct ath10k *ar = arvif->ar;
409*da8fa4e3SBjoern A. Zeeb
410*da8fa4e3SBjoern A. Zeeb switch (arvif->vdev_type) {
411*da8fa4e3SBjoern A. Zeeb case WMI_VDEV_TYPE_STA:
412*da8fa4e3SBjoern A. Zeeb if (ar->nlo_enabled) {
413*da8fa4e3SBjoern A. Zeeb struct wmi_pno_scan_req *pno;
414*da8fa4e3SBjoern A. Zeeb
415*da8fa4e3SBjoern A. Zeeb pno = kzalloc(sizeof(*pno), GFP_KERNEL);
416*da8fa4e3SBjoern A. Zeeb if (!pno)
417*da8fa4e3SBjoern A. Zeeb return -ENOMEM;
418*da8fa4e3SBjoern A. Zeeb
419*da8fa4e3SBjoern A. Zeeb pno->enable = 0;
420*da8fa4e3SBjoern A. Zeeb ar->nlo_enabled = false;
421*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
422*da8fa4e3SBjoern A. Zeeb kfree(pno);
423*da8fa4e3SBjoern A. Zeeb }
424*da8fa4e3SBjoern A. Zeeb break;
425*da8fa4e3SBjoern A. Zeeb default:
426*da8fa4e3SBjoern A. Zeeb break;
427*da8fa4e3SBjoern A. Zeeb }
428*da8fa4e3SBjoern A. Zeeb return ret;
429*da8fa4e3SBjoern A. Zeeb }
430*da8fa4e3SBjoern A. Zeeb
ath10k_wow_nlo_cleanup(struct ath10k * ar)431*da8fa4e3SBjoern A. Zeeb static int ath10k_wow_nlo_cleanup(struct ath10k *ar)
432*da8fa4e3SBjoern A. Zeeb {
433*da8fa4e3SBjoern A. Zeeb struct ath10k_vif *arvif;
434*da8fa4e3SBjoern A. Zeeb int ret = 0;
435*da8fa4e3SBjoern A. Zeeb
436*da8fa4e3SBjoern A. Zeeb lockdep_assert_held(&ar->conf_mutex);
437*da8fa4e3SBjoern A. Zeeb
438*da8fa4e3SBjoern A. Zeeb list_for_each_entry(arvif, &ar->arvifs, list) {
439*da8fa4e3SBjoern A. Zeeb ret = ath10k_vif_wow_clean_nlo(arvif);
440*da8fa4e3SBjoern A. Zeeb if (ret) {
441*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n",
442*da8fa4e3SBjoern A. Zeeb arvif->vdev_id, ret);
443*da8fa4e3SBjoern A. Zeeb return ret;
444*da8fa4e3SBjoern A. Zeeb }
445*da8fa4e3SBjoern A. Zeeb }
446*da8fa4e3SBjoern A. Zeeb
447*da8fa4e3SBjoern A. Zeeb return 0;
448*da8fa4e3SBjoern A. Zeeb }
449*da8fa4e3SBjoern A. Zeeb
ath10k_wow_enable(struct ath10k * ar)450*da8fa4e3SBjoern A. Zeeb static int ath10k_wow_enable(struct ath10k *ar)
451*da8fa4e3SBjoern A. Zeeb {
452*da8fa4e3SBjoern A. Zeeb int ret;
453*da8fa4e3SBjoern A. Zeeb
454*da8fa4e3SBjoern A. Zeeb lockdep_assert_held(&ar->conf_mutex);
455*da8fa4e3SBjoern A. Zeeb
456*da8fa4e3SBjoern A. Zeeb reinit_completion(&ar->target_suspend);
457*da8fa4e3SBjoern A. Zeeb
458*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_enable(ar);
459*da8fa4e3SBjoern A. Zeeb if (ret) {
460*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
461*da8fa4e3SBjoern A. Zeeb return ret;
462*da8fa4e3SBjoern A. Zeeb }
463*da8fa4e3SBjoern A. Zeeb
464*da8fa4e3SBjoern A. Zeeb ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
465*da8fa4e3SBjoern A. Zeeb if (ret == 0) {
466*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "timed out while waiting for suspend completion\n");
467*da8fa4e3SBjoern A. Zeeb return -ETIMEDOUT;
468*da8fa4e3SBjoern A. Zeeb }
469*da8fa4e3SBjoern A. Zeeb
470*da8fa4e3SBjoern A. Zeeb return 0;
471*da8fa4e3SBjoern A. Zeeb }
472*da8fa4e3SBjoern A. Zeeb
ath10k_wow_wakeup(struct ath10k * ar)473*da8fa4e3SBjoern A. Zeeb static int ath10k_wow_wakeup(struct ath10k *ar)
474*da8fa4e3SBjoern A. Zeeb {
475*da8fa4e3SBjoern A. Zeeb int ret;
476*da8fa4e3SBjoern A. Zeeb
477*da8fa4e3SBjoern A. Zeeb lockdep_assert_held(&ar->conf_mutex);
478*da8fa4e3SBjoern A. Zeeb
479*da8fa4e3SBjoern A. Zeeb reinit_completion(&ar->wow.wakeup_completed);
480*da8fa4e3SBjoern A. Zeeb
481*da8fa4e3SBjoern A. Zeeb ret = ath10k_wmi_wow_host_wakeup_ind(ar);
482*da8fa4e3SBjoern A. Zeeb if (ret) {
483*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
484*da8fa4e3SBjoern A. Zeeb ret);
485*da8fa4e3SBjoern A. Zeeb return ret;
486*da8fa4e3SBjoern A. Zeeb }
487*da8fa4e3SBjoern A. Zeeb
488*da8fa4e3SBjoern A. Zeeb ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
489*da8fa4e3SBjoern A. Zeeb if (ret == 0) {
490*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
491*da8fa4e3SBjoern A. Zeeb return -ETIMEDOUT;
492*da8fa4e3SBjoern A. Zeeb }
493*da8fa4e3SBjoern A. Zeeb
494*da8fa4e3SBjoern A. Zeeb return 0;
495*da8fa4e3SBjoern A. Zeeb }
496*da8fa4e3SBjoern A. Zeeb
ath10k_wow_op_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)497*da8fa4e3SBjoern A. Zeeb int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
498*da8fa4e3SBjoern A. Zeeb struct cfg80211_wowlan *wowlan)
499*da8fa4e3SBjoern A. Zeeb {
500*da8fa4e3SBjoern A. Zeeb struct ath10k *ar = hw->priv;
501*da8fa4e3SBjoern A. Zeeb int ret;
502*da8fa4e3SBjoern A. Zeeb
503*da8fa4e3SBjoern A. Zeeb mutex_lock(&ar->conf_mutex);
504*da8fa4e3SBjoern A. Zeeb
505*da8fa4e3SBjoern A. Zeeb if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
506*da8fa4e3SBjoern A. Zeeb ar->running_fw->fw_file.fw_features))) {
507*da8fa4e3SBjoern A. Zeeb ret = 1;
508*da8fa4e3SBjoern A. Zeeb goto exit;
509*da8fa4e3SBjoern A. Zeeb }
510*da8fa4e3SBjoern A. Zeeb
511*da8fa4e3SBjoern A. Zeeb ret = ath10k_wow_cleanup(ar);
512*da8fa4e3SBjoern A. Zeeb if (ret) {
513*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
514*da8fa4e3SBjoern A. Zeeb ret);
515*da8fa4e3SBjoern A. Zeeb goto exit;
516*da8fa4e3SBjoern A. Zeeb }
517*da8fa4e3SBjoern A. Zeeb
518*da8fa4e3SBjoern A. Zeeb ret = ath10k_wow_set_wakeups(ar, wowlan);
519*da8fa4e3SBjoern A. Zeeb if (ret) {
520*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
521*da8fa4e3SBjoern A. Zeeb ret);
522*da8fa4e3SBjoern A. Zeeb goto cleanup;
523*da8fa4e3SBjoern A. Zeeb }
524*da8fa4e3SBjoern A. Zeeb
525*da8fa4e3SBjoern A. Zeeb ath10k_mac_wait_tx_complete(ar);
526*da8fa4e3SBjoern A. Zeeb
527*da8fa4e3SBjoern A. Zeeb ret = ath10k_wow_enable(ar);
528*da8fa4e3SBjoern A. Zeeb if (ret) {
529*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to start wow: %d\n", ret);
530*da8fa4e3SBjoern A. Zeeb goto cleanup;
531*da8fa4e3SBjoern A. Zeeb }
532*da8fa4e3SBjoern A. Zeeb
533*da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_suspend(ar);
534*da8fa4e3SBjoern A. Zeeb if (ret) {
535*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
536*da8fa4e3SBjoern A. Zeeb goto wakeup;
537*da8fa4e3SBjoern A. Zeeb }
538*da8fa4e3SBjoern A. Zeeb
539*da8fa4e3SBjoern A. Zeeb goto exit;
540*da8fa4e3SBjoern A. Zeeb
541*da8fa4e3SBjoern A. Zeeb wakeup:
542*da8fa4e3SBjoern A. Zeeb ath10k_wow_wakeup(ar);
543*da8fa4e3SBjoern A. Zeeb
544*da8fa4e3SBjoern A. Zeeb cleanup:
545*da8fa4e3SBjoern A. Zeeb ath10k_wow_cleanup(ar);
546*da8fa4e3SBjoern A. Zeeb
547*da8fa4e3SBjoern A. Zeeb exit:
548*da8fa4e3SBjoern A. Zeeb mutex_unlock(&ar->conf_mutex);
549*da8fa4e3SBjoern A. Zeeb return ret ? 1 : 0;
550*da8fa4e3SBjoern A. Zeeb }
551*da8fa4e3SBjoern A. Zeeb
ath10k_wow_op_set_wakeup(struct ieee80211_hw * hw,bool enabled)552*da8fa4e3SBjoern A. Zeeb void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
553*da8fa4e3SBjoern A. Zeeb {
554*da8fa4e3SBjoern A. Zeeb struct ath10k *ar = hw->priv;
555*da8fa4e3SBjoern A. Zeeb
556*da8fa4e3SBjoern A. Zeeb mutex_lock(&ar->conf_mutex);
557*da8fa4e3SBjoern A. Zeeb if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
558*da8fa4e3SBjoern A. Zeeb ar->running_fw->fw_file.fw_features)) {
559*da8fa4e3SBjoern A. Zeeb device_set_wakeup_enable(ar->dev, enabled);
560*da8fa4e3SBjoern A. Zeeb }
561*da8fa4e3SBjoern A. Zeeb mutex_unlock(&ar->conf_mutex);
562*da8fa4e3SBjoern A. Zeeb }
563*da8fa4e3SBjoern A. Zeeb
ath10k_wow_op_resume(struct ieee80211_hw * hw)564*da8fa4e3SBjoern A. Zeeb int ath10k_wow_op_resume(struct ieee80211_hw *hw)
565*da8fa4e3SBjoern A. Zeeb {
566*da8fa4e3SBjoern A. Zeeb struct ath10k *ar = hw->priv;
567*da8fa4e3SBjoern A. Zeeb int ret;
568*da8fa4e3SBjoern A. Zeeb
569*da8fa4e3SBjoern A. Zeeb mutex_lock(&ar->conf_mutex);
570*da8fa4e3SBjoern A. Zeeb
571*da8fa4e3SBjoern A. Zeeb if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
572*da8fa4e3SBjoern A. Zeeb ar->running_fw->fw_file.fw_features))) {
573*da8fa4e3SBjoern A. Zeeb ret = 1;
574*da8fa4e3SBjoern A. Zeeb goto exit;
575*da8fa4e3SBjoern A. Zeeb }
576*da8fa4e3SBjoern A. Zeeb
577*da8fa4e3SBjoern A. Zeeb ret = ath10k_hif_resume(ar);
578*da8fa4e3SBjoern A. Zeeb if (ret) {
579*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to resume hif: %d\n", ret);
580*da8fa4e3SBjoern A. Zeeb goto exit;
581*da8fa4e3SBjoern A. Zeeb }
582*da8fa4e3SBjoern A. Zeeb
583*da8fa4e3SBjoern A. Zeeb ret = ath10k_wow_wakeup(ar);
584*da8fa4e3SBjoern A. Zeeb if (ret)
585*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
586*da8fa4e3SBjoern A. Zeeb
587*da8fa4e3SBjoern A. Zeeb ret = ath10k_wow_nlo_cleanup(ar);
588*da8fa4e3SBjoern A. Zeeb if (ret)
589*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret);
590*da8fa4e3SBjoern A. Zeeb
591*da8fa4e3SBjoern A. Zeeb exit:
592*da8fa4e3SBjoern A. Zeeb if (ret) {
593*da8fa4e3SBjoern A. Zeeb switch (ar->state) {
594*da8fa4e3SBjoern A. Zeeb case ATH10K_STATE_ON:
595*da8fa4e3SBjoern A. Zeeb ar->state = ATH10K_STATE_RESTARTING;
596*da8fa4e3SBjoern A. Zeeb ret = 1;
597*da8fa4e3SBjoern A. Zeeb break;
598*da8fa4e3SBjoern A. Zeeb case ATH10K_STATE_OFF:
599*da8fa4e3SBjoern A. Zeeb case ATH10K_STATE_RESTARTING:
600*da8fa4e3SBjoern A. Zeeb case ATH10K_STATE_RESTARTED:
601*da8fa4e3SBjoern A. Zeeb case ATH10K_STATE_UTF:
602*da8fa4e3SBjoern A. Zeeb case ATH10K_STATE_WEDGED:
603*da8fa4e3SBjoern A. Zeeb ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
604*da8fa4e3SBjoern A. Zeeb ar->state);
605*da8fa4e3SBjoern A. Zeeb ret = -EIO;
606*da8fa4e3SBjoern A. Zeeb break;
607*da8fa4e3SBjoern A. Zeeb }
608*da8fa4e3SBjoern A. Zeeb }
609*da8fa4e3SBjoern A. Zeeb
610*da8fa4e3SBjoern A. Zeeb mutex_unlock(&ar->conf_mutex);
611*da8fa4e3SBjoern A. Zeeb return ret;
612*da8fa4e3SBjoern A. Zeeb }
613*da8fa4e3SBjoern A. Zeeb
ath10k_wow_init(struct ath10k * ar)614*da8fa4e3SBjoern A. Zeeb int ath10k_wow_init(struct ath10k *ar)
615*da8fa4e3SBjoern A. Zeeb {
616*da8fa4e3SBjoern A. Zeeb if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
617*da8fa4e3SBjoern A. Zeeb ar->running_fw->fw_file.fw_features))
618*da8fa4e3SBjoern A. Zeeb return 0;
619*da8fa4e3SBjoern A. Zeeb
620*da8fa4e3SBjoern A. Zeeb if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
621*da8fa4e3SBjoern A. Zeeb return -EINVAL;
622*da8fa4e3SBjoern A. Zeeb
623*da8fa4e3SBjoern A. Zeeb ar->wow.wowlan_support = ath10k_wowlan_support;
624*da8fa4e3SBjoern A. Zeeb
625*da8fa4e3SBjoern A. Zeeb if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
626*da8fa4e3SBjoern A. Zeeb ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
627*da8fa4e3SBjoern A. Zeeb ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
628*da8fa4e3SBjoern A. Zeeb }
629*da8fa4e3SBjoern A. Zeeb
630*da8fa4e3SBjoern A. Zeeb if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
631*da8fa4e3SBjoern A. Zeeb ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
632*da8fa4e3SBjoern A. Zeeb ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
633*da8fa4e3SBjoern A. Zeeb }
634*da8fa4e3SBjoern A. Zeeb
635*da8fa4e3SBjoern A. Zeeb ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
636*da8fa4e3SBjoern A. Zeeb ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
637*da8fa4e3SBjoern A. Zeeb
638*da8fa4e3SBjoern A. Zeeb device_set_wakeup_capable(ar->dev, true);
639*da8fa4e3SBjoern A. Zeeb
640*da8fa4e3SBjoern A. Zeeb return 0;
641*da8fa4e3SBjoern A. Zeeb }
642