xref: /openbmc/linux/drivers/net/wireless/ath/ath10k/wow.c (revision 9144f784f852f9a125cabe9927b986d909bfa439)
1f0553ca9SKalle Valo // SPDX-License-Identifier: ISC
25fd3ac3cSJanusz Dziedzic /*
38b1083d6SKalle Valo  * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
4fa3440faSWen Gong  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
5*358c36eaSJeff Johnson  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
65fd3ac3cSJanusz Dziedzic  */
75fd3ac3cSJanusz Dziedzic 
85fd3ac3cSJanusz Dziedzic #include "mac.h"
95fd3ac3cSJanusz Dziedzic 
105fd3ac3cSJanusz Dziedzic #include <net/mac80211.h>
115fd3ac3cSJanusz Dziedzic #include "hif.h"
125fd3ac3cSJanusz Dziedzic #include "core.h"
135fd3ac3cSJanusz Dziedzic #include "debug.h"
145fd3ac3cSJanusz Dziedzic #include "wmi.h"
155fd3ac3cSJanusz Dziedzic #include "wmi-ops.h"
165fd3ac3cSJanusz Dziedzic 
175fd3ac3cSJanusz Dziedzic static const struct wiphy_wowlan_support ath10k_wowlan_support = {
185fd3ac3cSJanusz Dziedzic 	.flags = WIPHY_WOWLAN_DISCONNECT |
195fd3ac3cSJanusz Dziedzic 		 WIPHY_WOWLAN_MAGIC_PKT,
2025c86619SJanusz Dziedzic 	.pattern_min_len = WOW_MIN_PATTERN_SIZE,
2125c86619SJanusz Dziedzic 	.pattern_max_len = WOW_MAX_PATTERN_SIZE,
2225c86619SJanusz Dziedzic 	.max_pkt_offset = WOW_MAX_PKT_OFFSET,
235fd3ac3cSJanusz Dziedzic };
245fd3ac3cSJanusz Dziedzic 
ath10k_wow_vif_cleanup(struct ath10k_vif * arvif)255fd3ac3cSJanusz Dziedzic static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
265fd3ac3cSJanusz Dziedzic {
275fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
285fd3ac3cSJanusz Dziedzic 	int i, ret;
295fd3ac3cSJanusz Dziedzic 
305fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
315fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
325fd3ac3cSJanusz Dziedzic 		if (ret) {
335fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
345fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
355fd3ac3cSJanusz Dziedzic 			return ret;
365fd3ac3cSJanusz Dziedzic 		}
375fd3ac3cSJanusz Dziedzic 	}
385fd3ac3cSJanusz Dziedzic 
3925c86619SJanusz Dziedzic 	for (i = 0; i < ar->wow.max_num_patterns; i++) {
4025c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
4125c86619SJanusz Dziedzic 		if (ret) {
4225c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
4325c86619SJanusz Dziedzic 				    i, arvif->vdev_id, ret);
4425c86619SJanusz Dziedzic 			return ret;
4525c86619SJanusz Dziedzic 		}
4625c86619SJanusz Dziedzic 	}
4725c86619SJanusz Dziedzic 
485fd3ac3cSJanusz Dziedzic 	return 0;
495fd3ac3cSJanusz Dziedzic }
505fd3ac3cSJanusz Dziedzic 
ath10k_wow_cleanup(struct ath10k * ar)515fd3ac3cSJanusz Dziedzic static int ath10k_wow_cleanup(struct ath10k *ar)
525fd3ac3cSJanusz Dziedzic {
535fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
545fd3ac3cSJanusz Dziedzic 	int ret;
555fd3ac3cSJanusz Dziedzic 
565fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
575fd3ac3cSJanusz Dziedzic 
585fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
595fd3ac3cSJanusz Dziedzic 		ret = ath10k_wow_vif_cleanup(arvif);
605fd3ac3cSJanusz Dziedzic 		if (ret) {
615fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
625fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
635fd3ac3cSJanusz Dziedzic 			return ret;
645fd3ac3cSJanusz Dziedzic 		}
655fd3ac3cSJanusz Dziedzic 	}
665fd3ac3cSJanusz Dziedzic 
675fd3ac3cSJanusz Dziedzic 	return 0;
685fd3ac3cSJanusz Dziedzic }
695fd3ac3cSJanusz Dziedzic 
70bdf2bd9aSKalle Valo /*
71fa3440faSWen Gong  * Convert a 802.3 format to a 802.11 format.
72fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
73fa3440faSWen Gong  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
74fa3440faSWen Gong  *         +------------+-----------+--------+----------------+
75fa3440faSWen Gong  *                |__         |_______    |____________  |________
76fa3440faSWen Gong  *                   |                |                |          |
77fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
78fa3440faSWen Gong  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
79fa3440faSWen Gong  *         +--+------------+----+-----------+---------------+-----------+
80fa3440faSWen Gong  */
ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern * new,const struct cfg80211_pkt_pattern * old)81bdf2bd9aSKalle Valo static void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
82fa3440faSWen Gong 					     const struct cfg80211_pkt_pattern *old)
83fa3440faSWen Gong {
84fa3440faSWen Gong 	u8 hdr_8023_pattern[ETH_HLEN] = {};
85fa3440faSWen Gong 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
86fa3440faSWen Gong 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
87fa3440faSWen Gong 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
88fa3440faSWen Gong 
89fa3440faSWen Gong 	int total_len = old->pkt_offset + old->pattern_len;
90fa3440faSWen Gong 	int hdr_80211_end_offset;
91fa3440faSWen Gong 
92fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_pattern =
93fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
94fa3440faSWen Gong 	struct ieee80211_hdr_3addr *new_hdr_mask =
95fa3440faSWen Gong 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
96fa3440faSWen Gong 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
97fa3440faSWen Gong 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
98fa3440faSWen Gong 	int hdr_len = sizeof(*new_hdr_pattern);
99fa3440faSWen Gong 
100fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_pattern =
101fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
102fa3440faSWen Gong 	struct rfc1042_hdr *new_rfc_mask =
103fa3440faSWen Gong 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
104fa3440faSWen Gong 	int rfc_len = sizeof(*new_rfc_pattern);
105fa3440faSWen Gong 
106fa3440faSWen Gong 	memcpy(hdr_8023_pattern + old->pkt_offset,
107fa3440faSWen Gong 	       old->pattern, ETH_HLEN - old->pkt_offset);
108fa3440faSWen Gong 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
109fa3440faSWen Gong 	       old->mask, ETH_HLEN - old->pkt_offset);
110fa3440faSWen Gong 
111fa3440faSWen Gong 	/* Copy destination address */
112fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
113fa3440faSWen Gong 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
114fa3440faSWen Gong 
115fa3440faSWen Gong 	/* Copy source address */
116fa3440faSWen Gong 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
117fa3440faSWen Gong 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
118fa3440faSWen Gong 
119fa3440faSWen Gong 	/* Copy logic link type */
120fa3440faSWen Gong 	memcpy(&new_rfc_pattern->snap_type,
121fa3440faSWen Gong 	       &old_hdr_pattern->h_proto,
122fa3440faSWen Gong 	       sizeof(old_hdr_pattern->h_proto));
123fa3440faSWen Gong 	memcpy(&new_rfc_mask->snap_type,
124fa3440faSWen Gong 	       &old_hdr_mask->h_proto,
125fa3440faSWen Gong 	       sizeof(old_hdr_mask->h_proto));
126fa3440faSWen Gong 
127c8cb0964SYangtao Li 	/* Calculate new pkt_offset */
128fa3440faSWen Gong 	if (old->pkt_offset < ETH_ALEN)
129fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
130fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
131fa3440faSWen Gong 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
132fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset +
133fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3) -
134fa3440faSWen Gong 			offsetof(struct ethhdr, h_source);
135fa3440faSWen Gong 	else
136fa3440faSWen Gong 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
137fa3440faSWen Gong 
138c8cb0964SYangtao Li 	/* Calculate new hdr end offset */
139fa3440faSWen Gong 	if (total_len > ETH_HLEN)
140fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len;
141fa3440faSWen Gong 	else if (total_len > offsetof(struct ethhdr, h_proto))
142fa3440faSWen Gong 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
143fa3440faSWen Gong 	else if (total_len > ETH_ALEN)
144fa3440faSWen Gong 		hdr_80211_end_offset = total_len - ETH_ALEN +
145fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr3);
146fa3440faSWen Gong 	else
147fa3440faSWen Gong 		hdr_80211_end_offset = total_len +
148fa3440faSWen Gong 			offsetof(struct ieee80211_hdr_3addr, addr1);
149fa3440faSWen Gong 
150fa3440faSWen Gong 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
151fa3440faSWen Gong 
152fa3440faSWen Gong 	memcpy((u8 *)new->pattern,
153fa3440faSWen Gong 	       hdr_80211_pattern + new->pkt_offset,
154fa3440faSWen Gong 	       new->pattern_len);
155fa3440faSWen Gong 	memcpy((u8 *)new->mask,
156fa3440faSWen Gong 	       hdr_80211_bit_mask + new->pkt_offset,
157fa3440faSWen Gong 	       new->pattern_len);
158fa3440faSWen Gong 
159fa3440faSWen Gong 	if (total_len > ETH_HLEN) {
160fa3440faSWen Gong 		/* Copy frame body */
161fa3440faSWen Gong 		memcpy((u8 *)new->pattern + new->pattern_len,
162fa3440faSWen Gong 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
163fa3440faSWen Gong 		       total_len - ETH_HLEN);
164fa3440faSWen Gong 		memcpy((u8 *)new->mask + new->pattern_len,
165fa3440faSWen Gong 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
166fa3440faSWen Gong 		       total_len - ETH_HLEN);
167fa3440faSWen Gong 
168fa3440faSWen Gong 		new->pattern_len += total_len - ETH_HLEN;
169fa3440faSWen Gong 	}
170fa3440faSWen Gong }
171fa3440faSWen Gong 
ath10k_wmi_pno_check(struct ath10k * ar,u32 vdev_id,struct cfg80211_sched_scan_request * nd_config,struct wmi_pno_scan_req * pno)172ce834e28SWen Gong static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id,
173ce834e28SWen Gong 				struct cfg80211_sched_scan_request *nd_config,
174ce834e28SWen Gong 				struct wmi_pno_scan_req *pno)
175ce834e28SWen Gong {
176ce834e28SWen Gong 	int i, j, ret = 0;
177ce834e28SWen Gong 	u8 ssid_len;
178ce834e28SWen Gong 
179ce834e28SWen Gong 	pno->enable = 1;
180ce834e28SWen Gong 	pno->vdev_id = vdev_id;
181ce834e28SWen Gong 	pno->uc_networks_count = nd_config->n_match_sets;
182ce834e28SWen Gong 
183ce834e28SWen Gong 	if (!pno->uc_networks_count ||
184ce834e28SWen Gong 	    pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
185ce834e28SWen Gong 		return -EINVAL;
186ce834e28SWen Gong 
187ce834e28SWen Gong 	if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
188ce834e28SWen Gong 		return -EINVAL;
189ce834e28SWen Gong 
190ce834e28SWen Gong 	/* Filling per profile  params */
191ce834e28SWen Gong 	for (i = 0; i < pno->uc_networks_count; i++) {
192ce834e28SWen Gong 		ssid_len = nd_config->match_sets[i].ssid.ssid_len;
193ce834e28SWen Gong 
194ce834e28SWen Gong 		if (ssid_len == 0 || ssid_len > 32)
195ce834e28SWen Gong 			return -EINVAL;
196ce834e28SWen Gong 
197ce834e28SWen Gong 		pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len);
198ce834e28SWen Gong 
199ce834e28SWen Gong 		memcpy(pno->a_networks[i].ssid.ssid,
200ce834e28SWen Gong 		       nd_config->match_sets[i].ssid.ssid,
201ce834e28SWen Gong 		       nd_config->match_sets[i].ssid.ssid_len);
202ce834e28SWen Gong 		pno->a_networks[i].authentication = 0;
203ce834e28SWen Gong 		pno->a_networks[i].encryption     = 0;
204ce834e28SWen Gong 		pno->a_networks[i].bcast_nw_type  = 0;
205ce834e28SWen Gong 
206ce834e28SWen Gong 		/*Copying list of valid channel into request */
207ce834e28SWen Gong 		pno->a_networks[i].channel_count = nd_config->n_channels;
208ce834e28SWen Gong 		pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
209ce834e28SWen Gong 
210ce834e28SWen Gong 		for (j = 0; j < nd_config->n_channels; j++) {
211ce834e28SWen Gong 			pno->a_networks[i].channels[j] =
212ce834e28SWen Gong 					nd_config->channels[j]->center_freq;
213ce834e28SWen Gong 		}
214ce834e28SWen Gong 	}
215ce834e28SWen Gong 
216ce834e28SWen Gong 	/* set scan to passive if no SSIDs are specified in the request */
217ce834e28SWen Gong 	if (nd_config->n_ssids == 0)
218ce834e28SWen Gong 		pno->do_passive_scan = true;
219ce834e28SWen Gong 	else
220ce834e28SWen Gong 		pno->do_passive_scan = false;
221ce834e28SWen Gong 
222ce834e28SWen Gong 	for (i = 0; i < nd_config->n_ssids; i++) {
223ce834e28SWen Gong 		j = 0;
224ce834e28SWen Gong 		while (j < pno->uc_networks_count) {
225ce834e28SWen Gong 			if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) ==
226ce834e28SWen Gong 				nd_config->ssids[i].ssid_len &&
227ce834e28SWen Gong 			(memcmp(pno->a_networks[j].ssid.ssid,
228ce834e28SWen Gong 				nd_config->ssids[i].ssid,
229ce834e28SWen Gong 				__le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) {
230ce834e28SWen Gong 				pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
231ce834e28SWen Gong 				break;
232ce834e28SWen Gong 			}
233ce834e28SWen Gong 			j++;
234ce834e28SWen Gong 		}
235ce834e28SWen Gong 	}
236ce834e28SWen Gong 
237ce834e28SWen Gong 	if (nd_config->n_scan_plans == 2) {
238ce834e28SWen Gong 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
239ce834e28SWen Gong 		pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
240ce834e28SWen Gong 		pno->slow_scan_period =
241ce834e28SWen Gong 			nd_config->scan_plans[1].interval * MSEC_PER_SEC;
242ce834e28SWen Gong 	} else if (nd_config->n_scan_plans == 1) {
243ce834e28SWen Gong 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
244ce834e28SWen Gong 		pno->fast_scan_max_cycles = 1;
245ce834e28SWen Gong 		pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
246ce834e28SWen Gong 	} else {
247ce834e28SWen Gong 		ath10k_warn(ar, "Invalid number of scan plans %d !!",
248ce834e28SWen Gong 			    nd_config->n_scan_plans);
249ce834e28SWen Gong 	}
250ce834e28SWen Gong 
251ce834e28SWen Gong 	if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
252ce834e28SWen Gong 		/* enable mac randomization */
253ce834e28SWen Gong 		pno->enable_pno_scan_randomization = 1;
254ce834e28SWen Gong 		memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
255ce834e28SWen Gong 		memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
256ce834e28SWen Gong 	}
257ce834e28SWen Gong 
258ce834e28SWen Gong 	pno->delay_start_time = nd_config->delay;
259ce834e28SWen Gong 
260ce834e28SWen Gong 	/* Current FW does not support min-max range for dwell time */
261ce834e28SWen Gong 	pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
262ce834e28SWen Gong 	pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
263ce834e28SWen Gong 	return ret;
264ce834e28SWen Gong }
265ce834e28SWen Gong 
ath10k_vif_wow_set_wakeups(struct ath10k_vif * arvif,struct cfg80211_wowlan * wowlan)2665fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
2675fd3ac3cSJanusz Dziedzic 				      struct cfg80211_wowlan *wowlan)
2685fd3ac3cSJanusz Dziedzic {
2695fd3ac3cSJanusz Dziedzic 	int ret, i;
2705fd3ac3cSJanusz Dziedzic 	unsigned long wow_mask = 0;
2715fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = arvif->ar;
27225c86619SJanusz Dziedzic 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
27325c86619SJanusz Dziedzic 	int pattern_id = 0;
2745fd3ac3cSJanusz Dziedzic 
2755fd3ac3cSJanusz Dziedzic 	/* Setup requested WOW features */
2765fd3ac3cSJanusz Dziedzic 	switch (arvif->vdev_type) {
2775fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_IBSS:
2785fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
2791885c0f7SGustavo A. R. Silva 		fallthrough;
2805fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_AP:
2815fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
2825fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
2835fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
2845fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
2855fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
2865fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_HTT_EVENT, &wow_mask);
2875fd3ac3cSJanusz Dziedzic 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
2885fd3ac3cSJanusz Dziedzic 		break;
2895fd3ac3cSJanusz Dziedzic 	case WMI_VDEV_TYPE_STA:
2905fd3ac3cSJanusz Dziedzic 		if (wowlan->disconnect) {
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_BMISS_EVENT, &wow_mask);
2945fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
2955fd3ac3cSJanusz Dziedzic 		}
2965fd3ac3cSJanusz Dziedzic 
2975fd3ac3cSJanusz Dziedzic 		if (wowlan->magic_pkt)
2985fd3ac3cSJanusz Dziedzic 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
299ce834e28SWen Gong 
300ce834e28SWen Gong 		if (wowlan->nd_config) {
301ce834e28SWen Gong 			struct wmi_pno_scan_req *pno;
302ce834e28SWen Gong 			int ret;
303ce834e28SWen Gong 
304ce834e28SWen Gong 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
305ce834e28SWen Gong 			if (!pno)
306ce834e28SWen Gong 				return -ENOMEM;
307ce834e28SWen Gong 
308ce834e28SWen Gong 			ar->nlo_enabled = true;
309ce834e28SWen Gong 
310ce834e28SWen Gong 			ret = ath10k_wmi_pno_check(ar, arvif->vdev_id,
311ce834e28SWen Gong 						   wowlan->nd_config, pno);
312ce834e28SWen Gong 			if (!ret) {
313ce834e28SWen Gong 				ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
314ce834e28SWen Gong 				__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
315ce834e28SWen Gong 			}
316ce834e28SWen Gong 
317ce834e28SWen Gong 			kfree(pno);
318ce834e28SWen Gong 		}
3195fd3ac3cSJanusz Dziedzic 		break;
3205fd3ac3cSJanusz Dziedzic 	default:
3215fd3ac3cSJanusz Dziedzic 		break;
3225fd3ac3cSJanusz Dziedzic 	}
3235fd3ac3cSJanusz Dziedzic 
32425c86619SJanusz Dziedzic 	for (i = 0; i < wowlan->n_patterns; i++) {
32525c86619SJanusz Dziedzic 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
326fa3440faSWen Gong 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
327fa3440faSWen Gong 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
328fa3440faSWen Gong 		struct cfg80211_pkt_pattern new_pattern = {};
329fa3440faSWen Gong 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
33025c86619SJanusz Dziedzic 		int j;
33125c86619SJanusz Dziedzic 
332fa3440faSWen Gong 		new_pattern.pattern = ath_pattern;
333fa3440faSWen Gong 		new_pattern.mask = ath_bitmask;
33425c86619SJanusz Dziedzic 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
33525c86619SJanusz Dziedzic 			continue;
33625c86619SJanusz Dziedzic 		/* convert bytemask to bitmask */
33725c86619SJanusz Dziedzic 		for (j = 0; j < patterns[i].pattern_len; j++)
33825c86619SJanusz Dziedzic 			if (patterns[i].mask[j / 8] & BIT(j % 8))
33925c86619SJanusz Dziedzic 				bitmask[j] = 0xff;
340fa3440faSWen Gong 		old_pattern.mask = bitmask;
341fa3440faSWen Gong 
342fa3440faSWen Gong 		if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
343e3fb3d44SWen Gong 			if (patterns[i].pkt_offset < ETH_HLEN) {
344fa3440faSWen Gong 				ath10k_wow_convert_8023_to_80211(&new_pattern,
345fa3440faSWen Gong 								 &old_pattern);
346e3fb3d44SWen Gong 			} else {
347e3fb3d44SWen Gong 				new_pattern = old_pattern;
348fa3440faSWen Gong 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
349fa3440faSWen Gong 			}
350e3fb3d44SWen Gong 		}
351fa3440faSWen Gong 
352fa3440faSWen Gong 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
353fa3440faSWen Gong 			return -EINVAL;
35425c86619SJanusz Dziedzic 
35525c86619SJanusz Dziedzic 		ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
35625c86619SJanusz Dziedzic 						 pattern_id,
357fa3440faSWen Gong 						 new_pattern.pattern,
358fa3440faSWen Gong 						 new_pattern.mask,
359fa3440faSWen Gong 						 new_pattern.pattern_len,
360fa3440faSWen Gong 						 new_pattern.pkt_offset);
36125c86619SJanusz Dziedzic 		if (ret) {
36225c86619SJanusz Dziedzic 			ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
36325c86619SJanusz Dziedzic 				    pattern_id,
36425c86619SJanusz Dziedzic 				    arvif->vdev_id, ret);
36525c86619SJanusz Dziedzic 			return ret;
36625c86619SJanusz Dziedzic 		}
36725c86619SJanusz Dziedzic 
36825c86619SJanusz Dziedzic 		pattern_id++;
36925c86619SJanusz Dziedzic 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
37025c86619SJanusz Dziedzic 	}
37125c86619SJanusz Dziedzic 
3725fd3ac3cSJanusz Dziedzic 	for (i = 0; i < WOW_EVENT_MAX; i++) {
3735fd3ac3cSJanusz Dziedzic 		if (!test_bit(i, &wow_mask))
3745fd3ac3cSJanusz Dziedzic 			continue;
3755fd3ac3cSJanusz Dziedzic 		ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
3765fd3ac3cSJanusz Dziedzic 		if (ret) {
3775fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
3785fd3ac3cSJanusz Dziedzic 				    wow_wakeup_event(i), arvif->vdev_id, ret);
3795fd3ac3cSJanusz Dziedzic 			return ret;
3805fd3ac3cSJanusz Dziedzic 		}
3815fd3ac3cSJanusz Dziedzic 	}
3825fd3ac3cSJanusz Dziedzic 
3835fd3ac3cSJanusz Dziedzic 	return 0;
3845fd3ac3cSJanusz Dziedzic }
3855fd3ac3cSJanusz Dziedzic 
ath10k_wow_set_wakeups(struct ath10k * ar,struct cfg80211_wowlan * wowlan)3865fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar,
3875fd3ac3cSJanusz Dziedzic 				  struct cfg80211_wowlan *wowlan)
3885fd3ac3cSJanusz Dziedzic {
3895fd3ac3cSJanusz Dziedzic 	struct ath10k_vif *arvif;
3905fd3ac3cSJanusz Dziedzic 	int ret;
3915fd3ac3cSJanusz Dziedzic 
3925fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
3935fd3ac3cSJanusz Dziedzic 
3945fd3ac3cSJanusz Dziedzic 	list_for_each_entry(arvif, &ar->arvifs, list) {
3955fd3ac3cSJanusz Dziedzic 		ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
3965fd3ac3cSJanusz Dziedzic 		if (ret) {
3975fd3ac3cSJanusz Dziedzic 			ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
3985fd3ac3cSJanusz Dziedzic 				    arvif->vdev_id, ret);
3995fd3ac3cSJanusz Dziedzic 			return ret;
4005fd3ac3cSJanusz Dziedzic 		}
4015fd3ac3cSJanusz Dziedzic 	}
4025fd3ac3cSJanusz Dziedzic 
4035fd3ac3cSJanusz Dziedzic 	return 0;
4045fd3ac3cSJanusz Dziedzic }
4055fd3ac3cSJanusz Dziedzic 
ath10k_vif_wow_clean_nlo(struct ath10k_vif * arvif)406ce834e28SWen Gong static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif)
407ce834e28SWen Gong {
408ce834e28SWen Gong 	int ret = 0;
409ce834e28SWen Gong 	struct ath10k *ar = arvif->ar;
410ce834e28SWen Gong 
411ce834e28SWen Gong 	switch (arvif->vdev_type) {
412ce834e28SWen Gong 	case WMI_VDEV_TYPE_STA:
413ce834e28SWen Gong 		if (ar->nlo_enabled) {
414ce834e28SWen Gong 			struct wmi_pno_scan_req *pno;
415ce834e28SWen Gong 
416ce834e28SWen Gong 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
417ce834e28SWen Gong 			if (!pno)
418ce834e28SWen Gong 				return -ENOMEM;
419ce834e28SWen Gong 
420ce834e28SWen Gong 			pno->enable = 0;
421ce834e28SWen Gong 			ar->nlo_enabled = false;
422ce834e28SWen Gong 			ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
423ce834e28SWen Gong 			kfree(pno);
424ce834e28SWen Gong 		}
425ce834e28SWen Gong 		break;
426ce834e28SWen Gong 	default:
427ce834e28SWen Gong 		break;
428ce834e28SWen Gong 	}
429ce834e28SWen Gong 	return ret;
430ce834e28SWen Gong }
431ce834e28SWen Gong 
ath10k_wow_nlo_cleanup(struct ath10k * ar)432ce834e28SWen Gong static int ath10k_wow_nlo_cleanup(struct ath10k *ar)
433ce834e28SWen Gong {
434ce834e28SWen Gong 	struct ath10k_vif *arvif;
435ce834e28SWen Gong 	int ret = 0;
436ce834e28SWen Gong 
437ce834e28SWen Gong 	lockdep_assert_held(&ar->conf_mutex);
438ce834e28SWen Gong 
439ce834e28SWen Gong 	list_for_each_entry(arvif, &ar->arvifs, list) {
440ce834e28SWen Gong 		ret = ath10k_vif_wow_clean_nlo(arvif);
441ce834e28SWen Gong 		if (ret) {
442ce834e28SWen Gong 			ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n",
443ce834e28SWen Gong 				    arvif->vdev_id, ret);
444ce834e28SWen Gong 			return ret;
445ce834e28SWen Gong 		}
446ce834e28SWen Gong 	}
447ce834e28SWen Gong 
448ce834e28SWen Gong 	return 0;
449ce834e28SWen Gong }
450ce834e28SWen Gong 
ath10k_wow_enable(struct ath10k * ar)4515fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar)
4525fd3ac3cSJanusz Dziedzic {
4535fd3ac3cSJanusz Dziedzic 	int ret;
4545fd3ac3cSJanusz Dziedzic 
4555fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4565fd3ac3cSJanusz Dziedzic 
4575fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->target_suspend);
4585fd3ac3cSJanusz Dziedzic 
4595fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_enable(ar);
4605fd3ac3cSJanusz Dziedzic 	if (ret) {
4615fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
4625fd3ac3cSJanusz Dziedzic 		return ret;
4635fd3ac3cSJanusz Dziedzic 	}
4645fd3ac3cSJanusz Dziedzic 
4655fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
4665fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
4675fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for suspend completion\n");
4685fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
4695fd3ac3cSJanusz Dziedzic 	}
4705fd3ac3cSJanusz Dziedzic 
4715fd3ac3cSJanusz Dziedzic 	return 0;
4725fd3ac3cSJanusz Dziedzic }
4735fd3ac3cSJanusz Dziedzic 
ath10k_wow_wakeup(struct ath10k * ar)4745fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar)
4755fd3ac3cSJanusz Dziedzic {
4765fd3ac3cSJanusz Dziedzic 	int ret;
4775fd3ac3cSJanusz Dziedzic 
4785fd3ac3cSJanusz Dziedzic 	lockdep_assert_held(&ar->conf_mutex);
4795fd3ac3cSJanusz Dziedzic 
4805fd3ac3cSJanusz Dziedzic 	reinit_completion(&ar->wow.wakeup_completed);
4815fd3ac3cSJanusz Dziedzic 
4825fd3ac3cSJanusz Dziedzic 	ret = ath10k_wmi_wow_host_wakeup_ind(ar);
4835fd3ac3cSJanusz Dziedzic 	if (ret) {
4845fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
4855fd3ac3cSJanusz Dziedzic 			    ret);
4865fd3ac3cSJanusz Dziedzic 		return ret;
4875fd3ac3cSJanusz Dziedzic 	}
4885fd3ac3cSJanusz Dziedzic 
4895fd3ac3cSJanusz Dziedzic 	ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
4905fd3ac3cSJanusz Dziedzic 	if (ret == 0) {
4915fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
4925fd3ac3cSJanusz Dziedzic 		return -ETIMEDOUT;
4935fd3ac3cSJanusz Dziedzic 	}
4945fd3ac3cSJanusz Dziedzic 
4955fd3ac3cSJanusz Dziedzic 	return 0;
4965fd3ac3cSJanusz Dziedzic }
4975fd3ac3cSJanusz Dziedzic 
ath10k_wow_op_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)4985fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
4995fd3ac3cSJanusz Dziedzic 			  struct cfg80211_wowlan *wowlan)
5005fd3ac3cSJanusz Dziedzic {
5015fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
5025fd3ac3cSJanusz Dziedzic 	int ret;
5035fd3ac3cSJanusz Dziedzic 
5045fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
5055fd3ac3cSJanusz Dziedzic 
5065fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
507c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
5085fd3ac3cSJanusz Dziedzic 		ret = 1;
5095fd3ac3cSJanusz Dziedzic 		goto exit;
5105fd3ac3cSJanusz Dziedzic 	}
5115fd3ac3cSJanusz Dziedzic 
5125fd3ac3cSJanusz Dziedzic 	ret =  ath10k_wow_cleanup(ar);
5135fd3ac3cSJanusz Dziedzic 	if (ret) {
5145fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
5155fd3ac3cSJanusz Dziedzic 			    ret);
5165fd3ac3cSJanusz Dziedzic 		goto exit;
5175fd3ac3cSJanusz Dziedzic 	}
5185fd3ac3cSJanusz Dziedzic 
5195fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_set_wakeups(ar, wowlan);
5205fd3ac3cSJanusz Dziedzic 	if (ret) {
5215fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
5225fd3ac3cSJanusz Dziedzic 			    ret);
5235fd3ac3cSJanusz Dziedzic 		goto cleanup;
5245fd3ac3cSJanusz Dziedzic 	}
5255fd3ac3cSJanusz Dziedzic 
526828853acSWen Gong 	ath10k_mac_wait_tx_complete(ar);
527828853acSWen Gong 
5285fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_enable(ar);
5295fd3ac3cSJanusz Dziedzic 	if (ret) {
5305fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to start wow: %d\n", ret);
5315fd3ac3cSJanusz Dziedzic 		goto cleanup;
5325fd3ac3cSJanusz Dziedzic 	}
5335fd3ac3cSJanusz Dziedzic 
5345fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_suspend(ar);
5355fd3ac3cSJanusz Dziedzic 	if (ret) {
5365fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
5375fd3ac3cSJanusz Dziedzic 		goto wakeup;
5385fd3ac3cSJanusz Dziedzic 	}
5395fd3ac3cSJanusz Dziedzic 
5405fd3ac3cSJanusz Dziedzic 	goto exit;
5415fd3ac3cSJanusz Dziedzic 
5425fd3ac3cSJanusz Dziedzic wakeup:
5435fd3ac3cSJanusz Dziedzic 	ath10k_wow_wakeup(ar);
5445fd3ac3cSJanusz Dziedzic 
5455fd3ac3cSJanusz Dziedzic cleanup:
5465fd3ac3cSJanusz Dziedzic 	ath10k_wow_cleanup(ar);
5475fd3ac3cSJanusz Dziedzic 
5485fd3ac3cSJanusz Dziedzic exit:
5495fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
5505fd3ac3cSJanusz Dziedzic 	return ret ? 1 : 0;
5515fd3ac3cSJanusz Dziedzic }
5525fd3ac3cSJanusz Dziedzic 
ath10k_wow_op_set_wakeup(struct ieee80211_hw * hw,bool enabled)553393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
554393b706cSRyan Hsu {
555393b706cSRyan Hsu 	struct ath10k *ar = hw->priv;
556393b706cSRyan Hsu 
557393b706cSRyan Hsu 	mutex_lock(&ar->conf_mutex);
558393b706cSRyan Hsu 	if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
559393b706cSRyan Hsu 		     ar->running_fw->fw_file.fw_features)) {
560393b706cSRyan Hsu 		device_set_wakeup_enable(ar->dev, enabled);
561393b706cSRyan Hsu 	}
562393b706cSRyan Hsu 	mutex_unlock(&ar->conf_mutex);
563393b706cSRyan Hsu }
564393b706cSRyan Hsu 
ath10k_wow_op_resume(struct ieee80211_hw * hw)5655fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw)
5665fd3ac3cSJanusz Dziedzic {
5675fd3ac3cSJanusz Dziedzic 	struct ath10k *ar = hw->priv;
5685fd3ac3cSJanusz Dziedzic 	int ret;
5695fd3ac3cSJanusz Dziedzic 
5705fd3ac3cSJanusz Dziedzic 	mutex_lock(&ar->conf_mutex);
5715fd3ac3cSJanusz Dziedzic 
5725fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
573c4cdf753SKalle Valo 			      ar->running_fw->fw_file.fw_features))) {
5745fd3ac3cSJanusz Dziedzic 		ret = 1;
5755fd3ac3cSJanusz Dziedzic 		goto exit;
5765fd3ac3cSJanusz Dziedzic 	}
5775fd3ac3cSJanusz Dziedzic 
5785fd3ac3cSJanusz Dziedzic 	ret = ath10k_hif_resume(ar);
5795fd3ac3cSJanusz Dziedzic 	if (ret) {
5805fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to resume hif: %d\n", ret);
5815fd3ac3cSJanusz Dziedzic 		goto exit;
5825fd3ac3cSJanusz Dziedzic 	}
5835fd3ac3cSJanusz Dziedzic 
5845fd3ac3cSJanusz Dziedzic 	ret = ath10k_wow_wakeup(ar);
5855fd3ac3cSJanusz Dziedzic 	if (ret)
5865fd3ac3cSJanusz Dziedzic 		ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
5875fd3ac3cSJanusz Dziedzic 
588ce834e28SWen Gong 	ret = ath10k_wow_nlo_cleanup(ar);
589ce834e28SWen Gong 	if (ret)
590ce834e28SWen Gong 		ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret);
591ce834e28SWen Gong 
5925fd3ac3cSJanusz Dziedzic exit:
5936f7429c2SMichal Kazior 	if (ret) {
5946f7429c2SMichal Kazior 		switch (ar->state) {
5956f7429c2SMichal Kazior 		case ATH10K_STATE_ON:
5966f7429c2SMichal Kazior 			ar->state = ATH10K_STATE_RESTARTING;
5976f7429c2SMichal Kazior 			ret = 1;
5986f7429c2SMichal Kazior 			break;
5996f7429c2SMichal Kazior 		case ATH10K_STATE_OFF:
6006f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTING:
6016f7429c2SMichal Kazior 		case ATH10K_STATE_RESTARTED:
6026f7429c2SMichal Kazior 		case ATH10K_STATE_UTF:
6036f7429c2SMichal Kazior 		case ATH10K_STATE_WEDGED:
6046f7429c2SMichal Kazior 			ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
6056f7429c2SMichal Kazior 				    ar->state);
6066f7429c2SMichal Kazior 			ret = -EIO;
6076f7429c2SMichal Kazior 			break;
6086f7429c2SMichal Kazior 		}
6096f7429c2SMichal Kazior 	}
6106f7429c2SMichal Kazior 
6115fd3ac3cSJanusz Dziedzic 	mutex_unlock(&ar->conf_mutex);
6126f7429c2SMichal Kazior 	return ret;
6135fd3ac3cSJanusz Dziedzic }
6145fd3ac3cSJanusz Dziedzic 
ath10k_wow_init(struct ath10k * ar)6155fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar)
6165fd3ac3cSJanusz Dziedzic {
617c4cdf753SKalle Valo 	if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
618c4cdf753SKalle Valo 		      ar->running_fw->fw_file.fw_features))
6195fd3ac3cSJanusz Dziedzic 		return 0;
6205fd3ac3cSJanusz Dziedzic 
6215fd3ac3cSJanusz Dziedzic 	if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
6225fd3ac3cSJanusz Dziedzic 		return -EINVAL;
6235fd3ac3cSJanusz Dziedzic 
62425c86619SJanusz Dziedzic 	ar->wow.wowlan_support = ath10k_wowlan_support;
625fa3440faSWen Gong 
626fa3440faSWen Gong 	if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
627fa3440faSWen Gong 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
628fa3440faSWen Gong 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
629fa3440faSWen Gong 	}
630fa3440faSWen Gong 
631ce834e28SWen Gong 	if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
632ce834e28SWen Gong 		ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
633ce834e28SWen Gong 		ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
634ce834e28SWen Gong 	}
635ce834e28SWen Gong 
63625c86619SJanusz Dziedzic 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
63725c86619SJanusz Dziedzic 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
6385fd3ac3cSJanusz Dziedzic 
639393b706cSRyan Hsu 	device_set_wakeup_capable(ar->dev, true);
640393b706cSRyan Hsu 
6415fd3ac3cSJanusz Dziedzic 	return 0;
6425fd3ac3cSJanusz Dziedzic }
643