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