xref: /freebsd/sys/contrib/dev/athk/ath11k/wow.c (revision 28348caeee6ee98251b0aaa026e8d52b5032e92c)
1dd4f32aeSBjoern A. Zeeb // SPDX-License-Identifier: BSD-3-Clause-Clear
2dd4f32aeSBjoern A. Zeeb /*
3dd4f32aeSBjoern A. Zeeb  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
4*28348caeSBjoern A. Zeeb  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
5dd4f32aeSBjoern A. Zeeb  */
6dd4f32aeSBjoern A. Zeeb 
7dd4f32aeSBjoern A. Zeeb #include <linux/delay.h>
8dd4f32aeSBjoern A. Zeeb 
9dd4f32aeSBjoern A. Zeeb #include "mac.h"
10*28348caeSBjoern A. Zeeb 
11*28348caeSBjoern A. Zeeb #include <net/mac80211.h>
12dd4f32aeSBjoern A. Zeeb #include "core.h"
13dd4f32aeSBjoern A. Zeeb #include "hif.h"
14dd4f32aeSBjoern A. Zeeb #include "debug.h"
15dd4f32aeSBjoern A. Zeeb #include "wmi.h"
16dd4f32aeSBjoern A. Zeeb #include "wow.h"
17*28348caeSBjoern A. Zeeb #include "dp_rx.h"
18*28348caeSBjoern A. Zeeb 
19*28348caeSBjoern A. Zeeb static const struct wiphy_wowlan_support ath11k_wowlan_support = {
20*28348caeSBjoern A. Zeeb 	.flags = WIPHY_WOWLAN_DISCONNECT |
21*28348caeSBjoern A. Zeeb 		 WIPHY_WOWLAN_MAGIC_PKT |
22*28348caeSBjoern A. Zeeb 		 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
23*28348caeSBjoern A. Zeeb 		 WIPHY_WOWLAN_GTK_REKEY_FAILURE,
24*28348caeSBjoern A. Zeeb 	.pattern_min_len = WOW_MIN_PATTERN_SIZE,
25*28348caeSBjoern A. Zeeb 	.pattern_max_len = WOW_MAX_PATTERN_SIZE,
26*28348caeSBjoern A. Zeeb 	.max_pkt_offset = WOW_MAX_PKT_OFFSET,
27*28348caeSBjoern A. Zeeb };
28dd4f32aeSBjoern A. Zeeb 
ath11k_wow_enable(struct ath11k_base * ab)29dd4f32aeSBjoern A. Zeeb int ath11k_wow_enable(struct ath11k_base *ab)
30dd4f32aeSBjoern A. Zeeb {
31dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
32dd4f32aeSBjoern A. Zeeb 	int i, ret;
33dd4f32aeSBjoern A. Zeeb 
34dd4f32aeSBjoern A. Zeeb 	clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
35dd4f32aeSBjoern A. Zeeb 
36dd4f32aeSBjoern A. Zeeb 	for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) {
37dd4f32aeSBjoern A. Zeeb 		reinit_completion(&ab->htc_suspend);
38dd4f32aeSBjoern A. Zeeb 
39dd4f32aeSBjoern A. Zeeb 		ret = ath11k_wmi_wow_enable(ar);
40dd4f32aeSBjoern A. Zeeb 		if (ret) {
41dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab, "failed to issue wow enable: %d\n", ret);
42dd4f32aeSBjoern A. Zeeb 			return ret;
43dd4f32aeSBjoern A. Zeeb 		}
44dd4f32aeSBjoern A. Zeeb 
45dd4f32aeSBjoern A. Zeeb 		ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ);
46dd4f32aeSBjoern A. Zeeb 		if (ret == 0) {
47dd4f32aeSBjoern A. Zeeb 			ath11k_warn(ab,
48dd4f32aeSBjoern A. Zeeb 				    "timed out while waiting for htc suspend completion\n");
49dd4f32aeSBjoern A. Zeeb 			return -ETIMEDOUT;
50dd4f32aeSBjoern A. Zeeb 		}
51dd4f32aeSBjoern A. Zeeb 
52dd4f32aeSBjoern A. Zeeb 		if (test_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags))
53dd4f32aeSBjoern A. Zeeb 			/* success, suspend complete received */
54dd4f32aeSBjoern A. Zeeb 			return 0;
55dd4f32aeSBjoern A. Zeeb 
56dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "htc suspend not complete, retrying (try %d)\n",
57dd4f32aeSBjoern A. Zeeb 			    i);
58dd4f32aeSBjoern A. Zeeb 		msleep(ATH11K_WOW_RETRY_WAIT_MS);
59dd4f32aeSBjoern A. Zeeb 	}
60dd4f32aeSBjoern A. Zeeb 
61dd4f32aeSBjoern A. Zeeb 	ath11k_warn(ab, "htc suspend not complete, failing after %d tries\n", i);
62dd4f32aeSBjoern A. Zeeb 
63dd4f32aeSBjoern A. Zeeb 	return -ETIMEDOUT;
64dd4f32aeSBjoern A. Zeeb }
65dd4f32aeSBjoern A. Zeeb 
ath11k_wow_wakeup(struct ath11k_base * ab)66dd4f32aeSBjoern A. Zeeb int ath11k_wow_wakeup(struct ath11k_base *ab)
67dd4f32aeSBjoern A. Zeeb {
68dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
69dd4f32aeSBjoern A. Zeeb 	int ret;
70dd4f32aeSBjoern A. Zeeb 
71*28348caeSBjoern A. Zeeb 	/* In the case of WCN6750, WoW wakeup is done
72*28348caeSBjoern A. Zeeb 	 * by sending SMP2P power save exit message
73*28348caeSBjoern A. Zeeb 	 * to the target processor.
74*28348caeSBjoern A. Zeeb 	 */
75*28348caeSBjoern A. Zeeb 	if (ab->hw_params.smp2p_wow_exit)
76*28348caeSBjoern A. Zeeb 		return 0;
77*28348caeSBjoern A. Zeeb 
78dd4f32aeSBjoern A. Zeeb 	reinit_completion(&ab->wow.wakeup_completed);
79dd4f32aeSBjoern A. Zeeb 
80dd4f32aeSBjoern A. Zeeb 	ret = ath11k_wmi_wow_host_wakeup_ind(ar);
81dd4f32aeSBjoern A. Zeeb 	if (ret) {
82dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "failed to send wow wakeup indication: %d\n",
83dd4f32aeSBjoern A. Zeeb 			    ret);
84dd4f32aeSBjoern A. Zeeb 		return ret;
85dd4f32aeSBjoern A. Zeeb 	}
86dd4f32aeSBjoern A. Zeeb 
87dd4f32aeSBjoern A. Zeeb 	ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ);
88dd4f32aeSBjoern A. Zeeb 	if (ret == 0) {
89dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n");
90dd4f32aeSBjoern A. Zeeb 		return -ETIMEDOUT;
91dd4f32aeSBjoern A. Zeeb 	}
92dd4f32aeSBjoern A. Zeeb 
93dd4f32aeSBjoern A. Zeeb 	return 0;
94dd4f32aeSBjoern A. Zeeb }
95*28348caeSBjoern A. Zeeb 
ath11k_wow_vif_cleanup(struct ath11k_vif * arvif)96*28348caeSBjoern A. Zeeb static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif)
97*28348caeSBjoern A. Zeeb {
98*28348caeSBjoern A. Zeeb 	struct ath11k *ar = arvif->ar;
99*28348caeSBjoern A. Zeeb 	int i, ret;
100*28348caeSBjoern A. Zeeb 
101*28348caeSBjoern A. Zeeb 	for (i = 0; i < WOW_EVENT_MAX; i++) {
102*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
103*28348caeSBjoern A. Zeeb 		if (ret) {
104*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
105*28348caeSBjoern A. Zeeb 				    wow_wakeup_event(i), arvif->vdev_id, ret);
106*28348caeSBjoern A. Zeeb 			return ret;
107*28348caeSBjoern A. Zeeb 		}
108*28348caeSBjoern A. Zeeb 	}
109*28348caeSBjoern A. Zeeb 
110*28348caeSBjoern A. Zeeb 	for (i = 0; i < ar->wow.max_num_patterns; i++) {
111*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
112*28348caeSBjoern A. Zeeb 		if (ret) {
113*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n",
114*28348caeSBjoern A. Zeeb 				    i, arvif->vdev_id, ret);
115*28348caeSBjoern A. Zeeb 			return ret;
116*28348caeSBjoern A. Zeeb 		}
117*28348caeSBjoern A. Zeeb 	}
118*28348caeSBjoern A. Zeeb 
119*28348caeSBjoern A. Zeeb 	return 0;
120*28348caeSBjoern A. Zeeb }
121*28348caeSBjoern A. Zeeb 
ath11k_wow_cleanup(struct ath11k * ar)122*28348caeSBjoern A. Zeeb static int ath11k_wow_cleanup(struct ath11k *ar)
123*28348caeSBjoern A. Zeeb {
124*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
125*28348caeSBjoern A. Zeeb 	int ret;
126*28348caeSBjoern A. Zeeb 
127*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
128*28348caeSBjoern A. Zeeb 
129*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
130*28348caeSBjoern A. Zeeb 		ret = ath11k_wow_vif_cleanup(arvif);
131*28348caeSBjoern A. Zeeb 		if (ret) {
132*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n",
133*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, ret);
134*28348caeSBjoern A. Zeeb 			return ret;
135*28348caeSBjoern A. Zeeb 		}
136*28348caeSBjoern A. Zeeb 	}
137*28348caeSBjoern A. Zeeb 
138*28348caeSBjoern A. Zeeb 	return 0;
139*28348caeSBjoern A. Zeeb }
140*28348caeSBjoern A. Zeeb 
141*28348caeSBjoern A. Zeeb /* Convert a 802.3 format to a 802.11 format.
142*28348caeSBjoern A. Zeeb  *         +------------+-----------+--------+----------------+
143*28348caeSBjoern A. Zeeb  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
144*28348caeSBjoern A. Zeeb  *         +------------+-----------+--------+----------------+
145*28348caeSBjoern A. Zeeb  *                |__         |_______    |____________  |________
146*28348caeSBjoern A. Zeeb  *                   |                |                |          |
147*28348caeSBjoern A. Zeeb  *         +--+------------+----+-----------+---------------+-----------+
148*28348caeSBjoern A. Zeeb  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
149*28348caeSBjoern A. Zeeb  *         +--+------------+----+-----------+---------------+-----------+
150*28348caeSBjoern A. Zeeb  */
ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern * new,const struct cfg80211_pkt_pattern * old)151*28348caeSBjoern A. Zeeb static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
152*28348caeSBjoern A. Zeeb 					     const struct cfg80211_pkt_pattern *old)
153*28348caeSBjoern A. Zeeb {
154*28348caeSBjoern A. Zeeb 	u8 hdr_8023_pattern[ETH_HLEN] = {};
155*28348caeSBjoern A. Zeeb 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
156*28348caeSBjoern A. Zeeb 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
157*28348caeSBjoern A. Zeeb 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
158*28348caeSBjoern A. Zeeb 
159*28348caeSBjoern A. Zeeb 	int total_len = old->pkt_offset + old->pattern_len;
160*28348caeSBjoern A. Zeeb 	int hdr_80211_end_offset;
161*28348caeSBjoern A. Zeeb 
162*28348caeSBjoern A. Zeeb 	struct ieee80211_hdr_3addr *new_hdr_pattern =
163*28348caeSBjoern A. Zeeb 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
164*28348caeSBjoern A. Zeeb 	struct ieee80211_hdr_3addr *new_hdr_mask =
165*28348caeSBjoern A. Zeeb 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
166*28348caeSBjoern A. Zeeb 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
167*28348caeSBjoern A. Zeeb 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
168*28348caeSBjoern A. Zeeb 	int hdr_len = sizeof(*new_hdr_pattern);
169*28348caeSBjoern A. Zeeb 
170*28348caeSBjoern A. Zeeb 	struct rfc1042_hdr *new_rfc_pattern =
171*28348caeSBjoern A. Zeeb 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
172*28348caeSBjoern A. Zeeb 	struct rfc1042_hdr *new_rfc_mask =
173*28348caeSBjoern A. Zeeb 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
174*28348caeSBjoern A. Zeeb 	int rfc_len = sizeof(*new_rfc_pattern);
175*28348caeSBjoern A. Zeeb 
176*28348caeSBjoern A. Zeeb 	memcpy(hdr_8023_pattern + old->pkt_offset,
177*28348caeSBjoern A. Zeeb 	       old->pattern, ETH_HLEN - old->pkt_offset);
178*28348caeSBjoern A. Zeeb 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
179*28348caeSBjoern A. Zeeb 	       old->mask, ETH_HLEN - old->pkt_offset);
180*28348caeSBjoern A. Zeeb 
181*28348caeSBjoern A. Zeeb 	/* Copy destination address */
182*28348caeSBjoern A. Zeeb 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
183*28348caeSBjoern A. Zeeb 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
184*28348caeSBjoern A. Zeeb 
185*28348caeSBjoern A. Zeeb 	/* Copy source address */
186*28348caeSBjoern A. Zeeb 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
187*28348caeSBjoern A. Zeeb 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
188*28348caeSBjoern A. Zeeb 
189*28348caeSBjoern A. Zeeb 	/* Copy logic link type */
190*28348caeSBjoern A. Zeeb 	memcpy(&new_rfc_pattern->snap_type,
191*28348caeSBjoern A. Zeeb 	       &old_hdr_pattern->h_proto,
192*28348caeSBjoern A. Zeeb 	       sizeof(old_hdr_pattern->h_proto));
193*28348caeSBjoern A. Zeeb 	memcpy(&new_rfc_mask->snap_type,
194*28348caeSBjoern A. Zeeb 	       &old_hdr_mask->h_proto,
195*28348caeSBjoern A. Zeeb 	       sizeof(old_hdr_mask->h_proto));
196*28348caeSBjoern A. Zeeb 
197*28348caeSBjoern A. Zeeb 	/* Compute new pkt_offset */
198*28348caeSBjoern A. Zeeb 	if (old->pkt_offset < ETH_ALEN)
199*28348caeSBjoern A. Zeeb 		new->pkt_offset = old->pkt_offset +
200*28348caeSBjoern A. Zeeb 			offsetof(struct ieee80211_hdr_3addr, addr1);
201*28348caeSBjoern A. Zeeb 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
202*28348caeSBjoern A. Zeeb 		new->pkt_offset = old->pkt_offset +
203*28348caeSBjoern A. Zeeb 			offsetof(struct ieee80211_hdr_3addr, addr3) -
204*28348caeSBjoern A. Zeeb 			offsetof(struct ethhdr, h_source);
205*28348caeSBjoern A. Zeeb 	else
206*28348caeSBjoern A. Zeeb 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
207*28348caeSBjoern A. Zeeb 
208*28348caeSBjoern A. Zeeb 	/* Compute new hdr end offset */
209*28348caeSBjoern A. Zeeb 	if (total_len > ETH_HLEN)
210*28348caeSBjoern A. Zeeb 		hdr_80211_end_offset = hdr_len + rfc_len;
211*28348caeSBjoern A. Zeeb 	else if (total_len > offsetof(struct ethhdr, h_proto))
212*28348caeSBjoern A. Zeeb 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
213*28348caeSBjoern A. Zeeb 	else if (total_len > ETH_ALEN)
214*28348caeSBjoern A. Zeeb 		hdr_80211_end_offset = total_len - ETH_ALEN +
215*28348caeSBjoern A. Zeeb 			offsetof(struct ieee80211_hdr_3addr, addr3);
216*28348caeSBjoern A. Zeeb 	else
217*28348caeSBjoern A. Zeeb 		hdr_80211_end_offset = total_len +
218*28348caeSBjoern A. Zeeb 			offsetof(struct ieee80211_hdr_3addr, addr1);
219*28348caeSBjoern A. Zeeb 
220*28348caeSBjoern A. Zeeb 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
221*28348caeSBjoern A. Zeeb 
222*28348caeSBjoern A. Zeeb 	memcpy((u8 *)new->pattern,
223*28348caeSBjoern A. Zeeb 	       hdr_80211_pattern + new->pkt_offset,
224*28348caeSBjoern A. Zeeb 	       new->pattern_len);
225*28348caeSBjoern A. Zeeb 	memcpy((u8 *)new->mask,
226*28348caeSBjoern A. Zeeb 	       hdr_80211_bit_mask + new->pkt_offset,
227*28348caeSBjoern A. Zeeb 	       new->pattern_len);
228*28348caeSBjoern A. Zeeb 
229*28348caeSBjoern A. Zeeb 	if (total_len > ETH_HLEN) {
230*28348caeSBjoern A. Zeeb 		/* Copy frame body */
231*28348caeSBjoern A. Zeeb 		memcpy((u8 *)new->pattern + new->pattern_len,
232*28348caeSBjoern A. Zeeb 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
233*28348caeSBjoern A. Zeeb 		       total_len - ETH_HLEN);
234*28348caeSBjoern A. Zeeb 		memcpy((u8 *)new->mask + new->pattern_len,
235*28348caeSBjoern A. Zeeb 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
236*28348caeSBjoern A. Zeeb 		       total_len - ETH_HLEN);
237*28348caeSBjoern A. Zeeb 
238*28348caeSBjoern A. Zeeb 		new->pattern_len += total_len - ETH_HLEN;
239*28348caeSBjoern A. Zeeb 	}
240*28348caeSBjoern A. Zeeb }
241*28348caeSBjoern A. Zeeb 
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)242*28348caeSBjoern A. Zeeb static int ath11k_wmi_pno_check_and_convert(struct ath11k *ar, u32 vdev_id,
243*28348caeSBjoern A. Zeeb 					    struct cfg80211_sched_scan_request *nd_config,
244*28348caeSBjoern A. Zeeb 					    struct wmi_pno_scan_req *pno)
245*28348caeSBjoern A. Zeeb {
246*28348caeSBjoern A. Zeeb 	int i, j;
247*28348caeSBjoern A. Zeeb 	u8 ssid_len;
248*28348caeSBjoern A. Zeeb 
249*28348caeSBjoern A. Zeeb 	pno->enable = 1;
250*28348caeSBjoern A. Zeeb 	pno->vdev_id = vdev_id;
251*28348caeSBjoern A. Zeeb 	pno->uc_networks_count = nd_config->n_match_sets;
252*28348caeSBjoern A. Zeeb 
253*28348caeSBjoern A. Zeeb 	if (!pno->uc_networks_count ||
254*28348caeSBjoern A. Zeeb 	    pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
255*28348caeSBjoern A. Zeeb 		return -EINVAL;
256*28348caeSBjoern A. Zeeb 
257*28348caeSBjoern A. Zeeb 	if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
258*28348caeSBjoern A. Zeeb 		return -EINVAL;
259*28348caeSBjoern A. Zeeb 
260*28348caeSBjoern A. Zeeb 	/* Filling per profile params */
261*28348caeSBjoern A. Zeeb 	for (i = 0; i < pno->uc_networks_count; i++) {
262*28348caeSBjoern A. Zeeb 		ssid_len = nd_config->match_sets[i].ssid.ssid_len;
263*28348caeSBjoern A. Zeeb 
264*28348caeSBjoern A. Zeeb 		if (ssid_len == 0 || ssid_len > 32)
265*28348caeSBjoern A. Zeeb 			return -EINVAL;
266*28348caeSBjoern A. Zeeb 
267*28348caeSBjoern A. Zeeb 		pno->a_networks[i].ssid.ssid_len = ssid_len;
268*28348caeSBjoern A. Zeeb 
269*28348caeSBjoern A. Zeeb 		memcpy(pno->a_networks[i].ssid.ssid,
270*28348caeSBjoern A. Zeeb 		       nd_config->match_sets[i].ssid.ssid,
271*28348caeSBjoern A. Zeeb 		       nd_config->match_sets[i].ssid.ssid_len);
272*28348caeSBjoern A. Zeeb 		pno->a_networks[i].authentication = 0;
273*28348caeSBjoern A. Zeeb 		pno->a_networks[i].encryption     = 0;
274*28348caeSBjoern A. Zeeb 		pno->a_networks[i].bcast_nw_type  = 0;
275*28348caeSBjoern A. Zeeb 
276*28348caeSBjoern A. Zeeb 		/* Copying list of valid channel into request */
277*28348caeSBjoern A. Zeeb 		pno->a_networks[i].channel_count = nd_config->n_channels;
278*28348caeSBjoern A. Zeeb 		pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
279*28348caeSBjoern A. Zeeb 
280*28348caeSBjoern A. Zeeb 		for (j = 0; j < nd_config->n_channels; j++) {
281*28348caeSBjoern A. Zeeb 			pno->a_networks[i].channels[j] =
282*28348caeSBjoern A. Zeeb 					nd_config->channels[j]->center_freq;
283*28348caeSBjoern A. Zeeb 		}
284*28348caeSBjoern A. Zeeb 	}
285*28348caeSBjoern A. Zeeb 
286*28348caeSBjoern A. Zeeb 	/* set scan to passive if no SSIDs are specified in the request */
287*28348caeSBjoern A. Zeeb 	if (nd_config->n_ssids == 0)
288*28348caeSBjoern A. Zeeb 		pno->do_passive_scan = true;
289*28348caeSBjoern A. Zeeb 	else
290*28348caeSBjoern A. Zeeb 		pno->do_passive_scan = false;
291*28348caeSBjoern A. Zeeb 
292*28348caeSBjoern A. Zeeb 	for (i = 0; i < nd_config->n_ssids; i++) {
293*28348caeSBjoern A. Zeeb 		j = 0;
294*28348caeSBjoern A. Zeeb 		while (j < pno->uc_networks_count) {
295*28348caeSBjoern A. Zeeb 			if (pno->a_networks[j].ssid.ssid_len ==
296*28348caeSBjoern A. Zeeb 				nd_config->ssids[i].ssid_len &&
297*28348caeSBjoern A. Zeeb 			(memcmp(pno->a_networks[j].ssid.ssid,
298*28348caeSBjoern A. Zeeb 				nd_config->ssids[i].ssid,
299*28348caeSBjoern A. Zeeb 				pno->a_networks[j].ssid.ssid_len) == 0)) {
300*28348caeSBjoern A. Zeeb 				pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
301*28348caeSBjoern A. Zeeb 				break;
302*28348caeSBjoern A. Zeeb 			}
303*28348caeSBjoern A. Zeeb 			j++;
304*28348caeSBjoern A. Zeeb 		}
305*28348caeSBjoern A. Zeeb 	}
306*28348caeSBjoern A. Zeeb 
307*28348caeSBjoern A. Zeeb 	if (nd_config->n_scan_plans == 2) {
308*28348caeSBjoern A. Zeeb 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
309*28348caeSBjoern A. Zeeb 		pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
310*28348caeSBjoern A. Zeeb 		pno->slow_scan_period =
311*28348caeSBjoern A. Zeeb 			nd_config->scan_plans[1].interval * MSEC_PER_SEC;
312*28348caeSBjoern A. Zeeb 	} else if (nd_config->n_scan_plans == 1) {
313*28348caeSBjoern A. Zeeb 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
314*28348caeSBjoern A. Zeeb 		pno->fast_scan_max_cycles = 1;
315*28348caeSBjoern A. Zeeb 		pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
316*28348caeSBjoern A. Zeeb 	} else {
317*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "Invalid number of scan plans %d !!",
318*28348caeSBjoern A. Zeeb 			    nd_config->n_scan_plans);
319*28348caeSBjoern A. Zeeb 	}
320*28348caeSBjoern A. Zeeb 
321*28348caeSBjoern A. Zeeb 	if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
322*28348caeSBjoern A. Zeeb 		/* enable mac randomization */
323*28348caeSBjoern A. Zeeb 		pno->enable_pno_scan_randomization = 1;
324*28348caeSBjoern A. Zeeb 		memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
325*28348caeSBjoern A. Zeeb 		memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
326*28348caeSBjoern A. Zeeb 	}
327*28348caeSBjoern A. Zeeb 
328*28348caeSBjoern A. Zeeb 	pno->delay_start_time = nd_config->delay;
329*28348caeSBjoern A. Zeeb 
330*28348caeSBjoern A. Zeeb 	/* Current FW does not support min-max range for dwell time */
331*28348caeSBjoern A. Zeeb 	pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
332*28348caeSBjoern A. Zeeb 	pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
333*28348caeSBjoern A. Zeeb 
334*28348caeSBjoern A. Zeeb 	return 0;
335*28348caeSBjoern A. Zeeb }
336*28348caeSBjoern A. Zeeb 
ath11k_vif_wow_set_wakeups(struct ath11k_vif * arvif,struct cfg80211_wowlan * wowlan)337*28348caeSBjoern A. Zeeb static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif,
338*28348caeSBjoern A. Zeeb 				      struct cfg80211_wowlan *wowlan)
339*28348caeSBjoern A. Zeeb {
340*28348caeSBjoern A. Zeeb 	int ret, i;
341*28348caeSBjoern A. Zeeb 	unsigned long wow_mask = 0;
342*28348caeSBjoern A. Zeeb 	struct ath11k *ar = arvif->ar;
343*28348caeSBjoern A. Zeeb 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
344*28348caeSBjoern A. Zeeb 	int pattern_id = 0;
345*28348caeSBjoern A. Zeeb 
346*28348caeSBjoern A. Zeeb 	/* Setup requested WOW features */
347*28348caeSBjoern A. Zeeb 	switch (arvif->vdev_type) {
348*28348caeSBjoern A. Zeeb 	case WMI_VDEV_TYPE_IBSS:
349*28348caeSBjoern A. Zeeb 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
350*28348caeSBjoern A. Zeeb 		fallthrough;
351*28348caeSBjoern A. Zeeb 	case WMI_VDEV_TYPE_AP:
352*28348caeSBjoern A. Zeeb 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
353*28348caeSBjoern A. Zeeb 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
354*28348caeSBjoern A. Zeeb 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
355*28348caeSBjoern A. Zeeb 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
356*28348caeSBjoern A. Zeeb 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
357*28348caeSBjoern A. Zeeb 		__set_bit(WOW_HTT_EVENT, &wow_mask);
358*28348caeSBjoern A. Zeeb 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
359*28348caeSBjoern A. Zeeb 		break;
360*28348caeSBjoern A. Zeeb 	case WMI_VDEV_TYPE_STA:
361*28348caeSBjoern A. Zeeb 		if (wowlan->disconnect) {
362*28348caeSBjoern A. Zeeb 			__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
363*28348caeSBjoern A. Zeeb 			__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
364*28348caeSBjoern A. Zeeb 			__set_bit(WOW_BMISS_EVENT, &wow_mask);
365*28348caeSBjoern A. Zeeb 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
366*28348caeSBjoern A. Zeeb 		}
367*28348caeSBjoern A. Zeeb 
368*28348caeSBjoern A. Zeeb 		if (wowlan->magic_pkt)
369*28348caeSBjoern A. Zeeb 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
370*28348caeSBjoern A. Zeeb 
371*28348caeSBjoern A. Zeeb 		if (wowlan->nd_config) {
372*28348caeSBjoern A. Zeeb 			struct wmi_pno_scan_req *pno;
373*28348caeSBjoern A. Zeeb 			int ret;
374*28348caeSBjoern A. Zeeb 
375*28348caeSBjoern A. Zeeb 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
376*28348caeSBjoern A. Zeeb 			if (!pno)
377*28348caeSBjoern A. Zeeb 				return -ENOMEM;
378*28348caeSBjoern A. Zeeb 
379*28348caeSBjoern A. Zeeb 			ar->nlo_enabled = true;
380*28348caeSBjoern A. Zeeb 
381*28348caeSBjoern A. Zeeb 			ret = ath11k_wmi_pno_check_and_convert(ar, arvif->vdev_id,
382*28348caeSBjoern A. Zeeb 							       wowlan->nd_config, pno);
383*28348caeSBjoern A. Zeeb 			if (!ret) {
384*28348caeSBjoern A. Zeeb 				ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
385*28348caeSBjoern A. Zeeb 				__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
386*28348caeSBjoern A. Zeeb 			}
387*28348caeSBjoern A. Zeeb 
388*28348caeSBjoern A. Zeeb 			kfree(pno);
389*28348caeSBjoern A. Zeeb 		}
390*28348caeSBjoern A. Zeeb 		break;
391*28348caeSBjoern A. Zeeb 	default:
392*28348caeSBjoern A. Zeeb 		break;
393*28348caeSBjoern A. Zeeb 	}
394*28348caeSBjoern A. Zeeb 
395*28348caeSBjoern A. Zeeb 	for (i = 0; i < wowlan->n_patterns; i++) {
396*28348caeSBjoern A. Zeeb 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
397*28348caeSBjoern A. Zeeb 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
398*28348caeSBjoern A. Zeeb 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
399*28348caeSBjoern A. Zeeb 		struct cfg80211_pkt_pattern new_pattern = {};
400*28348caeSBjoern A. Zeeb 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
401*28348caeSBjoern A. Zeeb 		int j;
402*28348caeSBjoern A. Zeeb 
403*28348caeSBjoern A. Zeeb 		new_pattern.pattern = ath_pattern;
404*28348caeSBjoern A. Zeeb 		new_pattern.mask = ath_bitmask;
405*28348caeSBjoern A. Zeeb 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
406*28348caeSBjoern A. Zeeb 			continue;
407*28348caeSBjoern A. Zeeb 		/* convert bytemask to bitmask */
408*28348caeSBjoern A. Zeeb 		for (j = 0; j < patterns[i].pattern_len; j++)
409*28348caeSBjoern A. Zeeb 			if (patterns[i].mask[j / 8] & BIT(j % 8))
410*28348caeSBjoern A. Zeeb 				bitmask[j] = 0xff;
411*28348caeSBjoern A. Zeeb 		old_pattern.mask = bitmask;
412*28348caeSBjoern A. Zeeb 
413*28348caeSBjoern A. Zeeb 		if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
414*28348caeSBjoern A. Zeeb 		    ATH11K_HW_TXRX_NATIVE_WIFI) {
415*28348caeSBjoern A. Zeeb 			if (patterns[i].pkt_offset < ETH_HLEN) {
416*28348caeSBjoern A. Zeeb 				u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {};
417*28348caeSBjoern A. Zeeb 
418*28348caeSBjoern A. Zeeb 				memcpy(pattern_ext, old_pattern.pattern,
419*28348caeSBjoern A. Zeeb 				       old_pattern.pattern_len);
420*28348caeSBjoern A. Zeeb 				old_pattern.pattern = pattern_ext;
421*28348caeSBjoern A. Zeeb 				ath11k_wow_convert_8023_to_80211(&new_pattern,
422*28348caeSBjoern A. Zeeb 								 &old_pattern);
423*28348caeSBjoern A. Zeeb 			} else {
424*28348caeSBjoern A. Zeeb 				new_pattern = old_pattern;
425*28348caeSBjoern A. Zeeb 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
426*28348caeSBjoern A. Zeeb 			}
427*28348caeSBjoern A. Zeeb 		}
428*28348caeSBjoern A. Zeeb 
429*28348caeSBjoern A. Zeeb 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
430*28348caeSBjoern A. Zeeb 			return -EINVAL;
431*28348caeSBjoern A. Zeeb 
432*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id,
433*28348caeSBjoern A. Zeeb 						 pattern_id,
434*28348caeSBjoern A. Zeeb 						 new_pattern.pattern,
435*28348caeSBjoern A. Zeeb 						 new_pattern.mask,
436*28348caeSBjoern A. Zeeb 						 new_pattern.pattern_len,
437*28348caeSBjoern A. Zeeb 						 new_pattern.pkt_offset);
438*28348caeSBjoern A. Zeeb 		if (ret) {
439*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n",
440*28348caeSBjoern A. Zeeb 				    pattern_id,
441*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, ret);
442*28348caeSBjoern A. Zeeb 			return ret;
443*28348caeSBjoern A. Zeeb 		}
444*28348caeSBjoern A. Zeeb 
445*28348caeSBjoern A. Zeeb 		pattern_id++;
446*28348caeSBjoern A. Zeeb 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
447*28348caeSBjoern A. Zeeb 	}
448*28348caeSBjoern A. Zeeb 
449*28348caeSBjoern A. Zeeb 	for (i = 0; i < WOW_EVENT_MAX; i++) {
450*28348caeSBjoern A. Zeeb 		if (!test_bit(i, &wow_mask))
451*28348caeSBjoern A. Zeeb 			continue;
452*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
453*28348caeSBjoern A. Zeeb 		if (ret) {
454*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n",
455*28348caeSBjoern A. Zeeb 				    wow_wakeup_event(i), arvif->vdev_id, ret);
456*28348caeSBjoern A. Zeeb 			return ret;
457*28348caeSBjoern A. Zeeb 		}
458*28348caeSBjoern A. Zeeb 	}
459*28348caeSBjoern A. Zeeb 
460*28348caeSBjoern A. Zeeb 	return 0;
461*28348caeSBjoern A. Zeeb }
462*28348caeSBjoern A. Zeeb 
ath11k_wow_set_wakeups(struct ath11k * ar,struct cfg80211_wowlan * wowlan)463*28348caeSBjoern A. Zeeb static int ath11k_wow_set_wakeups(struct ath11k *ar,
464*28348caeSBjoern A. Zeeb 				  struct cfg80211_wowlan *wowlan)
465*28348caeSBjoern A. Zeeb {
466*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
467*28348caeSBjoern A. Zeeb 	int ret;
468*28348caeSBjoern A. Zeeb 
469*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
470*28348caeSBjoern A. Zeeb 
471*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
472*28348caeSBjoern A. Zeeb 		ret = ath11k_vif_wow_set_wakeups(arvif, wowlan);
473*28348caeSBjoern A. Zeeb 		if (ret) {
474*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n",
475*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, ret);
476*28348caeSBjoern A. Zeeb 			return ret;
477*28348caeSBjoern A. Zeeb 		}
478*28348caeSBjoern A. Zeeb 	}
479*28348caeSBjoern A. Zeeb 
480*28348caeSBjoern A. Zeeb 	return 0;
481*28348caeSBjoern A. Zeeb }
482*28348caeSBjoern A. Zeeb 
ath11k_vif_wow_clean_nlo(struct ath11k_vif * arvif)483*28348caeSBjoern A. Zeeb static int ath11k_vif_wow_clean_nlo(struct ath11k_vif *arvif)
484*28348caeSBjoern A. Zeeb {
485*28348caeSBjoern A. Zeeb 	int ret = 0;
486*28348caeSBjoern A. Zeeb 	struct ath11k *ar = arvif->ar;
487*28348caeSBjoern A. Zeeb 
488*28348caeSBjoern A. Zeeb 	switch (arvif->vdev_type) {
489*28348caeSBjoern A. Zeeb 	case WMI_VDEV_TYPE_STA:
490*28348caeSBjoern A. Zeeb 		if (ar->nlo_enabled) {
491*28348caeSBjoern A. Zeeb 			struct wmi_pno_scan_req *pno;
492*28348caeSBjoern A. Zeeb 
493*28348caeSBjoern A. Zeeb 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
494*28348caeSBjoern A. Zeeb 			if (!pno)
495*28348caeSBjoern A. Zeeb 				return -ENOMEM;
496*28348caeSBjoern A. Zeeb 
497*28348caeSBjoern A. Zeeb 			pno->enable = 0;
498*28348caeSBjoern A. Zeeb 			ar->nlo_enabled = false;
499*28348caeSBjoern A. Zeeb 			ret = ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
500*28348caeSBjoern A. Zeeb 			kfree(pno);
501*28348caeSBjoern A. Zeeb 		}
502*28348caeSBjoern A. Zeeb 		break;
503*28348caeSBjoern A. Zeeb 	default:
504*28348caeSBjoern A. Zeeb 		break;
505*28348caeSBjoern A. Zeeb 	}
506*28348caeSBjoern A. Zeeb 	return ret;
507*28348caeSBjoern A. Zeeb }
508*28348caeSBjoern A. Zeeb 
ath11k_wow_nlo_cleanup(struct ath11k * ar)509*28348caeSBjoern A. Zeeb static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
510*28348caeSBjoern A. Zeeb {
511*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
512*28348caeSBjoern A. Zeeb 	int ret;
513*28348caeSBjoern A. Zeeb 
514*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
515*28348caeSBjoern A. Zeeb 
516*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
517*28348caeSBjoern A. Zeeb 		ret = ath11k_vif_wow_clean_nlo(arvif);
518*28348caeSBjoern A. Zeeb 		if (ret) {
519*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n",
520*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, ret);
521*28348caeSBjoern A. Zeeb 			return ret;
522*28348caeSBjoern A. Zeeb 		}
523*28348caeSBjoern A. Zeeb 	}
524*28348caeSBjoern A. Zeeb 
525*28348caeSBjoern A. Zeeb 	return 0;
526*28348caeSBjoern A. Zeeb }
527*28348caeSBjoern A. Zeeb 
ath11k_wow_set_hw_filter(struct ath11k * ar)528*28348caeSBjoern A. Zeeb static int ath11k_wow_set_hw_filter(struct ath11k *ar)
529*28348caeSBjoern A. Zeeb {
530*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
531*28348caeSBjoern A. Zeeb 	u32 bitmap;
532*28348caeSBjoern A. Zeeb 	int ret;
533*28348caeSBjoern A. Zeeb 
534*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
535*28348caeSBjoern A. Zeeb 
536*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
537*28348caeSBjoern A. Zeeb 		bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC |
538*28348caeSBjoern A. Zeeb 			WMI_HW_DATA_FILTER_DROP_NON_ARP_BC;
539*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id,
540*28348caeSBjoern A. Zeeb 						    bitmap,
541*28348caeSBjoern A. Zeeb 						    true);
542*28348caeSBjoern A. Zeeb 		if (ret) {
543*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
544*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, ret);
545*28348caeSBjoern A. Zeeb 			return ret;
546*28348caeSBjoern A. Zeeb 		}
547*28348caeSBjoern A. Zeeb 	}
548*28348caeSBjoern A. Zeeb 
549*28348caeSBjoern A. Zeeb 	return 0;
550*28348caeSBjoern A. Zeeb }
551*28348caeSBjoern A. Zeeb 
ath11k_wow_clear_hw_filter(struct ath11k * ar)552*28348caeSBjoern A. Zeeb static int ath11k_wow_clear_hw_filter(struct ath11k *ar)
553*28348caeSBjoern A. Zeeb {
554*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
555*28348caeSBjoern A. Zeeb 	int ret;
556*28348caeSBjoern A. Zeeb 
557*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
558*28348caeSBjoern A. Zeeb 
559*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
560*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false);
561*28348caeSBjoern A. Zeeb 
562*28348caeSBjoern A. Zeeb 		if (ret) {
563*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
564*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, ret);
565*28348caeSBjoern A. Zeeb 			return ret;
566*28348caeSBjoern A. Zeeb 		}
567*28348caeSBjoern A. Zeeb 	}
568*28348caeSBjoern A. Zeeb 
569*28348caeSBjoern A. Zeeb 	return 0;
570*28348caeSBjoern A. Zeeb }
571*28348caeSBjoern A. Zeeb 
ath11k_wow_arp_ns_offload(struct ath11k * ar,bool enable)572*28348caeSBjoern A. Zeeb static int ath11k_wow_arp_ns_offload(struct ath11k *ar, bool enable)
573*28348caeSBjoern A. Zeeb {
574*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
575*28348caeSBjoern A. Zeeb 	int ret;
576*28348caeSBjoern A. Zeeb 
577*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
578*28348caeSBjoern A. Zeeb 
579*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
580*28348caeSBjoern A. Zeeb 		if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
581*28348caeSBjoern A. Zeeb 			continue;
582*28348caeSBjoern A. Zeeb 
583*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_arp_ns_offload(ar, arvif, enable);
584*28348caeSBjoern A. Zeeb 
585*28348caeSBjoern A. Zeeb 		if (ret) {
586*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n",
587*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, enable, ret);
588*28348caeSBjoern A. Zeeb 			return ret;
589*28348caeSBjoern A. Zeeb 		}
590*28348caeSBjoern A. Zeeb 	}
591*28348caeSBjoern A. Zeeb 
592*28348caeSBjoern A. Zeeb 	return 0;
593*28348caeSBjoern A. Zeeb }
594*28348caeSBjoern A. Zeeb 
ath11k_gtk_rekey_offload(struct ath11k * ar,bool enable)595*28348caeSBjoern A. Zeeb static int ath11k_gtk_rekey_offload(struct ath11k *ar, bool enable)
596*28348caeSBjoern A. Zeeb {
597*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
598*28348caeSBjoern A. Zeeb 	int ret;
599*28348caeSBjoern A. Zeeb 
600*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
601*28348caeSBjoern A. Zeeb 
602*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
603*28348caeSBjoern A. Zeeb 		if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
604*28348caeSBjoern A. Zeeb 		    !arvif->is_up ||
605*28348caeSBjoern A. Zeeb 		    !arvif->rekey_data.enable_offload)
606*28348caeSBjoern A. Zeeb 			continue;
607*28348caeSBjoern A. Zeeb 
608*28348caeSBjoern A. Zeeb 		/* get rekey info before disable rekey offload */
609*28348caeSBjoern A. Zeeb 		if (!enable) {
610*28348caeSBjoern A. Zeeb 			ret = ath11k_wmi_gtk_rekey_getinfo(ar, arvif);
611*28348caeSBjoern A. Zeeb 			if (ret) {
612*28348caeSBjoern A. Zeeb 				ath11k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n",
613*28348caeSBjoern A. Zeeb 					    arvif->vdev_id, ret);
614*28348caeSBjoern A. Zeeb 				return ret;
615*28348caeSBjoern A. Zeeb 			}
616*28348caeSBjoern A. Zeeb 		}
617*28348caeSBjoern A. Zeeb 
618*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_gtk_rekey_offload(ar, arvif, enable);
619*28348caeSBjoern A. Zeeb 
620*28348caeSBjoern A. Zeeb 		if (ret) {
621*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n",
622*28348caeSBjoern A. Zeeb 				    arvif->vdev_id, enable, ret);
623*28348caeSBjoern A. Zeeb 			return ret;
624*28348caeSBjoern A. Zeeb 		}
625*28348caeSBjoern A. Zeeb 	}
626*28348caeSBjoern A. Zeeb 
627*28348caeSBjoern A. Zeeb 	return 0;
628*28348caeSBjoern A. Zeeb }
629*28348caeSBjoern A. Zeeb 
ath11k_wow_protocol_offload(struct ath11k * ar,bool enable)630*28348caeSBjoern A. Zeeb static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable)
631*28348caeSBjoern A. Zeeb {
632*28348caeSBjoern A. Zeeb 	int ret;
633*28348caeSBjoern A. Zeeb 
634*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_arp_ns_offload(ar, enable);
635*28348caeSBjoern A. Zeeb 	if (ret) {
636*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to offload ARP and NS %d %d\n",
637*28348caeSBjoern A. Zeeb 			    enable, ret);
638*28348caeSBjoern A. Zeeb 		return ret;
639*28348caeSBjoern A. Zeeb 	}
640*28348caeSBjoern A. Zeeb 
641*28348caeSBjoern A. Zeeb 	ret = ath11k_gtk_rekey_offload(ar, enable);
642*28348caeSBjoern A. Zeeb 	if (ret) {
643*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to offload gtk rekey %d %d\n",
644*28348caeSBjoern A. Zeeb 			    enable, ret);
645*28348caeSBjoern A. Zeeb 		return ret;
646*28348caeSBjoern A. Zeeb 	}
647*28348caeSBjoern A. Zeeb 
648*28348caeSBjoern A. Zeeb 	return 0;
649*28348caeSBjoern A. Zeeb }
650*28348caeSBjoern A. Zeeb 
ath11k_wow_set_keepalive(struct ath11k * ar,enum wmi_sta_keepalive_method method,u32 interval)651*28348caeSBjoern A. Zeeb static int ath11k_wow_set_keepalive(struct ath11k *ar,
652*28348caeSBjoern A. Zeeb 				    enum wmi_sta_keepalive_method method,
653*28348caeSBjoern A. Zeeb 				    u32 interval)
654*28348caeSBjoern A. Zeeb {
655*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
656*28348caeSBjoern A. Zeeb 	int ret;
657*28348caeSBjoern A. Zeeb 
658*28348caeSBjoern A. Zeeb 	lockdep_assert_held(&ar->conf_mutex);
659*28348caeSBjoern A. Zeeb 
660*28348caeSBjoern A. Zeeb 	list_for_each_entry(arvif, &ar->arvifs, list) {
661*28348caeSBjoern A. Zeeb 		ret = ath11k_mac_vif_set_keepalive(arvif, method, interval);
662*28348caeSBjoern A. Zeeb 		if (ret)
663*28348caeSBjoern A. Zeeb 			return ret;
664*28348caeSBjoern A. Zeeb 	}
665*28348caeSBjoern A. Zeeb 
666*28348caeSBjoern A. Zeeb 	return 0;
667*28348caeSBjoern A. Zeeb }
668*28348caeSBjoern A. Zeeb 
ath11k_wow_op_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)669*28348caeSBjoern A. Zeeb int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
670*28348caeSBjoern A. Zeeb 			  struct cfg80211_wowlan *wowlan)
671*28348caeSBjoern A. Zeeb {
672*28348caeSBjoern A. Zeeb 	struct ath11k *ar = hw->priv;
673*28348caeSBjoern A. Zeeb 	int ret;
674*28348caeSBjoern A. Zeeb 
675*28348caeSBjoern A. Zeeb 	ret = ath11k_mac_wait_tx_complete(ar);
676*28348caeSBjoern A. Zeeb 	if (ret) {
677*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret);
678*28348caeSBjoern A. Zeeb 		return ret;
679*28348caeSBjoern A. Zeeb 	}
680*28348caeSBjoern A. Zeeb 
681*28348caeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
682*28348caeSBjoern A. Zeeb 
683*28348caeSBjoern A. Zeeb 	ret = ath11k_dp_rx_pktlog_stop(ar->ab, true);
684*28348caeSBjoern A. Zeeb 	if (ret) {
685*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab,
686*28348caeSBjoern A. Zeeb 			    "failed to stop dp rx (and timer) pktlog during wow suspend: %d\n",
687*28348caeSBjoern A. Zeeb 			    ret);
688*28348caeSBjoern A. Zeeb 		goto exit;
689*28348caeSBjoern A. Zeeb 	}
690*28348caeSBjoern A. Zeeb 
691*28348caeSBjoern A. Zeeb 	ret =  ath11k_wow_cleanup(ar);
692*28348caeSBjoern A. Zeeb 	if (ret) {
693*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n",
694*28348caeSBjoern A. Zeeb 			    ret);
695*28348caeSBjoern A. Zeeb 		goto exit;
696*28348caeSBjoern A. Zeeb 	}
697*28348caeSBjoern A. Zeeb 
698*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_set_wakeups(ar, wowlan);
699*28348caeSBjoern A. Zeeb 	if (ret) {
700*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n",
701*28348caeSBjoern A. Zeeb 			    ret);
702*28348caeSBjoern A. Zeeb 		goto cleanup;
703*28348caeSBjoern A. Zeeb 	}
704*28348caeSBjoern A. Zeeb 
705*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_protocol_offload(ar, true);
706*28348caeSBjoern A. Zeeb 	if (ret) {
707*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to set wow protocol offload events: %d\n",
708*28348caeSBjoern A. Zeeb 			    ret);
709*28348caeSBjoern A. Zeeb 		goto cleanup;
710*28348caeSBjoern A. Zeeb 	}
711*28348caeSBjoern A. Zeeb 
712*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_set_hw_filter(ar);
713*28348caeSBjoern A. Zeeb 	if (ret) {
714*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to set hw filter: %d\n",
715*28348caeSBjoern A. Zeeb 			    ret);
716*28348caeSBjoern A. Zeeb 		goto cleanup;
717*28348caeSBjoern A. Zeeb 	}
718*28348caeSBjoern A. Zeeb 
719*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_set_keepalive(ar,
720*28348caeSBjoern A. Zeeb 				       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
721*28348caeSBjoern A. Zeeb 				       WMI_STA_KEEPALIVE_INTERVAL_DEFAULT);
722*28348caeSBjoern A. Zeeb 	if (ret) {
723*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret);
724*28348caeSBjoern A. Zeeb 		goto cleanup;
725*28348caeSBjoern A. Zeeb 	}
726*28348caeSBjoern A. Zeeb 
727*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_enable(ar->ab);
728*28348caeSBjoern A. Zeeb 	if (ret) {
729*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
730*28348caeSBjoern A. Zeeb 		goto cleanup;
731*28348caeSBjoern A. Zeeb 	}
732*28348caeSBjoern A. Zeeb 
733*28348caeSBjoern A. Zeeb 	ret = ath11k_dp_rx_pktlog_stop(ar->ab, false);
734*28348caeSBjoern A. Zeeb 	if (ret) {
735*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab,
736*28348caeSBjoern A. Zeeb 			    "failed to stop dp rx pktlog during wow suspend: %d\n",
737*28348caeSBjoern A. Zeeb 			    ret);
738*28348caeSBjoern A. Zeeb 		goto cleanup;
739*28348caeSBjoern A. Zeeb 	}
740*28348caeSBjoern A. Zeeb 
741*28348caeSBjoern A. Zeeb 	ath11k_ce_stop_shadow_timers(ar->ab);
742*28348caeSBjoern A. Zeeb 	ath11k_dp_stop_shadow_timers(ar->ab);
743*28348caeSBjoern A. Zeeb 
744*28348caeSBjoern A. Zeeb 	ath11k_hif_irq_disable(ar->ab);
745*28348caeSBjoern A. Zeeb 	ath11k_hif_ce_irq_disable(ar->ab);
746*28348caeSBjoern A. Zeeb 
747*28348caeSBjoern A. Zeeb 	ret = ath11k_hif_suspend(ar->ab);
748*28348caeSBjoern A. Zeeb 	if (ret) {
749*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret);
750*28348caeSBjoern A. Zeeb 		goto wakeup;
751*28348caeSBjoern A. Zeeb 	}
752*28348caeSBjoern A. Zeeb 
753*28348caeSBjoern A. Zeeb 	goto exit;
754*28348caeSBjoern A. Zeeb 
755*28348caeSBjoern A. Zeeb wakeup:
756*28348caeSBjoern A. Zeeb 	ath11k_wow_wakeup(ar->ab);
757*28348caeSBjoern A. Zeeb 
758*28348caeSBjoern A. Zeeb cleanup:
759*28348caeSBjoern A. Zeeb 	ath11k_wow_cleanup(ar);
760*28348caeSBjoern A. Zeeb 
761*28348caeSBjoern A. Zeeb exit:
762*28348caeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
763*28348caeSBjoern A. Zeeb 	return ret ? 1 : 0;
764*28348caeSBjoern A. Zeeb }
765*28348caeSBjoern A. Zeeb 
ath11k_wow_op_set_wakeup(struct ieee80211_hw * hw,bool enabled)766*28348caeSBjoern A. Zeeb void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
767*28348caeSBjoern A. Zeeb {
768*28348caeSBjoern A. Zeeb 	struct ath11k *ar = hw->priv;
769*28348caeSBjoern A. Zeeb 
770*28348caeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
771*28348caeSBjoern A. Zeeb 	device_set_wakeup_enable(ar->ab->dev, enabled);
772*28348caeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
773*28348caeSBjoern A. Zeeb }
774*28348caeSBjoern A. Zeeb 
ath11k_wow_op_resume(struct ieee80211_hw * hw)775*28348caeSBjoern A. Zeeb int ath11k_wow_op_resume(struct ieee80211_hw *hw)
776*28348caeSBjoern A. Zeeb {
777*28348caeSBjoern A. Zeeb 	struct ath11k *ar = hw->priv;
778*28348caeSBjoern A. Zeeb 	int ret;
779*28348caeSBjoern A. Zeeb 
780*28348caeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
781*28348caeSBjoern A. Zeeb 
782*28348caeSBjoern A. Zeeb 	ret = ath11k_hif_resume(ar->ab);
783*28348caeSBjoern A. Zeeb 	if (ret) {
784*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret);
785*28348caeSBjoern A. Zeeb 		goto exit;
786*28348caeSBjoern A. Zeeb 	}
787*28348caeSBjoern A. Zeeb 
788*28348caeSBjoern A. Zeeb 	ath11k_hif_ce_irq_enable(ar->ab);
789*28348caeSBjoern A. Zeeb 	ath11k_hif_irq_enable(ar->ab);
790*28348caeSBjoern A. Zeeb 
791*28348caeSBjoern A. Zeeb 	ret = ath11k_dp_rx_pktlog_start(ar->ab);
792*28348caeSBjoern A. Zeeb 	if (ret) {
793*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret);
794*28348caeSBjoern A. Zeeb 		goto exit;
795*28348caeSBjoern A. Zeeb 	}
796*28348caeSBjoern A. Zeeb 
797*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_wakeup(ar->ab);
798*28348caeSBjoern A. Zeeb 	if (ret) {
799*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret);
800*28348caeSBjoern A. Zeeb 		goto exit;
801*28348caeSBjoern A. Zeeb 	}
802*28348caeSBjoern A. Zeeb 
803*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_nlo_cleanup(ar);
804*28348caeSBjoern A. Zeeb 	if (ret) {
805*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret);
806*28348caeSBjoern A. Zeeb 		goto exit;
807*28348caeSBjoern A. Zeeb 	}
808*28348caeSBjoern A. Zeeb 
809*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_clear_hw_filter(ar);
810*28348caeSBjoern A. Zeeb 	if (ret) {
811*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
812*28348caeSBjoern A. Zeeb 		goto exit;
813*28348caeSBjoern A. Zeeb 	}
814*28348caeSBjoern A. Zeeb 
815*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_protocol_offload(ar, false);
816*28348caeSBjoern A. Zeeb 	if (ret) {
817*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n",
818*28348caeSBjoern A. Zeeb 			    ret);
819*28348caeSBjoern A. Zeeb 		goto exit;
820*28348caeSBjoern A. Zeeb 	}
821*28348caeSBjoern A. Zeeb 
822*28348caeSBjoern A. Zeeb 	ret = ath11k_wow_set_keepalive(ar,
823*28348caeSBjoern A. Zeeb 				       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
824*28348caeSBjoern A. Zeeb 				       WMI_STA_KEEPALIVE_INTERVAL_DISABLE);
825*28348caeSBjoern A. Zeeb 	if (ret) {
826*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret);
827*28348caeSBjoern A. Zeeb 		goto exit;
828*28348caeSBjoern A. Zeeb 	}
829*28348caeSBjoern A. Zeeb 
830*28348caeSBjoern A. Zeeb exit:
831*28348caeSBjoern A. Zeeb 	if (ret) {
832*28348caeSBjoern A. Zeeb 		switch (ar->state) {
833*28348caeSBjoern A. Zeeb 		case ATH11K_STATE_ON:
834*28348caeSBjoern A. Zeeb 			ar->state = ATH11K_STATE_RESTARTING;
835*28348caeSBjoern A. Zeeb 			ret = 1;
836*28348caeSBjoern A. Zeeb 			break;
837*28348caeSBjoern A. Zeeb 		case ATH11K_STATE_OFF:
838*28348caeSBjoern A. Zeeb 		case ATH11K_STATE_RESTARTING:
839*28348caeSBjoern A. Zeeb 		case ATH11K_STATE_RESTARTED:
840*28348caeSBjoern A. Zeeb 		case ATH11K_STATE_WEDGED:
841*28348caeSBjoern A. Zeeb 		case ATH11K_STATE_FTM:
842*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
843*28348caeSBjoern A. Zeeb 				    ar->state);
844*28348caeSBjoern A. Zeeb 			ret = -EIO;
845*28348caeSBjoern A. Zeeb 			break;
846*28348caeSBjoern A. Zeeb 		}
847*28348caeSBjoern A. Zeeb 	}
848*28348caeSBjoern A. Zeeb 
849*28348caeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
850*28348caeSBjoern A. Zeeb 	return ret;
851*28348caeSBjoern A. Zeeb }
852*28348caeSBjoern A. Zeeb 
ath11k_wow_init(struct ath11k * ar)853*28348caeSBjoern A. Zeeb int ath11k_wow_init(struct ath11k *ar)
854*28348caeSBjoern A. Zeeb {
855*28348caeSBjoern A. Zeeb 	if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))
856*28348caeSBjoern A. Zeeb 		return 0;
857*28348caeSBjoern A. Zeeb 
858*28348caeSBjoern A. Zeeb 	ar->wow.wowlan_support = ath11k_wowlan_support;
859*28348caeSBjoern A. Zeeb 
860*28348caeSBjoern A. Zeeb 	if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
861*28348caeSBjoern A. Zeeb 	    ATH11K_HW_TXRX_NATIVE_WIFI) {
862*28348caeSBjoern A. Zeeb 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
863*28348caeSBjoern A. Zeeb 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
864*28348caeSBjoern A. Zeeb 	}
865*28348caeSBjoern A. Zeeb 
866*28348caeSBjoern A. Zeeb 	if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) {
867*28348caeSBjoern A. Zeeb 		ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
868*28348caeSBjoern A. Zeeb 		ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
869*28348caeSBjoern A. Zeeb 	}
870*28348caeSBjoern A. Zeeb 
871*28348caeSBjoern A. Zeeb 	ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS;
872*28348caeSBjoern A. Zeeb 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
873*28348caeSBjoern A. Zeeb 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
874*28348caeSBjoern A. Zeeb 
875*28348caeSBjoern A. Zeeb 	device_set_wakeup_capable(ar->ab->dev, true);
876*28348caeSBjoern A. Zeeb 
877*28348caeSBjoern A. Zeeb 	return 0;
878*28348caeSBjoern A. Zeeb }
879