xref: /linux/drivers/net/wireless/ath/ath11k/wow.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
179802b13SCarl Huang // SPDX-License-Identifier: BSD-3-Clause-Clear
279802b13SCarl Huang /*
379802b13SCarl Huang  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
4*b43310e4SGovindaraj Saminathan  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
579802b13SCarl Huang  */
679802b13SCarl Huang 
779802b13SCarl Huang #include <linux/delay.h>
879802b13SCarl Huang 
979802b13SCarl Huang #include "mac.h"
10ba9177fcSCarl Huang 
11ba9177fcSCarl Huang #include <net/mac80211.h>
1279802b13SCarl Huang #include "core.h"
1379802b13SCarl Huang #include "hif.h"
1479802b13SCarl Huang #include "debug.h"
1579802b13SCarl Huang #include "wmi.h"
1679802b13SCarl Huang #include "wow.h"
1790bf5c8dSCarl Huang #include "dp_rx.h"
1879802b13SCarl Huang 
19ba9177fcSCarl Huang static const struct wiphy_wowlan_support ath11k_wowlan_support = {
20ba9177fcSCarl Huang 	.flags = WIPHY_WOWLAN_DISCONNECT |
21a16d9b50SCarl Huang 		 WIPHY_WOWLAN_MAGIC_PKT |
22a16d9b50SCarl Huang 		 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
23a16d9b50SCarl Huang 		 WIPHY_WOWLAN_GTK_REKEY_FAILURE,
24ba9177fcSCarl Huang 	.pattern_min_len = WOW_MIN_PATTERN_SIZE,
25ba9177fcSCarl Huang 	.pattern_max_len = WOW_MAX_PATTERN_SIZE,
26ba9177fcSCarl Huang 	.max_pkt_offset = WOW_MAX_PKT_OFFSET,
27ba9177fcSCarl Huang };
28ba9177fcSCarl Huang 
ath11k_wow_enable(struct ath11k_base * ab)2979802b13SCarl Huang int ath11k_wow_enable(struct ath11k_base *ab)
3079802b13SCarl Huang {
3179802b13SCarl Huang 	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
3279802b13SCarl Huang 	int i, ret;
3379802b13SCarl Huang 
3479802b13SCarl Huang 	clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
3579802b13SCarl Huang 
3679802b13SCarl Huang 	for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) {
3779802b13SCarl Huang 		reinit_completion(&ab->htc_suspend);
3879802b13SCarl Huang 
3979802b13SCarl Huang 		ret = ath11k_wmi_wow_enable(ar);
4079802b13SCarl Huang 		if (ret) {
4179802b13SCarl Huang 			ath11k_warn(ab, "failed to issue wow enable: %d\n", ret);
4279802b13SCarl Huang 			return ret;
4379802b13SCarl Huang 		}
4479802b13SCarl Huang 
4579802b13SCarl Huang 		ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ);
4679802b13SCarl Huang 		if (ret == 0) {
4779802b13SCarl Huang 			ath11k_warn(ab,
4879802b13SCarl Huang 				    "timed out while waiting for htc suspend completion\n");
4979802b13SCarl Huang 			return -ETIMEDOUT;
5079802b13SCarl Huang 		}
5179802b13SCarl Huang 
5279802b13SCarl Huang 		if (test_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags))
5379802b13SCarl Huang 			/* success, suspend complete received */
5479802b13SCarl Huang 			return 0;
5579802b13SCarl Huang 
5679802b13SCarl Huang 		ath11k_warn(ab, "htc suspend not complete, retrying (try %d)\n",
5779802b13SCarl Huang 			    i);
5879802b13SCarl Huang 		msleep(ATH11K_WOW_RETRY_WAIT_MS);
5979802b13SCarl Huang 	}
6079802b13SCarl Huang 
6179802b13SCarl Huang 	ath11k_warn(ab, "htc suspend not complete, failing after %d tries\n", i);
6279802b13SCarl Huang 
6379802b13SCarl Huang 	return -ETIMEDOUT;
6479802b13SCarl Huang }
6579802b13SCarl Huang 
ath11k_wow_wakeup(struct ath11k_base * ab)6679802b13SCarl Huang int ath11k_wow_wakeup(struct ath11k_base *ab)
6779802b13SCarl Huang {
6879802b13SCarl Huang 	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
6979802b13SCarl Huang 	int ret;
7079802b13SCarl Huang 
7169ccee61SManikanta Pubbisetty 	/* In the case of WCN6750, WoW wakeup is done
7269ccee61SManikanta Pubbisetty 	 * by sending SMP2P power save exit message
7369ccee61SManikanta Pubbisetty 	 * to the target processor.
7469ccee61SManikanta Pubbisetty 	 */
7569ccee61SManikanta Pubbisetty 	if (ab->hw_params.smp2p_wow_exit)
7669ccee61SManikanta Pubbisetty 		return 0;
7769ccee61SManikanta Pubbisetty 
7879802b13SCarl Huang 	reinit_completion(&ab->wow.wakeup_completed);
7979802b13SCarl Huang 
8079802b13SCarl Huang 	ret = ath11k_wmi_wow_host_wakeup_ind(ar);
8179802b13SCarl Huang 	if (ret) {
8279802b13SCarl Huang 		ath11k_warn(ab, "failed to send wow wakeup indication: %d\n",
8379802b13SCarl Huang 			    ret);
8479802b13SCarl Huang 		return ret;
8579802b13SCarl Huang 	}
8679802b13SCarl Huang 
8779802b13SCarl Huang 	ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ);
8879802b13SCarl Huang 	if (ret == 0) {
8979802b13SCarl Huang 		ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n");
9079802b13SCarl Huang 		return -ETIMEDOUT;
9179802b13SCarl Huang 	}
9279802b13SCarl Huang 
9379802b13SCarl Huang 	return 0;
9479802b13SCarl Huang }
95ba9177fcSCarl Huang 
ath11k_wow_vif_cleanup(struct ath11k_vif * arvif)96ba9177fcSCarl Huang static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif)
97ba9177fcSCarl Huang {
98ba9177fcSCarl Huang 	struct ath11k *ar = arvif->ar;
99ba9177fcSCarl Huang 	int i, ret;
100ba9177fcSCarl Huang 
101ba9177fcSCarl Huang 	for (i = 0; i < WOW_EVENT_MAX; i++) {
102ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
103ba9177fcSCarl Huang 		if (ret) {
104ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
105ba9177fcSCarl Huang 				    wow_wakeup_event(i), arvif->vdev_id, ret);
106ba9177fcSCarl Huang 			return ret;
107ba9177fcSCarl Huang 		}
108ba9177fcSCarl Huang 	}
109ba9177fcSCarl Huang 
110ba9177fcSCarl Huang 	for (i = 0; i < ar->wow.max_num_patterns; i++) {
111ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
112ba9177fcSCarl Huang 		if (ret) {
113ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n",
114ba9177fcSCarl Huang 				    i, arvif->vdev_id, ret);
115ba9177fcSCarl Huang 			return ret;
116ba9177fcSCarl Huang 		}
117ba9177fcSCarl Huang 	}
118ba9177fcSCarl Huang 
119ba9177fcSCarl Huang 	return 0;
120ba9177fcSCarl Huang }
121ba9177fcSCarl Huang 
ath11k_wow_cleanup(struct ath11k * ar)122ba9177fcSCarl Huang static int ath11k_wow_cleanup(struct ath11k *ar)
123ba9177fcSCarl Huang {
124ba9177fcSCarl Huang 	struct ath11k_vif *arvif;
125ba9177fcSCarl Huang 	int ret;
126ba9177fcSCarl Huang 
127ba9177fcSCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
128ba9177fcSCarl Huang 
129ba9177fcSCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
130ba9177fcSCarl Huang 		ret = ath11k_wow_vif_cleanup(arvif);
131ba9177fcSCarl Huang 		if (ret) {
132ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n",
133ba9177fcSCarl Huang 				    arvif->vdev_id, ret);
134ba9177fcSCarl Huang 			return ret;
135ba9177fcSCarl Huang 		}
136ba9177fcSCarl Huang 	}
137ba9177fcSCarl Huang 
138ba9177fcSCarl Huang 	return 0;
139ba9177fcSCarl Huang }
140ba9177fcSCarl Huang 
141ba9177fcSCarl Huang /* Convert a 802.3 format to a 802.11 format.
142ba9177fcSCarl Huang  *         +------------+-----------+--------+----------------+
143ba9177fcSCarl Huang  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
144ba9177fcSCarl Huang  *         +------------+-----------+--------+----------------+
145ba9177fcSCarl Huang  *                |__         |_______    |____________  |________
146ba9177fcSCarl Huang  *                   |                |                |          |
147ba9177fcSCarl Huang  *         +--+------------+----+-----------+---------------+-----------+
148ba9177fcSCarl Huang  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
149ba9177fcSCarl Huang  *         +--+------------+----+-----------+---------------+-----------+
150ba9177fcSCarl Huang  */
ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern * new,const struct cfg80211_pkt_pattern * old)151ba9177fcSCarl Huang static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
152ba9177fcSCarl Huang 					     const struct cfg80211_pkt_pattern *old)
153ba9177fcSCarl Huang {
154ba9177fcSCarl Huang 	u8 hdr_8023_pattern[ETH_HLEN] = {};
155ba9177fcSCarl Huang 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
156ba9177fcSCarl Huang 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
157ba9177fcSCarl Huang 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
158ba9177fcSCarl Huang 
159ba9177fcSCarl Huang 	int total_len = old->pkt_offset + old->pattern_len;
160ba9177fcSCarl Huang 	int hdr_80211_end_offset;
161ba9177fcSCarl Huang 
162ba9177fcSCarl Huang 	struct ieee80211_hdr_3addr *new_hdr_pattern =
163ba9177fcSCarl Huang 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
164ba9177fcSCarl Huang 	struct ieee80211_hdr_3addr *new_hdr_mask =
165ba9177fcSCarl Huang 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
166ba9177fcSCarl Huang 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
167ba9177fcSCarl Huang 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
168ba9177fcSCarl Huang 	int hdr_len = sizeof(*new_hdr_pattern);
169ba9177fcSCarl Huang 
170ba9177fcSCarl Huang 	struct rfc1042_hdr *new_rfc_pattern =
171ba9177fcSCarl Huang 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
172ba9177fcSCarl Huang 	struct rfc1042_hdr *new_rfc_mask =
173ba9177fcSCarl Huang 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
174ba9177fcSCarl Huang 	int rfc_len = sizeof(*new_rfc_pattern);
175ba9177fcSCarl Huang 
176ba9177fcSCarl Huang 	memcpy(hdr_8023_pattern + old->pkt_offset,
177ba9177fcSCarl Huang 	       old->pattern, ETH_HLEN - old->pkt_offset);
178ba9177fcSCarl Huang 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
179ba9177fcSCarl Huang 	       old->mask, ETH_HLEN - old->pkt_offset);
180ba9177fcSCarl Huang 
181ba9177fcSCarl Huang 	/* Copy destination address */
182ba9177fcSCarl Huang 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
183ba9177fcSCarl Huang 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
184ba9177fcSCarl Huang 
185ba9177fcSCarl Huang 	/* Copy source address */
186ba9177fcSCarl Huang 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
187ba9177fcSCarl Huang 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
188ba9177fcSCarl Huang 
189ba9177fcSCarl Huang 	/* Copy logic link type */
190ba9177fcSCarl Huang 	memcpy(&new_rfc_pattern->snap_type,
191ba9177fcSCarl Huang 	       &old_hdr_pattern->h_proto,
192ba9177fcSCarl Huang 	       sizeof(old_hdr_pattern->h_proto));
193ba9177fcSCarl Huang 	memcpy(&new_rfc_mask->snap_type,
194ba9177fcSCarl Huang 	       &old_hdr_mask->h_proto,
195ba9177fcSCarl Huang 	       sizeof(old_hdr_mask->h_proto));
196ba9177fcSCarl Huang 
197ba9177fcSCarl Huang 	/* Compute new pkt_offset */
198ba9177fcSCarl Huang 	if (old->pkt_offset < ETH_ALEN)
199ba9177fcSCarl Huang 		new->pkt_offset = old->pkt_offset +
200ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr1);
201ba9177fcSCarl Huang 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
202ba9177fcSCarl Huang 		new->pkt_offset = old->pkt_offset +
203ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr3) -
204ba9177fcSCarl Huang 			offsetof(struct ethhdr, h_source);
205ba9177fcSCarl Huang 	else
206ba9177fcSCarl Huang 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
207ba9177fcSCarl Huang 
208ba9177fcSCarl Huang 	/* Compute new hdr end offset */
209ba9177fcSCarl Huang 	if (total_len > ETH_HLEN)
210ba9177fcSCarl Huang 		hdr_80211_end_offset = hdr_len + rfc_len;
211ba9177fcSCarl Huang 	else if (total_len > offsetof(struct ethhdr, h_proto))
212ba9177fcSCarl Huang 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
213ba9177fcSCarl Huang 	else if (total_len > ETH_ALEN)
214ba9177fcSCarl Huang 		hdr_80211_end_offset = total_len - ETH_ALEN +
215ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr3);
216ba9177fcSCarl Huang 	else
217ba9177fcSCarl Huang 		hdr_80211_end_offset = total_len +
218ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr1);
219ba9177fcSCarl Huang 
220ba9177fcSCarl Huang 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
221ba9177fcSCarl Huang 
222ba9177fcSCarl Huang 	memcpy((u8 *)new->pattern,
223ba9177fcSCarl Huang 	       hdr_80211_pattern + new->pkt_offset,
224ba9177fcSCarl Huang 	       new->pattern_len);
225ba9177fcSCarl Huang 	memcpy((u8 *)new->mask,
226ba9177fcSCarl Huang 	       hdr_80211_bit_mask + new->pkt_offset,
227ba9177fcSCarl Huang 	       new->pattern_len);
228ba9177fcSCarl Huang 
229ba9177fcSCarl Huang 	if (total_len > ETH_HLEN) {
230ba9177fcSCarl Huang 		/* Copy frame body */
231ba9177fcSCarl Huang 		memcpy((u8 *)new->pattern + new->pattern_len,
232ba9177fcSCarl Huang 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
233ba9177fcSCarl Huang 		       total_len - ETH_HLEN);
234ba9177fcSCarl Huang 		memcpy((u8 *)new->mask + new->pattern_len,
235ba9177fcSCarl Huang 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
236ba9177fcSCarl Huang 		       total_len - ETH_HLEN);
237ba9177fcSCarl Huang 
238ba9177fcSCarl Huang 		new->pattern_len += total_len - ETH_HLEN;
239ba9177fcSCarl Huang 	}
240ba9177fcSCarl Huang }
241ba9177fcSCarl Huang 
ath11k_wmi_pno_check_and_convert(struct ath11k * ar,u32 vdev_id,struct cfg80211_sched_scan_request * nd_config,struct wmi_pno_scan_req * pno)242fec4b898SCarl Huang static int ath11k_wmi_pno_check_and_convert(struct ath11k *ar, u32 vdev_id,
243fec4b898SCarl Huang 					    struct cfg80211_sched_scan_request *nd_config,
244fec4b898SCarl Huang 					    struct wmi_pno_scan_req *pno)
245fec4b898SCarl Huang {
246fec4b898SCarl Huang 	int i, j;
247fec4b898SCarl Huang 	u8 ssid_len;
248fec4b898SCarl Huang 
249fec4b898SCarl Huang 	pno->enable = 1;
250fec4b898SCarl Huang 	pno->vdev_id = vdev_id;
251fec4b898SCarl Huang 	pno->uc_networks_count = nd_config->n_match_sets;
252fec4b898SCarl Huang 
253fec4b898SCarl Huang 	if (!pno->uc_networks_count ||
254fec4b898SCarl Huang 	    pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
255fec4b898SCarl Huang 		return -EINVAL;
256fec4b898SCarl Huang 
257fec4b898SCarl Huang 	if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
258fec4b898SCarl Huang 		return -EINVAL;
259fec4b898SCarl Huang 
260fec4b898SCarl Huang 	/* Filling per profile params */
261fec4b898SCarl Huang 	for (i = 0; i < pno->uc_networks_count; i++) {
262fec4b898SCarl Huang 		ssid_len = nd_config->match_sets[i].ssid.ssid_len;
263fec4b898SCarl Huang 
264fec4b898SCarl Huang 		if (ssid_len == 0 || ssid_len > 32)
265fec4b898SCarl Huang 			return -EINVAL;
266fec4b898SCarl Huang 
267fec4b898SCarl Huang 		pno->a_networks[i].ssid.ssid_len = ssid_len;
268fec4b898SCarl Huang 
269fec4b898SCarl Huang 		memcpy(pno->a_networks[i].ssid.ssid,
270fec4b898SCarl Huang 		       nd_config->match_sets[i].ssid.ssid,
271fec4b898SCarl Huang 		       nd_config->match_sets[i].ssid.ssid_len);
272fec4b898SCarl Huang 		pno->a_networks[i].authentication = 0;
273fec4b898SCarl Huang 		pno->a_networks[i].encryption     = 0;
274fec4b898SCarl Huang 		pno->a_networks[i].bcast_nw_type  = 0;
275fec4b898SCarl Huang 
276fec4b898SCarl Huang 		/* Copying list of valid channel into request */
277fec4b898SCarl Huang 		pno->a_networks[i].channel_count = nd_config->n_channels;
278fec4b898SCarl Huang 		pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
279fec4b898SCarl Huang 
280fec4b898SCarl Huang 		for (j = 0; j < nd_config->n_channels; j++) {
281fec4b898SCarl Huang 			pno->a_networks[i].channels[j] =
282fec4b898SCarl Huang 					nd_config->channels[j]->center_freq;
283fec4b898SCarl Huang 		}
284fec4b898SCarl Huang 	}
285fec4b898SCarl Huang 
286fec4b898SCarl Huang 	/* set scan to passive if no SSIDs are specified in the request */
287fec4b898SCarl Huang 	if (nd_config->n_ssids == 0)
288fec4b898SCarl Huang 		pno->do_passive_scan = true;
289fec4b898SCarl Huang 	else
290fec4b898SCarl Huang 		pno->do_passive_scan = false;
291fec4b898SCarl Huang 
292fec4b898SCarl Huang 	for (i = 0; i < nd_config->n_ssids; i++) {
293fec4b898SCarl Huang 		j = 0;
294fec4b898SCarl Huang 		while (j < pno->uc_networks_count) {
295fec4b898SCarl Huang 			if (pno->a_networks[j].ssid.ssid_len ==
296fec4b898SCarl Huang 				nd_config->ssids[i].ssid_len &&
297fec4b898SCarl Huang 			(memcmp(pno->a_networks[j].ssid.ssid,
298fec4b898SCarl Huang 				nd_config->ssids[i].ssid,
299fec4b898SCarl Huang 				pno->a_networks[j].ssid.ssid_len) == 0)) {
300fec4b898SCarl Huang 				pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
301fec4b898SCarl Huang 				break;
302fec4b898SCarl Huang 			}
303fec4b898SCarl Huang 			j++;
304fec4b898SCarl Huang 		}
305fec4b898SCarl Huang 	}
306fec4b898SCarl Huang 
307fec4b898SCarl Huang 	if (nd_config->n_scan_plans == 2) {
308fec4b898SCarl Huang 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
309fec4b898SCarl Huang 		pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
310fec4b898SCarl Huang 		pno->slow_scan_period =
311fec4b898SCarl Huang 			nd_config->scan_plans[1].interval * MSEC_PER_SEC;
312fec4b898SCarl Huang 	} else if (nd_config->n_scan_plans == 1) {
313fec4b898SCarl Huang 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
314fec4b898SCarl Huang 		pno->fast_scan_max_cycles = 1;
315fec4b898SCarl Huang 		pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
316fec4b898SCarl Huang 	} else {
317fec4b898SCarl Huang 		ath11k_warn(ar->ab, "Invalid number of scan plans %d !!",
318fec4b898SCarl Huang 			    nd_config->n_scan_plans);
319fec4b898SCarl Huang 	}
320fec4b898SCarl Huang 
321fec4b898SCarl Huang 	if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
322fec4b898SCarl Huang 		/* enable mac randomization */
323fec4b898SCarl Huang 		pno->enable_pno_scan_randomization = 1;
324fec4b898SCarl Huang 		memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
325fec4b898SCarl Huang 		memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
326fec4b898SCarl Huang 	}
327fec4b898SCarl Huang 
328fec4b898SCarl Huang 	pno->delay_start_time = nd_config->delay;
329fec4b898SCarl Huang 
330fec4b898SCarl Huang 	/* Current FW does not support min-max range for dwell time */
331fec4b898SCarl Huang 	pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
332fec4b898SCarl Huang 	pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
333fec4b898SCarl Huang 
334fec4b898SCarl Huang 	return 0;
335fec4b898SCarl Huang }
336fec4b898SCarl Huang 
ath11k_vif_wow_set_wakeups(struct ath11k_vif * arvif,struct cfg80211_wowlan * wowlan)337ba9177fcSCarl Huang static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif,
338ba9177fcSCarl Huang 				      struct cfg80211_wowlan *wowlan)
339ba9177fcSCarl Huang {
340ba9177fcSCarl Huang 	int ret, i;
341ba9177fcSCarl Huang 	unsigned long wow_mask = 0;
342ba9177fcSCarl Huang 	struct ath11k *ar = arvif->ar;
343ba9177fcSCarl Huang 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
344ba9177fcSCarl Huang 	int pattern_id = 0;
345ba9177fcSCarl Huang 
346ba9177fcSCarl Huang 	/* Setup requested WOW features */
347ba9177fcSCarl Huang 	switch (arvif->vdev_type) {
348ba9177fcSCarl Huang 	case WMI_VDEV_TYPE_IBSS:
349ba9177fcSCarl Huang 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
350ba9177fcSCarl Huang 		fallthrough;
351ba9177fcSCarl Huang 	case WMI_VDEV_TYPE_AP:
352ba9177fcSCarl Huang 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
353ba9177fcSCarl Huang 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
354ba9177fcSCarl Huang 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
355ba9177fcSCarl Huang 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
356ba9177fcSCarl Huang 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
357ba9177fcSCarl Huang 		__set_bit(WOW_HTT_EVENT, &wow_mask);
358ba9177fcSCarl Huang 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
359ba9177fcSCarl Huang 		break;
360ba9177fcSCarl Huang 	case WMI_VDEV_TYPE_STA:
361ba9177fcSCarl Huang 		if (wowlan->disconnect) {
362ba9177fcSCarl Huang 			__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
363ba9177fcSCarl Huang 			__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
364ba9177fcSCarl Huang 			__set_bit(WOW_BMISS_EVENT, &wow_mask);
365ba9177fcSCarl Huang 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
366ba9177fcSCarl Huang 		}
367ba9177fcSCarl Huang 
368ba9177fcSCarl Huang 		if (wowlan->magic_pkt)
369ba9177fcSCarl Huang 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
370fec4b898SCarl Huang 
371fec4b898SCarl Huang 		if (wowlan->nd_config) {
372fec4b898SCarl Huang 			struct wmi_pno_scan_req *pno;
373fec4b898SCarl Huang 			int ret;
374fec4b898SCarl Huang 
375fec4b898SCarl Huang 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
376fec4b898SCarl Huang 			if (!pno)
377fec4b898SCarl Huang 				return -ENOMEM;
378fec4b898SCarl Huang 
379fec4b898SCarl Huang 			ar->nlo_enabled = true;
380fec4b898SCarl Huang 
381fec4b898SCarl Huang 			ret = ath11k_wmi_pno_check_and_convert(ar, arvif->vdev_id,
382fec4b898SCarl Huang 							       wowlan->nd_config, pno);
383fec4b898SCarl Huang 			if (!ret) {
384fec4b898SCarl Huang 				ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
385fec4b898SCarl Huang 				__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
386fec4b898SCarl Huang 			}
387fec4b898SCarl Huang 
388fec4b898SCarl Huang 			kfree(pno);
389fec4b898SCarl Huang 		}
390ba9177fcSCarl Huang 		break;
391ba9177fcSCarl Huang 	default:
392ba9177fcSCarl Huang 		break;
393ba9177fcSCarl Huang 	}
394ba9177fcSCarl Huang 
395ba9177fcSCarl Huang 	for (i = 0; i < wowlan->n_patterns; i++) {
396ba9177fcSCarl Huang 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
397ba9177fcSCarl Huang 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
398ba9177fcSCarl Huang 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
399ba9177fcSCarl Huang 		struct cfg80211_pkt_pattern new_pattern = {};
400ba9177fcSCarl Huang 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
401ba9177fcSCarl Huang 		int j;
402ba9177fcSCarl Huang 
403ba9177fcSCarl Huang 		new_pattern.pattern = ath_pattern;
404ba9177fcSCarl Huang 		new_pattern.mask = ath_bitmask;
405ba9177fcSCarl Huang 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
406ba9177fcSCarl Huang 			continue;
407ba9177fcSCarl Huang 		/* convert bytemask to bitmask */
408ba9177fcSCarl Huang 		for (j = 0; j < patterns[i].pattern_len; j++)
409ba9177fcSCarl Huang 			if (patterns[i].mask[j / 8] & BIT(j % 8))
410ba9177fcSCarl Huang 				bitmask[j] = 0xff;
411ba9177fcSCarl Huang 		old_pattern.mask = bitmask;
412ba9177fcSCarl Huang 
413ba9177fcSCarl Huang 		if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
414ba9177fcSCarl Huang 		    ATH11K_HW_TXRX_NATIVE_WIFI) {
415ba9177fcSCarl Huang 			if (patterns[i].pkt_offset < ETH_HLEN) {
416ba9177fcSCarl Huang 				u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {};
417ba9177fcSCarl Huang 
418ba9177fcSCarl Huang 				memcpy(pattern_ext, old_pattern.pattern,
419ba9177fcSCarl Huang 				       old_pattern.pattern_len);
420ba9177fcSCarl Huang 				old_pattern.pattern = pattern_ext;
421ba9177fcSCarl Huang 				ath11k_wow_convert_8023_to_80211(&new_pattern,
422ba9177fcSCarl Huang 								 &old_pattern);
423ba9177fcSCarl Huang 			} else {
424ba9177fcSCarl Huang 				new_pattern = old_pattern;
425ba9177fcSCarl Huang 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
426ba9177fcSCarl Huang 			}
427ba9177fcSCarl Huang 		}
428ba9177fcSCarl Huang 
429ba9177fcSCarl Huang 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
430ba9177fcSCarl Huang 			return -EINVAL;
431ba9177fcSCarl Huang 
432ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id,
433ba9177fcSCarl Huang 						 pattern_id,
434ba9177fcSCarl Huang 						 new_pattern.pattern,
435ba9177fcSCarl Huang 						 new_pattern.mask,
436ba9177fcSCarl Huang 						 new_pattern.pattern_len,
437ba9177fcSCarl Huang 						 new_pattern.pkt_offset);
438ba9177fcSCarl Huang 		if (ret) {
439ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n",
440ba9177fcSCarl Huang 				    pattern_id,
441ba9177fcSCarl Huang 				    arvif->vdev_id, ret);
442ba9177fcSCarl Huang 			return ret;
443ba9177fcSCarl Huang 		}
444ba9177fcSCarl Huang 
445ba9177fcSCarl Huang 		pattern_id++;
446ba9177fcSCarl Huang 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
447ba9177fcSCarl Huang 	}
448ba9177fcSCarl Huang 
449ba9177fcSCarl Huang 	for (i = 0; i < WOW_EVENT_MAX; i++) {
450ba9177fcSCarl Huang 		if (!test_bit(i, &wow_mask))
451ba9177fcSCarl Huang 			continue;
452ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
453ba9177fcSCarl Huang 		if (ret) {
454ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n",
455ba9177fcSCarl Huang 				    wow_wakeup_event(i), arvif->vdev_id, ret);
456ba9177fcSCarl Huang 			return ret;
457ba9177fcSCarl Huang 		}
458ba9177fcSCarl Huang 	}
459ba9177fcSCarl Huang 
460ba9177fcSCarl Huang 	return 0;
461ba9177fcSCarl Huang }
462ba9177fcSCarl Huang 
ath11k_wow_set_wakeups(struct ath11k * ar,struct cfg80211_wowlan * wowlan)463ba9177fcSCarl Huang static int ath11k_wow_set_wakeups(struct ath11k *ar,
464ba9177fcSCarl Huang 				  struct cfg80211_wowlan *wowlan)
465ba9177fcSCarl Huang {
466ba9177fcSCarl Huang 	struct ath11k_vif *arvif;
467ba9177fcSCarl Huang 	int ret;
468ba9177fcSCarl Huang 
469ba9177fcSCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
470ba9177fcSCarl Huang 
471ba9177fcSCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
472ba9177fcSCarl Huang 		ret = ath11k_vif_wow_set_wakeups(arvif, wowlan);
473ba9177fcSCarl Huang 		if (ret) {
474ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n",
475ba9177fcSCarl Huang 				    arvif->vdev_id, ret);
476ba9177fcSCarl Huang 			return ret;
477ba9177fcSCarl Huang 		}
478ba9177fcSCarl Huang 	}
479ba9177fcSCarl Huang 
480ba9177fcSCarl Huang 	return 0;
481ba9177fcSCarl Huang }
482ba9177fcSCarl Huang 
ath11k_vif_wow_clean_nlo(struct ath11k_vif * arvif)483fec4b898SCarl Huang static int ath11k_vif_wow_clean_nlo(struct ath11k_vif *arvif)
484fec4b898SCarl Huang {
485fec4b898SCarl Huang 	int ret = 0;
486fec4b898SCarl Huang 	struct ath11k *ar = arvif->ar;
487fec4b898SCarl Huang 
488fec4b898SCarl Huang 	switch (arvif->vdev_type) {
489fec4b898SCarl Huang 	case WMI_VDEV_TYPE_STA:
490fec4b898SCarl Huang 		if (ar->nlo_enabled) {
491fec4b898SCarl Huang 			struct wmi_pno_scan_req *pno;
492fec4b898SCarl Huang 
493fec4b898SCarl Huang 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
494fec4b898SCarl Huang 			if (!pno)
495fec4b898SCarl Huang 				return -ENOMEM;
496fec4b898SCarl Huang 
497fec4b898SCarl Huang 			pno->enable = 0;
498fec4b898SCarl Huang 			ar->nlo_enabled = false;
499fec4b898SCarl Huang 			ret = ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
500fec4b898SCarl Huang 			kfree(pno);
501fec4b898SCarl Huang 		}
502fec4b898SCarl Huang 		break;
503fec4b898SCarl Huang 	default:
504fec4b898SCarl Huang 		break;
505fec4b898SCarl Huang 	}
506fec4b898SCarl Huang 	return ret;
507fec4b898SCarl Huang }
508fec4b898SCarl Huang 
ath11k_wow_nlo_cleanup(struct ath11k * ar)509fec4b898SCarl Huang static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
510fec4b898SCarl Huang {
511fec4b898SCarl Huang 	struct ath11k_vif *arvif;
512fec4b898SCarl Huang 	int ret;
513fec4b898SCarl Huang 
514fec4b898SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
515fec4b898SCarl Huang 
516fec4b898SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
517fec4b898SCarl Huang 		ret = ath11k_vif_wow_clean_nlo(arvif);
518fec4b898SCarl Huang 		if (ret) {
519fec4b898SCarl Huang 			ath11k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n",
520fec4b898SCarl Huang 				    arvif->vdev_id, ret);
521fec4b898SCarl Huang 			return ret;
522fec4b898SCarl Huang 		}
523fec4b898SCarl Huang 	}
524fec4b898SCarl Huang 
525fec4b898SCarl Huang 	return 0;
526fec4b898SCarl Huang }
527fec4b898SCarl Huang 
ath11k_wow_set_hw_filter(struct ath11k * ar)528c417b247SCarl Huang static int ath11k_wow_set_hw_filter(struct ath11k *ar)
529c417b247SCarl Huang {
530c417b247SCarl Huang 	struct ath11k_vif *arvif;
531c417b247SCarl Huang 	u32 bitmap;
532c417b247SCarl Huang 	int ret;
533c417b247SCarl Huang 
534c417b247SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
535c417b247SCarl Huang 
536c417b247SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
537c417b247SCarl Huang 		bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC |
538c417b247SCarl Huang 			WMI_HW_DATA_FILTER_DROP_NON_ARP_BC;
539c417b247SCarl Huang 		ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id,
540c417b247SCarl Huang 						    bitmap,
541c417b247SCarl Huang 						    true);
542c417b247SCarl Huang 		if (ret) {
543c417b247SCarl Huang 			ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
544c417b247SCarl Huang 				    arvif->vdev_id, ret);
545c417b247SCarl Huang 			return ret;
546c417b247SCarl Huang 		}
547c417b247SCarl Huang 	}
548c417b247SCarl Huang 
549c417b247SCarl Huang 	return 0;
550c417b247SCarl Huang }
551c417b247SCarl Huang 
ath11k_wow_clear_hw_filter(struct ath11k * ar)552c417b247SCarl Huang static int ath11k_wow_clear_hw_filter(struct ath11k *ar)
553c417b247SCarl Huang {
554c417b247SCarl Huang 	struct ath11k_vif *arvif;
555c417b247SCarl Huang 	int ret;
556c417b247SCarl Huang 
557c417b247SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
558c417b247SCarl Huang 
559c417b247SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
560c417b247SCarl Huang 		ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false);
561c417b247SCarl Huang 
562c417b247SCarl Huang 		if (ret) {
563c417b247SCarl Huang 			ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
564c417b247SCarl Huang 				    arvif->vdev_id, ret);
565c417b247SCarl Huang 			return ret;
566c417b247SCarl Huang 		}
567c417b247SCarl Huang 	}
568c417b247SCarl Huang 
569c417b247SCarl Huang 	return 0;
570c417b247SCarl Huang }
571c417b247SCarl Huang 
ath11k_wow_arp_ns_offload(struct ath11k * ar,bool enable)572c3c36bfeSCarl Huang static int ath11k_wow_arp_ns_offload(struct ath11k *ar, bool enable)
573c3c36bfeSCarl Huang {
574c3c36bfeSCarl Huang 	struct ath11k_vif *arvif;
575c3c36bfeSCarl Huang 	int ret;
576c3c36bfeSCarl Huang 
577c3c36bfeSCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
578c3c36bfeSCarl Huang 
579c3c36bfeSCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
580c3c36bfeSCarl Huang 		if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
581c3c36bfeSCarl Huang 			continue;
582c3c36bfeSCarl Huang 
583c3c36bfeSCarl Huang 		ret = ath11k_wmi_arp_ns_offload(ar, arvif, enable);
584c3c36bfeSCarl Huang 
585c3c36bfeSCarl Huang 		if (ret) {
586c3c36bfeSCarl Huang 			ath11k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n",
587c3c36bfeSCarl Huang 				    arvif->vdev_id, enable, ret);
588c3c36bfeSCarl Huang 			return ret;
589c3c36bfeSCarl Huang 		}
590c3c36bfeSCarl Huang 	}
591c3c36bfeSCarl Huang 
592c3c36bfeSCarl Huang 	return 0;
593c3c36bfeSCarl Huang }
594c3c36bfeSCarl Huang 
ath11k_gtk_rekey_offload(struct ath11k * ar,bool enable)595a16d9b50SCarl Huang static int ath11k_gtk_rekey_offload(struct ath11k *ar, bool enable)
596a16d9b50SCarl Huang {
597a16d9b50SCarl Huang 	struct ath11k_vif *arvif;
598a16d9b50SCarl Huang 	int ret;
599a16d9b50SCarl Huang 
600a16d9b50SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
601a16d9b50SCarl Huang 
602a16d9b50SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
603a16d9b50SCarl Huang 		if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
604a16d9b50SCarl Huang 		    !arvif->is_up ||
605a16d9b50SCarl Huang 		    !arvif->rekey_data.enable_offload)
606a16d9b50SCarl Huang 			continue;
607a16d9b50SCarl Huang 
608a16d9b50SCarl Huang 		/* get rekey info before disable rekey offload */
609a16d9b50SCarl Huang 		if (!enable) {
610a16d9b50SCarl Huang 			ret = ath11k_wmi_gtk_rekey_getinfo(ar, arvif);
611a16d9b50SCarl Huang 			if (ret) {
612a16d9b50SCarl Huang 				ath11k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n",
613a16d9b50SCarl Huang 					    arvif->vdev_id, ret);
614a16d9b50SCarl Huang 				return ret;
615a16d9b50SCarl Huang 			}
616a16d9b50SCarl Huang 		}
617a16d9b50SCarl Huang 
618a16d9b50SCarl Huang 		ret = ath11k_wmi_gtk_rekey_offload(ar, arvif, enable);
619a16d9b50SCarl Huang 
620a16d9b50SCarl Huang 		if (ret) {
621a16d9b50SCarl Huang 			ath11k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n",
622a16d9b50SCarl Huang 				    arvif->vdev_id, enable, ret);
623a16d9b50SCarl Huang 			return ret;
624a16d9b50SCarl Huang 		}
625a16d9b50SCarl Huang 	}
626a16d9b50SCarl Huang 
627a16d9b50SCarl Huang 	return 0;
628a16d9b50SCarl Huang }
629a16d9b50SCarl Huang 
ath11k_wow_protocol_offload(struct ath11k * ar,bool enable)630c3c36bfeSCarl Huang static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable)
631c3c36bfeSCarl Huang {
632c3c36bfeSCarl Huang 	int ret;
633c3c36bfeSCarl Huang 
634c3c36bfeSCarl Huang 	ret = ath11k_wow_arp_ns_offload(ar, enable);
635c3c36bfeSCarl Huang 	if (ret) {
636c3c36bfeSCarl Huang 		ath11k_warn(ar->ab, "failed to offload ARP and NS %d %d\n",
637c3c36bfeSCarl Huang 			    enable, ret);
638c3c36bfeSCarl Huang 		return ret;
639c3c36bfeSCarl Huang 	}
640c3c36bfeSCarl Huang 
641a16d9b50SCarl Huang 	ret = ath11k_gtk_rekey_offload(ar, enable);
642a16d9b50SCarl Huang 	if (ret) {
643a16d9b50SCarl Huang 		ath11k_warn(ar->ab, "failed to offload gtk rekey %d %d\n",
644a16d9b50SCarl Huang 			    enable, ret);
645a16d9b50SCarl Huang 		return ret;
646a16d9b50SCarl Huang 	}
647a16d9b50SCarl Huang 
648c3c36bfeSCarl Huang 	return 0;
649c3c36bfeSCarl Huang }
650c3c36bfeSCarl Huang 
ath11k_wow_set_keepalive(struct ath11k * ar,enum wmi_sta_keepalive_method method,u32 interval)6510f84a156SBaochen Qiang static int ath11k_wow_set_keepalive(struct ath11k *ar,
6520f84a156SBaochen Qiang 				    enum wmi_sta_keepalive_method method,
6530f84a156SBaochen Qiang 				    u32 interval)
6540f84a156SBaochen Qiang {
6550f84a156SBaochen Qiang 	struct ath11k_vif *arvif;
6560f84a156SBaochen Qiang 	int ret;
6570f84a156SBaochen Qiang 
6580f84a156SBaochen Qiang 	lockdep_assert_held(&ar->conf_mutex);
6590f84a156SBaochen Qiang 
6600f84a156SBaochen Qiang 	list_for_each_entry(arvif, &ar->arvifs, list) {
6610f84a156SBaochen Qiang 		ret = ath11k_mac_vif_set_keepalive(arvif, method, interval);
6620f84a156SBaochen Qiang 		if (ret)
6630f84a156SBaochen Qiang 			return ret;
6640f84a156SBaochen Qiang 	}
6650f84a156SBaochen Qiang 
6660f84a156SBaochen Qiang 	return 0;
6670f84a156SBaochen Qiang }
6680f84a156SBaochen Qiang 
ath11k_wow_op_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)669ba9177fcSCarl Huang int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
670ba9177fcSCarl Huang 			  struct cfg80211_wowlan *wowlan)
671ba9177fcSCarl Huang {
672ba9177fcSCarl Huang 	struct ath11k *ar = hw->priv;
673ba9177fcSCarl Huang 	int ret;
674ba9177fcSCarl Huang 
675d78c8b71SBaochen Qiang 	ret = ath11k_mac_wait_tx_complete(ar);
676d78c8b71SBaochen Qiang 	if (ret) {
677d78c8b71SBaochen Qiang 		ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret);
678d78c8b71SBaochen Qiang 		return ret;
679d78c8b71SBaochen Qiang 	}
680d78c8b71SBaochen Qiang 
681ba9177fcSCarl Huang 	mutex_lock(&ar->conf_mutex);
682ba9177fcSCarl Huang 
68390bf5c8dSCarl Huang 	ret = ath11k_dp_rx_pktlog_stop(ar->ab, true);
68490bf5c8dSCarl Huang 	if (ret) {
68590bf5c8dSCarl Huang 		ath11k_warn(ar->ab,
68690bf5c8dSCarl Huang 			    "failed to stop dp rx (and timer) pktlog during wow suspend: %d\n",
68790bf5c8dSCarl Huang 			    ret);
68890bf5c8dSCarl Huang 		goto exit;
68990bf5c8dSCarl Huang 	}
69090bf5c8dSCarl Huang 
691ba9177fcSCarl Huang 	ret =  ath11k_wow_cleanup(ar);
692ba9177fcSCarl Huang 	if (ret) {
693ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n",
694ba9177fcSCarl Huang 			    ret);
695ba9177fcSCarl Huang 		goto exit;
696ba9177fcSCarl Huang 	}
697ba9177fcSCarl Huang 
698ba9177fcSCarl Huang 	ret = ath11k_wow_set_wakeups(ar, wowlan);
699ba9177fcSCarl Huang 	if (ret) {
700ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n",
701ba9177fcSCarl Huang 			    ret);
702ba9177fcSCarl Huang 		goto cleanup;
703ba9177fcSCarl Huang 	}
704ba9177fcSCarl Huang 
705c3c36bfeSCarl Huang 	ret = ath11k_wow_protocol_offload(ar, true);
706c3c36bfeSCarl Huang 	if (ret) {
707c3c36bfeSCarl Huang 		ath11k_warn(ar->ab, "failed to set wow protocol offload events: %d\n",
708c3c36bfeSCarl Huang 			    ret);
709c3c36bfeSCarl Huang 		goto cleanup;
710c3c36bfeSCarl Huang 	}
711c3c36bfeSCarl Huang 
712c417b247SCarl Huang 	ret = ath11k_wow_set_hw_filter(ar);
713c417b247SCarl Huang 	if (ret) {
714c417b247SCarl Huang 		ath11k_warn(ar->ab, "failed to set hw filter: %d\n",
715c417b247SCarl Huang 			    ret);
716c417b247SCarl Huang 		goto cleanup;
717c417b247SCarl Huang 	}
718c417b247SCarl Huang 
7190f84a156SBaochen Qiang 	ret = ath11k_wow_set_keepalive(ar,
7200f84a156SBaochen Qiang 				       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
7210f84a156SBaochen Qiang 				       WMI_STA_KEEPALIVE_INTERVAL_DEFAULT);
7220f84a156SBaochen Qiang 	if (ret) {
7230f84a156SBaochen Qiang 		ath11k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret);
7240f84a156SBaochen Qiang 		goto cleanup;
7250f84a156SBaochen Qiang 	}
7260f84a156SBaochen Qiang 
727ba9177fcSCarl Huang 	ret = ath11k_wow_enable(ar->ab);
728ba9177fcSCarl Huang 	if (ret) {
729ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
730ba9177fcSCarl Huang 		goto cleanup;
731ba9177fcSCarl Huang 	}
732ba9177fcSCarl Huang 
73390bf5c8dSCarl Huang 	ret = ath11k_dp_rx_pktlog_stop(ar->ab, false);
73490bf5c8dSCarl Huang 	if (ret) {
73590bf5c8dSCarl Huang 		ath11k_warn(ar->ab,
73690bf5c8dSCarl Huang 			    "failed to stop dp rx pktlog during wow suspend: %d\n",
73790bf5c8dSCarl Huang 			    ret);
73890bf5c8dSCarl Huang 		goto cleanup;
73990bf5c8dSCarl Huang 	}
74090bf5c8dSCarl Huang 
741ba9177fcSCarl Huang 	ath11k_ce_stop_shadow_timers(ar->ab);
742ba9177fcSCarl Huang 	ath11k_dp_stop_shadow_timers(ar->ab);
743ba9177fcSCarl Huang 
744ba9177fcSCarl Huang 	ath11k_hif_irq_disable(ar->ab);
745ba9177fcSCarl Huang 	ath11k_hif_ce_irq_disable(ar->ab);
746ba9177fcSCarl Huang 
747ba9177fcSCarl Huang 	ret = ath11k_hif_suspend(ar->ab);
748ba9177fcSCarl Huang 	if (ret) {
749ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret);
750ba9177fcSCarl Huang 		goto wakeup;
751ba9177fcSCarl Huang 	}
752ba9177fcSCarl Huang 
753ba9177fcSCarl Huang 	goto exit;
754ba9177fcSCarl Huang 
755ba9177fcSCarl Huang wakeup:
756ba9177fcSCarl Huang 	ath11k_wow_wakeup(ar->ab);
757ba9177fcSCarl Huang 
758ba9177fcSCarl Huang cleanup:
759ba9177fcSCarl Huang 	ath11k_wow_cleanup(ar);
760ba9177fcSCarl Huang 
761ba9177fcSCarl Huang exit:
762ba9177fcSCarl Huang 	mutex_unlock(&ar->conf_mutex);
763ba9177fcSCarl Huang 	return ret ? 1 : 0;
764ba9177fcSCarl Huang }
765ba9177fcSCarl Huang 
ath11k_wow_op_set_wakeup(struct ieee80211_hw * hw,bool enabled)766ba9177fcSCarl Huang void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
767ba9177fcSCarl Huang {
768ba9177fcSCarl Huang 	struct ath11k *ar = hw->priv;
769ba9177fcSCarl Huang 
770ba9177fcSCarl Huang 	mutex_lock(&ar->conf_mutex);
771ba9177fcSCarl Huang 	device_set_wakeup_enable(ar->ab->dev, enabled);
772ba9177fcSCarl Huang 	mutex_unlock(&ar->conf_mutex);
773ba9177fcSCarl Huang }
774ba9177fcSCarl Huang 
ath11k_wow_op_resume(struct ieee80211_hw * hw)775ba9177fcSCarl Huang int ath11k_wow_op_resume(struct ieee80211_hw *hw)
776ba9177fcSCarl Huang {
777ba9177fcSCarl Huang 	struct ath11k *ar = hw->priv;
778ba9177fcSCarl Huang 	int ret;
779ba9177fcSCarl Huang 
780ba9177fcSCarl Huang 	mutex_lock(&ar->conf_mutex);
781ba9177fcSCarl Huang 
782ba9177fcSCarl Huang 	ret = ath11k_hif_resume(ar->ab);
783ba9177fcSCarl Huang 	if (ret) {
784ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret);
785ba9177fcSCarl Huang 		goto exit;
786ba9177fcSCarl Huang 	}
787ba9177fcSCarl Huang 
788ba9177fcSCarl Huang 	ath11k_hif_ce_irq_enable(ar->ab);
789ba9177fcSCarl Huang 	ath11k_hif_irq_enable(ar->ab);
790ba9177fcSCarl Huang 
79190bf5c8dSCarl Huang 	ret = ath11k_dp_rx_pktlog_start(ar->ab);
79290bf5c8dSCarl Huang 	if (ret) {
79390bf5c8dSCarl Huang 		ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret);
79460519441SYang Yingliang 		goto exit;
79590bf5c8dSCarl Huang 	}
79690bf5c8dSCarl Huang 
797ba9177fcSCarl Huang 	ret = ath11k_wow_wakeup(ar->ab);
798fec4b898SCarl Huang 	if (ret) {
799ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret);
800fec4b898SCarl Huang 		goto exit;
801fec4b898SCarl Huang 	}
802fec4b898SCarl Huang 
803fec4b898SCarl Huang 	ret = ath11k_wow_nlo_cleanup(ar);
804fec4b898SCarl Huang 	if (ret) {
805fec4b898SCarl Huang 		ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret);
806fec4b898SCarl Huang 		goto exit;
807fec4b898SCarl Huang 	}
808ba9177fcSCarl Huang 
809c417b247SCarl Huang 	ret = ath11k_wow_clear_hw_filter(ar);
810c417b247SCarl Huang 	if (ret) {
811c417b247SCarl Huang 		ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
812c417b247SCarl Huang 		goto exit;
813c417b247SCarl Huang 	}
814c417b247SCarl Huang 
815c3c36bfeSCarl Huang 	ret = ath11k_wow_protocol_offload(ar, false);
816c3c36bfeSCarl Huang 	if (ret) {
817c3c36bfeSCarl Huang 		ath11k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n",
818c3c36bfeSCarl Huang 			    ret);
819c3c36bfeSCarl Huang 		goto exit;
820c3c36bfeSCarl Huang 	}
821c3c36bfeSCarl Huang 
8220f84a156SBaochen Qiang 	ret = ath11k_wow_set_keepalive(ar,
8230f84a156SBaochen Qiang 				       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
8240f84a156SBaochen Qiang 				       WMI_STA_KEEPALIVE_INTERVAL_DISABLE);
8250f84a156SBaochen Qiang 	if (ret) {
8260f84a156SBaochen Qiang 		ath11k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret);
8270f84a156SBaochen Qiang 		goto exit;
8280f84a156SBaochen Qiang 	}
8290f84a156SBaochen Qiang 
830ba9177fcSCarl Huang exit:
831ba9177fcSCarl Huang 	if (ret) {
832ba9177fcSCarl Huang 		switch (ar->state) {
833ba9177fcSCarl Huang 		case ATH11K_STATE_ON:
834ba9177fcSCarl Huang 			ar->state = ATH11K_STATE_RESTARTING;
835ba9177fcSCarl Huang 			ret = 1;
836ba9177fcSCarl Huang 			break;
837ba9177fcSCarl Huang 		case ATH11K_STATE_OFF:
838ba9177fcSCarl Huang 		case ATH11K_STATE_RESTARTING:
839ba9177fcSCarl Huang 		case ATH11K_STATE_RESTARTED:
840ba9177fcSCarl Huang 		case ATH11K_STATE_WEDGED:
841*b43310e4SGovindaraj Saminathan 		case ATH11K_STATE_FTM:
842ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
843ba9177fcSCarl Huang 				    ar->state);
844ba9177fcSCarl Huang 			ret = -EIO;
845ba9177fcSCarl Huang 			break;
846ba9177fcSCarl Huang 		}
847ba9177fcSCarl Huang 	}
848ba9177fcSCarl Huang 
849ba9177fcSCarl Huang 	mutex_unlock(&ar->conf_mutex);
850ba9177fcSCarl Huang 	return ret;
851ba9177fcSCarl Huang }
852ba9177fcSCarl Huang 
ath11k_wow_init(struct ath11k * ar)853ba9177fcSCarl Huang int ath11k_wow_init(struct ath11k *ar)
854ba9177fcSCarl Huang {
855633469e3SNagarajan Maran 	if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))
856633469e3SNagarajan Maran 		return 0;
857ba9177fcSCarl Huang 
858ba9177fcSCarl Huang 	ar->wow.wowlan_support = ath11k_wowlan_support;
859ba9177fcSCarl Huang 
860ba9177fcSCarl Huang 	if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
861ba9177fcSCarl Huang 	    ATH11K_HW_TXRX_NATIVE_WIFI) {
862ba9177fcSCarl Huang 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
863ba9177fcSCarl Huang 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
864ba9177fcSCarl Huang 	}
865ba9177fcSCarl Huang 
866fec4b898SCarl Huang 	if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) {
867fec4b898SCarl Huang 		ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
868fec4b898SCarl Huang 		ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
869fec4b898SCarl Huang 	}
870fec4b898SCarl Huang 
871ba9177fcSCarl Huang 	ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS;
872ba9177fcSCarl Huang 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
873ba9177fcSCarl Huang 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
874ba9177fcSCarl Huang 
875ba9177fcSCarl Huang 	device_set_wakeup_capable(ar->ab->dev, true);
876ba9177fcSCarl Huang 
877ba9177fcSCarl Huang 	return 0;
878ba9177fcSCarl Huang }
879