xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/wow.c (revision ce834e280f2f8753ab0432fb5b1912547abc5330)
15fd3ac3cSJanusz Dziedzic /*
28b1083d6SKalle Valo  * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
3fa3440faSWen Gong  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
45fd3ac3cSJanusz Dziedzic  *
55fd3ac3cSJanusz Dziedzic  * Permission to use, copy, modify, and/or distribute this software for any
65fd3ac3cSJanusz Dziedzic  * purpose with or without fee is hereby granted, provided that the above
75fd3ac3cSJanusz Dziedzic  * copyright notice and this permission notice appear in all copies.
85fd3ac3cSJanusz Dziedzic  *
95fd3ac3cSJanusz Dziedzic  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
105fd3ac3cSJanusz Dziedzic  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
115fd3ac3cSJanusz Dziedzic  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
125fd3ac3cSJanusz Dziedzic  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
135fd3ac3cSJanusz Dziedzic  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
145fd3ac3cSJanusz Dziedzic  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
155fd3ac3cSJanusz Dziedzic  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165fd3ac3cSJanusz Dziedzic  */
175fd3ac3cSJanusz Dziedzic 
185fd3ac3cSJanusz Dziedzic #include "mac.h"
195fd3ac3cSJanusz Dziedzic 
205fd3ac3cSJanusz Dziedzic #include <net/mac80211.h>
215fd3ac3cSJanusz Dziedzic #include "hif.h"
225fd3ac3cSJanusz Dziedzic #include "core.h"
235fd3ac3cSJanusz Dziedzic #include "debug.h"
245fd3ac3cSJanusz Dziedzic #include "wmi.h"
255fd3ac3cSJanusz Dziedzic #include "wmi-ops.h"
265fd3ac3cSJanusz Dziedzic 
275fd3ac3cSJanusz Dziedzic static const struct wiphy_wowlan_support ath10k_wowlan_support = {
285fd3ac3cSJanusz Dziedzic 	.flags = WIPHY_WOWLAN_DISCONNECT |
295fd3ac3cSJanusz Dziedzic 		 WIPHY_WOWLAN_MAGIC_PKT,
3025c86619SJanusz Dziedzic 	.pattern_min_len = WOW_MIN_PATTERN_SIZE,
3125c86619SJanusz Dziedzic 	.pattern_max_len = WOW_MAX_PATTERN_SIZE,
3225c86619SJanusz Dziedzic 	.max_pkt_offset = WOW_MAX_PKT_OFFSET,
335fd3ac3cSJanusz Dziedzic };
345fd3ac3cSJanusz Dziedzic 
355fd3ac3cSJanusz Dziedzic static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
365fd3ac3cSJanusz Dziedzic {
375fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
385fd3ac3cSJanusz Dziedzic 	int i, ret;
395fd3ac3cSJanusz Dziedzic 
405fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
415fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
425fd3ac3cSJanusz Dziedzic 		if (ret) {
435fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
445fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
455fd3ac3cSJanusz Dziedzic 			return ret;
465fd3ac3cSJanusz Dziedzic 		}
475fd3ac3cSJanusz Dziedzic 	}
485fd3ac3cSJanusz Dziedzic 
4925c86619SJanusz Dziedzic 	for (i = 0; i < ar->wow.max_num_patterns; i++) {
5025c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
5125c86619SJanusz Dziedzic 		if (ret) {
5225c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
5325c86619SJanusz Dziedzic 				    i, arvif->vdev_id, ret);
5425c86619SJanusz Dziedzic 			return ret;
5525c86619SJanusz Dziedzic 		}
5625c86619SJanusz Dziedzic 	}
5725c86619SJanusz Dziedzic 
585fd3ac3cSJanusz Dziedzic 	return 0;
595fd3ac3cSJanusz Dziedzic }
605fd3ac3cSJanusz Dziedzic 
615fd3ac3cSJanusz Dziedzic static int ath10k_wow_cleanup(struct ath10k *ar)
625fd3ac3cSJanusz Dziedzic {
635fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
645fd3ac3cSJanusz Dziedzic 	int ret;
655fd3ac3cSJanusz Dziedzic 
665fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
675fd3ac3cSJanusz Dziedzic 
685fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
695fd3ac3cSJanusz Dziedzic 		ret = ath10k_wow_vif_cleanup(arvif);
705fd3ac3cSJanusz Dziedzic 		if (ret) {
715fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
725fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
735fd3ac3cSJanusz Dziedzic 			return ret;
745fd3ac3cSJanusz Dziedzic 		}
755fd3ac3cSJanusz Dziedzic 	}
765fd3ac3cSJanusz Dziedzic 
775fd3ac3cSJanusz Dziedzic 	return 0;
785fd3ac3cSJanusz Dziedzic }
795fd3ac3cSJanusz Dziedzic 
80fa3440faSWen Gong /**
81fa3440faSWen Gong  * Convert a 802.3 format to a 802.11 format.
82fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
83fa3440faSWen Gong  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
84fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
85fa3440faSWen Gong  *                |__         |_______    |____________  |________
86fa3440faSWen Gong  *                   |                |                |          |
87fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
88fa3440faSWen Gong  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
89fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
90fa3440faSWen Gong  */
91fa3440faSWen Gong static void ath10k_wow_convert_8023_to_80211
92fa3440faSWen Gong 					(struct cfg80211_pkt_pattern *new,
93fa3440faSWen Gong 					const struct cfg80211_pkt_pattern *old)
94fa3440faSWen Gong {
95fa3440faSWen Gong 	u8 hdr_8023_pattern[ETH_HLEN] = {};
96fa3440faSWen Gong 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
97fa3440faSWen Gong 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
98fa3440faSWen Gong 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
99fa3440faSWen Gong 
100fa3440faSWen Gong 	int total_len = old->pkt_offset + old->pattern_len;
101fa3440faSWen Gong 	int hdr_80211_end_offset;
102fa3440faSWen Gong 
103fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_pattern =
104fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
105fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_mask =
106fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
107fa3440faSWen Gong 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
108fa3440faSWen Gong 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
109fa3440faSWen Gong 	int hdr_len = sizeof(*new_hdr_pattern);
110fa3440faSWen Gong 
111fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_pattern =
112fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
113fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_mask =
114fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
115fa3440faSWen Gong 	int rfc_len = sizeof(*new_rfc_pattern);
116fa3440faSWen Gong 
117fa3440faSWen Gong 	memcpy(hdr_8023_pattern + old->pkt_offset,
118fa3440faSWen Gong 	       old->pattern, ETH_HLEN - old->pkt_offset);
119fa3440faSWen Gong 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
120fa3440faSWen Gong 	       old->mask, ETH_HLEN - old->pkt_offset);
121fa3440faSWen Gong 
122fa3440faSWen Gong 	/* Copy destination address */
123fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
124fa3440faSWen Gong 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
125fa3440faSWen Gong 
126fa3440faSWen Gong 	/* Copy source address */
127fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
128fa3440faSWen Gong 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
129fa3440faSWen Gong 
130fa3440faSWen Gong 	/* Copy logic link type */
131fa3440faSWen Gong 	memcpy(&new_rfc_pattern->snap_type,
132fa3440faSWen Gong 	       &old_hdr_pattern->h_proto,
133fa3440faSWen Gong 	       sizeof(old_hdr_pattern->h_proto));
134fa3440faSWen Gong 	memcpy(&new_rfc_mask->snap_type,
135fa3440faSWen Gong 	       &old_hdr_mask->h_proto,
136fa3440faSWen Gong 	       sizeof(old_hdr_mask->h_proto));
137fa3440faSWen Gong 
138fa3440faSWen Gong 	/* Caculate new pkt_offset */
139fa3440faSWen Gong 	if (old->pkt_offset < ETH_ALEN)
140fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
141fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
142fa3440faSWen Gong 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
143fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
144fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3) -
145fa3440faSWen Gong 			offsetof(struct ethhdr, h_source);
146fa3440faSWen Gong 	else
147fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
148fa3440faSWen Gong 
149fa3440faSWen Gong 	/* Caculate new hdr end offset */
150fa3440faSWen Gong 	if (total_len > ETH_HLEN)
151fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len;
152fa3440faSWen Gong 	else if (total_len > offsetof(struct ethhdr, h_proto))
153fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
154fa3440faSWen Gong 	else if (total_len > ETH_ALEN)
155fa3440faSWen Gong 		hdr_80211_end_offset = total_len - ETH_ALEN +
156fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3);
157fa3440faSWen Gong 	else
158fa3440faSWen Gong 		hdr_80211_end_offset = total_len +
159fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
160fa3440faSWen Gong 
161fa3440faSWen Gong 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
162fa3440faSWen Gong 
163fa3440faSWen Gong 	memcpy((u8 *)new->pattern,
164fa3440faSWen Gong 	       hdr_80211_pattern + new->pkt_offset,
165fa3440faSWen Gong 	       new->pattern_len);
166fa3440faSWen Gong 	memcpy((u8 *)new->mask,
167fa3440faSWen Gong 	       hdr_80211_bit_mask + new->pkt_offset,
168fa3440faSWen Gong 	       new->pattern_len);
169fa3440faSWen Gong 
170fa3440faSWen Gong 	if (total_len > ETH_HLEN) {
171fa3440faSWen Gong 		/* Copy frame body */
172fa3440faSWen Gong 		memcpy((u8 *)new->pattern + new->pattern_len,
173fa3440faSWen Gong 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
174fa3440faSWen Gong 		       total_len - ETH_HLEN);
175fa3440faSWen Gong 		memcpy((u8 *)new->mask + new->pattern_len,
176fa3440faSWen Gong 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
177fa3440faSWen Gong 		       total_len - ETH_HLEN);
178fa3440faSWen Gong 
179fa3440faSWen Gong 		new->pattern_len += total_len - ETH_HLEN;
180fa3440faSWen Gong 	}
181fa3440faSWen Gong }
182fa3440faSWen Gong 
183*ce834e28SWen Gong static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id,
184*ce834e28SWen Gong 				struct cfg80211_sched_scan_request *nd_config,
185*ce834e28SWen Gong 				struct wmi_pno_scan_req *pno)
186*ce834e28SWen Gong {
187*ce834e28SWen Gong 	int i, j, ret = 0;
188*ce834e28SWen Gong 	u8 ssid_len;
189*ce834e28SWen Gong 
190*ce834e28SWen Gong 	pno->enable = 1;
191*ce834e28SWen Gong 	pno->vdev_id = vdev_id;
192*ce834e28SWen Gong 	pno->uc_networks_count = nd_config->n_match_sets;
193*ce834e28SWen Gong 
194*ce834e28SWen Gong 	if (!pno->uc_networks_count ||
195*ce834e28SWen Gong 	    pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
196*ce834e28SWen Gong 		return -EINVAL;
197*ce834e28SWen Gong 
198*ce834e28SWen Gong 	if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
199*ce834e28SWen Gong 		return -EINVAL;
200*ce834e28SWen Gong 
201*ce834e28SWen Gong 	/* Filling per profile  params */
202*ce834e28SWen Gong 	for (i = 0; i < pno->uc_networks_count; i++) {
203*ce834e28SWen Gong 		ssid_len = nd_config->match_sets[i].ssid.ssid_len;
204*ce834e28SWen Gong 
205*ce834e28SWen Gong 		if (ssid_len == 0 || ssid_len > 32)
206*ce834e28SWen Gong 			return -EINVAL;
207*ce834e28SWen Gong 
208*ce834e28SWen Gong 		pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len);
209*ce834e28SWen Gong 
210*ce834e28SWen Gong 		memcpy(pno->a_networks[i].ssid.ssid,
211*ce834e28SWen Gong 		       nd_config->match_sets[i].ssid.ssid,
212*ce834e28SWen Gong 		       nd_config->match_sets[i].ssid.ssid_len);
213*ce834e28SWen Gong 		pno->a_networks[i].authentication = 0;
214*ce834e28SWen Gong 		pno->a_networks[i].encryption     = 0;
215*ce834e28SWen Gong 		pno->a_networks[i].bcast_nw_type  = 0;
216*ce834e28SWen Gong 
217*ce834e28SWen Gong 		/*Copying list of valid channel into request */
218*ce834e28SWen Gong 		pno->a_networks[i].channel_count = nd_config->n_channels;
219*ce834e28SWen Gong 		pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
220*ce834e28SWen Gong 
221*ce834e28SWen Gong 		for (j = 0; j < nd_config->n_channels; j++) {
222*ce834e28SWen Gong 			pno->a_networks[i].channels[j] =
223*ce834e28SWen Gong 					nd_config->channels[j]->center_freq;
224*ce834e28SWen Gong 		}
225*ce834e28SWen Gong 	}
226*ce834e28SWen Gong 
227*ce834e28SWen Gong 	/* set scan to passive if no SSIDs are specified in the request */
228*ce834e28SWen Gong 	if (nd_config->n_ssids == 0)
229*ce834e28SWen Gong 		pno->do_passive_scan = true;
230*ce834e28SWen Gong 	else
231*ce834e28SWen Gong 		pno->do_passive_scan = false;
232*ce834e28SWen Gong 
233*ce834e28SWen Gong 	for (i = 0; i < nd_config->n_ssids; i++) {
234*ce834e28SWen Gong 		j = 0;
235*ce834e28SWen Gong 		while (j < pno->uc_networks_count) {
236*ce834e28SWen Gong 			if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) ==
237*ce834e28SWen Gong 				nd_config->ssids[i].ssid_len &&
238*ce834e28SWen Gong 			(memcmp(pno->a_networks[j].ssid.ssid,
239*ce834e28SWen Gong 				nd_config->ssids[i].ssid,
240*ce834e28SWen Gong 				__le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) {
241*ce834e28SWen Gong 				pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
242*ce834e28SWen Gong 				break;
243*ce834e28SWen Gong 			}
244*ce834e28SWen Gong 			j++;
245*ce834e28SWen Gong 		}
246*ce834e28SWen Gong 	}
247*ce834e28SWen Gong 
248*ce834e28SWen Gong 	if (nd_config->n_scan_plans == 2) {
249*ce834e28SWen Gong 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
250*ce834e28SWen Gong 		pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
251*ce834e28SWen Gong 		pno->slow_scan_period =
252*ce834e28SWen Gong 			nd_config->scan_plans[1].interval * MSEC_PER_SEC;
253*ce834e28SWen Gong 	} else if (nd_config->n_scan_plans == 1) {
254*ce834e28SWen Gong 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
255*ce834e28SWen Gong 		pno->fast_scan_max_cycles = 1;
256*ce834e28SWen Gong 		pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
257*ce834e28SWen Gong 	} else {
258*ce834e28SWen Gong 		ath10k_warn(ar, "Invalid number of scan plans %d !!",
259*ce834e28SWen Gong 			    nd_config->n_scan_plans);
260*ce834e28SWen Gong 	}
261*ce834e28SWen Gong 
262*ce834e28SWen Gong 	if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
263*ce834e28SWen Gong 		/* enable mac randomization */
264*ce834e28SWen Gong 		pno->enable_pno_scan_randomization = 1;
265*ce834e28SWen Gong 		memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
266*ce834e28SWen Gong 		memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
267*ce834e28SWen Gong 	}
268*ce834e28SWen Gong 
269*ce834e28SWen Gong 	pno->delay_start_time = nd_config->delay;
270*ce834e28SWen Gong 
271*ce834e28SWen Gong 	/* Current FW does not support min-max range for dwell time */
272*ce834e28SWen Gong 	pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
273*ce834e28SWen Gong 	pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
274*ce834e28SWen Gong 	return ret;
275*ce834e28SWen Gong }
276*ce834e28SWen Gong 
2775fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
2785fd3ac3cSJanusz Dziedzic 				      struct cfg80211_wowlan *wowlan)
2795fd3ac3cSJanusz Dziedzic {
2805fd3ac3cSJanusz Dziedzic 	int ret, i;
2815fd3ac3cSJanusz Dziedzic 	unsigned long wow_mask = 0;
2825fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
28325c86619SJanusz Dziedzic 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
28425c86619SJanusz Dziedzic 	int pattern_id = 0;
2855fd3ac3cSJanusz Dziedzic 
2865fd3ac3cSJanusz Dziedzic 	/* Setup requested WOW features */
2875fd3ac3cSJanusz Dziedzic 	switch (arvif->vdev_type) {
2885fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_IBSS:
2895fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
2905fd3ac3cSJanusz Dziedzic 		 /* fall through */
2915fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_AP:
2925fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
2935fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
2945fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
2955fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
2965fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
2975fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_HTT_EVENT, &wow_mask);
2985fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
2995fd3ac3cSJanusz Dziedzic 		break;
3005fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_STA:
3015fd3ac3cSJanusz Dziedzic 		if (wowlan->disconnect) {
3025fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
3035fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
3045fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_BMISS_EVENT, &wow_mask);
3055fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
3065fd3ac3cSJanusz Dziedzic 		}
3075fd3ac3cSJanusz Dziedzic 
3085fd3ac3cSJanusz Dziedzic 		if (wowlan->magic_pkt)
3095fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
310*ce834e28SWen Gong 
311*ce834e28SWen Gong 		if (wowlan->nd_config) {
312*ce834e28SWen Gong 			struct wmi_pno_scan_req *pno;
313*ce834e28SWen Gong 			int ret;
314*ce834e28SWen Gong 
315*ce834e28SWen Gong 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
316*ce834e28SWen Gong 			if (!pno)
317*ce834e28SWen Gong 				return -ENOMEM;
318*ce834e28SWen Gong 
319*ce834e28SWen Gong 			ar->nlo_enabled = true;
320*ce834e28SWen Gong 
321*ce834e28SWen Gong 			ret = ath10k_wmi_pno_check(ar, arvif->vdev_id,
322*ce834e28SWen Gong 						   wowlan->nd_config, pno);
323*ce834e28SWen Gong 			if (!ret) {
324*ce834e28SWen Gong 				ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
325*ce834e28SWen Gong 				__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
326*ce834e28SWen Gong 			}
327*ce834e28SWen Gong 
328*ce834e28SWen Gong 			kfree(pno);
329*ce834e28SWen Gong 		}
3305fd3ac3cSJanusz Dziedzic 		break;
3315fd3ac3cSJanusz Dziedzic 	default:
3325fd3ac3cSJanusz Dziedzic 		break;
3335fd3ac3cSJanusz Dziedzic 	}
3345fd3ac3cSJanusz Dziedzic 
33525c86619SJanusz Dziedzic 	for (i = 0; i < wowlan->n_patterns; i++) {
33625c86619SJanusz Dziedzic 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
337fa3440faSWen Gong 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
338fa3440faSWen Gong 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
339fa3440faSWen Gong 		struct cfg80211_pkt_pattern new_pattern = {};
340fa3440faSWen Gong 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
34125c86619SJanusz Dziedzic 		int j;
34225c86619SJanusz Dziedzic 
343fa3440faSWen Gong 		new_pattern.pattern = ath_pattern;
344fa3440faSWen Gong 		new_pattern.mask = ath_bitmask;
34525c86619SJanusz Dziedzic 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
34625c86619SJanusz Dziedzic 			continue;
34725c86619SJanusz Dziedzic 		/* convert bytemask to bitmask */
34825c86619SJanusz Dziedzic 		for (j = 0; j < patterns[i].pattern_len; j++)
34925c86619SJanusz Dziedzic 			if (patterns[i].mask[j / 8] & BIT(j % 8))
35025c86619SJanusz Dziedzic 				bitmask[j] = 0xff;
351fa3440faSWen Gong 		old_pattern.mask = bitmask;
352fa3440faSWen Gong 		new_pattern = old_pattern;
353fa3440faSWen Gong 
354fa3440faSWen Gong 		if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
355fa3440faSWen Gong 			if (patterns[i].pkt_offset < ETH_HLEN)
356fa3440faSWen Gong 				ath10k_wow_convert_8023_to_80211(&new_pattern,
357fa3440faSWen Gong 								 &old_pattern);
358fa3440faSWen Gong 			else
359fa3440faSWen Gong 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
360fa3440faSWen Gong 		}
361fa3440faSWen Gong 
362fa3440faSWen Gong 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
363fa3440faSWen Gong 			return -EINVAL;
36425c86619SJanusz Dziedzic 
36525c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
36625c86619SJanusz Dziedzic 						 pattern_id,
367fa3440faSWen Gong 						 new_pattern.pattern,
368fa3440faSWen Gong 						 new_pattern.mask,
369fa3440faSWen Gong 						 new_pattern.pattern_len,
370fa3440faSWen Gong 						 new_pattern.pkt_offset);
37125c86619SJanusz Dziedzic 		if (ret) {
37225c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
37325c86619SJanusz Dziedzic 				    pattern_id,
37425c86619SJanusz Dziedzic 				    arvif->vdev_id, ret);
37525c86619SJanusz Dziedzic 			return ret;
37625c86619SJanusz Dziedzic 		}
37725c86619SJanusz Dziedzic 
37825c86619SJanusz Dziedzic 		pattern_id++;
37925c86619SJanusz Dziedzic 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
38025c86619SJanusz Dziedzic 	}
38125c86619SJanusz Dziedzic 
3825fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
3835fd3ac3cSJanusz Dziedzic 		if (!test_bit(i, &wow_mask))
3845fd3ac3cSJanusz Dziedzic 			continue;
3855fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
3865fd3ac3cSJanusz Dziedzic 		if (ret) {
3875fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
3885fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
3895fd3ac3cSJanusz Dziedzic 			return ret;
3905fd3ac3cSJanusz Dziedzic 		}
3915fd3ac3cSJanusz Dziedzic 	}
3925fd3ac3cSJanusz Dziedzic 
3935fd3ac3cSJanusz Dziedzic 	return 0;
3945fd3ac3cSJanusz Dziedzic }
3955fd3ac3cSJanusz Dziedzic 
3965fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar,
3975fd3ac3cSJanusz Dziedzic 				  struct cfg80211_wowlan *wowlan)
3985fd3ac3cSJanusz Dziedzic {
3995fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
4005fd3ac3cSJanusz Dziedzic 	int ret;
4015fd3ac3cSJanusz Dziedzic 
4025fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4035fd3ac3cSJanusz Dziedzic 
4045fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
4055fd3ac3cSJanusz Dziedzic 		ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
4065fd3ac3cSJanusz Dziedzic 		if (ret) {
4075fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
4085fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
4095fd3ac3cSJanusz Dziedzic 			return ret;
4105fd3ac3cSJanusz Dziedzic 		}
4115fd3ac3cSJanusz Dziedzic 	}
4125fd3ac3cSJanusz Dziedzic 
4135fd3ac3cSJanusz Dziedzic 	return 0;
4145fd3ac3cSJanusz Dziedzic }
4155fd3ac3cSJanusz Dziedzic 
416*ce834e28SWen Gong static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif)
417*ce834e28SWen Gong {
418*ce834e28SWen Gong 	int ret = 0;
419*ce834e28SWen Gong 	struct ath10k *ar = arvif->ar;
420*ce834e28SWen Gong 
421*ce834e28SWen Gong 	switch (arvif->vdev_type) {
422*ce834e28SWen Gong 	case WMI_VDEV_TYPE_STA:
423*ce834e28SWen Gong 		if (ar->nlo_enabled) {
424*ce834e28SWen Gong 			struct wmi_pno_scan_req *pno;
425*ce834e28SWen Gong 
426*ce834e28SWen Gong 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
427*ce834e28SWen Gong 			if (!pno)
428*ce834e28SWen Gong 				return -ENOMEM;
429*ce834e28SWen Gong 
430*ce834e28SWen Gong 			pno->enable = 0;
431*ce834e28SWen Gong 			ar->nlo_enabled = false;
432*ce834e28SWen Gong 			ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
433*ce834e28SWen Gong 			kfree(pno);
434*ce834e28SWen Gong 		}
435*ce834e28SWen Gong 		break;
436*ce834e28SWen Gong 	default:
437*ce834e28SWen Gong 		break;
438*ce834e28SWen Gong 	}
439*ce834e28SWen Gong 	return ret;
440*ce834e28SWen Gong }
441*ce834e28SWen Gong 
442*ce834e28SWen Gong static int ath10k_wow_nlo_cleanup(struct ath10k *ar)
443*ce834e28SWen Gong {
444*ce834e28SWen Gong 	struct ath10k_vif *arvif;
445*ce834e28SWen Gong 	int ret = 0;
446*ce834e28SWen Gong 
447*ce834e28SWen Gong 	lockdep_assert_held(&ar->conf_mutex);
448*ce834e28SWen Gong 
449*ce834e28SWen Gong 	list_for_each_entry(arvif, &ar->arvifs, list) {
450*ce834e28SWen Gong 		ret = ath10k_vif_wow_clean_nlo(arvif);
451*ce834e28SWen Gong 		if (ret) {
452*ce834e28SWen Gong 			ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n",
453*ce834e28SWen Gong 				    arvif->vdev_id, ret);
454*ce834e28SWen Gong 			return ret;
455*ce834e28SWen Gong 		}
456*ce834e28SWen Gong 	}
457*ce834e28SWen Gong 
458*ce834e28SWen Gong 	return 0;
459*ce834e28SWen Gong }
460*ce834e28SWen Gong 
4615fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar)
4625fd3ac3cSJanusz Dziedzic {
4635fd3ac3cSJanusz Dziedzic 	int ret;
4645fd3ac3cSJanusz Dziedzic 
4655fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4665fd3ac3cSJanusz Dziedzic 
4675fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->target_suspend);
4685fd3ac3cSJanusz Dziedzic 
4695fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_enable(ar);
4705fd3ac3cSJanusz Dziedzic 	if (ret) {
4715fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
4725fd3ac3cSJanusz Dziedzic 		return ret;
4735fd3ac3cSJanusz Dziedzic 	}
4745fd3ac3cSJanusz Dziedzic 
4755fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
4765fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
4775fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for suspend completion\n");
4785fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
4795fd3ac3cSJanusz Dziedzic 	}
4805fd3ac3cSJanusz Dziedzic 
4815fd3ac3cSJanusz Dziedzic 	return 0;
4825fd3ac3cSJanusz Dziedzic }
4835fd3ac3cSJanusz Dziedzic 
4845fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar)
4855fd3ac3cSJanusz Dziedzic {
4865fd3ac3cSJanusz Dziedzic 	int ret;
4875fd3ac3cSJanusz Dziedzic 
4885fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4895fd3ac3cSJanusz Dziedzic 
4905fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->wow.wakeup_completed);
4915fd3ac3cSJanusz Dziedzic 
4925fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_host_wakeup_ind(ar);
4935fd3ac3cSJanusz Dziedzic 	if (ret) {
4945fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
4955fd3ac3cSJanusz Dziedzic 			    ret);
4965fd3ac3cSJanusz Dziedzic 		return ret;
4975fd3ac3cSJanusz Dziedzic 	}
4985fd3ac3cSJanusz Dziedzic 
4995fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
5005fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
5015fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
5025fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
5035fd3ac3cSJanusz Dziedzic 	}
5045fd3ac3cSJanusz Dziedzic 
5055fd3ac3cSJanusz Dziedzic 	return 0;
5065fd3ac3cSJanusz Dziedzic }
5075fd3ac3cSJanusz Dziedzic 
5085fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
5095fd3ac3cSJanusz Dziedzic 			  struct cfg80211_wowlan *wowlan)
5105fd3ac3cSJanusz Dziedzic {
5115fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
5125fd3ac3cSJanusz Dziedzic 	int ret;
5135fd3ac3cSJanusz Dziedzic 
5145fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
5155fd3ac3cSJanusz Dziedzic 
5165fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
517c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
5185fd3ac3cSJanusz Dziedzic 		ret = 1;
5195fd3ac3cSJanusz Dziedzic 		goto exit;
5205fd3ac3cSJanusz Dziedzic 	}
5215fd3ac3cSJanusz Dziedzic 
5225fd3ac3cSJanusz Dziedzic 	ret =  ath10k_wow_cleanup(ar);
5235fd3ac3cSJanusz Dziedzic 	if (ret) {
5245fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
5255fd3ac3cSJanusz Dziedzic 			    ret);
5265fd3ac3cSJanusz Dziedzic 		goto exit;
5275fd3ac3cSJanusz Dziedzic 	}
5285fd3ac3cSJanusz Dziedzic 
5295fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_set_wakeups(ar, wowlan);
5305fd3ac3cSJanusz Dziedzic 	if (ret) {
5315fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
5325fd3ac3cSJanusz Dziedzic 			    ret);
5335fd3ac3cSJanusz Dziedzic 		goto cleanup;
5345fd3ac3cSJanusz Dziedzic 	}
5355fd3ac3cSJanusz Dziedzic 
536828853acSWen Gong 	ath10k_mac_wait_tx_complete(ar);
537828853acSWen Gong 
5385fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_enable(ar);
5395fd3ac3cSJanusz Dziedzic 	if (ret) {
5405fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to start wow: %d\n", ret);
5415fd3ac3cSJanusz Dziedzic 		goto cleanup;
5425fd3ac3cSJanusz Dziedzic 	}
5435fd3ac3cSJanusz Dziedzic 
5445fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_suspend(ar);
5455fd3ac3cSJanusz Dziedzic 	if (ret) {
5465fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
5475fd3ac3cSJanusz Dziedzic 		goto wakeup;
5485fd3ac3cSJanusz Dziedzic 	}
5495fd3ac3cSJanusz Dziedzic 
5505fd3ac3cSJanusz Dziedzic 	goto exit;
5515fd3ac3cSJanusz Dziedzic 
5525fd3ac3cSJanusz Dziedzic wakeup:
5535fd3ac3cSJanusz Dziedzic 	ath10k_wow_wakeup(ar);
5545fd3ac3cSJanusz Dziedzic 
5555fd3ac3cSJanusz Dziedzic cleanup:
5565fd3ac3cSJanusz Dziedzic 	ath10k_wow_cleanup(ar);
5575fd3ac3cSJanusz Dziedzic 
5585fd3ac3cSJanusz Dziedzic exit:
5595fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
5605fd3ac3cSJanusz Dziedzic 	return ret ? 1 : 0;
5615fd3ac3cSJanusz Dziedzic }
5625fd3ac3cSJanusz Dziedzic 
563393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
564393b706cSRyan Hsu {
565393b706cSRyan Hsu 	struct ath10k *ar = hw->priv;
566393b706cSRyan Hsu 
567393b706cSRyan Hsu 	mutex_lock(&ar->conf_mutex);
568393b706cSRyan Hsu 	if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
569393b706cSRyan Hsu 		     ar->running_fw->fw_file.fw_features)) {
570393b706cSRyan Hsu 		device_set_wakeup_enable(ar->dev, enabled);
571393b706cSRyan Hsu 	}
572393b706cSRyan Hsu 	mutex_unlock(&ar->conf_mutex);
573393b706cSRyan Hsu }
574393b706cSRyan Hsu 
5755fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw)
5765fd3ac3cSJanusz Dziedzic {
5775fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
5785fd3ac3cSJanusz Dziedzic 	int ret;
5795fd3ac3cSJanusz Dziedzic 
5805fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
5815fd3ac3cSJanusz Dziedzic 
5825fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
583c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
5845fd3ac3cSJanusz Dziedzic 		ret = 1;
5855fd3ac3cSJanusz Dziedzic 		goto exit;
5865fd3ac3cSJanusz Dziedzic 	}
5875fd3ac3cSJanusz Dziedzic 
5885fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_resume(ar);
5895fd3ac3cSJanusz Dziedzic 	if (ret) {
5905fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to resume hif: %d\n", ret);
5915fd3ac3cSJanusz Dziedzic 		goto exit;
5925fd3ac3cSJanusz Dziedzic 	}
5935fd3ac3cSJanusz Dziedzic 
5945fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_wakeup(ar);
5955fd3ac3cSJanusz Dziedzic 	if (ret)
5965fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
5975fd3ac3cSJanusz Dziedzic 
598*ce834e28SWen Gong 	ret = ath10k_wow_nlo_cleanup(ar);
599*ce834e28SWen Gong 	if (ret)
600*ce834e28SWen Gong 		ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret);
601*ce834e28SWen Gong 
6025fd3ac3cSJanusz Dziedzic exit:
6036f7429c2SMichal Kazior 	if (ret) {
6046f7429c2SMichal Kazior 		switch (ar->state) {
6056f7429c2SMichal Kazior 		case ATH10K_STATE_ON:
6066f7429c2SMichal Kazior 			ar->state = ATH10K_STATE_RESTARTING;
6076f7429c2SMichal Kazior 			ret = 1;
6086f7429c2SMichal Kazior 			break;
6096f7429c2SMichal Kazior 		case ATH10K_STATE_OFF:
6106f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTING:
6116f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTED:
6126f7429c2SMichal Kazior 		case ATH10K_STATE_UTF:
6136f7429c2SMichal Kazior 		case ATH10K_STATE_WEDGED:
6146f7429c2SMichal Kazior 			ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
6156f7429c2SMichal Kazior 				    ar->state);
6166f7429c2SMichal Kazior 			ret = -EIO;
6176f7429c2SMichal Kazior 			break;
6186f7429c2SMichal Kazior 		}
6196f7429c2SMichal Kazior 	}
6206f7429c2SMichal Kazior 
6215fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
6226f7429c2SMichal Kazior 	return ret;
6235fd3ac3cSJanusz Dziedzic }
6245fd3ac3cSJanusz Dziedzic 
6255fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar)
6265fd3ac3cSJanusz Dziedzic {
627c4cdf753SKalle Valo 	if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
628c4cdf753SKalle Valo 		      ar->running_fw->fw_file.fw_features))
6295fd3ac3cSJanusz Dziedzic 		return 0;
6305fd3ac3cSJanusz Dziedzic 
6315fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
6325fd3ac3cSJanusz Dziedzic 		return -EINVAL;
6335fd3ac3cSJanusz Dziedzic 
63425c86619SJanusz Dziedzic 	ar->wow.wowlan_support = ath10k_wowlan_support;
635fa3440faSWen Gong 
636fa3440faSWen Gong 	if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
637fa3440faSWen Gong 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
638fa3440faSWen Gong 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
639fa3440faSWen Gong 	}
640fa3440faSWen Gong 
641*ce834e28SWen Gong 	if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
642*ce834e28SWen Gong 		ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
643*ce834e28SWen Gong 		ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
644*ce834e28SWen Gong 	}
645*ce834e28SWen Gong 
64625c86619SJanusz Dziedzic 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
64725c86619SJanusz Dziedzic 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
6485fd3ac3cSJanusz Dziedzic 
649393b706cSRyan Hsu 	device_set_wakeup_capable(ar->dev, true);
650393b706cSRyan Hsu 
6515fd3ac3cSJanusz Dziedzic 	return 0;
6525fd3ac3cSJanusz Dziedzic }
653