xref: /freebsd/sys/contrib/dev/broadcom/brcm80211/brcmfmac/pno.c (revision 902136e0fe112383ec64d2ef43a446063b5e6417)
1b4c3e9b5SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2b4c3e9b5SBjoern A. Zeeb /*
3b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2016 Broadcom
4b4c3e9b5SBjoern A. Zeeb  */
5b4c3e9b5SBjoern A. Zeeb #include <linux/netdevice.h>
6b4c3e9b5SBjoern A. Zeeb #include <linux/gcd.h>
7b4c3e9b5SBjoern A. Zeeb #include <net/cfg80211.h>
8b4c3e9b5SBjoern A. Zeeb 
9b4c3e9b5SBjoern A. Zeeb #include "core.h"
10b4c3e9b5SBjoern A. Zeeb #include "debug.h"
11b4c3e9b5SBjoern A. Zeeb #include "fwil.h"
12b4c3e9b5SBjoern A. Zeeb #include "fwil_types.h"
13b4c3e9b5SBjoern A. Zeeb #include "cfg80211.h"
14b4c3e9b5SBjoern A. Zeeb #include "pno.h"
15b4c3e9b5SBjoern A. Zeeb 
16b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_VERSION		2
17b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_REPEAT		4
18b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_FREQ_EXPO_MAX		3
19b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_IMMEDIATE_SCAN_BIT	3
20b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_ENABLE_BD_SCAN_BIT	5
21b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6
22b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_REPORT_SEPARATELY_BIT	11
23b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_SCAN_INCOMPLETE	0
24b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF
25b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_HIDDEN_BIT		2
26b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_SCHED_SCAN_PERIOD	30
27b4c3e9b5SBjoern A. Zeeb 
28b4c3e9b5SBjoern A. Zeeb #define BRCMF_PNO_MAX_BUCKETS		16
29b4c3e9b5SBjoern A. Zeeb #define GSCAN_BATCH_NO_THR_SET			101
30b4c3e9b5SBjoern A. Zeeb #define GSCAN_RETRY_THRESHOLD			3
31b4c3e9b5SBjoern A. Zeeb 
32b4c3e9b5SBjoern A. Zeeb struct brcmf_pno_info {
33b4c3e9b5SBjoern A. Zeeb 	int n_reqs;
34b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_sched_scan_request *reqs[BRCMF_PNO_MAX_BUCKETS];
35b4c3e9b5SBjoern A. Zeeb 	struct mutex req_lock;
36b4c3e9b5SBjoern A. Zeeb };
37b4c3e9b5SBjoern A. Zeeb 
38b4c3e9b5SBjoern A. Zeeb #define ifp_to_pno(_ifp)	((_ifp)->drvr->config->pno)
39b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_store_request(struct brcmf_pno_info * pi,struct cfg80211_sched_scan_request * req)40b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_store_request(struct brcmf_pno_info *pi,
41b4c3e9b5SBjoern A. Zeeb 				   struct cfg80211_sched_scan_request *req)
42b4c3e9b5SBjoern A. Zeeb {
43b4c3e9b5SBjoern A. Zeeb 	if (WARN(pi->n_reqs == BRCMF_PNO_MAX_BUCKETS,
44b4c3e9b5SBjoern A. Zeeb 		 "pno request storage full\n"))
45b4c3e9b5SBjoern A. Zeeb 		return -ENOSPC;
46b4c3e9b5SBjoern A. Zeeb 
47*902136e0SBjoern A. Zeeb #if defined(__linux__)
48b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "reqid=%llu\n", req->reqid);
49*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
50*902136e0SBjoern A. Zeeb 	brcmf_dbg(SCAN, "reqid=%ju\n", (uintmax_t)req->reqid);
51*902136e0SBjoern A. Zeeb #endif
52b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&pi->req_lock);
53b4c3e9b5SBjoern A. Zeeb 	pi->reqs[pi->n_reqs++] = req;
54b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&pi->req_lock);
55b4c3e9b5SBjoern A. Zeeb 	return 0;
56b4c3e9b5SBjoern A. Zeeb }
57b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_remove_request(struct brcmf_pno_info * pi,u64 reqid)58b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_remove_request(struct brcmf_pno_info *pi, u64 reqid)
59b4c3e9b5SBjoern A. Zeeb {
60b4c3e9b5SBjoern A. Zeeb 	int i, err = 0;
61b4c3e9b5SBjoern A. Zeeb 
62b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&pi->req_lock);
63b4c3e9b5SBjoern A. Zeeb 
64b4c3e9b5SBjoern A. Zeeb 	/* Nothing to do if we have no requests */
65b4c3e9b5SBjoern A. Zeeb 	if (pi->n_reqs == 0)
66b4c3e9b5SBjoern A. Zeeb 		goto done;
67b4c3e9b5SBjoern A. Zeeb 
68b4c3e9b5SBjoern A. Zeeb 	/* find request */
69b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < pi->n_reqs; i++) {
70b4c3e9b5SBjoern A. Zeeb 		if (pi->reqs[i]->reqid == reqid)
71b4c3e9b5SBjoern A. Zeeb 			break;
72b4c3e9b5SBjoern A. Zeeb 	}
73b4c3e9b5SBjoern A. Zeeb 	/* request not found */
74b4c3e9b5SBjoern A. Zeeb 	if (WARN(i == pi->n_reqs, "reqid not found\n")) {
75b4c3e9b5SBjoern A. Zeeb 		err = -ENOENT;
76b4c3e9b5SBjoern A. Zeeb 		goto done;
77b4c3e9b5SBjoern A. Zeeb 	}
78b4c3e9b5SBjoern A. Zeeb 
79*902136e0SBjoern A. Zeeb #if defined(__linux__)
80b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "reqid=%llu\n", reqid);
81*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
82*902136e0SBjoern A. Zeeb 	brcmf_dbg(SCAN, "reqid=%ju\n", (uintmax_t)reqid);
83*902136e0SBjoern A. Zeeb #endif
84b4c3e9b5SBjoern A. Zeeb 	pi->n_reqs--;
85b4c3e9b5SBjoern A. Zeeb 
86b4c3e9b5SBjoern A. Zeeb 	/* if last we are done */
87b4c3e9b5SBjoern A. Zeeb 	if (!pi->n_reqs || i == pi->n_reqs)
88b4c3e9b5SBjoern A. Zeeb 		goto done;
89b4c3e9b5SBjoern A. Zeeb 
90b4c3e9b5SBjoern A. Zeeb 	/* fill the gap with remaining requests */
91b4c3e9b5SBjoern A. Zeeb 	while (i <= pi->n_reqs - 1) {
92b4c3e9b5SBjoern A. Zeeb 		pi->reqs[i] = pi->reqs[i + 1];
93b4c3e9b5SBjoern A. Zeeb 		i++;
94b4c3e9b5SBjoern A. Zeeb 	}
95b4c3e9b5SBjoern A. Zeeb 
96b4c3e9b5SBjoern A. Zeeb done:
97b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&pi->req_lock);
98b4c3e9b5SBjoern A. Zeeb 	return err;
99b4c3e9b5SBjoern A. Zeeb }
100b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_channel_config(struct brcmf_if * ifp,struct brcmf_pno_config_le * cfg)101b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_channel_config(struct brcmf_if *ifp,
102b4c3e9b5SBjoern A. Zeeb 				    struct brcmf_pno_config_le *cfg)
103b4c3e9b5SBjoern A. Zeeb {
104b4c3e9b5SBjoern A. Zeeb 	cfg->reporttype = 0;
105b4c3e9b5SBjoern A. Zeeb 	cfg->flags = 0;
106b4c3e9b5SBjoern A. Zeeb 
107b4c3e9b5SBjoern A. Zeeb 	return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg));
108b4c3e9b5SBjoern A. Zeeb }
109b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_config(struct brcmf_if * ifp,u32 scan_freq,u32 mscan,u32 bestn)110b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
111b4c3e9b5SBjoern A. Zeeb 			    u32 mscan, u32 bestn)
112b4c3e9b5SBjoern A. Zeeb {
113b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
114b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_param_le pfn_param;
115b4c3e9b5SBjoern A. Zeeb 	u16 flags;
116b4c3e9b5SBjoern A. Zeeb 	u32 pfnmem;
117b4c3e9b5SBjoern A. Zeeb 	s32 err;
118b4c3e9b5SBjoern A. Zeeb 
119b4c3e9b5SBjoern A. Zeeb 	memset(&pfn_param, 0, sizeof(pfn_param));
120b4c3e9b5SBjoern A. Zeeb 	pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
121b4c3e9b5SBjoern A. Zeeb 
122b4c3e9b5SBjoern A. Zeeb 	/* set extra pno params */
123b4c3e9b5SBjoern A. Zeeb 	flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) |
124b4c3e9b5SBjoern A. Zeeb 		BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
125b4c3e9b5SBjoern A. Zeeb 	pfn_param.repeat = BRCMF_PNO_REPEAT;
126b4c3e9b5SBjoern A. Zeeb 	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
127b4c3e9b5SBjoern A. Zeeb 
128b4c3e9b5SBjoern A. Zeeb 	/* set up pno scan fr */
129b4c3e9b5SBjoern A. Zeeb 	pfn_param.scan_freq = cpu_to_le32(scan_freq);
130b4c3e9b5SBjoern A. Zeeb 
131b4c3e9b5SBjoern A. Zeeb 	if (mscan) {
132b4c3e9b5SBjoern A. Zeeb 		pfnmem = bestn;
133b4c3e9b5SBjoern A. Zeeb 
134b4c3e9b5SBjoern A. Zeeb 		/* set bestn in firmware */
135b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem);
136b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
137b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "failed to set pfnmem\n");
138b4c3e9b5SBjoern A. Zeeb 			goto exit;
139b4c3e9b5SBjoern A. Zeeb 		}
140b4c3e9b5SBjoern A. Zeeb 		/* get max mscan which the firmware supports */
141b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem);
142b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
143b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "failed to get pfnmem\n");
144b4c3e9b5SBjoern A. Zeeb 			goto exit;
145b4c3e9b5SBjoern A. Zeeb 		}
146b4c3e9b5SBjoern A. Zeeb 		mscan = min_t(u32, mscan, pfnmem);
147b4c3e9b5SBjoern A. Zeeb 		pfn_param.mscan = mscan;
148b4c3e9b5SBjoern A. Zeeb 		pfn_param.bestn = bestn;
149b4c3e9b5SBjoern A. Zeeb 		flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT);
150b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn);
151b4c3e9b5SBjoern A. Zeeb 	}
152b4c3e9b5SBjoern A. Zeeb 
153b4c3e9b5SBjoern A. Zeeb 	pfn_param.flags = cpu_to_le16(flags);
154b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
155b4c3e9b5SBjoern A. Zeeb 				       sizeof(pfn_param));
156b4c3e9b5SBjoern A. Zeeb 	if (err)
157b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "pfn_set failed, err=%d\n", err);
158b4c3e9b5SBjoern A. Zeeb 
159b4c3e9b5SBjoern A. Zeeb exit:
160b4c3e9b5SBjoern A. Zeeb 	return err;
161b4c3e9b5SBjoern A. Zeeb }
162b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_set_random(struct brcmf_if * ifp,struct brcmf_pno_info * pi)163b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi)
164b4c3e9b5SBjoern A. Zeeb {
165b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
166b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_macaddr_le pfn_mac;
167b4c3e9b5SBjoern A. Zeeb 	u8 *mac_addr = NULL;
168b4c3e9b5SBjoern A. Zeeb 	u8 *mac_mask = NULL;
169b4c3e9b5SBjoern A. Zeeb 	int err, i, ri;
170b4c3e9b5SBjoern A. Zeeb 
171b4c3e9b5SBjoern A. Zeeb 	for (ri = 0; ri < pi->n_reqs; ri++)
172b4c3e9b5SBjoern A. Zeeb 		if (pi->reqs[ri]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
173b4c3e9b5SBjoern A. Zeeb 			mac_addr = pi->reqs[ri]->mac_addr;
174b4c3e9b5SBjoern A. Zeeb 			mac_mask = pi->reqs[ri]->mac_addr_mask;
175b4c3e9b5SBjoern A. Zeeb 			break;
176b4c3e9b5SBjoern A. Zeeb 		}
177b4c3e9b5SBjoern A. Zeeb 
178b4c3e9b5SBjoern A. Zeeb 	/* no random mac requested */
179b4c3e9b5SBjoern A. Zeeb 	if (!mac_addr)
180b4c3e9b5SBjoern A. Zeeb 		return 0;
181b4c3e9b5SBjoern A. Zeeb 
182b4c3e9b5SBjoern A. Zeeb 	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
183b4c3e9b5SBjoern A. Zeeb 	pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
184b4c3e9b5SBjoern A. Zeeb 
185b4c3e9b5SBjoern A. Zeeb 	memcpy(pfn_mac.mac, mac_addr, ETH_ALEN);
186b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < ETH_ALEN; i++) {
187b4c3e9b5SBjoern A. Zeeb 		pfn_mac.mac[i] &= mac_mask[i];
188b4c3e9b5SBjoern A. Zeeb 		pfn_mac.mac[i] |= get_random_u8() & ~(mac_mask[i]);
189b4c3e9b5SBjoern A. Zeeb 	}
190b4c3e9b5SBjoern A. Zeeb 	/* Clear multi bit */
191b4c3e9b5SBjoern A. Zeeb 	pfn_mac.mac[0] &= 0xFE;
192b4c3e9b5SBjoern A. Zeeb 	/* Set locally administered */
193b4c3e9b5SBjoern A. Zeeb 	pfn_mac.mac[0] |= 0x02;
194b4c3e9b5SBjoern A. Zeeb 
195*902136e0SBjoern A. Zeeb #if defined(__linux__)
196b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "enabling random mac: reqid=%llu mac=%pM\n",
197b4c3e9b5SBjoern A. Zeeb 		  pi->reqs[ri]->reqid, pfn_mac.mac);
198*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
199*902136e0SBjoern A. Zeeb 	brcmf_dbg(SCAN, "enabling random mac: reqid=%ju mac=%6D\n",
200*902136e0SBjoern A. Zeeb 		  (uintmax_t)pi->reqs[ri]->reqid, pfn_mac.mac, ":");
201*902136e0SBjoern A. Zeeb #endif
202b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
203b4c3e9b5SBjoern A. Zeeb 				       sizeof(pfn_mac));
204b4c3e9b5SBjoern A. Zeeb 	if (err)
205b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "pfn_macaddr failed, err=%d\n", err);
206b4c3e9b5SBjoern A. Zeeb 
207b4c3e9b5SBjoern A. Zeeb 	return err;
208b4c3e9b5SBjoern A. Zeeb }
209b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_add_ssid(struct brcmf_if * ifp,struct cfg80211_ssid * ssid,bool active)210b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid,
211b4c3e9b5SBjoern A. Zeeb 			      bool active)
212b4c3e9b5SBjoern A. Zeeb {
213b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
214b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_net_param_le pfn;
215b4c3e9b5SBjoern A. Zeeb 	int err;
216b4c3e9b5SBjoern A. Zeeb 
217b4c3e9b5SBjoern A. Zeeb 	pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
218b4c3e9b5SBjoern A. Zeeb 	pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
219b4c3e9b5SBjoern A. Zeeb 	pfn.wsec = cpu_to_le32(0);
220b4c3e9b5SBjoern A. Zeeb 	pfn.infra = cpu_to_le32(1);
221b4c3e9b5SBjoern A. Zeeb 	pfn.flags = 0;
222b4c3e9b5SBjoern A. Zeeb 	if (active)
223b4c3e9b5SBjoern A. Zeeb 		pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
224b4c3e9b5SBjoern A. Zeeb 	pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len);
225b4c3e9b5SBjoern A. Zeeb 	memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len);
226b4c3e9b5SBjoern A. Zeeb 
227b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "adding ssid=%.32s (active=%d)\n", ssid->ssid, active);
228b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn));
229b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
230b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "adding failed: err=%d\n", err);
231b4c3e9b5SBjoern A. Zeeb 	return err;
232b4c3e9b5SBjoern A. Zeeb }
233b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_add_bssid(struct brcmf_if * ifp,const u8 * bssid)234b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_add_bssid(struct brcmf_if *ifp, const u8 *bssid)
235b4c3e9b5SBjoern A. Zeeb {
236b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
237b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_bssid_le bssid_cfg;
238b4c3e9b5SBjoern A. Zeeb 	int err;
239b4c3e9b5SBjoern A. Zeeb 
240b4c3e9b5SBjoern A. Zeeb 	memcpy(bssid_cfg.bssid, bssid, ETH_ALEN);
241b4c3e9b5SBjoern A. Zeeb 	bssid_cfg.flags = 0;
242b4c3e9b5SBjoern A. Zeeb 
243b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "adding bssid=%pM\n", bssid);
244b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "pfn_add_bssid", &bssid_cfg,
245b4c3e9b5SBjoern A. Zeeb 				       sizeof(bssid_cfg));
246b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
247b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "adding failed: err=%d\n", err);
248b4c3e9b5SBjoern A. Zeeb 	return err;
249b4c3e9b5SBjoern A. Zeeb }
250b4c3e9b5SBjoern A. Zeeb 
brcmf_is_ssid_active(struct cfg80211_ssid * ssid,struct cfg80211_sched_scan_request * req)251b4c3e9b5SBjoern A. Zeeb static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid,
252b4c3e9b5SBjoern A. Zeeb 				 struct cfg80211_sched_scan_request *req)
253b4c3e9b5SBjoern A. Zeeb {
254b4c3e9b5SBjoern A. Zeeb 	int i;
255b4c3e9b5SBjoern A. Zeeb 
256b4c3e9b5SBjoern A. Zeeb 	if (!ssid || !req->ssids || !req->n_ssids)
257b4c3e9b5SBjoern A. Zeeb 		return false;
258b4c3e9b5SBjoern A. Zeeb 
259b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < req->n_ssids; i++) {
260b4c3e9b5SBjoern A. Zeeb 		if (ssid->ssid_len == req->ssids[i].ssid_len) {
261b4c3e9b5SBjoern A. Zeeb 			if (!strncmp(ssid->ssid, req->ssids[i].ssid,
262b4c3e9b5SBjoern A. Zeeb 				     ssid->ssid_len))
263b4c3e9b5SBjoern A. Zeeb 				return true;
264b4c3e9b5SBjoern A. Zeeb 		}
265b4c3e9b5SBjoern A. Zeeb 	}
266b4c3e9b5SBjoern A. Zeeb 	return false;
267b4c3e9b5SBjoern A. Zeeb }
268b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_clean(struct brcmf_if * ifp)269b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_clean(struct brcmf_if *ifp)
270b4c3e9b5SBjoern A. Zeeb {
271b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
272b4c3e9b5SBjoern A. Zeeb 	int ret;
273b4c3e9b5SBjoern A. Zeeb 
274b4c3e9b5SBjoern A. Zeeb 	/* Disable pfn */
275b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0);
276b4c3e9b5SBjoern A. Zeeb 	if (ret == 0) {
277b4c3e9b5SBjoern A. Zeeb 		/* clear pfn */
278b4c3e9b5SBjoern A. Zeeb 		ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0);
279b4c3e9b5SBjoern A. Zeeb 	}
280b4c3e9b5SBjoern A. Zeeb 	if (ret < 0)
281b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "failed code %d\n", ret);
282b4c3e9b5SBjoern A. Zeeb 
283b4c3e9b5SBjoern A. Zeeb 	return ret;
284b4c3e9b5SBjoern A. Zeeb }
285b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_get_bucket_channels(struct cfg80211_sched_scan_request * r,struct brcmf_pno_config_le * pno_cfg)286b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_get_bucket_channels(struct cfg80211_sched_scan_request *r,
287b4c3e9b5SBjoern A. Zeeb 					 struct brcmf_pno_config_le *pno_cfg)
288b4c3e9b5SBjoern A. Zeeb {
289b4c3e9b5SBjoern A. Zeeb 	u32 n_chan = le32_to_cpu(pno_cfg->channel_num);
290b4c3e9b5SBjoern A. Zeeb 	u16 chan;
291b4c3e9b5SBjoern A. Zeeb 	int i, err = 0;
292b4c3e9b5SBjoern A. Zeeb 
293b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < r->n_channels; i++) {
294b4c3e9b5SBjoern A. Zeeb 		if (n_chan >= BRCMF_NUMCHANNELS) {
295b4c3e9b5SBjoern A. Zeeb 			err = -ENOSPC;
296b4c3e9b5SBjoern A. Zeeb 			goto done;
297b4c3e9b5SBjoern A. Zeeb 		}
298b4c3e9b5SBjoern A. Zeeb 		chan = r->channels[i]->hw_value;
299b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "[%d] Chan : %u\n", n_chan, chan);
300b4c3e9b5SBjoern A. Zeeb 		pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
301b4c3e9b5SBjoern A. Zeeb 	}
302b4c3e9b5SBjoern A. Zeeb 	/* return number of channels */
303b4c3e9b5SBjoern A. Zeeb 	err = n_chan;
304b4c3e9b5SBjoern A. Zeeb done:
305b4c3e9b5SBjoern A. Zeeb 	pno_cfg->channel_num = cpu_to_le32(n_chan);
306b4c3e9b5SBjoern A. Zeeb 	return err;
307b4c3e9b5SBjoern A. Zeeb }
308b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_prep_fwconfig(struct brcmf_pno_info * pi,struct brcmf_pno_config_le * pno_cfg,struct brcmf_gscan_bucket_config ** buckets,u32 * scan_freq)309b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_prep_fwconfig(struct brcmf_pno_info *pi,
310b4c3e9b5SBjoern A. Zeeb 				   struct brcmf_pno_config_le *pno_cfg,
311b4c3e9b5SBjoern A. Zeeb 				   struct brcmf_gscan_bucket_config **buckets,
312b4c3e9b5SBjoern A. Zeeb 				   u32 *scan_freq)
313b4c3e9b5SBjoern A. Zeeb {
314b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_sched_scan_request *sr;
315b4c3e9b5SBjoern A. Zeeb 	struct brcmf_gscan_bucket_config *fw_buckets;
316b4c3e9b5SBjoern A. Zeeb 	int i, err, chidx;
317b4c3e9b5SBjoern A. Zeeb 
318b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "n_reqs=%d\n", pi->n_reqs);
319b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(!pi->n_reqs))
320b4c3e9b5SBjoern A. Zeeb 		return -ENODATA;
321b4c3e9b5SBjoern A. Zeeb 
322b4c3e9b5SBjoern A. Zeeb 	/*
323b4c3e9b5SBjoern A. Zeeb 	 * actual scan period is determined using gcd() for each
324b4c3e9b5SBjoern A. Zeeb 	 * scheduled scan period.
325b4c3e9b5SBjoern A. Zeeb 	 */
326b4c3e9b5SBjoern A. Zeeb 	*scan_freq = pi->reqs[0]->scan_plans[0].interval;
327b4c3e9b5SBjoern A. Zeeb 	for (i = 1; i < pi->n_reqs; i++) {
328b4c3e9b5SBjoern A. Zeeb 		sr = pi->reqs[i];
329b4c3e9b5SBjoern A. Zeeb 		*scan_freq = gcd(sr->scan_plans[0].interval, *scan_freq);
330b4c3e9b5SBjoern A. Zeeb 	}
331b4c3e9b5SBjoern A. Zeeb 	if (*scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) {
332b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "scan period too small, using minimum\n");
333b4c3e9b5SBjoern A. Zeeb 		*scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD;
334b4c3e9b5SBjoern A. Zeeb 	}
335b4c3e9b5SBjoern A. Zeeb 
336b4c3e9b5SBjoern A. Zeeb 	*buckets = NULL;
337b4c3e9b5SBjoern A. Zeeb 	fw_buckets = kcalloc(pi->n_reqs, sizeof(*fw_buckets), GFP_KERNEL);
338b4c3e9b5SBjoern A. Zeeb 	if (!fw_buckets)
339b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
340b4c3e9b5SBjoern A. Zeeb 
341b4c3e9b5SBjoern A. Zeeb 	memset(pno_cfg, 0, sizeof(*pno_cfg));
342b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < pi->n_reqs; i++) {
343b4c3e9b5SBjoern A. Zeeb 		sr = pi->reqs[i];
344b4c3e9b5SBjoern A. Zeeb 		chidx = brcmf_pno_get_bucket_channels(sr, pno_cfg);
345b4c3e9b5SBjoern A. Zeeb 		if (chidx < 0) {
346b4c3e9b5SBjoern A. Zeeb 			err = chidx;
347b4c3e9b5SBjoern A. Zeeb 			goto fail;
348b4c3e9b5SBjoern A. Zeeb 		}
349b4c3e9b5SBjoern A. Zeeb 		fw_buckets[i].bucket_end_index = chidx - 1;
350b4c3e9b5SBjoern A. Zeeb 		fw_buckets[i].bucket_freq_multiple =
351b4c3e9b5SBjoern A. Zeeb 			sr->scan_plans[0].interval / *scan_freq;
352b4c3e9b5SBjoern A. Zeeb 		/* assure period is non-zero */
353b4c3e9b5SBjoern A. Zeeb 		if (!fw_buckets[i].bucket_freq_multiple)
354b4c3e9b5SBjoern A. Zeeb 			fw_buckets[i].bucket_freq_multiple = 1;
355b4c3e9b5SBjoern A. Zeeb 		fw_buckets[i].flag = BRCMF_PNO_REPORT_NO_BATCH;
356b4c3e9b5SBjoern A. Zeeb 	}
357b4c3e9b5SBjoern A. Zeeb 
358b4c3e9b5SBjoern A. Zeeb 	if (BRCMF_SCAN_ON()) {
359b4c3e9b5SBjoern A. Zeeb 		brcmf_err("base period=%u\n", *scan_freq);
360b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < pi->n_reqs; i++) {
361b4c3e9b5SBjoern A. Zeeb 			brcmf_err("[%d] period %u max %u repeat %u flag %x idx %u\n",
362b4c3e9b5SBjoern A. Zeeb 				  i, fw_buckets[i].bucket_freq_multiple,
363b4c3e9b5SBjoern A. Zeeb 				  le16_to_cpu(fw_buckets[i].max_freq_multiple),
364b4c3e9b5SBjoern A. Zeeb 				  fw_buckets[i].repeat, fw_buckets[i].flag,
365b4c3e9b5SBjoern A. Zeeb 				  fw_buckets[i].bucket_end_index);
366b4c3e9b5SBjoern A. Zeeb 		}
367b4c3e9b5SBjoern A. Zeeb 	}
368b4c3e9b5SBjoern A. Zeeb 	*buckets = fw_buckets;
369b4c3e9b5SBjoern A. Zeeb 	return pi->n_reqs;
370b4c3e9b5SBjoern A. Zeeb 
371b4c3e9b5SBjoern A. Zeeb fail:
372b4c3e9b5SBjoern A. Zeeb 	kfree(fw_buckets);
373b4c3e9b5SBjoern A. Zeeb 	return err;
374b4c3e9b5SBjoern A. Zeeb }
375b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_config_networks(struct brcmf_if * ifp,struct brcmf_pno_info * pi)376b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_config_networks(struct brcmf_if *ifp,
377b4c3e9b5SBjoern A. Zeeb 				     struct brcmf_pno_info *pi)
378b4c3e9b5SBjoern A. Zeeb {
379b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_sched_scan_request *r;
380b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_match_set *ms;
381b4c3e9b5SBjoern A. Zeeb 	bool active;
382b4c3e9b5SBjoern A. Zeeb 	int i, j, err = 0;
383b4c3e9b5SBjoern A. Zeeb 
384b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < pi->n_reqs; i++) {
385b4c3e9b5SBjoern A. Zeeb 		r = pi->reqs[i];
386b4c3e9b5SBjoern A. Zeeb 
387b4c3e9b5SBjoern A. Zeeb 		for (j = 0; j < r->n_match_sets; j++) {
388b4c3e9b5SBjoern A. Zeeb 			ms = &r->match_sets[j];
389b4c3e9b5SBjoern A. Zeeb 			if (ms->ssid.ssid_len) {
390b4c3e9b5SBjoern A. Zeeb 				active = brcmf_is_ssid_active(&ms->ssid, r);
391b4c3e9b5SBjoern A. Zeeb 				err = brcmf_pno_add_ssid(ifp, &ms->ssid,
392b4c3e9b5SBjoern A. Zeeb 							 active);
393b4c3e9b5SBjoern A. Zeeb 			}
394b4c3e9b5SBjoern A. Zeeb 			if (!err && is_valid_ether_addr(ms->bssid))
395b4c3e9b5SBjoern A. Zeeb 				err = brcmf_pno_add_bssid(ifp, ms->bssid);
396b4c3e9b5SBjoern A. Zeeb 
397b4c3e9b5SBjoern A. Zeeb 			if (err < 0)
398b4c3e9b5SBjoern A. Zeeb 				return err;
399b4c3e9b5SBjoern A. Zeeb 		}
400b4c3e9b5SBjoern A. Zeeb 	}
401b4c3e9b5SBjoern A. Zeeb 	return 0;
402b4c3e9b5SBjoern A. Zeeb }
403b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_config_sched_scans(struct brcmf_if * ifp)404b4c3e9b5SBjoern A. Zeeb static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp)
405b4c3e9b5SBjoern A. Zeeb {
406b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
407b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_info *pi;
408b4c3e9b5SBjoern A. Zeeb 	struct brcmf_gscan_config *gscan_cfg;
409b4c3e9b5SBjoern A. Zeeb 	struct brcmf_gscan_bucket_config *buckets;
410b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_config_le pno_cfg;
411b4c3e9b5SBjoern A. Zeeb 	size_t gsz;
412b4c3e9b5SBjoern A. Zeeb 	u32 scan_freq;
413b4c3e9b5SBjoern A. Zeeb 	int err, n_buckets;
414b4c3e9b5SBjoern A. Zeeb 
415b4c3e9b5SBjoern A. Zeeb 	pi = ifp_to_pno(ifp);
416b4c3e9b5SBjoern A. Zeeb 	n_buckets = brcmf_pno_prep_fwconfig(pi, &pno_cfg, &buckets,
417b4c3e9b5SBjoern A. Zeeb 					    &scan_freq);
418b4c3e9b5SBjoern A. Zeeb 	if (n_buckets < 0)
419b4c3e9b5SBjoern A. Zeeb 		return n_buckets;
420b4c3e9b5SBjoern A. Zeeb 
421b4c3e9b5SBjoern A. Zeeb 	gsz = struct_size(gscan_cfg, bucket, n_buckets);
422b4c3e9b5SBjoern A. Zeeb 	gscan_cfg = kzalloc(gsz, GFP_KERNEL);
423b4c3e9b5SBjoern A. Zeeb 	if (!gscan_cfg) {
424b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
425b4c3e9b5SBjoern A. Zeeb 		goto free_buckets;
426b4c3e9b5SBjoern A. Zeeb 	}
427b4c3e9b5SBjoern A. Zeeb 
428b4c3e9b5SBjoern A. Zeeb 	/* clean up everything */
429b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_clean(ifp);
430b4c3e9b5SBjoern A. Zeeb 	if  (err < 0) {
431b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "failed error=%d\n", err);
432b4c3e9b5SBjoern A. Zeeb 		goto free_gscan;
433b4c3e9b5SBjoern A. Zeeb 	}
434b4c3e9b5SBjoern A. Zeeb 
435b4c3e9b5SBjoern A. Zeeb 	/* configure pno */
436b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_config(ifp, scan_freq, 0, 0);
437b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
438b4c3e9b5SBjoern A. Zeeb 		goto free_gscan;
439b4c3e9b5SBjoern A. Zeeb 
440b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_channel_config(ifp, &pno_cfg);
441b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
442b4c3e9b5SBjoern A. Zeeb 		goto clean;
443b4c3e9b5SBjoern A. Zeeb 
444b4c3e9b5SBjoern A. Zeeb 	gscan_cfg->version = cpu_to_le16(BRCMF_GSCAN_CFG_VERSION);
445b4c3e9b5SBjoern A. Zeeb 	gscan_cfg->retry_threshold = GSCAN_RETRY_THRESHOLD;
446b4c3e9b5SBjoern A. Zeeb 	gscan_cfg->buffer_threshold = GSCAN_BATCH_NO_THR_SET;
447b4c3e9b5SBjoern A. Zeeb 	gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN;
448b4c3e9b5SBjoern A. Zeeb 
449b4c3e9b5SBjoern A. Zeeb 	gscan_cfg->count_of_channel_buckets = n_buckets;
450b4c3e9b5SBjoern A. Zeeb 	memcpy(gscan_cfg->bucket, buckets,
451b4c3e9b5SBjoern A. Zeeb 	       array_size(n_buckets, sizeof(*buckets)));
452b4c3e9b5SBjoern A. Zeeb 
453b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg, gsz);
454b4c3e9b5SBjoern A. Zeeb 
455b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
456b4c3e9b5SBjoern A. Zeeb 		goto clean;
457b4c3e9b5SBjoern A. Zeeb 
458b4c3e9b5SBjoern A. Zeeb 	/* configure random mac */
459b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_set_random(ifp, pi);
460b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
461b4c3e9b5SBjoern A. Zeeb 		goto clean;
462b4c3e9b5SBjoern A. Zeeb 
463b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_config_networks(ifp, pi);
464b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
465b4c3e9b5SBjoern A. Zeeb 		goto clean;
466b4c3e9b5SBjoern A. Zeeb 
467b4c3e9b5SBjoern A. Zeeb 	/* Enable the PNO */
468b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
469b4c3e9b5SBjoern A. Zeeb 
470b4c3e9b5SBjoern A. Zeeb clean:
471b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
472b4c3e9b5SBjoern A. Zeeb 		brcmf_pno_clean(ifp);
473b4c3e9b5SBjoern A. Zeeb free_gscan:
474b4c3e9b5SBjoern A. Zeeb 	kfree(gscan_cfg);
475b4c3e9b5SBjoern A. Zeeb free_buckets:
476b4c3e9b5SBjoern A. Zeeb 	kfree(buckets);
477b4c3e9b5SBjoern A. Zeeb 	return err;
478b4c3e9b5SBjoern A. Zeeb }
479b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_start_sched_scan(struct brcmf_if * ifp,struct cfg80211_sched_scan_request * req)480b4c3e9b5SBjoern A. Zeeb int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
481b4c3e9b5SBjoern A. Zeeb 			       struct cfg80211_sched_scan_request *req)
482b4c3e9b5SBjoern A. Zeeb {
483b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_info *pi;
484b4c3e9b5SBjoern A. Zeeb 	int ret;
485b4c3e9b5SBjoern A. Zeeb 
486*902136e0SBjoern A. Zeeb #if defined(__linux__)
487b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "reqid=%llu\n", req->reqid);
488*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
489*902136e0SBjoern A. Zeeb 	brcmf_dbg(TRACE, "reqid=%ju\n", (uintmax_t)req->reqid);
490*902136e0SBjoern A. Zeeb #endif
491b4c3e9b5SBjoern A. Zeeb 
492b4c3e9b5SBjoern A. Zeeb 	pi = ifp_to_pno(ifp);
493b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_pno_store_request(pi, req);
494b4c3e9b5SBjoern A. Zeeb 	if (ret < 0)
495b4c3e9b5SBjoern A. Zeeb 		return ret;
496b4c3e9b5SBjoern A. Zeeb 
497b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_pno_config_sched_scans(ifp);
498b4c3e9b5SBjoern A. Zeeb 	if (ret < 0) {
499b4c3e9b5SBjoern A. Zeeb 		brcmf_pno_remove_request(pi, req->reqid);
500b4c3e9b5SBjoern A. Zeeb 		if (pi->n_reqs)
501b4c3e9b5SBjoern A. Zeeb 			(void)brcmf_pno_config_sched_scans(ifp);
502b4c3e9b5SBjoern A. Zeeb 		return ret;
503b4c3e9b5SBjoern A. Zeeb 	}
504b4c3e9b5SBjoern A. Zeeb 	return 0;
505b4c3e9b5SBjoern A. Zeeb }
506b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_stop_sched_scan(struct brcmf_if * ifp,u64 reqid)507b4c3e9b5SBjoern A. Zeeb int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
508b4c3e9b5SBjoern A. Zeeb {
509b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_info *pi;
510b4c3e9b5SBjoern A. Zeeb 	int err;
511b4c3e9b5SBjoern A. Zeeb 
512*902136e0SBjoern A. Zeeb #if defined(__linux__)
513b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "reqid=%llu\n", reqid);
514*902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
515*902136e0SBjoern A. Zeeb 	brcmf_dbg(TRACE, "reqid=%ju\n", (uintmax_t)reqid);
516*902136e0SBjoern A. Zeeb #endif
517b4c3e9b5SBjoern A. Zeeb 
518b4c3e9b5SBjoern A. Zeeb 	pi = ifp_to_pno(ifp);
519b4c3e9b5SBjoern A. Zeeb 
520b4c3e9b5SBjoern A. Zeeb 	/* No PNO request */
521b4c3e9b5SBjoern A. Zeeb 	if (!pi->n_reqs)
522b4c3e9b5SBjoern A. Zeeb 		return 0;
523b4c3e9b5SBjoern A. Zeeb 
524b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_remove_request(pi, reqid);
525b4c3e9b5SBjoern A. Zeeb 	if (err)
526b4c3e9b5SBjoern A. Zeeb 		return err;
527b4c3e9b5SBjoern A. Zeeb 
528b4c3e9b5SBjoern A. Zeeb 	brcmf_pno_clean(ifp);
529b4c3e9b5SBjoern A. Zeeb 
530b4c3e9b5SBjoern A. Zeeb 	if (pi->n_reqs)
531b4c3e9b5SBjoern A. Zeeb 		(void)brcmf_pno_config_sched_scans(ifp);
532b4c3e9b5SBjoern A. Zeeb 
533b4c3e9b5SBjoern A. Zeeb 	return 0;
534b4c3e9b5SBjoern A. Zeeb }
535b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_attach(struct brcmf_cfg80211_info * cfg)536b4c3e9b5SBjoern A. Zeeb int brcmf_pno_attach(struct brcmf_cfg80211_info *cfg)
537b4c3e9b5SBjoern A. Zeeb {
538b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_info *pi;
539b4c3e9b5SBjoern A. Zeeb 
540b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "enter\n");
541b4c3e9b5SBjoern A. Zeeb 	pi = kzalloc(sizeof(*pi), GFP_KERNEL);
542b4c3e9b5SBjoern A. Zeeb 	if (!pi)
543b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
544b4c3e9b5SBjoern A. Zeeb 
545b4c3e9b5SBjoern A. Zeeb 	cfg->pno = pi;
546b4c3e9b5SBjoern A. Zeeb 	mutex_init(&pi->req_lock);
547b4c3e9b5SBjoern A. Zeeb 	return 0;
548b4c3e9b5SBjoern A. Zeeb }
549b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_detach(struct brcmf_cfg80211_info * cfg)550b4c3e9b5SBjoern A. Zeeb void brcmf_pno_detach(struct brcmf_cfg80211_info *cfg)
551b4c3e9b5SBjoern A. Zeeb {
552b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_info *pi;
553b4c3e9b5SBjoern A. Zeeb 
554b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "enter\n");
555b4c3e9b5SBjoern A. Zeeb 	pi = cfg->pno;
556b4c3e9b5SBjoern A. Zeeb 	cfg->pno = NULL;
557b4c3e9b5SBjoern A. Zeeb 
558b4c3e9b5SBjoern A. Zeeb 	WARN_ON(pi->n_reqs);
559b4c3e9b5SBjoern A. Zeeb 	mutex_destroy(&pi->req_lock);
560b4c3e9b5SBjoern A. Zeeb 	kfree(pi);
561b4c3e9b5SBjoern A. Zeeb }
562b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_wiphy_params(struct wiphy * wiphy,bool gscan)563b4c3e9b5SBjoern A. Zeeb void brcmf_pno_wiphy_params(struct wiphy *wiphy, bool gscan)
564b4c3e9b5SBjoern A. Zeeb {
565b4c3e9b5SBjoern A. Zeeb 	/* scheduled scan settings */
566b4c3e9b5SBjoern A. Zeeb 	wiphy->max_sched_scan_reqs = gscan ? BRCMF_PNO_MAX_BUCKETS : 1;
567b4c3e9b5SBjoern A. Zeeb 	wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
568b4c3e9b5SBjoern A. Zeeb 	wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
569b4c3e9b5SBjoern A. Zeeb 	wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
570b4c3e9b5SBjoern A. Zeeb 	wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD;
571b4c3e9b5SBjoern A. Zeeb }
572b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info * pi,u32 bucket)573b4c3e9b5SBjoern A. Zeeb u64 brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info *pi, u32 bucket)
574b4c3e9b5SBjoern A. Zeeb {
575b4c3e9b5SBjoern A. Zeeb 	u64 reqid = 0;
576b4c3e9b5SBjoern A. Zeeb 
577b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&pi->req_lock);
578b4c3e9b5SBjoern A. Zeeb 
579b4c3e9b5SBjoern A. Zeeb 	if (bucket < pi->n_reqs)
580b4c3e9b5SBjoern A. Zeeb 		reqid = pi->reqs[bucket]->reqid;
581b4c3e9b5SBjoern A. Zeeb 
582b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&pi->req_lock);
583b4c3e9b5SBjoern A. Zeeb 	return reqid;
584b4c3e9b5SBjoern A. Zeeb }
585b4c3e9b5SBjoern A. Zeeb 
brcmf_pno_get_bucket_map(struct brcmf_pno_info * pi,struct brcmf_pno_net_info_le * ni)586b4c3e9b5SBjoern A. Zeeb u32 brcmf_pno_get_bucket_map(struct brcmf_pno_info *pi,
587b4c3e9b5SBjoern A. Zeeb 			     struct brcmf_pno_net_info_le *ni)
588b4c3e9b5SBjoern A. Zeeb {
589b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_sched_scan_request *req;
590b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_match_set *ms;
591b4c3e9b5SBjoern A. Zeeb 	u32 bucket_map = 0;
592b4c3e9b5SBjoern A. Zeeb 	int i, j;
593b4c3e9b5SBjoern A. Zeeb 
594b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&pi->req_lock);
595b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < pi->n_reqs; i++) {
596b4c3e9b5SBjoern A. Zeeb 		req = pi->reqs[i];
597b4c3e9b5SBjoern A. Zeeb 
598b4c3e9b5SBjoern A. Zeeb 		if (!req->n_match_sets)
599b4c3e9b5SBjoern A. Zeeb 			continue;
600b4c3e9b5SBjoern A. Zeeb 		for (j = 0; j < req->n_match_sets; j++) {
601b4c3e9b5SBjoern A. Zeeb 			ms = &req->match_sets[j];
602b4c3e9b5SBjoern A. Zeeb 			if (ms->ssid.ssid_len == ni->SSID_len &&
603b4c3e9b5SBjoern A. Zeeb 			    !memcmp(ms->ssid.ssid, ni->SSID, ni->SSID_len)) {
604b4c3e9b5SBjoern A. Zeeb 				bucket_map |= BIT(i);
605b4c3e9b5SBjoern A. Zeeb 				break;
606b4c3e9b5SBjoern A. Zeeb 			}
607b4c3e9b5SBjoern A. Zeeb 			if (is_valid_ether_addr(ms->bssid) &&
608b4c3e9b5SBjoern A. Zeeb 			    !memcmp(ms->bssid, ni->bssid, ETH_ALEN)) {
609b4c3e9b5SBjoern A. Zeeb 				bucket_map |= BIT(i);
610b4c3e9b5SBjoern A. Zeeb 				break;
611b4c3e9b5SBjoern A. Zeeb 			}
612b4c3e9b5SBjoern A. Zeeb 		}
613b4c3e9b5SBjoern A. Zeeb 	}
614b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&pi->req_lock);
615b4c3e9b5SBjoern A. Zeeb 	return bucket_map;
616b4c3e9b5SBjoern A. Zeeb }
617