xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/wow.c (revision bdf2bd9aa684511bcb4271f185f735525ca27a70)
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 
80*bdf2bd9aSKalle Valo /*
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  */
91*bdf2bd9aSKalle Valo static void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
92fa3440faSWen Gong 					     const struct cfg80211_pkt_pattern *old)
93fa3440faSWen Gong {
94fa3440faSWen Gong 	u8 hdr_8023_pattern[ETH_HLEN] = {};
95fa3440faSWen Gong 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
96fa3440faSWen Gong 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
97fa3440faSWen Gong 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
98fa3440faSWen Gong 
99fa3440faSWen Gong 	int total_len = old->pkt_offset + old->pattern_len;
100fa3440faSWen Gong 	int hdr_80211_end_offset;
101fa3440faSWen Gong 
102fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_pattern =
103fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
104fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_mask =
105fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
106fa3440faSWen Gong 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
107fa3440faSWen Gong 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
108fa3440faSWen Gong 	int hdr_len = sizeof(*new_hdr_pattern);
109fa3440faSWen Gong 
110fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_pattern =
111fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
112fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_mask =
113fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
114fa3440faSWen Gong 	int rfc_len = sizeof(*new_rfc_pattern);
115fa3440faSWen Gong 
116fa3440faSWen Gong 	memcpy(hdr_8023_pattern + old->pkt_offset,
117fa3440faSWen Gong 	       old->pattern, ETH_HLEN - old->pkt_offset);
118fa3440faSWen Gong 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
119fa3440faSWen Gong 	       old->mask, ETH_HLEN - old->pkt_offset);
120fa3440faSWen Gong 
121fa3440faSWen Gong 	/* Copy destination address */
122fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
123fa3440faSWen Gong 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
124fa3440faSWen Gong 
125fa3440faSWen Gong 	/* Copy source address */
126fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
127fa3440faSWen Gong 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
128fa3440faSWen Gong 
129fa3440faSWen Gong 	/* Copy logic link type */
130fa3440faSWen Gong 	memcpy(&new_rfc_pattern->snap_type,
131fa3440faSWen Gong 	       &old_hdr_pattern->h_proto,
132fa3440faSWen Gong 	       sizeof(old_hdr_pattern->h_proto));
133fa3440faSWen Gong 	memcpy(&new_rfc_mask->snap_type,
134fa3440faSWen Gong 	       &old_hdr_mask->h_proto,
135fa3440faSWen Gong 	       sizeof(old_hdr_mask->h_proto));
136fa3440faSWen Gong 
137c8cb0964SYangtao Li 	/* Calculate new pkt_offset */
138fa3440faSWen Gong 	if (old->pkt_offset < ETH_ALEN)
139fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
140fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
141fa3440faSWen Gong 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
142fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
143fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3) -
144fa3440faSWen Gong 			offsetof(struct ethhdr, h_source);
145fa3440faSWen Gong 	else
146fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
147fa3440faSWen Gong 
148c8cb0964SYangtao Li 	/* Calculate new hdr end offset */
149fa3440faSWen Gong 	if (total_len > ETH_HLEN)
150fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len;
151fa3440faSWen Gong 	else if (total_len > offsetof(struct ethhdr, h_proto))
152fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
153fa3440faSWen Gong 	else if (total_len > ETH_ALEN)
154fa3440faSWen Gong 		hdr_80211_end_offset = total_len - ETH_ALEN +
155fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3);
156fa3440faSWen Gong 	else
157fa3440faSWen Gong 		hdr_80211_end_offset = total_len +
158fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
159fa3440faSWen Gong 
160fa3440faSWen Gong 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
161fa3440faSWen Gong 
162fa3440faSWen Gong 	memcpy((u8 *)new->pattern,
163fa3440faSWen Gong 	       hdr_80211_pattern + new->pkt_offset,
164fa3440faSWen Gong 	       new->pattern_len);
165fa3440faSWen Gong 	memcpy((u8 *)new->mask,
166fa3440faSWen Gong 	       hdr_80211_bit_mask + new->pkt_offset,
167fa3440faSWen Gong 	       new->pattern_len);
168fa3440faSWen Gong 
169fa3440faSWen Gong 	if (total_len > ETH_HLEN) {
170fa3440faSWen Gong 		/* Copy frame body */
171fa3440faSWen Gong 		memcpy((u8 *)new->pattern + new->pattern_len,
172fa3440faSWen Gong 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
173fa3440faSWen Gong 		       total_len - ETH_HLEN);
174fa3440faSWen Gong 		memcpy((u8 *)new->mask + new->pattern_len,
175fa3440faSWen Gong 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
176fa3440faSWen Gong 		       total_len - ETH_HLEN);
177fa3440faSWen Gong 
178fa3440faSWen Gong 		new->pattern_len += total_len - ETH_HLEN;
179fa3440faSWen Gong 	}
180fa3440faSWen Gong }
181fa3440faSWen Gong 
182ce834e28SWen Gong static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id,
183ce834e28SWen Gong 				struct cfg80211_sched_scan_request *nd_config,
184ce834e28SWen Gong 				struct wmi_pno_scan_req *pno)
185ce834e28SWen Gong {
186ce834e28SWen Gong 	int i, j, ret = 0;
187ce834e28SWen Gong 	u8 ssid_len;
188ce834e28SWen Gong 
189ce834e28SWen Gong 	pno->enable = 1;
190ce834e28SWen Gong 	pno->vdev_id = vdev_id;
191ce834e28SWen Gong 	pno->uc_networks_count = nd_config->n_match_sets;
192ce834e28SWen Gong 
193ce834e28SWen Gong 	if (!pno->uc_networks_count ||
194ce834e28SWen Gong 	    pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
195ce834e28SWen Gong 		return -EINVAL;
196ce834e28SWen Gong 
197ce834e28SWen Gong 	if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
198ce834e28SWen Gong 		return -EINVAL;
199ce834e28SWen Gong 
200ce834e28SWen Gong 	/* Filling per profile  params */
201ce834e28SWen Gong 	for (i = 0; i < pno->uc_networks_count; i++) {
202ce834e28SWen Gong 		ssid_len = nd_config->match_sets[i].ssid.ssid_len;
203ce834e28SWen Gong 
204ce834e28SWen Gong 		if (ssid_len == 0 || ssid_len > 32)
205ce834e28SWen Gong 			return -EINVAL;
206ce834e28SWen Gong 
207ce834e28SWen Gong 		pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len);
208ce834e28SWen Gong 
209ce834e28SWen Gong 		memcpy(pno->a_networks[i].ssid.ssid,
210ce834e28SWen Gong 		       nd_config->match_sets[i].ssid.ssid,
211ce834e28SWen Gong 		       nd_config->match_sets[i].ssid.ssid_len);
212ce834e28SWen Gong 		pno->a_networks[i].authentication = 0;
213ce834e28SWen Gong 		pno->a_networks[i].encryption     = 0;
214ce834e28SWen Gong 		pno->a_networks[i].bcast_nw_type  = 0;
215ce834e28SWen Gong 
216ce834e28SWen Gong 		/*Copying list of valid channel into request */
217ce834e28SWen Gong 		pno->a_networks[i].channel_count = nd_config->n_channels;
218ce834e28SWen Gong 		pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
219ce834e28SWen Gong 
220ce834e28SWen Gong 		for (j = 0; j < nd_config->n_channels; j++) {
221ce834e28SWen Gong 			pno->a_networks[i].channels[j] =
222ce834e28SWen Gong 					nd_config->channels[j]->center_freq;
223ce834e28SWen Gong 		}
224ce834e28SWen Gong 	}
225ce834e28SWen Gong 
226ce834e28SWen Gong 	/* set scan to passive if no SSIDs are specified in the request */
227ce834e28SWen Gong 	if (nd_config->n_ssids == 0)
228ce834e28SWen Gong 		pno->do_passive_scan = true;
229ce834e28SWen Gong 	else
230ce834e28SWen Gong 		pno->do_passive_scan = false;
231ce834e28SWen Gong 
232ce834e28SWen Gong 	for (i = 0; i < nd_config->n_ssids; i++) {
233ce834e28SWen Gong 		j = 0;
234ce834e28SWen Gong 		while (j < pno->uc_networks_count) {
235ce834e28SWen Gong 			if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) ==
236ce834e28SWen Gong 				nd_config->ssids[i].ssid_len &&
237ce834e28SWen Gong 			(memcmp(pno->a_networks[j].ssid.ssid,
238ce834e28SWen Gong 				nd_config->ssids[i].ssid,
239ce834e28SWen Gong 				__le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) {
240ce834e28SWen Gong 				pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
241ce834e28SWen Gong 				break;
242ce834e28SWen Gong 			}
243ce834e28SWen Gong 			j++;
244ce834e28SWen Gong 		}
245ce834e28SWen Gong 	}
246ce834e28SWen Gong 
247ce834e28SWen Gong 	if (nd_config->n_scan_plans == 2) {
248ce834e28SWen Gong 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
249ce834e28SWen Gong 		pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
250ce834e28SWen Gong 		pno->slow_scan_period =
251ce834e28SWen Gong 			nd_config->scan_plans[1].interval * MSEC_PER_SEC;
252ce834e28SWen Gong 	} else if (nd_config->n_scan_plans == 1) {
253ce834e28SWen Gong 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
254ce834e28SWen Gong 		pno->fast_scan_max_cycles = 1;
255ce834e28SWen Gong 		pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
256ce834e28SWen Gong 	} else {
257ce834e28SWen Gong 		ath10k_warn(ar, "Invalid number of scan plans %d !!",
258ce834e28SWen Gong 			    nd_config->n_scan_plans);
259ce834e28SWen Gong 	}
260ce834e28SWen Gong 
261ce834e28SWen Gong 	if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
262ce834e28SWen Gong 		/* enable mac randomization */
263ce834e28SWen Gong 		pno->enable_pno_scan_randomization = 1;
264ce834e28SWen Gong 		memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
265ce834e28SWen Gong 		memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
266ce834e28SWen Gong 	}
267ce834e28SWen Gong 
268ce834e28SWen Gong 	pno->delay_start_time = nd_config->delay;
269ce834e28SWen Gong 
270ce834e28SWen Gong 	/* Current FW does not support min-max range for dwell time */
271ce834e28SWen Gong 	pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
272ce834e28SWen Gong 	pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
273ce834e28SWen Gong 	return ret;
274ce834e28SWen Gong }
275ce834e28SWen Gong 
2765fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
2775fd3ac3cSJanusz Dziedzic 				      struct cfg80211_wowlan *wowlan)
2785fd3ac3cSJanusz Dziedzic {
2795fd3ac3cSJanusz Dziedzic 	int ret, i;
2805fd3ac3cSJanusz Dziedzic 	unsigned long wow_mask = 0;
2815fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
28225c86619SJanusz Dziedzic 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
28325c86619SJanusz Dziedzic 	int pattern_id = 0;
2845fd3ac3cSJanusz Dziedzic 
2855fd3ac3cSJanusz Dziedzic 	/* Setup requested WOW features */
2865fd3ac3cSJanusz Dziedzic 	switch (arvif->vdev_type) {
2875fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_IBSS:
2885fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
2895fd3ac3cSJanusz Dziedzic 		 /* fall through */
2905fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_AP:
2915fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
2925fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
2935fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
2945fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
2955fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
2965fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_HTT_EVENT, &wow_mask);
2975fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
2985fd3ac3cSJanusz Dziedzic 		break;
2995fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_STA:
3005fd3ac3cSJanusz Dziedzic 		if (wowlan->disconnect) {
3015fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
3025fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
3035fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_BMISS_EVENT, &wow_mask);
3045fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
3055fd3ac3cSJanusz Dziedzic 		}
3065fd3ac3cSJanusz Dziedzic 
3075fd3ac3cSJanusz Dziedzic 		if (wowlan->magic_pkt)
3085fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
309ce834e28SWen Gong 
310ce834e28SWen Gong 		if (wowlan->nd_config) {
311ce834e28SWen Gong 			struct wmi_pno_scan_req *pno;
312ce834e28SWen Gong 			int ret;
313ce834e28SWen Gong 
314ce834e28SWen Gong 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
315ce834e28SWen Gong 			if (!pno)
316ce834e28SWen Gong 				return -ENOMEM;
317ce834e28SWen Gong 
318ce834e28SWen Gong 			ar->nlo_enabled = true;
319ce834e28SWen Gong 
320ce834e28SWen Gong 			ret = ath10k_wmi_pno_check(ar, arvif->vdev_id,
321ce834e28SWen Gong 						   wowlan->nd_config, pno);
322ce834e28SWen Gong 			if (!ret) {
323ce834e28SWen Gong 				ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
324ce834e28SWen Gong 				__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
325ce834e28SWen Gong 			}
326ce834e28SWen Gong 
327ce834e28SWen Gong 			kfree(pno);
328ce834e28SWen Gong 		}
3295fd3ac3cSJanusz Dziedzic 		break;
3305fd3ac3cSJanusz Dziedzic 	default:
3315fd3ac3cSJanusz Dziedzic 		break;
3325fd3ac3cSJanusz Dziedzic 	}
3335fd3ac3cSJanusz Dziedzic 
33425c86619SJanusz Dziedzic 	for (i = 0; i < wowlan->n_patterns; i++) {
33525c86619SJanusz Dziedzic 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
336fa3440faSWen Gong 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
337fa3440faSWen Gong 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
338fa3440faSWen Gong 		struct cfg80211_pkt_pattern new_pattern = {};
339fa3440faSWen Gong 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
34025c86619SJanusz Dziedzic 		int j;
34125c86619SJanusz Dziedzic 
342fa3440faSWen Gong 		new_pattern.pattern = ath_pattern;
343fa3440faSWen Gong 		new_pattern.mask = ath_bitmask;
34425c86619SJanusz Dziedzic 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
34525c86619SJanusz Dziedzic 			continue;
34625c86619SJanusz Dziedzic 		/* convert bytemask to bitmask */
34725c86619SJanusz Dziedzic 		for (j = 0; j < patterns[i].pattern_len; j++)
34825c86619SJanusz Dziedzic 			if (patterns[i].mask[j / 8] & BIT(j % 8))
34925c86619SJanusz Dziedzic 				bitmask[j] = 0xff;
350fa3440faSWen Gong 		old_pattern.mask = bitmask;
351fa3440faSWen Gong 		new_pattern = old_pattern;
352fa3440faSWen Gong 
353fa3440faSWen Gong 		if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
354fa3440faSWen Gong 			if (patterns[i].pkt_offset < ETH_HLEN)
355fa3440faSWen Gong 				ath10k_wow_convert_8023_to_80211(&new_pattern,
356fa3440faSWen Gong 								 &old_pattern);
357fa3440faSWen Gong 			else
358fa3440faSWen Gong 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
359fa3440faSWen Gong 		}
360fa3440faSWen Gong 
361fa3440faSWen Gong 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
362fa3440faSWen Gong 			return -EINVAL;
36325c86619SJanusz Dziedzic 
36425c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
36525c86619SJanusz Dziedzic 						 pattern_id,
366fa3440faSWen Gong 						 new_pattern.pattern,
367fa3440faSWen Gong 						 new_pattern.mask,
368fa3440faSWen Gong 						 new_pattern.pattern_len,
369fa3440faSWen Gong 						 new_pattern.pkt_offset);
37025c86619SJanusz Dziedzic 		if (ret) {
37125c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
37225c86619SJanusz Dziedzic 				    pattern_id,
37325c86619SJanusz Dziedzic 				    arvif->vdev_id, ret);
37425c86619SJanusz Dziedzic 			return ret;
37525c86619SJanusz Dziedzic 		}
37625c86619SJanusz Dziedzic 
37725c86619SJanusz Dziedzic 		pattern_id++;
37825c86619SJanusz Dziedzic 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
37925c86619SJanusz Dziedzic 	}
38025c86619SJanusz Dziedzic 
3815fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
3825fd3ac3cSJanusz Dziedzic 		if (!test_bit(i, &wow_mask))
3835fd3ac3cSJanusz Dziedzic 			continue;
3845fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
3855fd3ac3cSJanusz Dziedzic 		if (ret) {
3865fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
3875fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
3885fd3ac3cSJanusz Dziedzic 			return ret;
3895fd3ac3cSJanusz Dziedzic 		}
3905fd3ac3cSJanusz Dziedzic 	}
3915fd3ac3cSJanusz Dziedzic 
3925fd3ac3cSJanusz Dziedzic 	return 0;
3935fd3ac3cSJanusz Dziedzic }
3945fd3ac3cSJanusz Dziedzic 
3955fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar,
3965fd3ac3cSJanusz Dziedzic 				  struct cfg80211_wowlan *wowlan)
3975fd3ac3cSJanusz Dziedzic {
3985fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
3995fd3ac3cSJanusz Dziedzic 	int ret;
4005fd3ac3cSJanusz Dziedzic 
4015fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4025fd3ac3cSJanusz Dziedzic 
4035fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
4045fd3ac3cSJanusz Dziedzic 		ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
4055fd3ac3cSJanusz Dziedzic 		if (ret) {
4065fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
4075fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
4085fd3ac3cSJanusz Dziedzic 			return ret;
4095fd3ac3cSJanusz Dziedzic 		}
4105fd3ac3cSJanusz Dziedzic 	}
4115fd3ac3cSJanusz Dziedzic 
4125fd3ac3cSJanusz Dziedzic 	return 0;
4135fd3ac3cSJanusz Dziedzic }
4145fd3ac3cSJanusz Dziedzic 
415ce834e28SWen Gong static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif)
416ce834e28SWen Gong {
417ce834e28SWen Gong 	int ret = 0;
418ce834e28SWen Gong 	struct ath10k *ar = arvif->ar;
419ce834e28SWen Gong 
420ce834e28SWen Gong 	switch (arvif->vdev_type) {
421ce834e28SWen Gong 	case WMI_VDEV_TYPE_STA:
422ce834e28SWen Gong 		if (ar->nlo_enabled) {
423ce834e28SWen Gong 			struct wmi_pno_scan_req *pno;
424ce834e28SWen Gong 
425ce834e28SWen Gong 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
426ce834e28SWen Gong 			if (!pno)
427ce834e28SWen Gong 				return -ENOMEM;
428ce834e28SWen Gong 
429ce834e28SWen Gong 			pno->enable = 0;
430ce834e28SWen Gong 			ar->nlo_enabled = false;
431ce834e28SWen Gong 			ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
432ce834e28SWen Gong 			kfree(pno);
433ce834e28SWen Gong 		}
434ce834e28SWen Gong 		break;
435ce834e28SWen Gong 	default:
436ce834e28SWen Gong 		break;
437ce834e28SWen Gong 	}
438ce834e28SWen Gong 	return ret;
439ce834e28SWen Gong }
440ce834e28SWen Gong 
441ce834e28SWen Gong static int ath10k_wow_nlo_cleanup(struct ath10k *ar)
442ce834e28SWen Gong {
443ce834e28SWen Gong 	struct ath10k_vif *arvif;
444ce834e28SWen Gong 	int ret = 0;
445ce834e28SWen Gong 
446ce834e28SWen Gong 	lockdep_assert_held(&ar->conf_mutex);
447ce834e28SWen Gong 
448ce834e28SWen Gong 	list_for_each_entry(arvif, &ar->arvifs, list) {
449ce834e28SWen Gong 		ret = ath10k_vif_wow_clean_nlo(arvif);
450ce834e28SWen Gong 		if (ret) {
451ce834e28SWen Gong 			ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n",
452ce834e28SWen Gong 				    arvif->vdev_id, ret);
453ce834e28SWen Gong 			return ret;
454ce834e28SWen Gong 		}
455ce834e28SWen Gong 	}
456ce834e28SWen Gong 
457ce834e28SWen Gong 	return 0;
458ce834e28SWen Gong }
459ce834e28SWen Gong 
4605fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar)
4615fd3ac3cSJanusz Dziedzic {
4625fd3ac3cSJanusz Dziedzic 	int ret;
4635fd3ac3cSJanusz Dziedzic 
4645fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4655fd3ac3cSJanusz Dziedzic 
4665fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->target_suspend);
4675fd3ac3cSJanusz Dziedzic 
4685fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_enable(ar);
4695fd3ac3cSJanusz Dziedzic 	if (ret) {
4705fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
4715fd3ac3cSJanusz Dziedzic 		return ret;
4725fd3ac3cSJanusz Dziedzic 	}
4735fd3ac3cSJanusz Dziedzic 
4745fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
4755fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
4765fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for suspend completion\n");
4775fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
4785fd3ac3cSJanusz Dziedzic 	}
4795fd3ac3cSJanusz Dziedzic 
4805fd3ac3cSJanusz Dziedzic 	return 0;
4815fd3ac3cSJanusz Dziedzic }
4825fd3ac3cSJanusz Dziedzic 
4835fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar)
4845fd3ac3cSJanusz Dziedzic {
4855fd3ac3cSJanusz Dziedzic 	int ret;
4865fd3ac3cSJanusz Dziedzic 
4875fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4885fd3ac3cSJanusz Dziedzic 
4895fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->wow.wakeup_completed);
4905fd3ac3cSJanusz Dziedzic 
4915fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_host_wakeup_ind(ar);
4925fd3ac3cSJanusz Dziedzic 	if (ret) {
4935fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
4945fd3ac3cSJanusz Dziedzic 			    ret);
4955fd3ac3cSJanusz Dziedzic 		return ret;
4965fd3ac3cSJanusz Dziedzic 	}
4975fd3ac3cSJanusz Dziedzic 
4985fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
4995fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
5005fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
5015fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
5025fd3ac3cSJanusz Dziedzic 	}
5035fd3ac3cSJanusz Dziedzic 
5045fd3ac3cSJanusz Dziedzic 	return 0;
5055fd3ac3cSJanusz Dziedzic }
5065fd3ac3cSJanusz Dziedzic 
5075fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
5085fd3ac3cSJanusz Dziedzic 			  struct cfg80211_wowlan *wowlan)
5095fd3ac3cSJanusz Dziedzic {
5105fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
5115fd3ac3cSJanusz Dziedzic 	int ret;
5125fd3ac3cSJanusz Dziedzic 
5135fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
5145fd3ac3cSJanusz Dziedzic 
5155fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
516c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
5175fd3ac3cSJanusz Dziedzic 		ret = 1;
5185fd3ac3cSJanusz Dziedzic 		goto exit;
5195fd3ac3cSJanusz Dziedzic 	}
5205fd3ac3cSJanusz Dziedzic 
5215fd3ac3cSJanusz Dziedzic 	ret =  ath10k_wow_cleanup(ar);
5225fd3ac3cSJanusz Dziedzic 	if (ret) {
5235fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
5245fd3ac3cSJanusz Dziedzic 			    ret);
5255fd3ac3cSJanusz Dziedzic 		goto exit;
5265fd3ac3cSJanusz Dziedzic 	}
5275fd3ac3cSJanusz Dziedzic 
5285fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_set_wakeups(ar, wowlan);
5295fd3ac3cSJanusz Dziedzic 	if (ret) {
5305fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
5315fd3ac3cSJanusz Dziedzic 			    ret);
5325fd3ac3cSJanusz Dziedzic 		goto cleanup;
5335fd3ac3cSJanusz Dziedzic 	}
5345fd3ac3cSJanusz Dziedzic 
535828853acSWen Gong 	ath10k_mac_wait_tx_complete(ar);
536828853acSWen Gong 
5375fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_enable(ar);
5385fd3ac3cSJanusz Dziedzic 	if (ret) {
5395fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to start wow: %d\n", ret);
5405fd3ac3cSJanusz Dziedzic 		goto cleanup;
5415fd3ac3cSJanusz Dziedzic 	}
5425fd3ac3cSJanusz Dziedzic 
5435fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_suspend(ar);
5445fd3ac3cSJanusz Dziedzic 	if (ret) {
5455fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
5465fd3ac3cSJanusz Dziedzic 		goto wakeup;
5475fd3ac3cSJanusz Dziedzic 	}
5485fd3ac3cSJanusz Dziedzic 
5495fd3ac3cSJanusz Dziedzic 	goto exit;
5505fd3ac3cSJanusz Dziedzic 
5515fd3ac3cSJanusz Dziedzic wakeup:
5525fd3ac3cSJanusz Dziedzic 	ath10k_wow_wakeup(ar);
5535fd3ac3cSJanusz Dziedzic 
5545fd3ac3cSJanusz Dziedzic cleanup:
5555fd3ac3cSJanusz Dziedzic 	ath10k_wow_cleanup(ar);
5565fd3ac3cSJanusz Dziedzic 
5575fd3ac3cSJanusz Dziedzic exit:
5585fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
5595fd3ac3cSJanusz Dziedzic 	return ret ? 1 : 0;
5605fd3ac3cSJanusz Dziedzic }
5615fd3ac3cSJanusz Dziedzic 
562393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
563393b706cSRyan Hsu {
564393b706cSRyan Hsu 	struct ath10k *ar = hw->priv;
565393b706cSRyan Hsu 
566393b706cSRyan Hsu 	mutex_lock(&ar->conf_mutex);
567393b706cSRyan Hsu 	if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
568393b706cSRyan Hsu 		     ar->running_fw->fw_file.fw_features)) {
569393b706cSRyan Hsu 		device_set_wakeup_enable(ar->dev, enabled);
570393b706cSRyan Hsu 	}
571393b706cSRyan Hsu 	mutex_unlock(&ar->conf_mutex);
572393b706cSRyan Hsu }
573393b706cSRyan Hsu 
5745fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw)
5755fd3ac3cSJanusz Dziedzic {
5765fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
5775fd3ac3cSJanusz Dziedzic 	int ret;
5785fd3ac3cSJanusz Dziedzic 
5795fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
5805fd3ac3cSJanusz Dziedzic 
5815fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
582c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
5835fd3ac3cSJanusz Dziedzic 		ret = 1;
5845fd3ac3cSJanusz Dziedzic 		goto exit;
5855fd3ac3cSJanusz Dziedzic 	}
5865fd3ac3cSJanusz Dziedzic 
5875fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_resume(ar);
5885fd3ac3cSJanusz Dziedzic 	if (ret) {
5895fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to resume hif: %d\n", ret);
5905fd3ac3cSJanusz Dziedzic 		goto exit;
5915fd3ac3cSJanusz Dziedzic 	}
5925fd3ac3cSJanusz Dziedzic 
5935fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_wakeup(ar);
5945fd3ac3cSJanusz Dziedzic 	if (ret)
5955fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
5965fd3ac3cSJanusz Dziedzic 
597ce834e28SWen Gong 	ret = ath10k_wow_nlo_cleanup(ar);
598ce834e28SWen Gong 	if (ret)
599ce834e28SWen Gong 		ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret);
600ce834e28SWen Gong 
6015fd3ac3cSJanusz Dziedzic exit:
6026f7429c2SMichal Kazior 	if (ret) {
6036f7429c2SMichal Kazior 		switch (ar->state) {
6046f7429c2SMichal Kazior 		case ATH10K_STATE_ON:
6056f7429c2SMichal Kazior 			ar->state = ATH10K_STATE_RESTARTING;
6066f7429c2SMichal Kazior 			ret = 1;
6076f7429c2SMichal Kazior 			break;
6086f7429c2SMichal Kazior 		case ATH10K_STATE_OFF:
6096f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTING:
6106f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTED:
6116f7429c2SMichal Kazior 		case ATH10K_STATE_UTF:
6126f7429c2SMichal Kazior 		case ATH10K_STATE_WEDGED:
6136f7429c2SMichal Kazior 			ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
6146f7429c2SMichal Kazior 				    ar->state);
6156f7429c2SMichal Kazior 			ret = -EIO;
6166f7429c2SMichal Kazior 			break;
6176f7429c2SMichal Kazior 		}
6186f7429c2SMichal Kazior 	}
6196f7429c2SMichal Kazior 
6205fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
6216f7429c2SMichal Kazior 	return ret;
6225fd3ac3cSJanusz Dziedzic }
6235fd3ac3cSJanusz Dziedzic 
6245fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar)
6255fd3ac3cSJanusz Dziedzic {
626c4cdf753SKalle Valo 	if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
627c4cdf753SKalle Valo 		      ar->running_fw->fw_file.fw_features))
6285fd3ac3cSJanusz Dziedzic 		return 0;
6295fd3ac3cSJanusz Dziedzic 
6305fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
6315fd3ac3cSJanusz Dziedzic 		return -EINVAL;
6325fd3ac3cSJanusz Dziedzic 
63325c86619SJanusz Dziedzic 	ar->wow.wowlan_support = ath10k_wowlan_support;
634fa3440faSWen Gong 
635fa3440faSWen Gong 	if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
636fa3440faSWen Gong 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
637fa3440faSWen Gong 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
638fa3440faSWen Gong 	}
639fa3440faSWen Gong 
640ce834e28SWen Gong 	if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
641ce834e28SWen Gong 		ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
642ce834e28SWen Gong 		ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
643ce834e28SWen Gong 	}
644ce834e28SWen Gong 
64525c86619SJanusz Dziedzic 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
64625c86619SJanusz Dziedzic 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
6475fd3ac3cSJanusz Dziedzic 
648393b706cSRyan Hsu 	device_set_wakeup_capable(ar->dev, true);
649393b706cSRyan Hsu 
6505fd3ac3cSJanusz Dziedzic 	return 0;
6515fd3ac3cSJanusz Dziedzic }
652