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