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