162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2018 - 2021, 2023 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <net/cfg80211.h> 662306a36Sopenharmony_ci#include "core.h" 762306a36Sopenharmony_ci#include "nl80211.h" 862306a36Sopenharmony_ci#include "rdev-ops.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, 1162306a36Sopenharmony_ci struct nlattr *ftmreq, 1262306a36Sopenharmony_ci struct cfg80211_pmsr_request_peer *out, 1362306a36Sopenharmony_ci struct genl_info *info) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa; 1662306a36Sopenharmony_ci struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1]; 1762306a36Sopenharmony_ci u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci /* validate existing data */ 2062306a36Sopenharmony_ci if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) { 2162306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth"); 2262306a36Sopenharmony_ci return -EINVAL; 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci /* no validation needed - was already done via nested policy */ 2662306a36Sopenharmony_ci nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, 2762306a36Sopenharmony_ci NULL, NULL); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) 3062306a36Sopenharmony_ci preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* set up values - struct is 0-initialized */ 3362306a36Sopenharmony_ci out->ftm.requested = true; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci switch (out->chandef.chan->band) { 3662306a36Sopenharmony_ci case NL80211_BAND_60GHZ: 3762306a36Sopenharmony_ci /* optional */ 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci default: 4062306a36Sopenharmony_ci if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) { 4162306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, 4262306a36Sopenharmony_ci "FTM: must specify preamble"); 4362306a36Sopenharmony_ci return -EINVAL; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (!(capa->ftm.preambles & BIT(preamble))) { 4862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 4962306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], 5062306a36Sopenharmony_ci "FTM: invalid preamble"); 5162306a36Sopenharmony_ci return -EINVAL; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci out->ftm.preamble = preamble; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci out->ftm.burst_period = 0; 5762306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]) 5862306a36Sopenharmony_ci out->ftm.burst_period = 5962306a36Sopenharmony_ci nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]; 6262306a36Sopenharmony_ci if (out->ftm.asap && !capa->ftm.asap) { 6362306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 6462306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP], 6562306a36Sopenharmony_ci "FTM: ASAP mode not supported"); 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!out->ftm.asap && !capa->ftm.non_asap) { 7062306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, 7162306a36Sopenharmony_ci "FTM: non-ASAP mode not supported"); 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci out->ftm.num_bursts_exp = 0; 7662306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) 7762306a36Sopenharmony_ci out->ftm.num_bursts_exp = 7862306a36Sopenharmony_ci nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (capa->ftm.max_bursts_exponent >= 0 && 8162306a36Sopenharmony_ci out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) { 8262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 8362306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP], 8462306a36Sopenharmony_ci "FTM: max NUM_BURSTS_EXP must be set lower than the device limit"); 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci out->ftm.burst_duration = 15; 8962306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) 9062306a36Sopenharmony_ci out->ftm.burst_duration = 9162306a36Sopenharmony_ci nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci out->ftm.ftms_per_burst = 0; 9462306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) 9562306a36Sopenharmony_ci out->ftm.ftms_per_burst = 9662306a36Sopenharmony_ci nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (capa->ftm.max_ftms_per_burst && 9962306a36Sopenharmony_ci (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst || 10062306a36Sopenharmony_ci out->ftm.ftms_per_burst == 0)) { 10162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 10262306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], 10362306a36Sopenharmony_ci "FTM: FTMs per burst must be set lower than the device limit but non-zero"); 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci out->ftm.ftmr_retries = 3; 10862306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]) 10962306a36Sopenharmony_ci out->ftm.ftmr_retries = 11062306a36Sopenharmony_ci nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI]; 11362306a36Sopenharmony_ci if (out->ftm.request_lci && !capa->ftm.request_lci) { 11462306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 11562306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI], 11662306a36Sopenharmony_ci "FTM: LCI request not supported"); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci out->ftm.request_civicloc = 12062306a36Sopenharmony_ci !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC]; 12162306a36Sopenharmony_ci if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) { 12262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 12362306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC], 12462306a36Sopenharmony_ci "FTM: civic location request not supported"); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci out->ftm.trigger_based = 12862306a36Sopenharmony_ci !!tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED]; 12962306a36Sopenharmony_ci if (out->ftm.trigger_based && !capa->ftm.trigger_based) { 13062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 13162306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED], 13262306a36Sopenharmony_ci "FTM: trigger based ranging is not supported"); 13362306a36Sopenharmony_ci return -EINVAL; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci out->ftm.non_trigger_based = 13762306a36Sopenharmony_ci !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED]; 13862306a36Sopenharmony_ci if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) { 13962306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 14062306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED], 14162306a36Sopenharmony_ci "FTM: trigger based ranging is not supported"); 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (out->ftm.trigger_based && out->ftm.non_trigger_based) { 14662306a36Sopenharmony_ci NL_SET_ERR_MSG(info->extack, 14762306a36Sopenharmony_ci "FTM: can't set both trigger based and non trigger based"); 14862306a36Sopenharmony_ci return -EINVAL; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if ((out->ftm.trigger_based || out->ftm.non_trigger_based) && 15262306a36Sopenharmony_ci out->ftm.preamble != NL80211_PREAMBLE_HE) { 15362306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 15462306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], 15562306a36Sopenharmony_ci "FTM: non EDCA based ranging must use HE preamble"); 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci out->ftm.lmr_feedback = 16062306a36Sopenharmony_ci !!tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK]; 16162306a36Sopenharmony_ci if (!out->ftm.trigger_based && !out->ftm.non_trigger_based && 16262306a36Sopenharmony_ci out->ftm.lmr_feedback) { 16362306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 16462306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK], 16562306a36Sopenharmony_ci "FTM: LMR feedback set for EDCA based ranging"); 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]) { 17062306a36Sopenharmony_ci if (!out->ftm.non_trigger_based && !out->ftm.trigger_based) { 17162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 17262306a36Sopenharmony_ci tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR], 17362306a36Sopenharmony_ci "FTM: BSS color set for EDCA based ranging"); 17462306a36Sopenharmony_ci return -EINVAL; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci out->ftm.bss_color = 17862306a36Sopenharmony_ci nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int pmsr_parse_peer(struct cfg80211_registered_device *rdev, 18562306a36Sopenharmony_ci struct nlattr *peer, 18662306a36Sopenharmony_ci struct cfg80211_pmsr_request_peer *out, 18762306a36Sopenharmony_ci struct genl_info *info) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; 19062306a36Sopenharmony_ci struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1]; 19162306a36Sopenharmony_ci struct nlattr *treq; 19262306a36Sopenharmony_ci int err, rem; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* no validation needed - was already done via nested policy */ 19562306a36Sopenharmony_ci nla_parse_nested_deprecated(tb, NL80211_PMSR_PEER_ATTR_MAX, peer, 19662306a36Sopenharmony_ci NULL, NULL); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] || 19962306a36Sopenharmony_ci !tb[NL80211_PMSR_PEER_ATTR_CHAN] || 20062306a36Sopenharmony_ci !tb[NL80211_PMSR_PEER_ATTR_REQ]) { 20162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, peer, 20262306a36Sopenharmony_ci "insufficient peer data"); 20362306a36Sopenharmony_ci return -EINVAL; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* reuse info->attrs */ 20962306a36Sopenharmony_ci memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1)); 21062306a36Sopenharmony_ci err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX, 21162306a36Sopenharmony_ci tb[NL80211_PMSR_PEER_ATTR_CHAN], 21262306a36Sopenharmony_ci NULL, info->extack); 21362306a36Sopenharmony_ci if (err) 21462306a36Sopenharmony_ci return err; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci err = nl80211_parse_chandef(rdev, info, &out->chandef); 21762306a36Sopenharmony_ci if (err) 21862306a36Sopenharmony_ci return err; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* no validation needed - was already done via nested policy */ 22162306a36Sopenharmony_ci nla_parse_nested_deprecated(req, NL80211_PMSR_REQ_ATTR_MAX, 22262306a36Sopenharmony_ci tb[NL80211_PMSR_PEER_ATTR_REQ], NULL, 22362306a36Sopenharmony_ci NULL); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!req[NL80211_PMSR_REQ_ATTR_DATA]) { 22662306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 22762306a36Sopenharmony_ci tb[NL80211_PMSR_PEER_ATTR_REQ], 22862306a36Sopenharmony_ci "missing request type/data"); 22962306a36Sopenharmony_ci return -EINVAL; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF]) 23362306a36Sopenharmony_ci out->report_ap_tsf = true; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) { 23662306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 23762306a36Sopenharmony_ci req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF], 23862306a36Sopenharmony_ci "reporting AP TSF is not supported"); 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) { 24362306a36Sopenharmony_ci switch (nla_type(treq)) { 24462306a36Sopenharmony_ci case NL80211_PMSR_TYPE_FTM: 24562306a36Sopenharmony_ci err = pmsr_parse_ftm(rdev, treq, out, info); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci default: 24862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, treq, 24962306a36Sopenharmony_ci "unsupported measurement type"); 25062306a36Sopenharmony_ci err = -EINVAL; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (err) 25562306a36Sopenharmony_ci return err; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS]; 26362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = info->user_ptr[0]; 26462306a36Sopenharmony_ci struct wireless_dev *wdev = info->user_ptr[1]; 26562306a36Sopenharmony_ci struct cfg80211_pmsr_request *req; 26662306a36Sopenharmony_ci struct nlattr *peers, *peer; 26762306a36Sopenharmony_ci int count, rem, err, idx; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!rdev->wiphy.pmsr_capa) 27062306a36Sopenharmony_ci return -EOPNOTSUPP; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!reqattr) 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci peers = nla_find(nla_data(reqattr), nla_len(reqattr), 27662306a36Sopenharmony_ci NL80211_PMSR_ATTR_PEERS); 27762306a36Sopenharmony_ci if (!peers) 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci count = 0; 28162306a36Sopenharmony_ci nla_for_each_nested(peer, peers, rem) { 28262306a36Sopenharmony_ci count++; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (count > rdev->wiphy.pmsr_capa->max_peers) { 28562306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, peer, 28662306a36Sopenharmony_ci "Too many peers used"); 28762306a36Sopenharmony_ci return -EINVAL; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci req = kzalloc(struct_size(req, peers, count), GFP_KERNEL); 29262306a36Sopenharmony_ci if (!req) 29362306a36Sopenharmony_ci return -ENOMEM; 29462306a36Sopenharmony_ci req->n_peers = count; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (info->attrs[NL80211_ATTR_TIMEOUT]) 29762306a36Sopenharmony_ci req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (info->attrs[NL80211_ATTR_MAC]) { 30062306a36Sopenharmony_ci if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) { 30162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 30262306a36Sopenharmony_ci info->attrs[NL80211_ATTR_MAC], 30362306a36Sopenharmony_ci "device cannot randomize MAC address"); 30462306a36Sopenharmony_ci err = -EINVAL; 30562306a36Sopenharmony_ci goto out_err; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci err = nl80211_parse_random_mac(info->attrs, req->mac_addr, 30962306a36Sopenharmony_ci req->mac_addr_mask); 31062306a36Sopenharmony_ci if (err) 31162306a36Sopenharmony_ci goto out_err; 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN); 31462306a36Sopenharmony_ci eth_broadcast_addr(req->mac_addr_mask); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci idx = 0; 31862306a36Sopenharmony_ci nla_for_each_nested(peer, peers, rem) { 31962306a36Sopenharmony_ci /* NB: this reuses info->attrs, but we no longer need it */ 32062306a36Sopenharmony_ci err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info); 32162306a36Sopenharmony_ci if (err) 32262306a36Sopenharmony_ci goto out_err; 32362306a36Sopenharmony_ci idx++; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci req->cookie = cfg80211_assign_cookie(rdev); 32662306a36Sopenharmony_ci req->nl_portid = info->snd_portid; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci err = rdev_start_pmsr(rdev, wdev, req); 32962306a36Sopenharmony_ci if (err) 33062306a36Sopenharmony_ci goto out_err; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci list_add_tail(&req->list, &wdev->pmsr_list); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci nl_set_extack_cookie_u64(info->extack, req->cookie); 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ciout_err: 33762306a36Sopenharmony_ci kfree(req); 33862306a36Sopenharmony_ci return err; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_civoid cfg80211_pmsr_complete(struct wireless_dev *wdev, 34262306a36Sopenharmony_ci struct cfg80211_pmsr_request *req, 34362306a36Sopenharmony_ci gfp_t gfp) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 34662306a36Sopenharmony_ci struct cfg80211_pmsr_request *tmp, *prev, *to_free = NULL; 34762306a36Sopenharmony_ci struct sk_buff *msg; 34862306a36Sopenharmony_ci void *hdr; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); 35362306a36Sopenharmony_ci if (!msg) 35462306a36Sopenharmony_ci goto free_request; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci hdr = nl80211hdr_put(msg, 0, 0, 0, 35762306a36Sopenharmony_ci NL80211_CMD_PEER_MEASUREMENT_COMPLETE); 35862306a36Sopenharmony_ci if (!hdr) 35962306a36Sopenharmony_ci goto free_msg; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || 36262306a36Sopenharmony_ci nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), 36362306a36Sopenharmony_ci NL80211_ATTR_PAD)) 36462306a36Sopenharmony_ci goto free_msg; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie, 36762306a36Sopenharmony_ci NL80211_ATTR_PAD)) 36862306a36Sopenharmony_ci goto free_msg; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci genlmsg_end(msg, hdr); 37162306a36Sopenharmony_ci genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid); 37262306a36Sopenharmony_ci goto free_request; 37362306a36Sopenharmony_cifree_msg: 37462306a36Sopenharmony_ci nlmsg_free(msg); 37562306a36Sopenharmony_cifree_request: 37662306a36Sopenharmony_ci spin_lock_bh(&wdev->pmsr_lock); 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * cfg80211_pmsr_process_abort() may have already moved this request 37962306a36Sopenharmony_ci * to the free list, and will free it later. In this case, don't free 38062306a36Sopenharmony_ci * it here. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci list_for_each_entry_safe(tmp, prev, &wdev->pmsr_list, list) { 38362306a36Sopenharmony_ci if (tmp == req) { 38462306a36Sopenharmony_ci list_del(&req->list); 38562306a36Sopenharmony_ci to_free = req; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci spin_unlock_bh(&wdev->pmsr_lock); 39062306a36Sopenharmony_ci kfree(to_free); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfg80211_pmsr_complete); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int nl80211_pmsr_send_ftm_res(struct sk_buff *msg, 39562306a36Sopenharmony_ci struct cfg80211_pmsr_result *res) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci if (res->status == NL80211_PMSR_STATUS_FAILURE) { 39862306a36Sopenharmony_ci if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, 39962306a36Sopenharmony_ci res->ftm.failure_reason)) 40062306a36Sopenharmony_ci goto error; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (res->ftm.failure_reason == 40362306a36Sopenharmony_ci NL80211_PMSR_FTM_FAILURE_PEER_BUSY && 40462306a36Sopenharmony_ci res->ftm.busy_retry_time && 40562306a36Sopenharmony_ci nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME, 40662306a36Sopenharmony_ci res->ftm.busy_retry_time)) 40762306a36Sopenharmony_ci goto error; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci#define PUT(tp, attr, val) \ 41362306a36Sopenharmony_ci do { \ 41462306a36Sopenharmony_ci if (nla_put_##tp(msg, \ 41562306a36Sopenharmony_ci NL80211_PMSR_FTM_RESP_ATTR_##attr, \ 41662306a36Sopenharmony_ci res->ftm.val)) \ 41762306a36Sopenharmony_ci goto error; \ 41862306a36Sopenharmony_ci } while (0) 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci#define PUTOPT(tp, attr, val) \ 42162306a36Sopenharmony_ci do { \ 42262306a36Sopenharmony_ci if (res->ftm.val##_valid) \ 42362306a36Sopenharmony_ci PUT(tp, attr, val); \ 42462306a36Sopenharmony_ci } while (0) 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci#define PUT_U64(attr, val) \ 42762306a36Sopenharmony_ci do { \ 42862306a36Sopenharmony_ci if (nla_put_u64_64bit(msg, \ 42962306a36Sopenharmony_ci NL80211_PMSR_FTM_RESP_ATTR_##attr,\ 43062306a36Sopenharmony_ci res->ftm.val, \ 43162306a36Sopenharmony_ci NL80211_PMSR_FTM_RESP_ATTR_PAD)) \ 43262306a36Sopenharmony_ci goto error; \ 43362306a36Sopenharmony_ci } while (0) 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci#define PUTOPT_U64(attr, val) \ 43662306a36Sopenharmony_ci do { \ 43762306a36Sopenharmony_ci if (res->ftm.val##_valid) \ 43862306a36Sopenharmony_ci PUT_U64(attr, val); \ 43962306a36Sopenharmony_ci } while (0) 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (res->ftm.burst_index >= 0) 44262306a36Sopenharmony_ci PUT(u32, BURST_INDEX, burst_index); 44362306a36Sopenharmony_ci PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts); 44462306a36Sopenharmony_ci PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes); 44562306a36Sopenharmony_ci PUT(u8, NUM_BURSTS_EXP, num_bursts_exp); 44662306a36Sopenharmony_ci PUT(u8, BURST_DURATION, burst_duration); 44762306a36Sopenharmony_ci PUT(u8, FTMS_PER_BURST, ftms_per_burst); 44862306a36Sopenharmony_ci PUTOPT(s32, RSSI_AVG, rssi_avg); 44962306a36Sopenharmony_ci PUTOPT(s32, RSSI_SPREAD, rssi_spread); 45062306a36Sopenharmony_ci if (res->ftm.tx_rate_valid && 45162306a36Sopenharmony_ci !nl80211_put_sta_rate(msg, &res->ftm.tx_rate, 45262306a36Sopenharmony_ci NL80211_PMSR_FTM_RESP_ATTR_TX_RATE)) 45362306a36Sopenharmony_ci goto error; 45462306a36Sopenharmony_ci if (res->ftm.rx_rate_valid && 45562306a36Sopenharmony_ci !nl80211_put_sta_rate(msg, &res->ftm.rx_rate, 45662306a36Sopenharmony_ci NL80211_PMSR_FTM_RESP_ATTR_RX_RATE)) 45762306a36Sopenharmony_ci goto error; 45862306a36Sopenharmony_ci PUTOPT_U64(RTT_AVG, rtt_avg); 45962306a36Sopenharmony_ci PUTOPT_U64(RTT_VARIANCE, rtt_variance); 46062306a36Sopenharmony_ci PUTOPT_U64(RTT_SPREAD, rtt_spread); 46162306a36Sopenharmony_ci PUTOPT_U64(DIST_AVG, dist_avg); 46262306a36Sopenharmony_ci PUTOPT_U64(DIST_VARIANCE, dist_variance); 46362306a36Sopenharmony_ci PUTOPT_U64(DIST_SPREAD, dist_spread); 46462306a36Sopenharmony_ci if (res->ftm.lci && res->ftm.lci_len && 46562306a36Sopenharmony_ci nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI, 46662306a36Sopenharmony_ci res->ftm.lci_len, res->ftm.lci)) 46762306a36Sopenharmony_ci goto error; 46862306a36Sopenharmony_ci if (res->ftm.civicloc && res->ftm.civicloc_len && 46962306a36Sopenharmony_ci nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, 47062306a36Sopenharmony_ci res->ftm.civicloc_len, res->ftm.civicloc)) 47162306a36Sopenharmony_ci goto error; 47262306a36Sopenharmony_ci#undef PUT 47362306a36Sopenharmony_ci#undef PUTOPT 47462306a36Sopenharmony_ci#undef PUT_U64 47562306a36Sopenharmony_ci#undef PUTOPT_U64 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_cierror: 47962306a36Sopenharmony_ci return -ENOSPC; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int nl80211_pmsr_send_result(struct sk_buff *msg, 48362306a36Sopenharmony_ci struct cfg80211_pmsr_result *res) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci pmsr = nla_nest_start_noflag(msg, NL80211_ATTR_PEER_MEASUREMENTS); 48862306a36Sopenharmony_ci if (!pmsr) 48962306a36Sopenharmony_ci goto error; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci peers = nla_nest_start_noflag(msg, NL80211_PMSR_ATTR_PEERS); 49262306a36Sopenharmony_ci if (!peers) 49362306a36Sopenharmony_ci goto error; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci peer = nla_nest_start_noflag(msg, 1); 49662306a36Sopenharmony_ci if (!peer) 49762306a36Sopenharmony_ci goto error; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr)) 50062306a36Sopenharmony_ci goto error; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci resp = nla_nest_start_noflag(msg, NL80211_PMSR_PEER_ATTR_RESP); 50362306a36Sopenharmony_ci if (!resp) 50462306a36Sopenharmony_ci goto error; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) || 50762306a36Sopenharmony_ci nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME, 50862306a36Sopenharmony_ci res->host_time, NL80211_PMSR_RESP_ATTR_PAD)) 50962306a36Sopenharmony_ci goto error; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (res->ap_tsf_valid && 51262306a36Sopenharmony_ci nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF, 51362306a36Sopenharmony_ci res->ap_tsf, NL80211_PMSR_RESP_ATTR_PAD)) 51462306a36Sopenharmony_ci goto error; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL)) 51762306a36Sopenharmony_ci goto error; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci data = nla_nest_start_noflag(msg, NL80211_PMSR_RESP_ATTR_DATA); 52062306a36Sopenharmony_ci if (!data) 52162306a36Sopenharmony_ci goto error; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci typedata = nla_nest_start_noflag(msg, res->type); 52462306a36Sopenharmony_ci if (!typedata) 52562306a36Sopenharmony_ci goto error; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci switch (res->type) { 52862306a36Sopenharmony_ci case NL80211_PMSR_TYPE_FTM: 52962306a36Sopenharmony_ci if (nl80211_pmsr_send_ftm_res(msg, res)) 53062306a36Sopenharmony_ci goto error; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci default: 53362306a36Sopenharmony_ci WARN_ON(1); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci nla_nest_end(msg, typedata); 53762306a36Sopenharmony_ci nla_nest_end(msg, data); 53862306a36Sopenharmony_ci nla_nest_end(msg, resp); 53962306a36Sopenharmony_ci nla_nest_end(msg, peer); 54062306a36Sopenharmony_ci nla_nest_end(msg, peers); 54162306a36Sopenharmony_ci nla_nest_end(msg, pmsr); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_cierror: 54562306a36Sopenharmony_ci return -ENOSPC; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_civoid cfg80211_pmsr_report(struct wireless_dev *wdev, 54962306a36Sopenharmony_ci struct cfg80211_pmsr_request *req, 55062306a36Sopenharmony_ci struct cfg80211_pmsr_result *result, 55162306a36Sopenharmony_ci gfp_t gfp) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 55462306a36Sopenharmony_ci struct sk_buff *msg; 55562306a36Sopenharmony_ci void *hdr; 55662306a36Sopenharmony_ci int err; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie, 55962306a36Sopenharmony_ci result->addr); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * Currently, only variable items are LCI and civic location, 56362306a36Sopenharmony_ci * both of which are reasonably short so we don't need to 56462306a36Sopenharmony_ci * worry about them here for the allocation. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); 56762306a36Sopenharmony_ci if (!msg) 56862306a36Sopenharmony_ci return; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT); 57162306a36Sopenharmony_ci if (!hdr) 57262306a36Sopenharmony_ci goto free; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || 57562306a36Sopenharmony_ci nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), 57662306a36Sopenharmony_ci NL80211_ATTR_PAD)) 57762306a36Sopenharmony_ci goto free; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie, 58062306a36Sopenharmony_ci NL80211_ATTR_PAD)) 58162306a36Sopenharmony_ci goto free; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci err = nl80211_pmsr_send_result(msg, result); 58462306a36Sopenharmony_ci if (err) { 58562306a36Sopenharmony_ci pr_err_ratelimited("peer measurement result: message didn't fit!"); 58662306a36Sopenharmony_ci goto free; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci genlmsg_end(msg, hdr); 59062306a36Sopenharmony_ci genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid); 59162306a36Sopenharmony_ci return; 59262306a36Sopenharmony_cifree: 59362306a36Sopenharmony_ci nlmsg_free(msg); 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfg80211_pmsr_report); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void cfg80211_pmsr_process_abort(struct wireless_dev *wdev) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 60062306a36Sopenharmony_ci struct cfg80211_pmsr_request *req, *tmp; 60162306a36Sopenharmony_ci LIST_HEAD(free_list); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci lockdep_assert_held(&wdev->mtx); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci spin_lock_bh(&wdev->pmsr_lock); 60662306a36Sopenharmony_ci list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) { 60762306a36Sopenharmony_ci if (req->nl_portid) 60862306a36Sopenharmony_ci continue; 60962306a36Sopenharmony_ci list_move_tail(&req->list, &free_list); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci spin_unlock_bh(&wdev->pmsr_lock); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci list_for_each_entry_safe(req, tmp, &free_list, list) { 61462306a36Sopenharmony_ci rdev_abort_pmsr(rdev, wdev, req); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci kfree(req); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_civoid cfg80211_pmsr_free_wk(struct work_struct *work) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct wireless_dev *wdev = container_of(work, struct wireless_dev, 62362306a36Sopenharmony_ci pmsr_free_wk); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci wiphy_lock(wdev->wiphy); 62662306a36Sopenharmony_ci wdev_lock(wdev); 62762306a36Sopenharmony_ci cfg80211_pmsr_process_abort(wdev); 62862306a36Sopenharmony_ci wdev_unlock(wdev); 62962306a36Sopenharmony_ci wiphy_unlock(wdev->wiphy); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_civoid cfg80211_pmsr_wdev_down(struct wireless_dev *wdev) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct cfg80211_pmsr_request *req; 63562306a36Sopenharmony_ci bool found = false; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci spin_lock_bh(&wdev->pmsr_lock); 63862306a36Sopenharmony_ci list_for_each_entry(req, &wdev->pmsr_list, list) { 63962306a36Sopenharmony_ci found = true; 64062306a36Sopenharmony_ci req->nl_portid = 0; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci spin_unlock_bh(&wdev->pmsr_lock); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (found) 64562306a36Sopenharmony_ci cfg80211_pmsr_process_abort(wdev); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci WARN_ON(!list_empty(&wdev->pmsr_list)); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_civoid cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct cfg80211_pmsr_request *req; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci spin_lock_bh(&wdev->pmsr_lock); 65562306a36Sopenharmony_ci list_for_each_entry(req, &wdev->pmsr_list, list) { 65662306a36Sopenharmony_ci if (req->nl_portid == portid) { 65762306a36Sopenharmony_ci req->nl_portid = 0; 65862306a36Sopenharmony_ci schedule_work(&wdev->pmsr_free_wk); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci spin_unlock_bh(&wdev->pmsr_lock); 66262306a36Sopenharmony_ci} 663