19bb7e0f2SJohannes Berg /* SPDX-License-Identifier: GPL-2.0 */ 29bb7e0f2SJohannes Berg /* 3b6584202SAvraham Stern * Copyright (C) 2018 - 2019 Intel Corporation 49bb7e0f2SJohannes Berg */ 59bb7e0f2SJohannes Berg #ifndef __PMSR_H 69bb7e0f2SJohannes Berg #define __PMSR_H 79bb7e0f2SJohannes Berg #include <net/cfg80211.h> 89bb7e0f2SJohannes Berg #include "core.h" 99bb7e0f2SJohannes Berg #include "nl80211.h" 109bb7e0f2SJohannes Berg #include "rdev-ops.h" 119bb7e0f2SJohannes Berg 129bb7e0f2SJohannes Berg static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, 139bb7e0f2SJohannes Berg struct nlattr *ftmreq, 149bb7e0f2SJohannes Berg struct cfg80211_pmsr_request_peer *out, 159bb7e0f2SJohannes Berg struct genl_info *info) 169bb7e0f2SJohannes Berg { 179bb7e0f2SJohannes Berg const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa; 189bb7e0f2SJohannes Berg struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1]; 199bb7e0f2SJohannes Berg u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */ 209bb7e0f2SJohannes Berg 219bb7e0f2SJohannes Berg /* validate existing data */ 229bb7e0f2SJohannes Berg if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) { 239bb7e0f2SJohannes Berg NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth"); 249bb7e0f2SJohannes Berg return -EINVAL; 259bb7e0f2SJohannes Berg } 269bb7e0f2SJohannes Berg 279bb7e0f2SJohannes Berg /* no validation needed - was already done via nested policy */ 288cb08174SJohannes Berg nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, 298cb08174SJohannes Berg NULL, NULL); 309bb7e0f2SJohannes Berg 319bb7e0f2SJohannes Berg if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) 329bb7e0f2SJohannes Berg preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]); 339bb7e0f2SJohannes Berg 349bb7e0f2SJohannes Berg /* set up values - struct is 0-initialized */ 359bb7e0f2SJohannes Berg out->ftm.requested = true; 369bb7e0f2SJohannes Berg 379bb7e0f2SJohannes Berg switch (out->chandef.chan->band) { 389bb7e0f2SJohannes Berg case NL80211_BAND_60GHZ: 399bb7e0f2SJohannes Berg /* optional */ 409bb7e0f2SJohannes Berg break; 419bb7e0f2SJohannes Berg default: 429bb7e0f2SJohannes Berg if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) { 439bb7e0f2SJohannes Berg NL_SET_ERR_MSG(info->extack, 449bb7e0f2SJohannes Berg "FTM: must specify preamble"); 459bb7e0f2SJohannes Berg return -EINVAL; 469bb7e0f2SJohannes Berg } 479bb7e0f2SJohannes Berg } 489bb7e0f2SJohannes Berg 499bb7e0f2SJohannes Berg if (!(capa->ftm.preambles & BIT(preamble))) { 509bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 519bb7e0f2SJohannes Berg tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], 529bb7e0f2SJohannes Berg "FTM: invalid preamble"); 539bb7e0f2SJohannes Berg return -EINVAL; 549bb7e0f2SJohannes Berg } 559bb7e0f2SJohannes Berg 569bb7e0f2SJohannes Berg out->ftm.preamble = preamble; 579bb7e0f2SJohannes Berg 589bb7e0f2SJohannes Berg out->ftm.burst_period = 0; 599bb7e0f2SJohannes Berg if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]) 609bb7e0f2SJohannes Berg out->ftm.burst_period = 619bb7e0f2SJohannes Berg nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]); 629bb7e0f2SJohannes Berg 639bb7e0f2SJohannes Berg out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]; 649bb7e0f2SJohannes Berg if (out->ftm.asap && !capa->ftm.asap) { 659bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 669bb7e0f2SJohannes Berg tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP], 679bb7e0f2SJohannes Berg "FTM: ASAP mode not supported"); 689bb7e0f2SJohannes Berg return -EINVAL; 699bb7e0f2SJohannes Berg } 709bb7e0f2SJohannes Berg 719bb7e0f2SJohannes Berg if (!out->ftm.asap && !capa->ftm.non_asap) { 729bb7e0f2SJohannes Berg NL_SET_ERR_MSG(info->extack, 739bb7e0f2SJohannes Berg "FTM: non-ASAP mode not supported"); 749bb7e0f2SJohannes Berg return -EINVAL; 759bb7e0f2SJohannes Berg } 769bb7e0f2SJohannes Berg 779bb7e0f2SJohannes Berg out->ftm.num_bursts_exp = 0; 789bb7e0f2SJohannes Berg if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) 799bb7e0f2SJohannes Berg out->ftm.num_bursts_exp = 809bb7e0f2SJohannes Berg nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]); 819bb7e0f2SJohannes Berg 829bb7e0f2SJohannes Berg if (capa->ftm.max_bursts_exponent >= 0 && 839bb7e0f2SJohannes Berg out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) { 849bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 859bb7e0f2SJohannes Berg tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP], 869bb7e0f2SJohannes Berg "FTM: max NUM_BURSTS_EXP must be set lower than the device limit"); 879bb7e0f2SJohannes Berg return -EINVAL; 889bb7e0f2SJohannes Berg } 899bb7e0f2SJohannes Berg 909bb7e0f2SJohannes Berg out->ftm.burst_duration = 15; 919bb7e0f2SJohannes Berg if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) 929bb7e0f2SJohannes Berg out->ftm.burst_duration = 939bb7e0f2SJohannes Berg nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); 949bb7e0f2SJohannes Berg 959bb7e0f2SJohannes Berg out->ftm.ftms_per_burst = 0; 969bb7e0f2SJohannes Berg if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) 979bb7e0f2SJohannes Berg out->ftm.ftms_per_burst = 989bb7e0f2SJohannes Berg nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); 999bb7e0f2SJohannes Berg 1009bb7e0f2SJohannes Berg if (capa->ftm.max_ftms_per_burst && 1019bb7e0f2SJohannes Berg (out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst || 1029bb7e0f2SJohannes Berg out->ftm.ftms_per_burst == 0)) { 1039bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 1049bb7e0f2SJohannes Berg tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], 1059bb7e0f2SJohannes Berg "FTM: FTMs per burst must be set lower than the device limit but non-zero"); 1069bb7e0f2SJohannes Berg return -EINVAL; 1079bb7e0f2SJohannes Berg } 1089bb7e0f2SJohannes Berg 1099bb7e0f2SJohannes Berg out->ftm.ftmr_retries = 3; 1109bb7e0f2SJohannes Berg if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]) 1119bb7e0f2SJohannes Berg out->ftm.ftmr_retries = 1129bb7e0f2SJohannes Berg nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]); 1139bb7e0f2SJohannes Berg 1149bb7e0f2SJohannes Berg out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI]; 1159bb7e0f2SJohannes Berg if (out->ftm.request_lci && !capa->ftm.request_lci) { 1169bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 1179bb7e0f2SJohannes Berg tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI], 1189bb7e0f2SJohannes Berg "FTM: LCI request not supported"); 1199bb7e0f2SJohannes Berg } 1209bb7e0f2SJohannes Berg 1219bb7e0f2SJohannes Berg out->ftm.request_civicloc = 1229bb7e0f2SJohannes Berg !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC]; 1239bb7e0f2SJohannes Berg if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) { 1249bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 1259bb7e0f2SJohannes Berg tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC], 1269bb7e0f2SJohannes Berg "FTM: civic location request not supported"); 1279bb7e0f2SJohannes Berg } 1289bb7e0f2SJohannes Berg 129efb5520dSAvraham Stern out->ftm.trigger_based = 130efb5520dSAvraham Stern !!tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED]; 131efb5520dSAvraham Stern if (out->ftm.trigger_based && !capa->ftm.trigger_based) { 132efb5520dSAvraham Stern NL_SET_ERR_MSG_ATTR(info->extack, 133efb5520dSAvraham Stern tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED], 134efb5520dSAvraham Stern "FTM: trigger based ranging is not supported"); 135efb5520dSAvraham Stern return -EINVAL; 136efb5520dSAvraham Stern } 137efb5520dSAvraham Stern 138efb5520dSAvraham Stern out->ftm.non_trigger_based = 139efb5520dSAvraham Stern !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED]; 140efb5520dSAvraham Stern if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) { 141efb5520dSAvraham Stern NL_SET_ERR_MSG_ATTR(info->extack, 142efb5520dSAvraham Stern tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED], 143efb5520dSAvraham Stern "FTM: trigger based ranging is not supported"); 144efb5520dSAvraham Stern return -EINVAL; 145efb5520dSAvraham Stern } 146efb5520dSAvraham Stern 147efb5520dSAvraham Stern if (out->ftm.trigger_based && out->ftm.non_trigger_based) { 148efb5520dSAvraham Stern NL_SET_ERR_MSG(info->extack, 149efb5520dSAvraham Stern "FTM: can't set both trigger based and non trigger based"); 150efb5520dSAvraham Stern return -EINVAL; 151efb5520dSAvraham Stern } 152efb5520dSAvraham Stern 153efb5520dSAvraham Stern if ((out->ftm.trigger_based || out->ftm.non_trigger_based) && 154efb5520dSAvraham Stern out->ftm.preamble != NL80211_PREAMBLE_HE) { 155efb5520dSAvraham Stern NL_SET_ERR_MSG_ATTR(info->extack, 156efb5520dSAvraham Stern tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], 157efb5520dSAvraham Stern "FTM: non EDCA based ranging must use HE preamble"); 158efb5520dSAvraham Stern return -EINVAL; 159efb5520dSAvraham Stern } 160efb5520dSAvraham Stern 1619bb7e0f2SJohannes Berg return 0; 1629bb7e0f2SJohannes Berg } 1639bb7e0f2SJohannes Berg 1649bb7e0f2SJohannes Berg static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, 1659bb7e0f2SJohannes Berg struct nlattr *peer, 1669bb7e0f2SJohannes Berg struct cfg80211_pmsr_request_peer *out, 1679bb7e0f2SJohannes Berg struct genl_info *info) 1689bb7e0f2SJohannes Berg { 1699bb7e0f2SJohannes Berg struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; 1709bb7e0f2SJohannes Berg struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1]; 1719bb7e0f2SJohannes Berg struct nlattr *treq; 1729bb7e0f2SJohannes Berg int err, rem; 1739bb7e0f2SJohannes Berg 1749bb7e0f2SJohannes Berg /* no validation needed - was already done via nested policy */ 1758cb08174SJohannes Berg nla_parse_nested_deprecated(tb, NL80211_PMSR_PEER_ATTR_MAX, peer, 1768cb08174SJohannes Berg NULL, NULL); 1779bb7e0f2SJohannes Berg 1789bb7e0f2SJohannes Berg if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] || 1799bb7e0f2SJohannes Berg !tb[NL80211_PMSR_PEER_ATTR_CHAN] || 1809bb7e0f2SJohannes Berg !tb[NL80211_PMSR_PEER_ATTR_REQ]) { 1819bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, peer, 1829bb7e0f2SJohannes Berg "insufficient peer data"); 1839bb7e0f2SJohannes Berg return -EINVAL; 1849bb7e0f2SJohannes Berg } 1859bb7e0f2SJohannes Berg 1869bb7e0f2SJohannes Berg memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN); 1879bb7e0f2SJohannes Berg 1889bb7e0f2SJohannes Berg /* reuse info->attrs */ 1899bb7e0f2SJohannes Berg memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1)); 1908cb08174SJohannes Berg err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX, 1919bb7e0f2SJohannes Berg tb[NL80211_PMSR_PEER_ATTR_CHAN], 192d15da2a2SJohannes Berg NULL, info->extack); 1939bb7e0f2SJohannes Berg if (err) 1949bb7e0f2SJohannes Berg return err; 1959bb7e0f2SJohannes Berg 1969bb7e0f2SJohannes Berg err = nl80211_parse_chandef(rdev, info, &out->chandef); 1979bb7e0f2SJohannes Berg if (err) 1989bb7e0f2SJohannes Berg return err; 1999bb7e0f2SJohannes Berg 2009bb7e0f2SJohannes Berg /* no validation needed - was already done via nested policy */ 2018cb08174SJohannes Berg nla_parse_nested_deprecated(req, NL80211_PMSR_REQ_ATTR_MAX, 2028cb08174SJohannes Berg tb[NL80211_PMSR_PEER_ATTR_REQ], NULL, 2038cb08174SJohannes Berg NULL); 2049bb7e0f2SJohannes Berg 2059bb7e0f2SJohannes Berg if (!req[NL80211_PMSR_REQ_ATTR_DATA]) { 2069bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 2079bb7e0f2SJohannes Berg tb[NL80211_PMSR_PEER_ATTR_REQ], 2089bb7e0f2SJohannes Berg "missing request type/data"); 2099bb7e0f2SJohannes Berg return -EINVAL; 2109bb7e0f2SJohannes Berg } 2119bb7e0f2SJohannes Berg 2129bb7e0f2SJohannes Berg if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF]) 2139bb7e0f2SJohannes Berg out->report_ap_tsf = true; 2149bb7e0f2SJohannes Berg 2159bb7e0f2SJohannes Berg if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) { 2169bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 2179bb7e0f2SJohannes Berg req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF], 2189bb7e0f2SJohannes Berg "reporting AP TSF is not supported"); 2199bb7e0f2SJohannes Berg return -EINVAL; 2209bb7e0f2SJohannes Berg } 2219bb7e0f2SJohannes Berg 2229bb7e0f2SJohannes Berg nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) { 2239bb7e0f2SJohannes Berg switch (nla_type(treq)) { 2249bb7e0f2SJohannes Berg case NL80211_PMSR_TYPE_FTM: 2259bb7e0f2SJohannes Berg err = pmsr_parse_ftm(rdev, treq, out, info); 2269bb7e0f2SJohannes Berg break; 2279bb7e0f2SJohannes Berg default: 2289bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, treq, 2299bb7e0f2SJohannes Berg "unsupported measurement type"); 2309bb7e0f2SJohannes Berg err = -EINVAL; 2319bb7e0f2SJohannes Berg } 2329bb7e0f2SJohannes Berg } 2339bb7e0f2SJohannes Berg 2349bb7e0f2SJohannes Berg if (err) 2359bb7e0f2SJohannes Berg return err; 2369bb7e0f2SJohannes Berg 2379bb7e0f2SJohannes Berg return 0; 2389bb7e0f2SJohannes Berg } 2399bb7e0f2SJohannes Berg 2409bb7e0f2SJohannes Berg int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info) 2419bb7e0f2SJohannes Berg { 2429bb7e0f2SJohannes Berg struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS]; 2439bb7e0f2SJohannes Berg struct cfg80211_registered_device *rdev = info->user_ptr[0]; 2449bb7e0f2SJohannes Berg struct wireless_dev *wdev = info->user_ptr[1]; 2459bb7e0f2SJohannes Berg struct cfg80211_pmsr_request *req; 2469bb7e0f2SJohannes Berg struct nlattr *peers, *peer; 2479bb7e0f2SJohannes Berg int count, rem, err, idx; 2489bb7e0f2SJohannes Berg 2499bb7e0f2SJohannes Berg if (!rdev->wiphy.pmsr_capa) 2509bb7e0f2SJohannes Berg return -EOPNOTSUPP; 2519bb7e0f2SJohannes Berg 2529bb7e0f2SJohannes Berg if (!reqattr) 2539bb7e0f2SJohannes Berg return -EINVAL; 2549bb7e0f2SJohannes Berg 2559bb7e0f2SJohannes Berg peers = nla_find(nla_data(reqattr), nla_len(reqattr), 2569bb7e0f2SJohannes Berg NL80211_PMSR_ATTR_PEERS); 2579bb7e0f2SJohannes Berg if (!peers) 2589bb7e0f2SJohannes Berg return -EINVAL; 2599bb7e0f2SJohannes Berg 2609bb7e0f2SJohannes Berg count = 0; 2619bb7e0f2SJohannes Berg nla_for_each_nested(peer, peers, rem) { 2629bb7e0f2SJohannes Berg count++; 2639bb7e0f2SJohannes Berg 2649bb7e0f2SJohannes Berg if (count > rdev->wiphy.pmsr_capa->max_peers) { 2659bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, peer, 2669bb7e0f2SJohannes Berg "Too many peers used"); 2679bb7e0f2SJohannes Berg return -EINVAL; 2689bb7e0f2SJohannes Berg } 2699bb7e0f2SJohannes Berg } 2709bb7e0f2SJohannes Berg 2719bb7e0f2SJohannes Berg req = kzalloc(struct_size(req, peers, count), GFP_KERNEL); 2729bb7e0f2SJohannes Berg if (!req) 2739bb7e0f2SJohannes Berg return -ENOMEM; 2749bb7e0f2SJohannes Berg 2759bb7e0f2SJohannes Berg if (info->attrs[NL80211_ATTR_TIMEOUT]) 2769bb7e0f2SJohannes Berg req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]); 2779bb7e0f2SJohannes Berg 2789bb7e0f2SJohannes Berg if (info->attrs[NL80211_ATTR_MAC]) { 2799bb7e0f2SJohannes Berg if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) { 2809bb7e0f2SJohannes Berg NL_SET_ERR_MSG_ATTR(info->extack, 2819bb7e0f2SJohannes Berg info->attrs[NL80211_ATTR_MAC], 2829bb7e0f2SJohannes Berg "device cannot randomize MAC address"); 2839bb7e0f2SJohannes Berg err = -EINVAL; 2849bb7e0f2SJohannes Berg goto out_err; 2859bb7e0f2SJohannes Berg } 2869bb7e0f2SJohannes Berg 2879bb7e0f2SJohannes Berg err = nl80211_parse_random_mac(info->attrs, req->mac_addr, 2889bb7e0f2SJohannes Berg req->mac_addr_mask); 2899bb7e0f2SJohannes Berg if (err) 2909bb7e0f2SJohannes Berg goto out_err; 2919bb7e0f2SJohannes Berg } else { 2920acd9928SJohannes Berg memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN); 29376763741SMao Wenan eth_broadcast_addr(req->mac_addr_mask); 2949bb7e0f2SJohannes Berg } 2959bb7e0f2SJohannes Berg 2969bb7e0f2SJohannes Berg idx = 0; 2979bb7e0f2SJohannes Berg nla_for_each_nested(peer, peers, rem) { 2989bb7e0f2SJohannes Berg /* NB: this reuses info->attrs, but we no longer need it */ 2999bb7e0f2SJohannes Berg err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info); 3009bb7e0f2SJohannes Berg if (err) 3019bb7e0f2SJohannes Berg goto out_err; 3029bb7e0f2SJohannes Berg idx++; 3039bb7e0f2SJohannes Berg } 3049bb7e0f2SJohannes Berg 3059bb7e0f2SJohannes Berg req->n_peers = count; 3069bb7e0f2SJohannes Berg req->cookie = cfg80211_assign_cookie(rdev); 307ff1bab1bSJohannes Berg req->nl_portid = info->snd_portid; 3089bb7e0f2SJohannes Berg 3099bb7e0f2SJohannes Berg err = rdev_start_pmsr(rdev, wdev, req); 3109bb7e0f2SJohannes Berg if (err) 3119bb7e0f2SJohannes Berg goto out_err; 3129bb7e0f2SJohannes Berg 3139bb7e0f2SJohannes Berg list_add_tail(&req->list, &wdev->pmsr_list); 3149bb7e0f2SJohannes Berg 3159bb7e0f2SJohannes Berg nl_set_extack_cookie_u64(info->extack, req->cookie); 3169bb7e0f2SJohannes Berg return 0; 3179bb7e0f2SJohannes Berg out_err: 3189bb7e0f2SJohannes Berg kfree(req); 3199bb7e0f2SJohannes Berg return err; 3209bb7e0f2SJohannes Berg } 3219bb7e0f2SJohannes Berg 3229bb7e0f2SJohannes Berg void cfg80211_pmsr_complete(struct wireless_dev *wdev, 3239bb7e0f2SJohannes Berg struct cfg80211_pmsr_request *req, 3249bb7e0f2SJohannes Berg gfp_t gfp) 3259bb7e0f2SJohannes Berg { 3269bb7e0f2SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 3279bb7e0f2SJohannes Berg struct sk_buff *msg; 3289bb7e0f2SJohannes Berg void *hdr; 3299bb7e0f2SJohannes Berg 3309bb7e0f2SJohannes Berg trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie); 3319bb7e0f2SJohannes Berg 3329bb7e0f2SJohannes Berg msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); 3339bb7e0f2SJohannes Berg if (!msg) 3349bb7e0f2SJohannes Berg goto free_request; 3359bb7e0f2SJohannes Berg 3369bb7e0f2SJohannes Berg hdr = nl80211hdr_put(msg, 0, 0, 0, 3379bb7e0f2SJohannes Berg NL80211_CMD_PEER_MEASUREMENT_COMPLETE); 3389bb7e0f2SJohannes Berg if (!hdr) 3399bb7e0f2SJohannes Berg goto free_msg; 3409bb7e0f2SJohannes Berg 3419bb7e0f2SJohannes Berg if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || 3429bb7e0f2SJohannes Berg nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), 3439bb7e0f2SJohannes Berg NL80211_ATTR_PAD)) 3449bb7e0f2SJohannes Berg goto free_msg; 3459bb7e0f2SJohannes Berg 3469bb7e0f2SJohannes Berg if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie, 3479bb7e0f2SJohannes Berg NL80211_ATTR_PAD)) 3489bb7e0f2SJohannes Berg goto free_msg; 3499bb7e0f2SJohannes Berg 3509bb7e0f2SJohannes Berg genlmsg_end(msg, hdr); 3519bb7e0f2SJohannes Berg genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid); 3529bb7e0f2SJohannes Berg goto free_request; 3539bb7e0f2SJohannes Berg free_msg: 3549bb7e0f2SJohannes Berg nlmsg_free(msg); 3559bb7e0f2SJohannes Berg free_request: 3569bb7e0f2SJohannes Berg spin_lock_bh(&wdev->pmsr_lock); 3579bb7e0f2SJohannes Berg list_del(&req->list); 3589bb7e0f2SJohannes Berg spin_unlock_bh(&wdev->pmsr_lock); 3599bb7e0f2SJohannes Berg kfree(req); 3609bb7e0f2SJohannes Berg } 3619bb7e0f2SJohannes Berg EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete); 3629bb7e0f2SJohannes Berg 3639bb7e0f2SJohannes Berg static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg, 3649bb7e0f2SJohannes Berg struct cfg80211_pmsr_result *res) 3659bb7e0f2SJohannes Berg { 3669bb7e0f2SJohannes Berg if (res->status == NL80211_PMSR_STATUS_FAILURE) { 3679bb7e0f2SJohannes Berg if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON, 3689bb7e0f2SJohannes Berg res->ftm.failure_reason)) 3699bb7e0f2SJohannes Berg goto error; 3709bb7e0f2SJohannes Berg 3719bb7e0f2SJohannes Berg if (res->ftm.failure_reason == 3729bb7e0f2SJohannes Berg NL80211_PMSR_FTM_FAILURE_PEER_BUSY && 3739bb7e0f2SJohannes Berg res->ftm.busy_retry_time && 3749bb7e0f2SJohannes Berg nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME, 3759bb7e0f2SJohannes Berg res->ftm.busy_retry_time)) 3769bb7e0f2SJohannes Berg goto error; 3779bb7e0f2SJohannes Berg 3789bb7e0f2SJohannes Berg return 0; 3799bb7e0f2SJohannes Berg } 3809bb7e0f2SJohannes Berg 3819bb7e0f2SJohannes Berg #define PUT(tp, attr, val) \ 3829bb7e0f2SJohannes Berg do { \ 3839bb7e0f2SJohannes Berg if (nla_put_##tp(msg, \ 3849bb7e0f2SJohannes Berg NL80211_PMSR_FTM_RESP_ATTR_##attr, \ 3859bb7e0f2SJohannes Berg res->ftm.val)) \ 3869bb7e0f2SJohannes Berg goto error; \ 3879bb7e0f2SJohannes Berg } while (0) 3889bb7e0f2SJohannes Berg 3899bb7e0f2SJohannes Berg #define PUTOPT(tp, attr, val) \ 3909bb7e0f2SJohannes Berg do { \ 3919bb7e0f2SJohannes Berg if (res->ftm.val##_valid) \ 3929bb7e0f2SJohannes Berg PUT(tp, attr, val); \ 3939bb7e0f2SJohannes Berg } while (0) 3949bb7e0f2SJohannes Berg 3959bb7e0f2SJohannes Berg #define PUT_U64(attr, val) \ 3969bb7e0f2SJohannes Berg do { \ 3979bb7e0f2SJohannes Berg if (nla_put_u64_64bit(msg, \ 3989bb7e0f2SJohannes Berg NL80211_PMSR_FTM_RESP_ATTR_##attr,\ 3999bb7e0f2SJohannes Berg res->ftm.val, \ 4009bb7e0f2SJohannes Berg NL80211_PMSR_FTM_RESP_ATTR_PAD)) \ 4019bb7e0f2SJohannes Berg goto error; \ 4029bb7e0f2SJohannes Berg } while (0) 4039bb7e0f2SJohannes Berg 4049bb7e0f2SJohannes Berg #define PUTOPT_U64(attr, val) \ 4059bb7e0f2SJohannes Berg do { \ 4069bb7e0f2SJohannes Berg if (res->ftm.val##_valid) \ 4079bb7e0f2SJohannes Berg PUT_U64(attr, val); \ 4089bb7e0f2SJohannes Berg } while (0) 4099bb7e0f2SJohannes Berg 4109bb7e0f2SJohannes Berg if (res->ftm.burst_index >= 0) 4119bb7e0f2SJohannes Berg PUT(u32, BURST_INDEX, burst_index); 4129bb7e0f2SJohannes Berg PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts); 4139bb7e0f2SJohannes Berg PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes); 4149bb7e0f2SJohannes Berg PUT(u8, NUM_BURSTS_EXP, num_bursts_exp); 4159bb7e0f2SJohannes Berg PUT(u8, BURST_DURATION, burst_duration); 4169bb7e0f2SJohannes Berg PUT(u8, FTMS_PER_BURST, ftms_per_burst); 4179bb7e0f2SJohannes Berg PUTOPT(s32, RSSI_AVG, rssi_avg); 4189bb7e0f2SJohannes Berg PUTOPT(s32, RSSI_SPREAD, rssi_spread); 4199bb7e0f2SJohannes Berg if (res->ftm.tx_rate_valid && 4209bb7e0f2SJohannes Berg !nl80211_put_sta_rate(msg, &res->ftm.tx_rate, 4219bb7e0f2SJohannes Berg NL80211_PMSR_FTM_RESP_ATTR_TX_RATE)) 4229bb7e0f2SJohannes Berg goto error; 4239bb7e0f2SJohannes Berg if (res->ftm.rx_rate_valid && 4249bb7e0f2SJohannes Berg !nl80211_put_sta_rate(msg, &res->ftm.rx_rate, 4259bb7e0f2SJohannes Berg NL80211_PMSR_FTM_RESP_ATTR_RX_RATE)) 4269bb7e0f2SJohannes Berg goto error; 4279bb7e0f2SJohannes Berg PUTOPT_U64(RTT_AVG, rtt_avg); 4289bb7e0f2SJohannes Berg PUTOPT_U64(RTT_VARIANCE, rtt_variance); 4299bb7e0f2SJohannes Berg PUTOPT_U64(RTT_SPREAD, rtt_spread); 4309bb7e0f2SJohannes Berg PUTOPT_U64(DIST_AVG, dist_avg); 4319bb7e0f2SJohannes Berg PUTOPT_U64(DIST_VARIANCE, dist_variance); 4329bb7e0f2SJohannes Berg PUTOPT_U64(DIST_SPREAD, dist_spread); 4339bb7e0f2SJohannes Berg if (res->ftm.lci && res->ftm.lci_len && 4349bb7e0f2SJohannes Berg nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI, 4359bb7e0f2SJohannes Berg res->ftm.lci_len, res->ftm.lci)) 4369bb7e0f2SJohannes Berg goto error; 4379bb7e0f2SJohannes Berg if (res->ftm.civicloc && res->ftm.civicloc_len && 4389bb7e0f2SJohannes Berg nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC, 4399bb7e0f2SJohannes Berg res->ftm.civicloc_len, res->ftm.civicloc)) 4409bb7e0f2SJohannes Berg goto error; 4419bb7e0f2SJohannes Berg #undef PUT 4429bb7e0f2SJohannes Berg #undef PUTOPT 4439bb7e0f2SJohannes Berg #undef PUT_U64 4449bb7e0f2SJohannes Berg #undef PUTOPT_U64 4459bb7e0f2SJohannes Berg 4469bb7e0f2SJohannes Berg return 0; 4479bb7e0f2SJohannes Berg error: 4489bb7e0f2SJohannes Berg return -ENOSPC; 4499bb7e0f2SJohannes Berg } 4509bb7e0f2SJohannes Berg 4519bb7e0f2SJohannes Berg static int nl80211_pmsr_send_result(struct sk_buff *msg, 4529bb7e0f2SJohannes Berg struct cfg80211_pmsr_result *res) 4539bb7e0f2SJohannes Berg { 4549bb7e0f2SJohannes Berg struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata; 4559bb7e0f2SJohannes Berg 456ae0be8deSMichal Kubecek pmsr = nla_nest_start_noflag(msg, NL80211_ATTR_PEER_MEASUREMENTS); 4579bb7e0f2SJohannes Berg if (!pmsr) 4589bb7e0f2SJohannes Berg goto error; 4599bb7e0f2SJohannes Berg 460ae0be8deSMichal Kubecek peers = nla_nest_start_noflag(msg, NL80211_PMSR_ATTR_PEERS); 4619bb7e0f2SJohannes Berg if (!peers) 4629bb7e0f2SJohannes Berg goto error; 4639bb7e0f2SJohannes Berg 464ae0be8deSMichal Kubecek peer = nla_nest_start_noflag(msg, 1); 4659bb7e0f2SJohannes Berg if (!peer) 4669bb7e0f2SJohannes Berg goto error; 4679bb7e0f2SJohannes Berg 4689bb7e0f2SJohannes Berg if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr)) 4699bb7e0f2SJohannes Berg goto error; 4709bb7e0f2SJohannes Berg 471ae0be8deSMichal Kubecek resp = nla_nest_start_noflag(msg, NL80211_PMSR_PEER_ATTR_RESP); 4729bb7e0f2SJohannes Berg if (!resp) 4739bb7e0f2SJohannes Berg goto error; 4749bb7e0f2SJohannes Berg 4759bb7e0f2SJohannes Berg if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) || 4769bb7e0f2SJohannes Berg nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME, 4779bb7e0f2SJohannes Berg res->host_time, NL80211_PMSR_RESP_ATTR_PAD)) 4789bb7e0f2SJohannes Berg goto error; 4799bb7e0f2SJohannes Berg 4809bb7e0f2SJohannes Berg if (res->ap_tsf_valid && 4819bb7e0f2SJohannes Berg nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF, 482b6584202SAvraham Stern res->ap_tsf, NL80211_PMSR_RESP_ATTR_PAD)) 4839bb7e0f2SJohannes Berg goto error; 4849bb7e0f2SJohannes Berg 4859bb7e0f2SJohannes Berg if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL)) 4869bb7e0f2SJohannes Berg goto error; 4879bb7e0f2SJohannes Berg 488ae0be8deSMichal Kubecek data = nla_nest_start_noflag(msg, NL80211_PMSR_RESP_ATTR_DATA); 4899bb7e0f2SJohannes Berg if (!data) 4909bb7e0f2SJohannes Berg goto error; 4919bb7e0f2SJohannes Berg 492ae0be8deSMichal Kubecek typedata = nla_nest_start_noflag(msg, res->type); 4939bb7e0f2SJohannes Berg if (!typedata) 4949bb7e0f2SJohannes Berg goto error; 4959bb7e0f2SJohannes Berg 4969bb7e0f2SJohannes Berg switch (res->type) { 4979bb7e0f2SJohannes Berg case NL80211_PMSR_TYPE_FTM: 4989bb7e0f2SJohannes Berg if (nl80211_pmsr_send_ftm_res(msg, res)) 4999bb7e0f2SJohannes Berg goto error; 5009bb7e0f2SJohannes Berg break; 5019bb7e0f2SJohannes Berg default: 5029bb7e0f2SJohannes Berg WARN_ON(1); 5039bb7e0f2SJohannes Berg } 5049bb7e0f2SJohannes Berg 5059bb7e0f2SJohannes Berg nla_nest_end(msg, typedata); 5069bb7e0f2SJohannes Berg nla_nest_end(msg, data); 5079bb7e0f2SJohannes Berg nla_nest_end(msg, resp); 5089bb7e0f2SJohannes Berg nla_nest_end(msg, peer); 5099bb7e0f2SJohannes Berg nla_nest_end(msg, peers); 5109bb7e0f2SJohannes Berg nla_nest_end(msg, pmsr); 5119bb7e0f2SJohannes Berg 5129bb7e0f2SJohannes Berg return 0; 5139bb7e0f2SJohannes Berg error: 5149bb7e0f2SJohannes Berg return -ENOSPC; 5159bb7e0f2SJohannes Berg } 5169bb7e0f2SJohannes Berg 5179bb7e0f2SJohannes Berg void cfg80211_pmsr_report(struct wireless_dev *wdev, 5189bb7e0f2SJohannes Berg struct cfg80211_pmsr_request *req, 5199bb7e0f2SJohannes Berg struct cfg80211_pmsr_result *result, 5209bb7e0f2SJohannes Berg gfp_t gfp) 5219bb7e0f2SJohannes Berg { 5229bb7e0f2SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 5239bb7e0f2SJohannes Berg struct sk_buff *msg; 5249bb7e0f2SJohannes Berg void *hdr; 5259bb7e0f2SJohannes Berg int err; 5269bb7e0f2SJohannes Berg 5279bb7e0f2SJohannes Berg trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie, 5289bb7e0f2SJohannes Berg result->addr); 5299bb7e0f2SJohannes Berg 5309bb7e0f2SJohannes Berg /* 5319bb7e0f2SJohannes Berg * Currently, only variable items are LCI and civic location, 5329bb7e0f2SJohannes Berg * both of which are reasonably short so we don't need to 5339bb7e0f2SJohannes Berg * worry about them here for the allocation. 5349bb7e0f2SJohannes Berg */ 5359bb7e0f2SJohannes Berg msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); 5369bb7e0f2SJohannes Berg if (!msg) 5379bb7e0f2SJohannes Berg return; 5389bb7e0f2SJohannes Berg 5399bb7e0f2SJohannes Berg hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT); 5409bb7e0f2SJohannes Berg if (!hdr) 5419bb7e0f2SJohannes Berg goto free; 5429bb7e0f2SJohannes Berg 5439bb7e0f2SJohannes Berg if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || 5449bb7e0f2SJohannes Berg nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), 5459bb7e0f2SJohannes Berg NL80211_ATTR_PAD)) 5469bb7e0f2SJohannes Berg goto free; 5479bb7e0f2SJohannes Berg 5489bb7e0f2SJohannes Berg if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie, 5499bb7e0f2SJohannes Berg NL80211_ATTR_PAD)) 5509bb7e0f2SJohannes Berg goto free; 5519bb7e0f2SJohannes Berg 5529bb7e0f2SJohannes Berg err = nl80211_pmsr_send_result(msg, result); 5539bb7e0f2SJohannes Berg if (err) { 5549bb7e0f2SJohannes Berg pr_err_ratelimited("peer measurement result: message didn't fit!"); 5559bb7e0f2SJohannes Berg goto free; 5569bb7e0f2SJohannes Berg } 5579bb7e0f2SJohannes Berg 5589bb7e0f2SJohannes Berg genlmsg_end(msg, hdr); 5599bb7e0f2SJohannes Berg genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid); 5609bb7e0f2SJohannes Berg return; 5619bb7e0f2SJohannes Berg free: 5629bb7e0f2SJohannes Berg nlmsg_free(msg); 5639bb7e0f2SJohannes Berg } 5649bb7e0f2SJohannes Berg EXPORT_SYMBOL_GPL(cfg80211_pmsr_report); 5659bb7e0f2SJohannes Berg 56673350424SJohannes Berg static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev) 5679bb7e0f2SJohannes Berg { 5689bb7e0f2SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 5699bb7e0f2SJohannes Berg struct cfg80211_pmsr_request *req, *tmp; 5709bb7e0f2SJohannes Berg LIST_HEAD(free_list); 5719bb7e0f2SJohannes Berg 57273350424SJohannes Berg lockdep_assert_held(&wdev->mtx); 57373350424SJohannes Berg 5749bb7e0f2SJohannes Berg spin_lock_bh(&wdev->pmsr_lock); 5759bb7e0f2SJohannes Berg list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) { 5769bb7e0f2SJohannes Berg if (req->nl_portid) 5779bb7e0f2SJohannes Berg continue; 5789bb7e0f2SJohannes Berg list_move_tail(&req->list, &free_list); 5799bb7e0f2SJohannes Berg } 5809bb7e0f2SJohannes Berg spin_unlock_bh(&wdev->pmsr_lock); 5819bb7e0f2SJohannes Berg 5829bb7e0f2SJohannes Berg list_for_each_entry_safe(req, tmp, &free_list, list) { 5839bb7e0f2SJohannes Berg rdev_abort_pmsr(rdev, wdev, req); 5849bb7e0f2SJohannes Berg 5859bb7e0f2SJohannes Berg kfree(req); 5869bb7e0f2SJohannes Berg } 5879bb7e0f2SJohannes Berg } 5889bb7e0f2SJohannes Berg 58973350424SJohannes Berg void cfg80211_pmsr_free_wk(struct work_struct *work) 59073350424SJohannes Berg { 59173350424SJohannes Berg struct wireless_dev *wdev = container_of(work, struct wireless_dev, 59273350424SJohannes Berg pmsr_free_wk); 59373350424SJohannes Berg 59473350424SJohannes Berg wdev_lock(wdev); 59573350424SJohannes Berg cfg80211_pmsr_process_abort(wdev); 59673350424SJohannes Berg wdev_unlock(wdev); 59773350424SJohannes Berg } 59873350424SJohannes Berg 5999bb7e0f2SJohannes Berg void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev) 6009bb7e0f2SJohannes Berg { 6019bb7e0f2SJohannes Berg struct cfg80211_pmsr_request *req; 6029bb7e0f2SJohannes Berg bool found = false; 6039bb7e0f2SJohannes Berg 6049bb7e0f2SJohannes Berg spin_lock_bh(&wdev->pmsr_lock); 6059bb7e0f2SJohannes Berg list_for_each_entry(req, &wdev->pmsr_list, list) { 6069bb7e0f2SJohannes Berg found = true; 6079bb7e0f2SJohannes Berg req->nl_portid = 0; 6089bb7e0f2SJohannes Berg } 6099bb7e0f2SJohannes Berg spin_unlock_bh(&wdev->pmsr_lock); 6109bb7e0f2SJohannes Berg 6119bb7e0f2SJohannes Berg if (found) 61273350424SJohannes Berg cfg80211_pmsr_process_abort(wdev); 61373350424SJohannes Berg 6149bb7e0f2SJohannes Berg WARN_ON(!list_empty(&wdev->pmsr_list)); 6159bb7e0f2SJohannes Berg } 6169bb7e0f2SJohannes Berg 6179bb7e0f2SJohannes Berg void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid) 6189bb7e0f2SJohannes Berg { 6199bb7e0f2SJohannes Berg struct cfg80211_pmsr_request *req; 6209bb7e0f2SJohannes Berg 6219bb7e0f2SJohannes Berg spin_lock_bh(&wdev->pmsr_lock); 6229bb7e0f2SJohannes Berg list_for_each_entry(req, &wdev->pmsr_list, list) { 6239bb7e0f2SJohannes Berg if (req->nl_portid == portid) { 6249bb7e0f2SJohannes Berg req->nl_portid = 0; 6259bb7e0f2SJohannes Berg schedule_work(&wdev->pmsr_free_wk); 6269bb7e0f2SJohannes Berg } 6279bb7e0f2SJohannes Berg } 6289bb7e0f2SJohannes Berg spin_unlock_bh(&wdev->pmsr_lock); 6299bb7e0f2SJohannes Berg } 6309bb7e0f2SJohannes Berg 6319bb7e0f2SJohannes Berg #endif /* __PMSR_H */ 632