xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
2ac55136fSArend Van Spriel /*
3ac55136fSArend Van Spriel  * Copyright (c) 2016 Broadcom
4ac55136fSArend Van Spriel  */
5ac55136fSArend Van Spriel #include <linux/netdevice.h>
6efc2c1faSArend Van Spriel #include <linux/gcd.h>
7ac55136fSArend Van Spriel #include <net/cfg80211.h>
8ac55136fSArend Van Spriel 
9ac55136fSArend Van Spriel #include "core.h"
10ac55136fSArend Van Spriel #include "debug.h"
11ac55136fSArend Van Spriel #include "fwil.h"
12ac55136fSArend Van Spriel #include "fwil_types.h"
13331e7894SArend Van Spriel #include "cfg80211.h"
14331e7894SArend Van Spriel #include "pno.h"
15ac55136fSArend Van Spriel 
16ac55136fSArend Van Spriel #define BRCMF_PNO_VERSION		2
17ac55136fSArend Van Spriel #define BRCMF_PNO_REPEAT		4
18ac55136fSArend Van Spriel #define BRCMF_PNO_FREQ_EXPO_MAX		3
19fca6cb2fSArend Van Spriel #define BRCMF_PNO_IMMEDIATE_SCAN_BIT	3
20fca6cb2fSArend Van Spriel #define BRCMF_PNO_ENABLE_BD_SCAN_BIT	5
21ac55136fSArend Van Spriel #define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT	6
22fca6cb2fSArend Van Spriel #define BRCMF_PNO_REPORT_SEPARATELY_BIT	11
23ac55136fSArend Van Spriel #define BRCMF_PNO_SCAN_INCOMPLETE	0
243e2e86abSArend Van Spriel #define BRCMF_PNO_WPA_AUTH_ANY		0xFFFFFFFF
253e2e86abSArend Van Spriel #define BRCMF_PNO_HIDDEN_BIT		2
263e48611dSArend Van Spriel #define BRCMF_PNO_SCHED_SCAN_PERIOD	30
27ac55136fSArend Van Spriel 
28efc2c1faSArend Van Spriel #define BRCMF_PNO_MAX_BUCKETS		16
29efc2c1faSArend Van Spriel #define GSCAN_BATCH_NO_THR_SET			101
30efc2c1faSArend Van Spriel #define GSCAN_RETRY_THRESHOLD			3
31efc2c1faSArend Van Spriel 
32efc2c1faSArend Van Spriel struct brcmf_pno_info {
33efc2c1faSArend Van Spriel 	int n_reqs;
34efc2c1faSArend Van Spriel 	struct cfg80211_sched_scan_request *reqs[BRCMF_PNO_MAX_BUCKETS];
3542596f76SArend Van Spriel 	struct mutex req_lock;
36efc2c1faSArend Van Spriel };
37efc2c1faSArend Van Spriel 
38efc2c1faSArend Van Spriel #define ifp_to_pno(_ifp)	((_ifp)->drvr->config->pno)
39efc2c1faSArend Van Spriel 
brcmf_pno_store_request(struct brcmf_pno_info * pi,struct cfg80211_sched_scan_request * req)40efc2c1faSArend Van Spriel static int brcmf_pno_store_request(struct brcmf_pno_info *pi,
41efc2c1faSArend Van Spriel 				   struct cfg80211_sched_scan_request *req)
42efc2c1faSArend Van Spriel {
43efc2c1faSArend Van Spriel 	if (WARN(pi->n_reqs == BRCMF_PNO_MAX_BUCKETS,
44efc2c1faSArend Van Spriel 		 "pno request storage full\n"))
45efc2c1faSArend Van Spriel 		return -ENOSPC;
46efc2c1faSArend Van Spriel 
47efc2c1faSArend Van Spriel 	brcmf_dbg(SCAN, "reqid=%llu\n", req->reqid);
4842596f76SArend Van Spriel 	mutex_lock(&pi->req_lock);
49efc2c1faSArend Van Spriel 	pi->reqs[pi->n_reqs++] = req;
5042596f76SArend Van Spriel 	mutex_unlock(&pi->req_lock);
51efc2c1faSArend Van Spriel 	return 0;
52efc2c1faSArend Van Spriel }
53efc2c1faSArend Van Spriel 
brcmf_pno_remove_request(struct brcmf_pno_info * pi,u64 reqid)54efc2c1faSArend Van Spriel static int brcmf_pno_remove_request(struct brcmf_pno_info *pi, u64 reqid)
55efc2c1faSArend Van Spriel {
5642596f76SArend Van Spriel 	int i, err = 0;
5742596f76SArend Van Spriel 
5842596f76SArend Van Spriel 	mutex_lock(&pi->req_lock);
59efc2c1faSArend Van Spriel 
601524cbf3SAdrian Ratiu 	/* Nothing to do if we have no requests */
611524cbf3SAdrian Ratiu 	if (pi->n_reqs == 0)
621524cbf3SAdrian Ratiu 		goto done;
631524cbf3SAdrian Ratiu 
64efc2c1faSArend Van Spriel 	/* find request */
65efc2c1faSArend Van Spriel 	for (i = 0; i < pi->n_reqs; i++) {
66efc2c1faSArend Van Spriel 		if (pi->reqs[i]->reqid == reqid)
67efc2c1faSArend Van Spriel 			break;
68efc2c1faSArend Van Spriel 	}
69efc2c1faSArend Van Spriel 	/* request not found */
70efc2c1faSArend Van Spriel 	if (WARN(i == pi->n_reqs, "reqid not found\n")) {
7142596f76SArend Van Spriel 		err = -ENOENT;
7242596f76SArend Van Spriel 		goto done;
73efc2c1faSArend Van Spriel 	}
74efc2c1faSArend Van Spriel 
75efc2c1faSArend Van Spriel 	brcmf_dbg(SCAN, "reqid=%llu\n", reqid);
76efc2c1faSArend Van Spriel 	pi->n_reqs--;
77efc2c1faSArend Van Spriel 
78efc2c1faSArend Van Spriel 	/* if last we are done */
79efc2c1faSArend Van Spriel 	if (!pi->n_reqs || i == pi->n_reqs)
8042596f76SArend Van Spriel 		goto done;
81efc2c1faSArend Van Spriel 
82efc2c1faSArend Van Spriel 	/* fill the gap with remaining requests */
83efc2c1faSArend Van Spriel 	while (i <= pi->n_reqs - 1) {
84efc2c1faSArend Van Spriel 		pi->reqs[i] = pi->reqs[i + 1];
85efc2c1faSArend Van Spriel 		i++;
86efc2c1faSArend Van Spriel 	}
8742596f76SArend Van Spriel 
8842596f76SArend Van Spriel done:
8942596f76SArend Van Spriel 	mutex_unlock(&pi->req_lock);
9042596f76SArend Van Spriel 	return err;
91efc2c1faSArend Van Spriel }
92efc2c1faSArend Van Spriel 
brcmf_pno_channel_config(struct brcmf_if * ifp,struct brcmf_pno_config_le * cfg)93331e7894SArend Van Spriel static int brcmf_pno_channel_config(struct brcmf_if *ifp,
94331e7894SArend Van Spriel 				    struct brcmf_pno_config_le *cfg)
95331e7894SArend Van Spriel {
96331e7894SArend Van Spriel 	cfg->reporttype = 0;
97331e7894SArend Van Spriel 	cfg->flags = 0;
98331e7894SArend Van Spriel 
99331e7894SArend Van Spriel 	return brcmf_fil_iovar_data_set(ifp, "pfn_cfg", cfg, sizeof(*cfg));
100331e7894SArend Van Spriel }
101331e7894SArend Van Spriel 
brcmf_pno_config(struct brcmf_if * ifp,u32 scan_freq,u32 mscan,u32 bestn)1023e48611dSArend Van Spriel static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
103fca6cb2fSArend Van Spriel 			    u32 mscan, u32 bestn)
104ac55136fSArend Van Spriel {
105dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
106ac55136fSArend Van Spriel 	struct brcmf_pno_param_le pfn_param;
107fca6cb2fSArend Van Spriel 	u16 flags;
108fca6cb2fSArend Van Spriel 	u32 pfnmem;
109ac55136fSArend Van Spriel 	s32 err;
110ac55136fSArend Van Spriel 
111ac55136fSArend Van Spriel 	memset(&pfn_param, 0, sizeof(pfn_param));
112ac55136fSArend Van Spriel 	pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
113ac55136fSArend Van Spriel 
114ac55136fSArend Van Spriel 	/* set extra pno params */
115fca6cb2fSArend Van Spriel 	flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) |
116fca6cb2fSArend Van Spriel 		BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
117ac55136fSArend Van Spriel 	pfn_param.repeat = BRCMF_PNO_REPEAT;
118ac55136fSArend Van Spriel 	pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
119ac55136fSArend Van Spriel 
120ac55136fSArend Van Spriel 	/* set up pno scan fr */
121fca6cb2fSArend Van Spriel 	pfn_param.scan_freq = cpu_to_le32(scan_freq);
122ac55136fSArend Van Spriel 
123fca6cb2fSArend Van Spriel 	if (mscan) {
124fca6cb2fSArend Van Spriel 		pfnmem = bestn;
125fca6cb2fSArend Van Spriel 
126fca6cb2fSArend Van Spriel 		/* set bestn in firmware */
127fca6cb2fSArend Van Spriel 		err = brcmf_fil_iovar_int_set(ifp, "pfnmem", pfnmem);
128fca6cb2fSArend Van Spriel 		if (err < 0) {
129dcb1471bSRafał Miłecki 			bphy_err(drvr, "failed to set pfnmem\n");
130fca6cb2fSArend Van Spriel 			goto exit;
131fca6cb2fSArend Van Spriel 		}
132fca6cb2fSArend Van Spriel 		/* get max mscan which the firmware supports */
133fca6cb2fSArend Van Spriel 		err = brcmf_fil_iovar_int_get(ifp, "pfnmem", &pfnmem);
134fca6cb2fSArend Van Spriel 		if (err < 0) {
135dcb1471bSRafał Miłecki 			bphy_err(drvr, "failed to get pfnmem\n");
136fca6cb2fSArend Van Spriel 			goto exit;
137fca6cb2fSArend Van Spriel 		}
138fca6cb2fSArend Van Spriel 		mscan = min_t(u32, mscan, pfnmem);
139fca6cb2fSArend Van Spriel 		pfn_param.mscan = mscan;
140fca6cb2fSArend Van Spriel 		pfn_param.bestn = bestn;
141fca6cb2fSArend Van Spriel 		flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT);
142fca6cb2fSArend Van Spriel 		brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn);
143fca6cb2fSArend Van Spriel 	}
144fca6cb2fSArend Van Spriel 
145fca6cb2fSArend Van Spriel 	pfn_param.flags = cpu_to_le16(flags);
146ac55136fSArend Van Spriel 	err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
147ac55136fSArend Van Spriel 				       sizeof(pfn_param));
148fca6cb2fSArend Van Spriel 	if (err)
149dcb1471bSRafał Miłecki 		bphy_err(drvr, "pfn_set failed, err=%d\n", err);
150fca6cb2fSArend Van Spriel 
151fca6cb2fSArend Van Spriel exit:
152ac55136fSArend Van Spriel 	return err;
153ac55136fSArend Van Spriel }
154ac55136fSArend Van Spriel 
brcmf_pno_set_random(struct brcmf_if * ifp,struct brcmf_pno_info * pi)155efc2c1faSArend Van Spriel static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi)
156fca6cb2fSArend Van Spriel {
157dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
158fca6cb2fSArend Van Spriel 	struct brcmf_pno_macaddr_le pfn_mac;
159efc2c1faSArend Van Spriel 	u8 *mac_addr = NULL;
160efc2c1faSArend Van Spriel 	u8 *mac_mask = NULL;
161aa666b68SWright Feng 	int err, i, ri;
162ac55136fSArend Van Spriel 
163aa666b68SWright Feng 	for (ri = 0; ri < pi->n_reqs; ri++)
164aa666b68SWright Feng 		if (pi->reqs[ri]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
165aa666b68SWright Feng 			mac_addr = pi->reqs[ri]->mac_addr;
166aa666b68SWright Feng 			mac_mask = pi->reqs[ri]->mac_addr_mask;
167efc2c1faSArend Van Spriel 			break;
168efc2c1faSArend Van Spriel 		}
169efc2c1faSArend Van Spriel 
170efc2c1faSArend Van Spriel 	/* no random mac requested */
171efc2c1faSArend Van Spriel 	if (!mac_addr)
172efc2c1faSArend Van Spriel 		return 0;
173efc2c1faSArend Van Spriel 
174ac55136fSArend Van Spriel 	pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
175ac55136fSArend Van Spriel 	pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
176ac55136fSArend Van Spriel 
177fca6cb2fSArend Van Spriel 	memcpy(pfn_mac.mac, mac_addr, ETH_ALEN);
178ac55136fSArend Van Spriel 	for (i = 0; i < ETH_ALEN; i++) {
179ac55136fSArend Van Spriel 		pfn_mac.mac[i] &= mac_mask[i];
180f743f16cSJason A. Donenfeld 		pfn_mac.mac[i] |= get_random_u8() & ~(mac_mask[i]);
181ac55136fSArend Van Spriel 	}
182ac55136fSArend Van Spriel 	/* Clear multi bit */
183ac55136fSArend Van Spriel 	pfn_mac.mac[0] &= 0xFE;
184ac55136fSArend Van Spriel 	/* Set locally administered */
185ac55136fSArend Van Spriel 	pfn_mac.mac[0] |= 0x02;
186ac55136fSArend Van Spriel 
187efc2c1faSArend Van Spriel 	brcmf_dbg(SCAN, "enabling random mac: reqid=%llu mac=%pM\n",
188aa666b68SWright Feng 		  pi->reqs[ri]->reqid, pfn_mac.mac);
189ac55136fSArend Van Spriel 	err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
190ac55136fSArend Van Spriel 				       sizeof(pfn_mac));
191ac55136fSArend Van Spriel 	if (err)
192dcb1471bSRafał Miłecki 		bphy_err(drvr, "pfn_macaddr failed, err=%d\n", err);
193ac55136fSArend Van Spriel 
194ac55136fSArend Van Spriel 	return err;
195ac55136fSArend Van Spriel }
196ac55136fSArend Van Spriel 
brcmf_pno_add_ssid(struct brcmf_if * ifp,struct cfg80211_ssid * ssid,bool active)1973e48611dSArend Van Spriel static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid,
1983e2e86abSArend Van Spriel 			      bool active)
1993e2e86abSArend Van Spriel {
200dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
2013e2e86abSArend Van Spriel 	struct brcmf_pno_net_param_le pfn;
20269897f39SArend Van Spriel 	int err;
2033e2e86abSArend Van Spriel 
2043e2e86abSArend Van Spriel 	pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
2053e2e86abSArend Van Spriel 	pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
2063e2e86abSArend Van Spriel 	pfn.wsec = cpu_to_le32(0);
2073e2e86abSArend Van Spriel 	pfn.infra = cpu_to_le32(1);
2082b66325dSArend Van Spriel 	pfn.flags = 0;
2093e2e86abSArend Van Spriel 	if (active)
2103e2e86abSArend Van Spriel 		pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
2113e2e86abSArend Van Spriel 	pfn.ssid.SSID_len = cpu_to_le32(ssid->ssid_len);
2123e2e86abSArend Van Spriel 	memcpy(pfn.ssid.SSID, ssid->ssid, ssid->ssid_len);
21369897f39SArend Van Spriel 
21469897f39SArend Van Spriel 	brcmf_dbg(SCAN, "adding ssid=%.32s (active=%d)\n", ssid->ssid, active);
21569897f39SArend Van Spriel 	err = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, sizeof(pfn));
21669897f39SArend Van Spriel 	if (err < 0)
217dcb1471bSRafał Miłecki 		bphy_err(drvr, "adding failed: err=%d\n", err);
21869897f39SArend Van Spriel 	return err;
21969897f39SArend Van Spriel }
22069897f39SArend Van Spriel 
brcmf_pno_add_bssid(struct brcmf_if * ifp,const u8 * bssid)22169897f39SArend Van Spriel static int brcmf_pno_add_bssid(struct brcmf_if *ifp, const u8 *bssid)
22269897f39SArend Van Spriel {
223dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
22469897f39SArend Van Spriel 	struct brcmf_pno_bssid_le bssid_cfg;
22569897f39SArend Van Spriel 	int err;
22669897f39SArend Van Spriel 
22769897f39SArend Van Spriel 	memcpy(bssid_cfg.bssid, bssid, ETH_ALEN);
22869897f39SArend Van Spriel 	bssid_cfg.flags = 0;
22969897f39SArend Van Spriel 
23069897f39SArend Van Spriel 	brcmf_dbg(SCAN, "adding bssid=%pM\n", bssid);
23169897f39SArend Van Spriel 	err = brcmf_fil_iovar_data_set(ifp, "pfn_add_bssid", &bssid_cfg,
23269897f39SArend Van Spriel 				       sizeof(bssid_cfg));
23369897f39SArend Van Spriel 	if (err < 0)
234dcb1471bSRafał Miłecki 		bphy_err(drvr, "adding failed: err=%d\n", err);
23569897f39SArend Van Spriel 	return err;
2363e2e86abSArend Van Spriel }
2373e2e86abSArend Van Spriel 
brcmf_is_ssid_active(struct cfg80211_ssid * ssid,struct cfg80211_sched_scan_request * req)2383e48611dSArend Van Spriel static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid,
2393e48611dSArend Van Spriel 				 struct cfg80211_sched_scan_request *req)
2403e48611dSArend Van Spriel {
2413e48611dSArend Van Spriel 	int i;
2423e48611dSArend Van Spriel 
2433e48611dSArend Van Spriel 	if (!ssid || !req->ssids || !req->n_ssids)
2443e48611dSArend Van Spriel 		return false;
2453e48611dSArend Van Spriel 
2463e48611dSArend Van Spriel 	for (i = 0; i < req->n_ssids; i++) {
2473e48611dSArend Van Spriel 		if (ssid->ssid_len == req->ssids[i].ssid_len) {
2483e48611dSArend Van Spriel 			if (!strncmp(ssid->ssid, req->ssids[i].ssid,
2493e48611dSArend Van Spriel 				     ssid->ssid_len))
2503e48611dSArend Van Spriel 				return true;
2513e48611dSArend Van Spriel 		}
2523e48611dSArend Van Spriel 	}
2533e48611dSArend Van Spriel 	return false;
2543e48611dSArend Van Spriel }
2553e48611dSArend Van Spriel 
brcmf_pno_clean(struct brcmf_if * ifp)256efc2c1faSArend Van Spriel static int brcmf_pno_clean(struct brcmf_if *ifp)
2573e48611dSArend Van Spriel {
258dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
2593e48611dSArend Van Spriel 	int ret;
2603e48611dSArend Van Spriel 
2613e48611dSArend Van Spriel 	/* Disable pfn */
2623e48611dSArend Van Spriel 	ret = brcmf_fil_iovar_int_set(ifp, "pfn", 0);
2633e48611dSArend Van Spriel 	if (ret == 0) {
2643e48611dSArend Van Spriel 		/* clear pfn */
2653e48611dSArend Van Spriel 		ret = brcmf_fil_iovar_data_set(ifp, "pfnclear", NULL, 0);
2663e48611dSArend Van Spriel 	}
2673e48611dSArend Van Spriel 	if (ret < 0)
268dcb1471bSRafał Miłecki 		bphy_err(drvr, "failed code %d\n", ret);
2693e48611dSArend Van Spriel 
2703e48611dSArend Van Spriel 	return ret;
2713e48611dSArend Van Spriel }
2723e48611dSArend Van Spriel 
brcmf_pno_get_bucket_channels(struct cfg80211_sched_scan_request * r,struct brcmf_pno_config_le * pno_cfg)273efc2c1faSArend Van Spriel static int brcmf_pno_get_bucket_channels(struct cfg80211_sched_scan_request *r,
274efc2c1faSArend Van Spriel 					 struct brcmf_pno_config_le *pno_cfg)
2753e48611dSArend Van Spriel {
276efc2c1faSArend Van Spriel 	u32 n_chan = le32_to_cpu(pno_cfg->channel_num);
277331e7894SArend Van Spriel 	u16 chan;
278efc2c1faSArend Van Spriel 	int i, err = 0;
279efc2c1faSArend Van Spriel 
280efc2c1faSArend Van Spriel 	for (i = 0; i < r->n_channels; i++) {
281efc2c1faSArend Van Spriel 		if (n_chan >= BRCMF_NUMCHANNELS) {
282efc2c1faSArend Van Spriel 			err = -ENOSPC;
283efc2c1faSArend Van Spriel 			goto done;
284efc2c1faSArend Van Spriel 		}
285efc2c1faSArend Van Spriel 		chan = r->channels[i]->hw_value;
286efc2c1faSArend Van Spriel 		brcmf_dbg(SCAN, "[%d] Chan : %u\n", n_chan, chan);
287efc2c1faSArend Van Spriel 		pno_cfg->channel_list[n_chan++] = cpu_to_le16(chan);
288efc2c1faSArend Van Spriel 	}
289efc2c1faSArend Van Spriel 	/* return number of channels */
290efc2c1faSArend Van Spriel 	err = n_chan;
291efc2c1faSArend Van Spriel done:
292efc2c1faSArend Van Spriel 	pno_cfg->channel_num = cpu_to_le32(n_chan);
293efc2c1faSArend Van Spriel 	return err;
294efc2c1faSArend Van Spriel }
295efc2c1faSArend Van Spriel 
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)296efc2c1faSArend Van Spriel static int brcmf_pno_prep_fwconfig(struct brcmf_pno_info *pi,
297efc2c1faSArend Van Spriel 				   struct brcmf_pno_config_le *pno_cfg,
298efc2c1faSArend Van Spriel 				   struct brcmf_gscan_bucket_config **buckets,
299efc2c1faSArend Van Spriel 				   u32 *scan_freq)
300efc2c1faSArend Van Spriel {
301efc2c1faSArend Van Spriel 	struct cfg80211_sched_scan_request *sr;
302efc2c1faSArend Van Spriel 	struct brcmf_gscan_bucket_config *fw_buckets;
303efc2c1faSArend Van Spriel 	int i, err, chidx;
304efc2c1faSArend Van Spriel 
305efc2c1faSArend Van Spriel 	brcmf_dbg(SCAN, "n_reqs=%d\n", pi->n_reqs);
306efc2c1faSArend Van Spriel 	if (WARN_ON(!pi->n_reqs))
307efc2c1faSArend Van Spriel 		return -ENODATA;
308efc2c1faSArend Van Spriel 
309efc2c1faSArend Van Spriel 	/*
310efc2c1faSArend Van Spriel 	 * actual scan period is determined using gcd() for each
311efc2c1faSArend Van Spriel 	 * scheduled scan period.
312efc2c1faSArend Van Spriel 	 */
313efc2c1faSArend Van Spriel 	*scan_freq = pi->reqs[0]->scan_plans[0].interval;
314efc2c1faSArend Van Spriel 	for (i = 1; i < pi->n_reqs; i++) {
315efc2c1faSArend Van Spriel 		sr = pi->reqs[i];
316efc2c1faSArend Van Spriel 		*scan_freq = gcd(sr->scan_plans[0].interval, *scan_freq);
317efc2c1faSArend Van Spriel 	}
318efc2c1faSArend Van Spriel 	if (*scan_freq < BRCMF_PNO_SCHED_SCAN_MIN_PERIOD) {
319efc2c1faSArend Van Spriel 		brcmf_dbg(SCAN, "scan period too small, using minimum\n");
320efc2c1faSArend Van Spriel 		*scan_freq = BRCMF_PNO_SCHED_SCAN_MIN_PERIOD;
321efc2c1faSArend Van Spriel 	}
322efc2c1faSArend Van Spriel 
323efc2c1faSArend Van Spriel 	*buckets = NULL;
324efc2c1faSArend Van Spriel 	fw_buckets = kcalloc(pi->n_reqs, sizeof(*fw_buckets), GFP_KERNEL);
325efc2c1faSArend Van Spriel 	if (!fw_buckets)
326efc2c1faSArend Van Spriel 		return -ENOMEM;
327efc2c1faSArend Van Spriel 
328efc2c1faSArend Van Spriel 	memset(pno_cfg, 0, sizeof(*pno_cfg));
329efc2c1faSArend Van Spriel 	for (i = 0; i < pi->n_reqs; i++) {
330efc2c1faSArend Van Spriel 		sr = pi->reqs[i];
331efc2c1faSArend Van Spriel 		chidx = brcmf_pno_get_bucket_channels(sr, pno_cfg);
332efc2c1faSArend Van Spriel 		if (chidx < 0) {
333efc2c1faSArend Van Spriel 			err = chidx;
334efc2c1faSArend Van Spriel 			goto fail;
335efc2c1faSArend Van Spriel 		}
336efc2c1faSArend Van Spriel 		fw_buckets[i].bucket_end_index = chidx - 1;
337efc2c1faSArend Van Spriel 		fw_buckets[i].bucket_freq_multiple =
338efc2c1faSArend Van Spriel 			sr->scan_plans[0].interval / *scan_freq;
339efc2c1faSArend Van Spriel 		/* assure period is non-zero */
340efc2c1faSArend Van Spriel 		if (!fw_buckets[i].bucket_freq_multiple)
341efc2c1faSArend Van Spriel 			fw_buckets[i].bucket_freq_multiple = 1;
342efc2c1faSArend Van Spriel 		fw_buckets[i].flag = BRCMF_PNO_REPORT_NO_BATCH;
343efc2c1faSArend Van Spriel 	}
344efc2c1faSArend Van Spriel 
345efc2c1faSArend Van Spriel 	if (BRCMF_SCAN_ON()) {
346efc2c1faSArend Van Spriel 		brcmf_err("base period=%u\n", *scan_freq);
347efc2c1faSArend Van Spriel 		for (i = 0; i < pi->n_reqs; i++) {
348efc2c1faSArend Van Spriel 			brcmf_err("[%d] period %u max %u repeat %u flag %x idx %u\n",
349efc2c1faSArend Van Spriel 				  i, fw_buckets[i].bucket_freq_multiple,
350efc2c1faSArend Van Spriel 				  le16_to_cpu(fw_buckets[i].max_freq_multiple),
351efc2c1faSArend Van Spriel 				  fw_buckets[i].repeat, fw_buckets[i].flag,
352efc2c1faSArend Van Spriel 				  fw_buckets[i].bucket_end_index);
353efc2c1faSArend Van Spriel 		}
354efc2c1faSArend Van Spriel 	}
355efc2c1faSArend Van Spriel 	*buckets = fw_buckets;
356efc2c1faSArend Van Spriel 	return pi->n_reqs;
357efc2c1faSArend Van Spriel 
358efc2c1faSArend Van Spriel fail:
359efc2c1faSArend Van Spriel 	kfree(fw_buckets);
360efc2c1faSArend Van Spriel 	return err;
361efc2c1faSArend Van Spriel }
362efc2c1faSArend Van Spriel 
brcmf_pno_config_networks(struct brcmf_if * ifp,struct brcmf_pno_info * pi)36369897f39SArend Van Spriel static int brcmf_pno_config_networks(struct brcmf_if *ifp,
364efc2c1faSArend Van Spriel 				     struct brcmf_pno_info *pi)
365efc2c1faSArend Van Spriel {
366efc2c1faSArend Van Spriel 	struct cfg80211_sched_scan_request *r;
367efc2c1faSArend Van Spriel 	struct cfg80211_match_set *ms;
368efc2c1faSArend Van Spriel 	bool active;
36969897f39SArend Van Spriel 	int i, j, err = 0;
370efc2c1faSArend Van Spriel 
371efc2c1faSArend Van Spriel 	for (i = 0; i < pi->n_reqs; i++) {
372efc2c1faSArend Van Spriel 		r = pi->reqs[i];
373efc2c1faSArend Van Spriel 
374efc2c1faSArend Van Spriel 		for (j = 0; j < r->n_match_sets; j++) {
375efc2c1faSArend Van Spriel 			ms = &r->match_sets[j];
37669897f39SArend Van Spriel 			if (ms->ssid.ssid_len) {
377efc2c1faSArend Van Spriel 				active = brcmf_is_ssid_active(&ms->ssid, r);
37869897f39SArend Van Spriel 				err = brcmf_pno_add_ssid(ifp, &ms->ssid,
37969897f39SArend Van Spriel 							 active);
380efc2c1faSArend Van Spriel 			}
38169897f39SArend Van Spriel 			if (!err && is_valid_ether_addr(ms->bssid))
38269897f39SArend Van Spriel 				err = brcmf_pno_add_bssid(ifp, ms->bssid);
38369897f39SArend Van Spriel 
38469897f39SArend Van Spriel 			if (err < 0)
38569897f39SArend Van Spriel 				return err;
386efc2c1faSArend Van Spriel 		}
387efc2c1faSArend Van Spriel 	}
388efc2c1faSArend Van Spriel 	return 0;
389efc2c1faSArend Van Spriel }
390efc2c1faSArend Van Spriel 
brcmf_pno_config_sched_scans(struct brcmf_if * ifp)391efc2c1faSArend Van Spriel static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp)
392efc2c1faSArend Van Spriel {
393dcb1471bSRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
394efc2c1faSArend Van Spriel 	struct brcmf_pno_info *pi;
395efc2c1faSArend Van Spriel 	struct brcmf_gscan_config *gscan_cfg;
396efc2c1faSArend Van Spriel 	struct brcmf_gscan_bucket_config *buckets;
397efc2c1faSArend Van Spriel 	struct brcmf_pno_config_le pno_cfg;
398efc2c1faSArend Van Spriel 	size_t gsz;
399efc2c1faSArend Van Spriel 	u32 scan_freq;
400efc2c1faSArend Van Spriel 	int err, n_buckets;
401efc2c1faSArend Van Spriel 
402efc2c1faSArend Van Spriel 	pi = ifp_to_pno(ifp);
403efc2c1faSArend Van Spriel 	n_buckets = brcmf_pno_prep_fwconfig(pi, &pno_cfg, &buckets,
404efc2c1faSArend Van Spriel 					    &scan_freq);
405efc2c1faSArend Van Spriel 	if (n_buckets < 0)
406efc2c1faSArend Van Spriel 		return n_buckets;
407efc2c1faSArend Van Spriel 
408*f0e0897bSGustavo A. R. Silva 	gsz = struct_size(gscan_cfg, bucket, n_buckets);
409efc2c1faSArend Van Spriel 	gscan_cfg = kzalloc(gsz, GFP_KERNEL);
410efc2c1faSArend Van Spriel 	if (!gscan_cfg) {
411efc2c1faSArend Van Spriel 		err = -ENOMEM;
412efc2c1faSArend Van Spriel 		goto free_buckets;
413efc2c1faSArend Van Spriel 	}
4143e48611dSArend Van Spriel 
4153e48611dSArend Van Spriel 	/* clean up everything */
416efc2c1faSArend Van Spriel 	err = brcmf_pno_clean(ifp);
417efc2c1faSArend Van Spriel 	if  (err < 0) {
418dcb1471bSRafał Miłecki 		bphy_err(drvr, "failed error=%d\n", err);
419efc2c1faSArend Van Spriel 		goto free_gscan;
4203e48611dSArend Van Spriel 	}
4213e48611dSArend Van Spriel 
4223e48611dSArend Van Spriel 	/* configure pno */
423efc2c1faSArend Van Spriel 	err = brcmf_pno_config(ifp, scan_freq, 0, 0);
424efc2c1faSArend Van Spriel 	if (err < 0)
425efc2c1faSArend Van Spriel 		goto free_gscan;
426efc2c1faSArend Van Spriel 
427efc2c1faSArend Van Spriel 	err = brcmf_pno_channel_config(ifp, &pno_cfg);
428efc2c1faSArend Van Spriel 	if (err < 0)
429efc2c1faSArend Van Spriel 		goto clean;
430efc2c1faSArend Van Spriel 
431efc2c1faSArend Van Spriel 	gscan_cfg->version = cpu_to_le16(BRCMF_GSCAN_CFG_VERSION);
432efc2c1faSArend Van Spriel 	gscan_cfg->retry_threshold = GSCAN_RETRY_THRESHOLD;
433efc2c1faSArend Van Spriel 	gscan_cfg->buffer_threshold = GSCAN_BATCH_NO_THR_SET;
434efc2c1faSArend Van Spriel 	gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN;
435efc2c1faSArend Van Spriel 
436efc2c1faSArend Van Spriel 	gscan_cfg->count_of_channel_buckets = n_buckets;
437*f0e0897bSGustavo A. R. Silva 	memcpy(gscan_cfg->bucket, buckets,
438*f0e0897bSGustavo A. R. Silva 	       array_size(n_buckets, sizeof(*buckets)));
439efc2c1faSArend Van Spriel 
440efc2c1faSArend Van Spriel 	err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg, gsz);
441efc2c1faSArend Van Spriel 
442efc2c1faSArend Van Spriel 	if (err < 0)
443efc2c1faSArend Van Spriel 		goto clean;
4443e48611dSArend Van Spriel 
4453e48611dSArend Van Spriel 	/* configure random mac */
446efc2c1faSArend Van Spriel 	err = brcmf_pno_set_random(ifp, pi);
447efc2c1faSArend Van Spriel 	if (err < 0)
448efc2c1faSArend Van Spriel 		goto clean;
4493e48611dSArend Van Spriel 
45069897f39SArend Van Spriel 	err = brcmf_pno_config_networks(ifp, pi);
451efc2c1faSArend Van Spriel 	if (err < 0)
452efc2c1faSArend Van Spriel 		goto clean;
453331e7894SArend Van Spriel 
4543e48611dSArend Van Spriel 	/* Enable the PNO */
455efc2c1faSArend Van Spriel 	err = brcmf_fil_iovar_int_set(ifp, "pfn", 1);
4563e48611dSArend Van Spriel 
457efc2c1faSArend Van Spriel clean:
458efc2c1faSArend Van Spriel 	if (err < 0)
459efc2c1faSArend Van Spriel 		brcmf_pno_clean(ifp);
460efc2c1faSArend Van Spriel free_gscan:
461efc2c1faSArend Van Spriel 	kfree(gscan_cfg);
462efc2c1faSArend Van Spriel free_buckets:
463efc2c1faSArend Van Spriel 	kfree(buckets);
464efc2c1faSArend Van Spriel 	return err;
465efc2c1faSArend Van Spriel }
466efc2c1faSArend Van Spriel 
brcmf_pno_start_sched_scan(struct brcmf_if * ifp,struct cfg80211_sched_scan_request * req)467efc2c1faSArend Van Spriel int brcmf_pno_start_sched_scan(struct brcmf_if *ifp,
468efc2c1faSArend Van Spriel 			       struct cfg80211_sched_scan_request *req)
469efc2c1faSArend Van Spriel {
470efc2c1faSArend Van Spriel 	struct brcmf_pno_info *pi;
471efc2c1faSArend Van Spriel 	int ret;
472efc2c1faSArend Van Spriel 
473efc2c1faSArend Van Spriel 	brcmf_dbg(TRACE, "reqid=%llu\n", req->reqid);
474efc2c1faSArend Van Spriel 
475efc2c1faSArend Van Spriel 	pi = ifp_to_pno(ifp);
476efc2c1faSArend Van Spriel 	ret = brcmf_pno_store_request(pi, req);
477efc2c1faSArend Van Spriel 	if (ret < 0)
4783e48611dSArend Van Spriel 		return ret;
479efc2c1faSArend Van Spriel 
480efc2c1faSArend Van Spriel 	ret = brcmf_pno_config_sched_scans(ifp);
481efc2c1faSArend Van Spriel 	if (ret < 0) {
482efc2c1faSArend Van Spriel 		brcmf_pno_remove_request(pi, req->reqid);
483efc2c1faSArend Van Spriel 		if (pi->n_reqs)
484efc2c1faSArend Van Spriel 			(void)brcmf_pno_config_sched_scans(ifp);
485efc2c1faSArend Van Spriel 		return ret;
486efc2c1faSArend Van Spriel 	}
487efc2c1faSArend Van Spriel 	return 0;
488efc2c1faSArend Van Spriel }
489efc2c1faSArend Van Spriel 
brcmf_pno_stop_sched_scan(struct brcmf_if * ifp,u64 reqid)490efc2c1faSArend Van Spriel int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
491efc2c1faSArend Van Spriel {
492efc2c1faSArend Van Spriel 	struct brcmf_pno_info *pi;
493efc2c1faSArend Van Spriel 	int err;
494efc2c1faSArend Van Spriel 
495efc2c1faSArend Van Spriel 	brcmf_dbg(TRACE, "reqid=%llu\n", reqid);
496efc2c1faSArend Van Spriel 
497efc2c1faSArend Van Spriel 	pi = ifp_to_pno(ifp);
4983a33bd84SLo-Hsiang Lo 
4993a33bd84SLo-Hsiang Lo 	/* No PNO request */
5003a33bd84SLo-Hsiang Lo 	if (!pi->n_reqs)
5013a33bd84SLo-Hsiang Lo 		return 0;
5023a33bd84SLo-Hsiang Lo 
503efc2c1faSArend Van Spriel 	err = brcmf_pno_remove_request(pi, reqid);
504efc2c1faSArend Van Spriel 	if (err)
505efc2c1faSArend Van Spriel 		return err;
506efc2c1faSArend Van Spriel 
507efc2c1faSArend Van Spriel 	brcmf_pno_clean(ifp);
508efc2c1faSArend Van Spriel 
509efc2c1faSArend Van Spriel 	if (pi->n_reqs)
510efc2c1faSArend Van Spriel 		(void)brcmf_pno_config_sched_scans(ifp);
511efc2c1faSArend Van Spriel 
512efc2c1faSArend Van Spriel 	return 0;
513efc2c1faSArend Van Spriel }
514efc2c1faSArend Van Spriel 
brcmf_pno_attach(struct brcmf_cfg80211_info * cfg)515efc2c1faSArend Van Spriel int brcmf_pno_attach(struct brcmf_cfg80211_info *cfg)
516efc2c1faSArend Van Spriel {
517efc2c1faSArend Van Spriel 	struct brcmf_pno_info *pi;
518efc2c1faSArend Van Spriel 
519efc2c1faSArend Van Spriel 	brcmf_dbg(TRACE, "enter\n");
520efc2c1faSArend Van Spriel 	pi = kzalloc(sizeof(*pi), GFP_KERNEL);
521efc2c1faSArend Van Spriel 	if (!pi)
522efc2c1faSArend Van Spriel 		return -ENOMEM;
523efc2c1faSArend Van Spriel 
524efc2c1faSArend Van Spriel 	cfg->pno = pi;
52542596f76SArend Van Spriel 	mutex_init(&pi->req_lock);
526efc2c1faSArend Van Spriel 	return 0;
527efc2c1faSArend Van Spriel }
528efc2c1faSArend Van Spriel 
brcmf_pno_detach(struct brcmf_cfg80211_info * cfg)529efc2c1faSArend Van Spriel void brcmf_pno_detach(struct brcmf_cfg80211_info *cfg)
530efc2c1faSArend Van Spriel {
531efc2c1faSArend Van Spriel 	struct brcmf_pno_info *pi;
532efc2c1faSArend Van Spriel 
533efc2c1faSArend Van Spriel 	brcmf_dbg(TRACE, "enter\n");
534efc2c1faSArend Van Spriel 	pi = cfg->pno;
535efc2c1faSArend Van Spriel 	cfg->pno = NULL;
536efc2c1faSArend Van Spriel 
537efc2c1faSArend Van Spriel 	WARN_ON(pi->n_reqs);
53842596f76SArend Van Spriel 	mutex_destroy(&pi->req_lock);
539efc2c1faSArend Van Spriel 	kfree(pi);
5403e48611dSArend Van Spriel }
5413e48611dSArend Van Spriel 
brcmf_pno_wiphy_params(struct wiphy * wiphy,bool gscan)54294ed6ffbSArend Van Spriel void brcmf_pno_wiphy_params(struct wiphy *wiphy, bool gscan)
54394ed6ffbSArend Van Spriel {
54494ed6ffbSArend Van Spriel 	/* scheduled scan settings */
545efc2c1faSArend Van Spriel 	wiphy->max_sched_scan_reqs = gscan ? BRCMF_PNO_MAX_BUCKETS : 1;
54694ed6ffbSArend Van Spriel 	wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
54794ed6ffbSArend Van Spriel 	wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
54894ed6ffbSArend Van Spriel 	wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
54994ed6ffbSArend Van Spriel 	wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD;
55094ed6ffbSArend Van Spriel }
55194ed6ffbSArend Van Spriel 
brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info * pi,u32 bucket)552efc2c1faSArend Van Spriel u64 brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info *pi, u32 bucket)
553efc2c1faSArend Van Spriel {
55442596f76SArend Van Spriel 	u64 reqid = 0;
555efc2c1faSArend Van Spriel 
55642596f76SArend Van Spriel 	mutex_lock(&pi->req_lock);
55742596f76SArend Van Spriel 
55842596f76SArend Van Spriel 	if (bucket < pi->n_reqs)
55942596f76SArend Van Spriel 		reqid = pi->reqs[bucket]->reqid;
56042596f76SArend Van Spriel 
56142596f76SArend Van Spriel 	mutex_unlock(&pi->req_lock);
56242596f76SArend Van Spriel 	return reqid;
563efc2c1faSArend Van Spriel }
564efc2c1faSArend Van Spriel 
brcmf_pno_get_bucket_map(struct brcmf_pno_info * pi,struct brcmf_pno_net_info_le * ni)565efc2c1faSArend Van Spriel u32 brcmf_pno_get_bucket_map(struct brcmf_pno_info *pi,
566efc2c1faSArend Van Spriel 			     struct brcmf_pno_net_info_le *ni)
567efc2c1faSArend Van Spriel {
568efc2c1faSArend Van Spriel 	struct cfg80211_sched_scan_request *req;
569efc2c1faSArend Van Spriel 	struct cfg80211_match_set *ms;
570efc2c1faSArend Van Spriel 	u32 bucket_map = 0;
571efc2c1faSArend Van Spriel 	int i, j;
572efc2c1faSArend Van Spriel 
57342596f76SArend Van Spriel 	mutex_lock(&pi->req_lock);
574efc2c1faSArend Van Spriel 	for (i = 0; i < pi->n_reqs; i++) {
575efc2c1faSArend Van Spriel 		req = pi->reqs[i];
576efc2c1faSArend Van Spriel 
577efc2c1faSArend Van Spriel 		if (!req->n_match_sets)
578efc2c1faSArend Van Spriel 			continue;
579efc2c1faSArend Van Spriel 		for (j = 0; j < req->n_match_sets; j++) {
580efc2c1faSArend Van Spriel 			ms = &req->match_sets[j];
581efc2c1faSArend Van Spriel 			if (ms->ssid.ssid_len == ni->SSID_len &&
58269897f39SArend Van Spriel 			    !memcmp(ms->ssid.ssid, ni->SSID, ni->SSID_len)) {
58369897f39SArend Van Spriel 				bucket_map |= BIT(i);
58469897f39SArend Van Spriel 				break;
58569897f39SArend Van Spriel 			}
58669897f39SArend Van Spriel 			if (is_valid_ether_addr(ms->bssid) &&
58769897f39SArend Van Spriel 			    !memcmp(ms->bssid, ni->bssid, ETH_ALEN)) {
588efc2c1faSArend Van Spriel 				bucket_map |= BIT(i);
589efc2c1faSArend Van Spriel 				break;
590efc2c1faSArend Van Spriel 			}
591efc2c1faSArend Van Spriel 		}
592efc2c1faSArend Van Spriel 	}
59342596f76SArend Van Spriel 	mutex_unlock(&pi->req_lock);
594efc2c1faSArend Van Spriel 	return bucket_map;
595efc2c1faSArend Van Spriel }
596