1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2bfcc09ddSBjoern A. Zeeb /* 3bfcc09ddSBjoern A. Zeeb * Copyright (C) 2015-2017 Intel Deutschland GmbH 4*a4128aadSBjoern A. Zeeb * Copyright (C) 2018-2024 Intel Corporation 5bfcc09ddSBjoern A. Zeeb */ 6bfcc09ddSBjoern A. Zeeb #include <linux/etherdevice.h> 7bfcc09ddSBjoern A. Zeeb #include <linux/math64.h> 8bfcc09ddSBjoern A. Zeeb #include <net/cfg80211.h> 9bfcc09ddSBjoern A. Zeeb #include "mvm.h" 10bfcc09ddSBjoern A. Zeeb #include "iwl-io.h" 11bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h" 12bfcc09ddSBjoern A. Zeeb #include "constants.h" 13bfcc09ddSBjoern A. Zeeb 14bfcc09ddSBjoern A. Zeeb struct iwl_mvm_loc_entry { 15bfcc09ddSBjoern A. Zeeb struct list_head list; 16bfcc09ddSBjoern A. Zeeb u8 addr[ETH_ALEN]; 17bfcc09ddSBjoern A. Zeeb u8 lci_len, civic_len; 18bfcc09ddSBjoern A. Zeeb u8 buf[]; 19bfcc09ddSBjoern A. Zeeb }; 20bfcc09ddSBjoern A. Zeeb 21bfcc09ddSBjoern A. Zeeb struct iwl_mvm_smooth_entry { 22bfcc09ddSBjoern A. Zeeb struct list_head list; 23bfcc09ddSBjoern A. Zeeb u8 addr[ETH_ALEN]; 24bfcc09ddSBjoern A. Zeeb s64 rtt_avg; 25bfcc09ddSBjoern A. Zeeb u64 host_time; 26bfcc09ddSBjoern A. Zeeb }; 27bfcc09ddSBjoern A. Zeeb 289af1bba4SBjoern A. Zeeb enum iwl_mvm_pasn_flags { 299af1bba4SBjoern A. Zeeb IWL_MVM_PASN_FLAG_HAS_HLTK = BIT(0), 309af1bba4SBjoern A. Zeeb }; 319af1bba4SBjoern A. Zeeb 32bfcc09ddSBjoern A. Zeeb struct iwl_mvm_ftm_pasn_entry { 33bfcc09ddSBjoern A. Zeeb struct list_head list; 34bfcc09ddSBjoern A. Zeeb u8 addr[ETH_ALEN]; 35bfcc09ddSBjoern A. Zeeb u8 hltk[HLTK_11AZ_LEN]; 36bfcc09ddSBjoern A. Zeeb u8 tk[TK_11AZ_LEN]; 37bfcc09ddSBjoern A. Zeeb u8 cipher; 38bfcc09ddSBjoern A. Zeeb u8 tx_pn[IEEE80211_CCMP_PN_LEN]; 39bfcc09ddSBjoern A. Zeeb u8 rx_pn[IEEE80211_CCMP_PN_LEN]; 409af1bba4SBjoern A. Zeeb u32 flags; 41bfcc09ddSBjoern A. Zeeb }; 42bfcc09ddSBjoern A. Zeeb 43*a4128aadSBjoern A. Zeeb struct iwl_mvm_ftm_iter_data { 44*a4128aadSBjoern A. Zeeb u8 *cipher; 45*a4128aadSBjoern A. Zeeb u8 *bssid; 46*a4128aadSBjoern A. Zeeb u8 *tk; 47*a4128aadSBjoern A. Zeeb }; 48*a4128aadSBjoern A. Zeeb 49bfcc09ddSBjoern A. Zeeb int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 50bfcc09ddSBjoern A. Zeeb u8 *addr, u32 cipher, u8 *tk, u32 tk_len, 51bfcc09ddSBjoern A. Zeeb u8 *hltk, u32 hltk_len) 52bfcc09ddSBjoern A. Zeeb { 53bfcc09ddSBjoern A. Zeeb struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn), 54bfcc09ddSBjoern A. Zeeb GFP_KERNEL); 55bfcc09ddSBjoern A. Zeeb u32 expected_tk_len; 56bfcc09ddSBjoern A. Zeeb 57bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 58bfcc09ddSBjoern A. Zeeb 59bfcc09ddSBjoern A. Zeeb if (!pasn) 60bfcc09ddSBjoern A. Zeeb return -ENOBUFS; 61bfcc09ddSBjoern A. Zeeb 62*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_remove_pasn_sta(mvm, addr); 63*a4128aadSBjoern A. Zeeb 64bfcc09ddSBjoern A. Zeeb pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher); 65bfcc09ddSBjoern A. Zeeb 66bfcc09ddSBjoern A. Zeeb switch (pasn->cipher) { 67bfcc09ddSBjoern A. Zeeb case IWL_LOCATION_CIPHER_CCMP_128: 68bfcc09ddSBjoern A. Zeeb case IWL_LOCATION_CIPHER_GCMP_128: 69bfcc09ddSBjoern A. Zeeb expected_tk_len = WLAN_KEY_LEN_CCMP; 70bfcc09ddSBjoern A. Zeeb break; 71bfcc09ddSBjoern A. Zeeb case IWL_LOCATION_CIPHER_GCMP_256: 72bfcc09ddSBjoern A. Zeeb expected_tk_len = WLAN_KEY_LEN_GCMP_256; 73bfcc09ddSBjoern A. Zeeb break; 74bfcc09ddSBjoern A. Zeeb default: 75bfcc09ddSBjoern A. Zeeb goto out; 76bfcc09ddSBjoern A. Zeeb } 77bfcc09ddSBjoern A. Zeeb 78bfcc09ddSBjoern A. Zeeb /* 79bfcc09ddSBjoern A. Zeeb * If associated to this AP and already have security context, 80bfcc09ddSBjoern A. Zeeb * the TK is already configured for this station, so it 81bfcc09ddSBjoern A. Zeeb * shouldn't be set again here. 82bfcc09ddSBjoern A. Zeeb */ 839af1bba4SBjoern A. Zeeb if (vif->cfg.assoc) { 84bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 859af1bba4SBjoern A. Zeeb struct ieee80211_bss_conf *link_conf; 869af1bba4SBjoern A. Zeeb unsigned int link_id; 87bfcc09ddSBjoern A. Zeeb struct ieee80211_sta *sta; 889af1bba4SBjoern A. Zeeb u8 sta_id; 89bfcc09ddSBjoern A. Zeeb 90bfcc09ddSBjoern A. Zeeb rcu_read_lock(); 919af1bba4SBjoern A. Zeeb for_each_vif_active_link(vif, link_conf, link_id) { 929af1bba4SBjoern A. Zeeb if (memcmp(addr, link_conf->bssid, ETH_ALEN)) 939af1bba4SBjoern A. Zeeb continue; 949af1bba4SBjoern A. Zeeb 959af1bba4SBjoern A. Zeeb sta_id = mvmvif->link[link_id]->ap_sta_id; 969af1bba4SBjoern A. Zeeb sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 97bfcc09ddSBjoern A. Zeeb if (!IS_ERR_OR_NULL(sta) && sta->mfp) 98bfcc09ddSBjoern A. Zeeb expected_tk_len = 0; 999af1bba4SBjoern A. Zeeb break; 1009af1bba4SBjoern A. Zeeb } 101bfcc09ddSBjoern A. Zeeb rcu_read_unlock(); 102bfcc09ddSBjoern A. Zeeb } 103bfcc09ddSBjoern A. Zeeb 1049af1bba4SBjoern A. Zeeb if (tk_len != expected_tk_len || 1059af1bba4SBjoern A. Zeeb (hltk_len && hltk_len != sizeof(pasn->hltk))) { 106bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n", 107bfcc09ddSBjoern A. Zeeb tk_len, hltk_len); 108bfcc09ddSBjoern A. Zeeb goto out; 109bfcc09ddSBjoern A. Zeeb } 110bfcc09ddSBjoern A. Zeeb 1119af1bba4SBjoern A. Zeeb if (!expected_tk_len && !hltk_len) { 1129af1bba4SBjoern A. Zeeb IWL_ERR(mvm, "TK and HLTK not set\n"); 1139af1bba4SBjoern A. Zeeb goto out; 1149af1bba4SBjoern A. Zeeb } 1159af1bba4SBjoern A. Zeeb 116bfcc09ddSBjoern A. Zeeb memcpy(pasn->addr, addr, sizeof(pasn->addr)); 1179af1bba4SBjoern A. Zeeb 1189af1bba4SBjoern A. Zeeb if (hltk_len) { 119bfcc09ddSBjoern A. Zeeb memcpy(pasn->hltk, hltk, sizeof(pasn->hltk)); 1209af1bba4SBjoern A. Zeeb pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK; 1219af1bba4SBjoern A. Zeeb } 122bfcc09ddSBjoern A. Zeeb 123bfcc09ddSBjoern A. Zeeb if (tk && tk_len) 124bfcc09ddSBjoern A. Zeeb memcpy(pasn->tk, tk, sizeof(pasn->tk)); 125bfcc09ddSBjoern A. Zeeb 126bfcc09ddSBjoern A. Zeeb list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list); 127bfcc09ddSBjoern A. Zeeb return 0; 128bfcc09ddSBjoern A. Zeeb out: 129bfcc09ddSBjoern A. Zeeb kfree(pasn); 130bfcc09ddSBjoern A. Zeeb return -EINVAL; 131bfcc09ddSBjoern A. Zeeb } 132bfcc09ddSBjoern A. Zeeb 133bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr) 134bfcc09ddSBjoern A. Zeeb { 135bfcc09ddSBjoern A. Zeeb struct iwl_mvm_ftm_pasn_entry *entry, *prev; 136bfcc09ddSBjoern A. Zeeb 137bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 138bfcc09ddSBjoern A. Zeeb 139bfcc09ddSBjoern A. Zeeb list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list, 140bfcc09ddSBjoern A. Zeeb list) { 141bfcc09ddSBjoern A. Zeeb if (memcmp(entry->addr, addr, sizeof(entry->addr))) 142bfcc09ddSBjoern A. Zeeb continue; 143bfcc09ddSBjoern A. Zeeb 144bfcc09ddSBjoern A. Zeeb list_del(&entry->list); 145bfcc09ddSBjoern A. Zeeb kfree(entry); 146bfcc09ddSBjoern A. Zeeb return; 147bfcc09ddSBjoern A. Zeeb } 148bfcc09ddSBjoern A. Zeeb } 149bfcc09ddSBjoern A. Zeeb 150bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm) 151bfcc09ddSBjoern A. Zeeb { 152bfcc09ddSBjoern A. Zeeb struct iwl_mvm_loc_entry *e, *t; 153bfcc09ddSBjoern A. Zeeb 154bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req = NULL; 155bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req_wdev = NULL; 156bfcc09ddSBjoern A. Zeeb memset(mvm->ftm_initiator.responses, 0, 157bfcc09ddSBjoern A. Zeeb sizeof(mvm->ftm_initiator.responses)); 158bfcc09ddSBjoern A. Zeeb 159bfcc09ddSBjoern A. Zeeb list_for_each_entry_safe(e, t, &mvm->ftm_initiator.loc_list, list) { 160bfcc09ddSBjoern A. Zeeb list_del(&e->list); 161bfcc09ddSBjoern A. Zeeb kfree(e); 162bfcc09ddSBjoern A. Zeeb } 163bfcc09ddSBjoern A. Zeeb } 164bfcc09ddSBjoern A. Zeeb 165bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_restart(struct iwl_mvm *mvm) 166bfcc09ddSBjoern A. Zeeb { 167bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_result result = { 168bfcc09ddSBjoern A. Zeeb .status = NL80211_PMSR_STATUS_FAILURE, 169bfcc09ddSBjoern A. Zeeb .final = 1, 170bfcc09ddSBjoern A. Zeeb .host_time = ktime_get_boottime_ns(), 171bfcc09ddSBjoern A. Zeeb .type = NL80211_PMSR_TYPE_FTM, 172bfcc09ddSBjoern A. Zeeb }; 173bfcc09ddSBjoern A. Zeeb int i; 174bfcc09ddSBjoern A. Zeeb 175bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 176bfcc09ddSBjoern A. Zeeb 177bfcc09ddSBjoern A. Zeeb if (!mvm->ftm_initiator.req) 178bfcc09ddSBjoern A. Zeeb return; 179bfcc09ddSBjoern A. Zeeb 180bfcc09ddSBjoern A. Zeeb for (i = 0; i < mvm->ftm_initiator.req->n_peers; i++) { 181bfcc09ddSBjoern A. Zeeb memcpy(result.addr, mvm->ftm_initiator.req->peers[i].addr, 182bfcc09ddSBjoern A. Zeeb ETH_ALEN); 183bfcc09ddSBjoern A. Zeeb result.ftm.burst_index = mvm->ftm_initiator.responses[i]; 184bfcc09ddSBjoern A. Zeeb 185bfcc09ddSBjoern A. Zeeb cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev, 186bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req, 187bfcc09ddSBjoern A. Zeeb &result, GFP_KERNEL); 188bfcc09ddSBjoern A. Zeeb } 189bfcc09ddSBjoern A. Zeeb 190bfcc09ddSBjoern A. Zeeb cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev, 191bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req, GFP_KERNEL); 192bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_reset(mvm); 193bfcc09ddSBjoern A. Zeeb } 194bfcc09ddSBjoern A. Zeeb 195bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm) 196bfcc09ddSBjoern A. Zeeb { 197bfcc09ddSBjoern A. Zeeb INIT_LIST_HEAD(&mvm->ftm_initiator.smooth.resp); 198bfcc09ddSBjoern A. Zeeb 199bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 200bfcc09ddSBjoern A. Zeeb "enable=%u, alpha=%u, age_jiffies=%u, thresh=(%u:%u)\n", 201bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH, 202bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA, 203bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * HZ, 204bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT, 205bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT); 206bfcc09ddSBjoern A. Zeeb } 207bfcc09ddSBjoern A. Zeeb 208bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm) 209bfcc09ddSBjoern A. Zeeb { 210bfcc09ddSBjoern A. Zeeb struct iwl_mvm_smooth_entry *se, *st; 211bfcc09ddSBjoern A. Zeeb 212bfcc09ddSBjoern A. Zeeb list_for_each_entry_safe(se, st, &mvm->ftm_initiator.smooth.resp, 213bfcc09ddSBjoern A. Zeeb list) { 214bfcc09ddSBjoern A. Zeeb list_del(&se->list); 215bfcc09ddSBjoern A. Zeeb kfree(se); 216bfcc09ddSBjoern A. Zeeb } 217bfcc09ddSBjoern A. Zeeb } 218bfcc09ddSBjoern A. Zeeb 219bfcc09ddSBjoern A. Zeeb static int 220bfcc09ddSBjoern A. Zeeb iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s) 221bfcc09ddSBjoern A. Zeeb { 222bfcc09ddSBjoern A. Zeeb switch (s) { 223bfcc09ddSBjoern A. Zeeb case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS: 224bfcc09ddSBjoern A. Zeeb return 0; 225bfcc09ddSBjoern A. Zeeb case IWL_TOF_RANGE_REQUEST_STATUS_BUSY: 226bfcc09ddSBjoern A. Zeeb return -EBUSY; 227bfcc09ddSBjoern A. Zeeb default: 228bfcc09ddSBjoern A. Zeeb WARN_ON_ONCE(1); 229bfcc09ddSBjoern A. Zeeb return -EIO; 230bfcc09ddSBjoern A. Zeeb } 231bfcc09ddSBjoern A. Zeeb } 232bfcc09ddSBjoern A. Zeeb 233bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_cmd_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 234bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v5 *cmd, 235bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 236bfcc09ddSBjoern A. Zeeb { 237bfcc09ddSBjoern A. Zeeb int i; 238bfcc09ddSBjoern A. Zeeb 239bfcc09ddSBjoern A. Zeeb cmd->request_id = req->cookie; 240bfcc09ddSBjoern A. Zeeb cmd->num_of_ap = req->n_peers; 241bfcc09ddSBjoern A. Zeeb 242bfcc09ddSBjoern A. Zeeb /* use maximum for "no timeout" or bigger than what we can do */ 243bfcc09ddSBjoern A. Zeeb if (!req->timeout || req->timeout > 255 * 100) 244bfcc09ddSBjoern A. Zeeb cmd->req_timeout = 255; 245bfcc09ddSBjoern A. Zeeb else 246bfcc09ddSBjoern A. Zeeb cmd->req_timeout = DIV_ROUND_UP(req->timeout, 100); 247bfcc09ddSBjoern A. Zeeb 248bfcc09ddSBjoern A. Zeeb /* 249bfcc09ddSBjoern A. Zeeb * We treat it always as random, since if not we'll 250bfcc09ddSBjoern A. Zeeb * have filled our local address there instead. 251bfcc09ddSBjoern A. Zeeb */ 252bfcc09ddSBjoern A. Zeeb cmd->macaddr_random = 1; 253bfcc09ddSBjoern A. Zeeb memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN); 254bfcc09ddSBjoern A. Zeeb for (i = 0; i < ETH_ALEN; i++) 255bfcc09ddSBjoern A. Zeeb cmd->macaddr_mask[i] = ~req->mac_addr_mask[i]; 256bfcc09ddSBjoern A. Zeeb 2579af1bba4SBjoern A. Zeeb if (vif->cfg.assoc) 258bfcc09ddSBjoern A. Zeeb memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN); 259bfcc09ddSBjoern A. Zeeb else 260bfcc09ddSBjoern A. Zeeb eth_broadcast_addr(cmd->range_req_bssid); 261bfcc09ddSBjoern A. Zeeb } 262bfcc09ddSBjoern A. Zeeb 263bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_cmd_common(struct iwl_mvm *mvm, 264bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif, 2657db7bfe1SBjoern A. Zeeb #if defined(__linux__) 266bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v9 *cmd, 2677db7bfe1SBjoern A. Zeeb #elif defined(__FreeBSD__) 2687db7bfe1SBjoern A. Zeeb struct iwl_tof_range_req_cmd_v9 *cmd, /* XXX-BZ Probably better solved by a common struct in fw for top parts of the struct. */ 2697db7bfe1SBjoern A. Zeeb #endif 270bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 271bfcc09ddSBjoern A. Zeeb { 272bfcc09ddSBjoern A. Zeeb int i; 273bfcc09ddSBjoern A. Zeeb 274bfcc09ddSBjoern A. Zeeb cmd->initiator_flags = 275bfcc09ddSBjoern A. Zeeb cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM | 276bfcc09ddSBjoern A. Zeeb IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT); 277bfcc09ddSBjoern A. Zeeb cmd->request_id = req->cookie; 278bfcc09ddSBjoern A. Zeeb cmd->num_of_ap = req->n_peers; 279bfcc09ddSBjoern A. Zeeb 280bfcc09ddSBjoern A. Zeeb /* 281bfcc09ddSBjoern A. Zeeb * Use a large value for "no timeout". Don't use the maximum value 282bfcc09ddSBjoern A. Zeeb * because of fw limitations. 283bfcc09ddSBjoern A. Zeeb */ 284bfcc09ddSBjoern A. Zeeb if (req->timeout) 285bfcc09ddSBjoern A. Zeeb cmd->req_timeout_ms = cpu_to_le32(req->timeout); 286bfcc09ddSBjoern A. Zeeb else 287bfcc09ddSBjoern A. Zeeb cmd->req_timeout_ms = cpu_to_le32(0xfffff); 288bfcc09ddSBjoern A. Zeeb 289bfcc09ddSBjoern A. Zeeb memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN); 290bfcc09ddSBjoern A. Zeeb for (i = 0; i < ETH_ALEN; i++) 291bfcc09ddSBjoern A. Zeeb cmd->macaddr_mask[i] = ~req->mac_addr_mask[i]; 292bfcc09ddSBjoern A. Zeeb 2939af1bba4SBjoern A. Zeeb if (vif->cfg.assoc) { 294bfcc09ddSBjoern A. Zeeb memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN); 295bfcc09ddSBjoern A. Zeeb 296bfcc09ddSBjoern A. Zeeb /* AP's TSF is only relevant if associated */ 297bfcc09ddSBjoern A. Zeeb for (i = 0; i < req->n_peers; i++) { 298bfcc09ddSBjoern A. Zeeb if (req->peers[i].report_ap_tsf) { 299bfcc09ddSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = 300bfcc09ddSBjoern A. Zeeb iwl_mvm_vif_from_mac80211(vif); 301bfcc09ddSBjoern A. Zeeb 302bfcc09ddSBjoern A. Zeeb cmd->tsf_mac_id = cpu_to_le32(mvmvif->id); 303bfcc09ddSBjoern A. Zeeb return; 304bfcc09ddSBjoern A. Zeeb } 305bfcc09ddSBjoern A. Zeeb } 306bfcc09ddSBjoern A. Zeeb } else { 307bfcc09ddSBjoern A. Zeeb eth_broadcast_addr(cmd->range_req_bssid); 308bfcc09ddSBjoern A. Zeeb } 309bfcc09ddSBjoern A. Zeeb 310bfcc09ddSBjoern A. Zeeb /* Don't report AP's TSF */ 311bfcc09ddSBjoern A. Zeeb cmd->tsf_mac_id = cpu_to_le32(0xff); 312bfcc09ddSBjoern A. Zeeb } 313bfcc09ddSBjoern A. Zeeb 314bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_cmd_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 315bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v8 *cmd, 316bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 317bfcc09ddSBjoern A. Zeeb { 318bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_common(mvm, vif, (void *)cmd, req); 319bfcc09ddSBjoern A. Zeeb } 320bfcc09ddSBjoern A. Zeeb 321bfcc09ddSBjoern A. Zeeb static int 322bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm *mvm, 323bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 324bfcc09ddSBjoern A. Zeeb u8 *channel, u8 *bandwidth, 325bfcc09ddSBjoern A. Zeeb u8 *ctrl_ch_position) 326bfcc09ddSBjoern A. Zeeb { 327bfcc09ddSBjoern A. Zeeb u32 freq = peer->chandef.chan->center_freq; 328bfcc09ddSBjoern A. Zeeb 329bfcc09ddSBjoern A. Zeeb *channel = ieee80211_frequency_to_channel(freq); 330bfcc09ddSBjoern A. Zeeb 331bfcc09ddSBjoern A. Zeeb switch (peer->chandef.width) { 332bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_20_NOHT: 333bfcc09ddSBjoern A. Zeeb *bandwidth = IWL_TOF_BW_20_LEGACY; 334bfcc09ddSBjoern A. Zeeb break; 335bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_20: 336bfcc09ddSBjoern A. Zeeb *bandwidth = IWL_TOF_BW_20_HT; 337bfcc09ddSBjoern A. Zeeb break; 338bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_40: 339bfcc09ddSBjoern A. Zeeb *bandwidth = IWL_TOF_BW_40; 340bfcc09ddSBjoern A. Zeeb break; 341bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_80: 342bfcc09ddSBjoern A. Zeeb *bandwidth = IWL_TOF_BW_80; 343bfcc09ddSBjoern A. Zeeb break; 344bfcc09ddSBjoern A. Zeeb default: 345bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n", 346bfcc09ddSBjoern A. Zeeb peer->chandef.width); 347bfcc09ddSBjoern A. Zeeb return -EINVAL; 348bfcc09ddSBjoern A. Zeeb } 349bfcc09ddSBjoern A. Zeeb 350bfcc09ddSBjoern A. Zeeb *ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ? 351bfcc09ddSBjoern A. Zeeb iwl_mvm_get_ctrl_pos(&peer->chandef) : 0; 352bfcc09ddSBjoern A. Zeeb 353bfcc09ddSBjoern A. Zeeb return 0; 354bfcc09ddSBjoern A. Zeeb } 355bfcc09ddSBjoern A. Zeeb 356bfcc09ddSBjoern A. Zeeb static int 357bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm, 358bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 359bfcc09ddSBjoern A. Zeeb u8 *channel, u8 *format_bw, 360bfcc09ddSBjoern A. Zeeb u8 *ctrl_ch_position) 361bfcc09ddSBjoern A. Zeeb { 362bfcc09ddSBjoern A. Zeeb u32 freq = peer->chandef.chan->center_freq; 363bfcc09ddSBjoern A. Zeeb u8 cmd_ver; 364bfcc09ddSBjoern A. Zeeb 365bfcc09ddSBjoern A. Zeeb *channel = ieee80211_frequency_to_channel(freq); 366bfcc09ddSBjoern A. Zeeb 367bfcc09ddSBjoern A. Zeeb switch (peer->chandef.width) { 368bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_20_NOHT: 369bfcc09ddSBjoern A. Zeeb *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; 370bfcc09ddSBjoern A. Zeeb *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 371bfcc09ddSBjoern A. Zeeb break; 372bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_20: 373bfcc09ddSBjoern A. Zeeb *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 374bfcc09ddSBjoern A. Zeeb *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 375bfcc09ddSBjoern A. Zeeb break; 376bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_40: 377bfcc09ddSBjoern A. Zeeb *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 378bfcc09ddSBjoern A. Zeeb *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; 379bfcc09ddSBjoern A. Zeeb break; 380bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_80: 381bfcc09ddSBjoern A. Zeeb *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; 382bfcc09ddSBjoern A. Zeeb *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; 383bfcc09ddSBjoern A. Zeeb break; 384bfcc09ddSBjoern A. Zeeb case NL80211_CHAN_WIDTH_160: 385d9836fb4SBjoern A. Zeeb cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, 386d9836fb4SBjoern A. Zeeb WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 387bfcc09ddSBjoern A. Zeeb IWL_FW_CMD_VER_UNKNOWN); 388bfcc09ddSBjoern A. Zeeb 389bfcc09ddSBjoern A. Zeeb if (cmd_ver >= 13) { 390bfcc09ddSBjoern A. Zeeb *format_bw = IWL_LOCATION_FRAME_FORMAT_HE; 391bfcc09ddSBjoern A. Zeeb *format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS; 392bfcc09ddSBjoern A. Zeeb break; 393bfcc09ddSBjoern A. Zeeb } 394bfcc09ddSBjoern A. Zeeb fallthrough; 395bfcc09ddSBjoern A. Zeeb default: 396bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n", 397bfcc09ddSBjoern A. Zeeb peer->chandef.width); 398bfcc09ddSBjoern A. Zeeb return -EINVAL; 399bfcc09ddSBjoern A. Zeeb } 400bfcc09ddSBjoern A. Zeeb 401bfcc09ddSBjoern A. Zeeb /* non EDCA based measurement must use HE preamble */ 402bfcc09ddSBjoern A. Zeeb if (peer->ftm.trigger_based || peer->ftm.non_trigger_based) 403bfcc09ddSBjoern A. Zeeb *format_bw |= IWL_LOCATION_FRAME_FORMAT_HE; 404bfcc09ddSBjoern A. Zeeb 405bfcc09ddSBjoern A. Zeeb *ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ? 406bfcc09ddSBjoern A. Zeeb iwl_mvm_get_ctrl_pos(&peer->chandef) : 0; 407bfcc09ddSBjoern A. Zeeb 408bfcc09ddSBjoern A. Zeeb return 0; 409bfcc09ddSBjoern A. Zeeb } 410bfcc09ddSBjoern A. Zeeb 411bfcc09ddSBjoern A. Zeeb static int 412bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v2(struct iwl_mvm *mvm, 413bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 414bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v2 *target) 415bfcc09ddSBjoern A. Zeeb { 416bfcc09ddSBjoern A. Zeeb int ret; 417bfcc09ddSBjoern A. Zeeb 418bfcc09ddSBjoern A. Zeeb ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num, 419bfcc09ddSBjoern A. Zeeb &target->bandwidth, 420bfcc09ddSBjoern A. Zeeb &target->ctrl_ch_position); 421bfcc09ddSBjoern A. Zeeb if (ret) 422bfcc09ddSBjoern A. Zeeb return ret; 423bfcc09ddSBjoern A. Zeeb 424bfcc09ddSBjoern A. Zeeb memcpy(target->bssid, peer->addr, ETH_ALEN); 425bfcc09ddSBjoern A. Zeeb target->burst_period = 426bfcc09ddSBjoern A. Zeeb cpu_to_le16(peer->ftm.burst_period); 427bfcc09ddSBjoern A. Zeeb target->samples_per_burst = peer->ftm.ftms_per_burst; 428bfcc09ddSBjoern A. Zeeb target->num_of_bursts = peer->ftm.num_bursts_exp; 429bfcc09ddSBjoern A. Zeeb target->measure_type = 0; /* regular two-sided FTM */ 430bfcc09ddSBjoern A. Zeeb target->retries_per_sample = peer->ftm.ftmr_retries; 431bfcc09ddSBjoern A. Zeeb target->asap_mode = peer->ftm.asap; 432bfcc09ddSBjoern A. Zeeb target->enable_dyn_ack = IWL_MVM_FTM_INITIATOR_DYNACK; 433bfcc09ddSBjoern A. Zeeb 434bfcc09ddSBjoern A. Zeeb if (peer->ftm.request_lci) 435bfcc09ddSBjoern A. Zeeb target->location_req |= IWL_TOF_LOC_LCI; 436bfcc09ddSBjoern A. Zeeb if (peer->ftm.request_civicloc) 437bfcc09ddSBjoern A. Zeeb target->location_req |= IWL_TOF_LOC_CIVIC; 438bfcc09ddSBjoern A. Zeeb 439bfcc09ddSBjoern A. Zeeb target->algo_type = IWL_MVM_FTM_INITIATOR_ALGO; 440bfcc09ddSBjoern A. Zeeb 441bfcc09ddSBjoern A. Zeeb return 0; 442bfcc09ddSBjoern A. Zeeb } 443bfcc09ddSBjoern A. Zeeb 444*a4128aadSBjoern A. Zeeb #define FTM_SET_FLAG(flag) (*flags |= \ 445bfcc09ddSBjoern A. Zeeb cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag)) 446bfcc09ddSBjoern A. Zeeb 447bfcc09ddSBjoern A. Zeeb static void 448*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_target_flags(struct iwl_mvm *mvm, 449*a4128aadSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 450*a4128aadSBjoern A. Zeeb __le32 *flags) 451*a4128aadSBjoern A. Zeeb { 452*a4128aadSBjoern A. Zeeb *flags = cpu_to_le32(0); 453*a4128aadSBjoern A. Zeeb 454*a4128aadSBjoern A. Zeeb if (peer->ftm.asap) 455*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(ASAP); 456*a4128aadSBjoern A. Zeeb 457*a4128aadSBjoern A. Zeeb if (peer->ftm.request_lci) 458*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(LCI_REQUEST); 459*a4128aadSBjoern A. Zeeb 460*a4128aadSBjoern A. Zeeb if (peer->ftm.request_civicloc) 461*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(CIVIC_REQUEST); 462*a4128aadSBjoern A. Zeeb 463*a4128aadSBjoern A. Zeeb if (IWL_MVM_FTM_INITIATOR_DYNACK) 464*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(DYN_ACK); 465*a4128aadSBjoern A. Zeeb 466*a4128aadSBjoern A. Zeeb if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG) 467*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(ALGO_LR); 468*a4128aadSBjoern A. Zeeb else if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT) 469*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(ALGO_FFT); 470*a4128aadSBjoern A. Zeeb 471*a4128aadSBjoern A. Zeeb if (peer->ftm.trigger_based) 472*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(TB); 473*a4128aadSBjoern A. Zeeb else if (peer->ftm.non_trigger_based) 474*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(NON_TB); 475*a4128aadSBjoern A. Zeeb 476*a4128aadSBjoern A. Zeeb if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) && 477*a4128aadSBjoern A. Zeeb peer->ftm.lmr_feedback) 478*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(LMR_FEEDBACK); 479*a4128aadSBjoern A. Zeeb } 480*a4128aadSBjoern A. Zeeb 481*a4128aadSBjoern A. Zeeb static void 482bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_common(struct iwl_mvm *mvm, 483bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 484bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v6 *target) 485bfcc09ddSBjoern A. Zeeb { 486bfcc09ddSBjoern A. Zeeb memcpy(target->bssid, peer->addr, ETH_ALEN); 487bfcc09ddSBjoern A. Zeeb target->burst_period = 488bfcc09ddSBjoern A. Zeeb cpu_to_le16(peer->ftm.burst_period); 489bfcc09ddSBjoern A. Zeeb target->samples_per_burst = peer->ftm.ftms_per_burst; 490bfcc09ddSBjoern A. Zeeb target->num_of_bursts = peer->ftm.num_bursts_exp; 491bfcc09ddSBjoern A. Zeeb target->ftmr_max_retries = peer->ftm.ftmr_retries; 492*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags); 493bfcc09ddSBjoern A. Zeeb } 494bfcc09ddSBjoern A. Zeeb 495bfcc09ddSBjoern A. Zeeb static int 496bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v3(struct iwl_mvm *mvm, 497bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 498bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v3 *target) 499bfcc09ddSBjoern A. Zeeb { 500bfcc09ddSBjoern A. Zeeb int ret; 501bfcc09ddSBjoern A. Zeeb 502bfcc09ddSBjoern A. Zeeb ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num, 503bfcc09ddSBjoern A. Zeeb &target->bandwidth, 504bfcc09ddSBjoern A. Zeeb &target->ctrl_ch_position); 505bfcc09ddSBjoern A. Zeeb if (ret) 506bfcc09ddSBjoern A. Zeeb return ret; 507bfcc09ddSBjoern A. Zeeb 508bfcc09ddSBjoern A. Zeeb /* 509bfcc09ddSBjoern A. Zeeb * Versions 3 and 4 has some common fields, so 510bfcc09ddSBjoern A. Zeeb * iwl_mvm_ftm_put_target_common() can be used for version 7 too. 511bfcc09ddSBjoern A. Zeeb */ 512bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target); 513bfcc09ddSBjoern A. Zeeb 514bfcc09ddSBjoern A. Zeeb return 0; 515bfcc09ddSBjoern A. Zeeb } 516bfcc09ddSBjoern A. Zeeb 517bfcc09ddSBjoern A. Zeeb static int 518bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v4(struct iwl_mvm *mvm, 519bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 520bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v4 *target) 521bfcc09ddSBjoern A. Zeeb { 522bfcc09ddSBjoern A. Zeeb int ret; 523bfcc09ddSBjoern A. Zeeb 524bfcc09ddSBjoern A. Zeeb ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num, 525bfcc09ddSBjoern A. Zeeb &target->format_bw, 526bfcc09ddSBjoern A. Zeeb &target->ctrl_ch_position); 527bfcc09ddSBjoern A. Zeeb if (ret) 528bfcc09ddSBjoern A. Zeeb return ret; 529bfcc09ddSBjoern A. Zeeb 530bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target); 531bfcc09ddSBjoern A. Zeeb 532bfcc09ddSBjoern A. Zeeb return 0; 533bfcc09ddSBjoern A. Zeeb } 534bfcc09ddSBjoern A. Zeeb 535*a4128aadSBjoern A. Zeeb static int iwl_mvm_ftm_set_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 536*a4128aadSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 537*a4128aadSBjoern A. Zeeb u8 *sta_id, __le32 *flags) 538*a4128aadSBjoern A. Zeeb { 539*a4128aadSBjoern A. Zeeb if (vif->cfg.assoc) { 540*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 541*a4128aadSBjoern A. Zeeb struct ieee80211_sta *sta; 542*a4128aadSBjoern A. Zeeb struct ieee80211_bss_conf *link_conf; 543*a4128aadSBjoern A. Zeeb unsigned int link_id; 544*a4128aadSBjoern A. Zeeb 545*a4128aadSBjoern A. Zeeb rcu_read_lock(); 546*a4128aadSBjoern A. Zeeb for_each_vif_active_link(vif, link_conf, link_id) { 547*a4128aadSBjoern A. Zeeb if (memcmp(peer->addr, link_conf->bssid, ETH_ALEN)) 548*a4128aadSBjoern A. Zeeb continue; 549*a4128aadSBjoern A. Zeeb 550*a4128aadSBjoern A. Zeeb *sta_id = mvmvif->link[link_id]->ap_sta_id; 551*a4128aadSBjoern A. Zeeb sta = rcu_dereference(mvm->fw_id_to_mac_id[*sta_id]); 552*a4128aadSBjoern A. Zeeb if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { 553*a4128aadSBjoern A. Zeeb rcu_read_unlock(); 554*a4128aadSBjoern A. Zeeb return PTR_ERR_OR_ZERO(sta); 555*a4128aadSBjoern A. Zeeb } 556*a4128aadSBjoern A. Zeeb 557*a4128aadSBjoern A. Zeeb if (sta->mfp && (peer->ftm.trigger_based || 558*a4128aadSBjoern A. Zeeb peer->ftm.non_trigger_based)) 559*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(PMF); 560*a4128aadSBjoern A. Zeeb break; 561*a4128aadSBjoern A. Zeeb } 562*a4128aadSBjoern A. Zeeb rcu_read_unlock(); 563*a4128aadSBjoern A. Zeeb 564*a4128aadSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS 565*a4128aadSBjoern A. Zeeb if (mvmvif->ftm_unprotected) { 566*a4128aadSBjoern A. Zeeb *sta_id = IWL_MVM_INVALID_STA; 567*a4128aadSBjoern A. Zeeb *flags &= ~cpu_to_le32(IWL_INITIATOR_AP_FLAGS_PMF); 568*a4128aadSBjoern A. Zeeb } 569*a4128aadSBjoern A. Zeeb #endif 570*a4128aadSBjoern A. Zeeb } else { 571*a4128aadSBjoern A. Zeeb *sta_id = IWL_MVM_INVALID_STA; 572*a4128aadSBjoern A. Zeeb } 573*a4128aadSBjoern A. Zeeb 574*a4128aadSBjoern A. Zeeb return 0; 575*a4128aadSBjoern A. Zeeb } 576*a4128aadSBjoern A. Zeeb 577bfcc09ddSBjoern A. Zeeb static int 578bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 579bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 580bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v6 *target) 581bfcc09ddSBjoern A. Zeeb { 582bfcc09ddSBjoern A. Zeeb int ret; 583bfcc09ddSBjoern A. Zeeb 584bfcc09ddSBjoern A. Zeeb ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num, 585bfcc09ddSBjoern A. Zeeb &target->format_bw, 586bfcc09ddSBjoern A. Zeeb &target->ctrl_ch_position); 587bfcc09ddSBjoern A. Zeeb if (ret) 588bfcc09ddSBjoern A. Zeeb return ret; 589bfcc09ddSBjoern A. Zeeb 590bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_common(mvm, peer, target); 591bfcc09ddSBjoern A. Zeeb 592*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id, 593*a4128aadSBjoern A. Zeeb &target->initiator_ap_flags); 594bfcc09ddSBjoern A. Zeeb 595bfcc09ddSBjoern A. Zeeb /* 596bfcc09ddSBjoern A. Zeeb * TODO: Beacon interval is currently unknown, so use the common value 597bfcc09ddSBjoern A. Zeeb * of 100 TUs. 598bfcc09ddSBjoern A. Zeeb */ 599bfcc09ddSBjoern A. Zeeb target->beacon_interval = cpu_to_le16(100); 600bfcc09ddSBjoern A. Zeeb return 0; 601bfcc09ddSBjoern A. Zeeb } 602bfcc09ddSBjoern A. Zeeb 603bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *hcmd) 604bfcc09ddSBjoern A. Zeeb { 605bfcc09ddSBjoern A. Zeeb u32 status; 606bfcc09ddSBjoern A. Zeeb int err = iwl_mvm_send_cmd_status(mvm, hcmd, &status); 607bfcc09ddSBjoern A. Zeeb 608bfcc09ddSBjoern A. Zeeb if (!err && status) { 609bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "FTM range request command failure, status: %u\n", 610bfcc09ddSBjoern A. Zeeb status); 611bfcc09ddSBjoern A. Zeeb err = iwl_ftm_range_request_status_to_err(status); 612bfcc09ddSBjoern A. Zeeb } 613bfcc09ddSBjoern A. Zeeb 614bfcc09ddSBjoern A. Zeeb return err; 615bfcc09ddSBjoern A. Zeeb } 616bfcc09ddSBjoern A. Zeeb 617bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 618bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 619bfcc09ddSBjoern A. Zeeb { 620bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v5 cmd_v5; 621bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 622d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 623bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 624bfcc09ddSBjoern A. Zeeb .data[0] = &cmd_v5, 625bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd_v5), 626bfcc09ddSBjoern A. Zeeb }; 627bfcc09ddSBjoern A. Zeeb u8 i; 628bfcc09ddSBjoern A. Zeeb int err; 629bfcc09ddSBjoern A. Zeeb 630bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_v5(mvm, vif, &cmd_v5, req); 631bfcc09ddSBjoern A. Zeeb 632bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd_v5.num_of_ap; i++) { 633bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 634bfcc09ddSBjoern A. Zeeb 635bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v2(mvm, peer, &cmd_v5.ap[i]); 636bfcc09ddSBjoern A. Zeeb if (err) 637bfcc09ddSBjoern A. Zeeb return err; 638bfcc09ddSBjoern A. Zeeb } 639bfcc09ddSBjoern A. Zeeb 640bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 641bfcc09ddSBjoern A. Zeeb } 642bfcc09ddSBjoern A. Zeeb 643bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 644bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 645bfcc09ddSBjoern A. Zeeb { 646bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v7 cmd_v7; 647bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 648d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 649bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 650bfcc09ddSBjoern A. Zeeb .data[0] = &cmd_v7, 651bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd_v7), 652bfcc09ddSBjoern A. Zeeb }; 653bfcc09ddSBjoern A. Zeeb u8 i; 654bfcc09ddSBjoern A. Zeeb int err; 655bfcc09ddSBjoern A. Zeeb 656bfcc09ddSBjoern A. Zeeb /* 657bfcc09ddSBjoern A. Zeeb * Versions 7 and 8 has the same structure except from the responders 658bfcc09ddSBjoern A. Zeeb * list, so iwl_mvm_ftm_cmd() can be used for version 7 too. 659bfcc09ddSBjoern A. Zeeb */ 660bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd_v7, req); 661bfcc09ddSBjoern A. Zeeb 662bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd_v7.num_of_ap; i++) { 663bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 664bfcc09ddSBjoern A. Zeeb 665bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v3(mvm, peer, &cmd_v7.ap[i]); 666bfcc09ddSBjoern A. Zeeb if (err) 667bfcc09ddSBjoern A. Zeeb return err; 668bfcc09ddSBjoern A. Zeeb } 669bfcc09ddSBjoern A. Zeeb 670bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 671bfcc09ddSBjoern A. Zeeb } 672bfcc09ddSBjoern A. Zeeb 673bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 674bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 675bfcc09ddSBjoern A. Zeeb { 676bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v8 cmd; 677bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 678d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 679bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 680bfcc09ddSBjoern A. Zeeb .data[0] = &cmd, 681bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd), 682bfcc09ddSBjoern A. Zeeb }; 683bfcc09ddSBjoern A. Zeeb u8 i; 684bfcc09ddSBjoern A. Zeeb int err; 685bfcc09ddSBjoern A. Zeeb 686bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd, req); 687bfcc09ddSBjoern A. Zeeb 688bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd.num_of_ap; i++) { 689bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 690bfcc09ddSBjoern A. Zeeb 691bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v4(mvm, peer, &cmd.ap[i]); 692bfcc09ddSBjoern A. Zeeb if (err) 693bfcc09ddSBjoern A. Zeeb return err; 694bfcc09ddSBjoern A. Zeeb } 695bfcc09ddSBjoern A. Zeeb 696bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 697bfcc09ddSBjoern A. Zeeb } 698bfcc09ddSBjoern A. Zeeb 699bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 700bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 701bfcc09ddSBjoern A. Zeeb { 702bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v9 cmd; 703bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 704d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 705bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 706bfcc09ddSBjoern A. Zeeb .data[0] = &cmd, 707bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd), 708bfcc09ddSBjoern A. Zeeb }; 709bfcc09ddSBjoern A. Zeeb u8 i; 710bfcc09ddSBjoern A. Zeeb int err; 711bfcc09ddSBjoern A. Zeeb 712bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_common(mvm, vif, &cmd, req); 713bfcc09ddSBjoern A. Zeeb 714bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd.num_of_ap; i++) { 715bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 716bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v6 *target = &cmd.ap[i]; 717bfcc09ddSBjoern A. Zeeb 718bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target(mvm, vif, peer, target); 719bfcc09ddSBjoern A. Zeeb if (err) 720bfcc09ddSBjoern A. Zeeb return err; 721bfcc09ddSBjoern A. Zeeb } 722bfcc09ddSBjoern A. Zeeb 723bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 724bfcc09ddSBjoern A. Zeeb } 725bfcc09ddSBjoern A. Zeeb 726bfcc09ddSBjoern A. Zeeb static void iter(struct ieee80211_hw *hw, 727bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif, 728bfcc09ddSBjoern A. Zeeb struct ieee80211_sta *sta, 729bfcc09ddSBjoern A. Zeeb struct ieee80211_key_conf *key, 730bfcc09ddSBjoern A. Zeeb void *data) 731bfcc09ddSBjoern A. Zeeb { 732*a4128aadSBjoern A. Zeeb struct iwl_mvm_ftm_iter_data *target = data; 733bfcc09ddSBjoern A. Zeeb 734bfcc09ddSBjoern A. Zeeb if (!sta || memcmp(sta->addr, target->bssid, ETH_ALEN)) 735bfcc09ddSBjoern A. Zeeb return; 736bfcc09ddSBjoern A. Zeeb 737bfcc09ddSBjoern A. Zeeb WARN_ON(!sta->mfp); 738bfcc09ddSBjoern A. Zeeb 739*a4128aadSBjoern A. Zeeb target->tk = key->key; 740*a4128aadSBjoern A. Zeeb *target->cipher = iwl_mvm_cipher_to_location_cipher(key->cipher); 741*a4128aadSBjoern A. Zeeb WARN_ON(*target->cipher == IWL_LOCATION_CIPHER_INVALID); 742bfcc09ddSBjoern A. Zeeb } 743bfcc09ddSBjoern A. Zeeb 744bfcc09ddSBjoern A. Zeeb static void 745bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 746*a4128aadSBjoern A. Zeeb u8 *bssid, u8 *cipher, u8 *hltk, u8 *tk, 747*a4128aadSBjoern A. Zeeb u8 *rx_pn, u8 *tx_pn, __le32 *flags) 748bfcc09ddSBjoern A. Zeeb { 749bfcc09ddSBjoern A. Zeeb struct iwl_mvm_ftm_pasn_entry *entry; 750*a4128aadSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS 751*a4128aadSBjoern A. Zeeb struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 752bfcc09ddSBjoern A. Zeeb 753*a4128aadSBjoern A. Zeeb if (mvmvif->ftm_unprotected) 754*a4128aadSBjoern A. Zeeb return; 755*a4128aadSBjoern A. Zeeb #endif 756*a4128aadSBjoern A. Zeeb 757*a4128aadSBjoern A. Zeeb if (!(le32_to_cpu(*flags) & (IWL_INITIATOR_AP_FLAGS_NON_TB | 758bfcc09ddSBjoern A. Zeeb IWL_INITIATOR_AP_FLAGS_TB))) 759bfcc09ddSBjoern A. Zeeb return; 760bfcc09ddSBjoern A. Zeeb 761bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 762bfcc09ddSBjoern A. Zeeb 763bfcc09ddSBjoern A. Zeeb list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) { 764*a4128aadSBjoern A. Zeeb if (memcmp(entry->addr, bssid, sizeof(entry->addr))) 765bfcc09ddSBjoern A. Zeeb continue; 766bfcc09ddSBjoern A. Zeeb 767*a4128aadSBjoern A. Zeeb *cipher = entry->cipher; 768bfcc09ddSBjoern A. Zeeb 7699af1bba4SBjoern A. Zeeb if (entry->flags & IWL_MVM_PASN_FLAG_HAS_HLTK) 770*a4128aadSBjoern A. Zeeb memcpy(hltk, entry->hltk, sizeof(entry->hltk)); 7719af1bba4SBjoern A. Zeeb else 772*a4128aadSBjoern A. Zeeb memset(hltk, 0, sizeof(entry->hltk)); 7739af1bba4SBjoern A. Zeeb 7749af1bba4SBjoern A. Zeeb if (vif->cfg.assoc && 775*a4128aadSBjoern A. Zeeb !memcmp(vif->bss_conf.bssid, bssid, ETH_ALEN)) { 776*a4128aadSBjoern A. Zeeb struct iwl_mvm_ftm_iter_data target; 777bfcc09ddSBjoern A. Zeeb 778*a4128aadSBjoern A. Zeeb target.bssid = bssid; 779*a4128aadSBjoern A. Zeeb ieee80211_iter_keys(mvm->hw, vif, iter, &target); 780*a4128aadSBjoern A. Zeeb } else { 781*a4128aadSBjoern A. Zeeb memcpy(tk, entry->tk, sizeof(entry->tk)); 782*a4128aadSBjoern A. Zeeb } 783bfcc09ddSBjoern A. Zeeb 784*a4128aadSBjoern A. Zeeb memcpy(rx_pn, entry->rx_pn, sizeof(entry->rx_pn)); 785*a4128aadSBjoern A. Zeeb memcpy(tx_pn, entry->tx_pn, sizeof(entry->tx_pn)); 786*a4128aadSBjoern A. Zeeb 787*a4128aadSBjoern A. Zeeb FTM_SET_FLAG(SECURED); 788bfcc09ddSBjoern A. Zeeb return; 789bfcc09ddSBjoern A. Zeeb } 790bfcc09ddSBjoern A. Zeeb } 791bfcc09ddSBjoern A. Zeeb 792bfcc09ddSBjoern A. Zeeb static int 793bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 794bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 795bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v7 *target) 796bfcc09ddSBjoern A. Zeeb { 797bfcc09ddSBjoern A. Zeeb int err = iwl_mvm_ftm_put_target(mvm, vif, peer, (void *)target); 798bfcc09ddSBjoern A. Zeeb if (err) 799bfcc09ddSBjoern A. Zeeb return err; 800bfcc09ddSBjoern A. Zeeb 801*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, 802*a4128aadSBjoern A. Zeeb &target->cipher, target->hltk, 803*a4128aadSBjoern A. Zeeb target->tk, target->rx_pn, 804*a4128aadSBjoern A. Zeeb target->tx_pn, 805*a4128aadSBjoern A. Zeeb &target->initiator_ap_flags); 806bfcc09ddSBjoern A. Zeeb return err; 807bfcc09ddSBjoern A. Zeeb } 808bfcc09ddSBjoern A. Zeeb 809bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm, 810bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif, 811bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 812bfcc09ddSBjoern A. Zeeb { 813bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v11 cmd; 814bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 815d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 816bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 817bfcc09ddSBjoern A. Zeeb .data[0] = &cmd, 818bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd), 819bfcc09ddSBjoern A. Zeeb }; 820bfcc09ddSBjoern A. Zeeb u8 i; 821bfcc09ddSBjoern A. Zeeb int err; 822bfcc09ddSBjoern A. Zeeb 823bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req); 824bfcc09ddSBjoern A. Zeeb 825bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd.num_of_ap; i++) { 826bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 827bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v7 *target = &cmd.ap[i]; 828bfcc09ddSBjoern A. Zeeb 829bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, target); 830bfcc09ddSBjoern A. Zeeb if (err) 831bfcc09ddSBjoern A. Zeeb return err; 832bfcc09ddSBjoern A. Zeeb } 833bfcc09ddSBjoern A. Zeeb 834bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 835bfcc09ddSBjoern A. Zeeb } 836bfcc09ddSBjoern A. Zeeb 837bfcc09ddSBjoern A. Zeeb static void 838bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_set_ndp_params(struct iwl_mvm *mvm, 839bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v8 *target) 840bfcc09ddSBjoern A. Zeeb { 841bfcc09ddSBjoern A. Zeeb /* Only 2 STS are supported on Tx */ 842bfcc09ddSBjoern A. Zeeb u32 i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 : 843bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_I2R_MAX_STS; 844bfcc09ddSBjoern A. Zeeb 845bfcc09ddSBjoern A. Zeeb target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP | 846bfcc09ddSBjoern A. Zeeb (IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS); 847bfcc09ddSBjoern A. Zeeb target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP | 848bfcc09ddSBjoern A. Zeeb (i2r_max_sts << IWL_LOCATION_MAX_STS_POS); 849bfcc09ddSBjoern A. Zeeb target->r2i_max_total_ltf = IWL_MVM_FTM_R2I_MAX_TOTAL_LTF; 850bfcc09ddSBjoern A. Zeeb target->i2r_max_total_ltf = IWL_MVM_FTM_I2R_MAX_TOTAL_LTF; 851bfcc09ddSBjoern A. Zeeb } 852bfcc09ddSBjoern A. Zeeb 853bfcc09ddSBjoern A. Zeeb static int 854bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 855bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 856bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v8 *target) 857bfcc09ddSBjoern A. Zeeb { 858bfcc09ddSBjoern A. Zeeb u32 flags; 859bfcc09ddSBjoern A. Zeeb int ret = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, (void *)target); 860bfcc09ddSBjoern A. Zeeb 861bfcc09ddSBjoern A. Zeeb if (ret) 862bfcc09ddSBjoern A. Zeeb return ret; 863bfcc09ddSBjoern A. Zeeb 864bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_set_ndp_params(mvm, target); 865bfcc09ddSBjoern A. Zeeb 866bfcc09ddSBjoern A. Zeeb /* 867bfcc09ddSBjoern A. Zeeb * If secure LTF is turned off, replace the flag with PMF only 868bfcc09ddSBjoern A. Zeeb */ 869bfcc09ddSBjoern A. Zeeb flags = le32_to_cpu(target->initiator_ap_flags); 870*a4128aadSBjoern A. Zeeb if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) { 871*a4128aadSBjoern A. Zeeb if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF) 872bfcc09ddSBjoern A. Zeeb flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED; 873*a4128aadSBjoern A. Zeeb 874bfcc09ddSBjoern A. Zeeb flags |= IWL_INITIATOR_AP_FLAGS_PMF; 875bfcc09ddSBjoern A. Zeeb target->initiator_ap_flags = cpu_to_le32(flags); 876bfcc09ddSBjoern A. Zeeb } 877bfcc09ddSBjoern A. Zeeb 878bfcc09ddSBjoern A. Zeeb return 0; 879bfcc09ddSBjoern A. Zeeb } 880bfcc09ddSBjoern A. Zeeb 881bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v12(struct iwl_mvm *mvm, 882bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif, 883bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 884bfcc09ddSBjoern A. Zeeb { 885bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v12 cmd; 886bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 887d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 888bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 889bfcc09ddSBjoern A. Zeeb .data[0] = &cmd, 890bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd), 891bfcc09ddSBjoern A. Zeeb }; 892bfcc09ddSBjoern A. Zeeb u8 i; 893bfcc09ddSBjoern A. Zeeb int err; 894bfcc09ddSBjoern A. Zeeb 895bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req); 896bfcc09ddSBjoern A. Zeeb 897bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd.num_of_ap; i++) { 898bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 899bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v8 *target = &cmd.ap[i]; 900bfcc09ddSBjoern A. Zeeb 901bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, target); 902bfcc09ddSBjoern A. Zeeb if (err) 903bfcc09ddSBjoern A. Zeeb return err; 904bfcc09ddSBjoern A. Zeeb } 905bfcc09ddSBjoern A. Zeeb 906bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 907bfcc09ddSBjoern A. Zeeb } 908bfcc09ddSBjoern A. Zeeb 909bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm, 910bfcc09ddSBjoern A. Zeeb struct ieee80211_vif *vif, 911bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 912bfcc09ddSBjoern A. Zeeb { 913bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v13 cmd; 914bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 915d9836fb4SBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 916bfcc09ddSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 917bfcc09ddSBjoern A. Zeeb .data[0] = &cmd, 918bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd), 919bfcc09ddSBjoern A. Zeeb }; 920bfcc09ddSBjoern A. Zeeb u8 i; 921bfcc09ddSBjoern A. Zeeb int err; 922bfcc09ddSBjoern A. Zeeb 923bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req); 924bfcc09ddSBjoern A. Zeeb 925bfcc09ddSBjoern A. Zeeb for (i = 0; i < cmd.num_of_ap; i++) { 926bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 927bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v9 *target = &cmd.ap[i]; 928bfcc09ddSBjoern A. Zeeb 929bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, (void *)target); 930bfcc09ddSBjoern A. Zeeb if (err) 931bfcc09ddSBjoern A. Zeeb return err; 932bfcc09ddSBjoern A. Zeeb 933bfcc09ddSBjoern A. Zeeb if (peer->ftm.trigger_based || peer->ftm.non_trigger_based) 934bfcc09ddSBjoern A. Zeeb target->bss_color = peer->ftm.bss_color; 935bfcc09ddSBjoern A. Zeeb 936bfcc09ddSBjoern A. Zeeb if (peer->ftm.non_trigger_based) { 937bfcc09ddSBjoern A. Zeeb target->min_time_between_msr = 938bfcc09ddSBjoern A. Zeeb cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR); 939bfcc09ddSBjoern A. Zeeb target->burst_period = 940bfcc09ddSBjoern A. Zeeb cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR); 941bfcc09ddSBjoern A. Zeeb } else { 942bfcc09ddSBjoern A. Zeeb target->min_time_between_msr = cpu_to_le16(0); 943bfcc09ddSBjoern A. Zeeb } 944bfcc09ddSBjoern A. Zeeb 945bfcc09ddSBjoern A. Zeeb target->band = 946bfcc09ddSBjoern A. Zeeb iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band); 947bfcc09ddSBjoern A. Zeeb } 948bfcc09ddSBjoern A. Zeeb 949bfcc09ddSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 950bfcc09ddSBjoern A. Zeeb } 951bfcc09ddSBjoern A. Zeeb 952*a4128aadSBjoern A. Zeeb static int 953*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 954*a4128aadSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer, 955*a4128aadSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v10 *target) 956*a4128aadSBjoern A. Zeeb { 957*a4128aadSBjoern A. Zeeb u32 i2r_max_sts, flags; 958*a4128aadSBjoern A. Zeeb int ret; 959*a4128aadSBjoern A. Zeeb 960*a4128aadSBjoern A. Zeeb ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num, 961*a4128aadSBjoern A. Zeeb &target->format_bw, 962*a4128aadSBjoern A. Zeeb &target->ctrl_ch_position); 963*a4128aadSBjoern A. Zeeb if (ret) 964*a4128aadSBjoern A. Zeeb return ret; 965*a4128aadSBjoern A. Zeeb 966*a4128aadSBjoern A. Zeeb memcpy(target->bssid, peer->addr, ETH_ALEN); 967*a4128aadSBjoern A. Zeeb target->burst_period = 968*a4128aadSBjoern A. Zeeb cpu_to_le16(peer->ftm.burst_period); 969*a4128aadSBjoern A. Zeeb target->samples_per_burst = peer->ftm.ftms_per_burst; 970*a4128aadSBjoern A. Zeeb target->num_of_bursts = peer->ftm.num_bursts_exp; 971*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags); 972*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id, 973*a4128aadSBjoern A. Zeeb &target->initiator_ap_flags); 974*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid, 975*a4128aadSBjoern A. Zeeb &target->cipher, target->hltk, 976*a4128aadSBjoern A. Zeeb target->tk, target->rx_pn, 977*a4128aadSBjoern A. Zeeb target->tx_pn, 978*a4128aadSBjoern A. Zeeb &target->initiator_ap_flags); 979*a4128aadSBjoern A. Zeeb 980*a4128aadSBjoern A. Zeeb i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 : 981*a4128aadSBjoern A. Zeeb IWL_MVM_FTM_I2R_MAX_STS; 982*a4128aadSBjoern A. Zeeb 983*a4128aadSBjoern A. Zeeb target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP | 984*a4128aadSBjoern A. Zeeb (IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS) | 985*a4128aadSBjoern A. Zeeb (IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS); 986*a4128aadSBjoern A. Zeeb target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP | 987*a4128aadSBjoern A. Zeeb (i2r_max_sts << IWL_LOCATION_MAX_STS_POS) | 988*a4128aadSBjoern A. Zeeb (IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS); 989*a4128aadSBjoern A. Zeeb 990*a4128aadSBjoern A. Zeeb if (peer->ftm.non_trigger_based) { 991*a4128aadSBjoern A. Zeeb target->min_time_between_msr = 992*a4128aadSBjoern A. Zeeb cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR); 993*a4128aadSBjoern A. Zeeb target->burst_period = 994*a4128aadSBjoern A. Zeeb cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR); 995*a4128aadSBjoern A. Zeeb } else { 996*a4128aadSBjoern A. Zeeb target->min_time_between_msr = cpu_to_le16(0); 997*a4128aadSBjoern A. Zeeb } 998*a4128aadSBjoern A. Zeeb 999*a4128aadSBjoern A. Zeeb target->band = 1000*a4128aadSBjoern A. Zeeb iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band); 1001*a4128aadSBjoern A. Zeeb 1002*a4128aadSBjoern A. Zeeb /* 1003*a4128aadSBjoern A. Zeeb * TODO: Beacon interval is currently unknown, so use the common value 1004*a4128aadSBjoern A. Zeeb * of 100 TUs. 1005*a4128aadSBjoern A. Zeeb */ 1006*a4128aadSBjoern A. Zeeb target->beacon_interval = cpu_to_le16(100); 1007*a4128aadSBjoern A. Zeeb 1008*a4128aadSBjoern A. Zeeb /* 1009*a4128aadSBjoern A. Zeeb * If secure LTF is turned off, replace the flag with PMF only 1010*a4128aadSBjoern A. Zeeb */ 1011*a4128aadSBjoern A. Zeeb flags = le32_to_cpu(target->initiator_ap_flags); 1012*a4128aadSBjoern A. Zeeb if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) { 1013*a4128aadSBjoern A. Zeeb if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF) 1014*a4128aadSBjoern A. Zeeb flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED; 1015*a4128aadSBjoern A. Zeeb 1016*a4128aadSBjoern A. Zeeb flags |= IWL_INITIATOR_AP_FLAGS_PMF; 1017*a4128aadSBjoern A. Zeeb target->initiator_ap_flags = cpu_to_le32(flags); 1018*a4128aadSBjoern A. Zeeb } 1019*a4128aadSBjoern A. Zeeb 1020*a4128aadSBjoern A. Zeeb return 0; 1021*a4128aadSBjoern A. Zeeb } 1022*a4128aadSBjoern A. Zeeb 1023*a4128aadSBjoern A. Zeeb static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm, 1024*a4128aadSBjoern A. Zeeb struct ieee80211_vif *vif, 1025*a4128aadSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 1026*a4128aadSBjoern A. Zeeb { 1027*a4128aadSBjoern A. Zeeb struct iwl_tof_range_req_cmd_v14 cmd; 1028*a4128aadSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 1029*a4128aadSBjoern A. Zeeb .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 1030*a4128aadSBjoern A. Zeeb .dataflags[0] = IWL_HCMD_DFL_DUP, 1031*a4128aadSBjoern A. Zeeb .data[0] = &cmd, 1032*a4128aadSBjoern A. Zeeb .len[0] = sizeof(cmd), 1033*a4128aadSBjoern A. Zeeb }; 1034*a4128aadSBjoern A. Zeeb u8 i; 1035*a4128aadSBjoern A. Zeeb int err; 1036*a4128aadSBjoern A. Zeeb 1037*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req); 1038*a4128aadSBjoern A. Zeeb 1039*a4128aadSBjoern A. Zeeb for (i = 0; i < cmd.num_of_ap; i++) { 1040*a4128aadSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 1041*a4128aadSBjoern A. Zeeb struct iwl_tof_range_req_ap_entry_v10 *target = &cmd.ap[i]; 1042*a4128aadSBjoern A. Zeeb 1043*a4128aadSBjoern A. Zeeb err = iwl_mvm_ftm_put_target_v10(mvm, vif, peer, target); 1044*a4128aadSBjoern A. Zeeb if (err) 1045*a4128aadSBjoern A. Zeeb return err; 1046*a4128aadSBjoern A. Zeeb } 1047*a4128aadSBjoern A. Zeeb 1048*a4128aadSBjoern A. Zeeb return iwl_mvm_ftm_send_cmd(mvm, &hcmd); 1049*a4128aadSBjoern A. Zeeb } 1050*a4128aadSBjoern A. Zeeb 1051bfcc09ddSBjoern A. Zeeb int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 1052bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request *req) 1053bfcc09ddSBjoern A. Zeeb { 1054bfcc09ddSBjoern A. Zeeb bool new_api = fw_has_api(&mvm->fw->ucode_capa, 1055bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ); 1056bfcc09ddSBjoern A. Zeeb int err; 1057bfcc09ddSBjoern A. Zeeb 1058bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1059bfcc09ddSBjoern A. Zeeb 1060bfcc09ddSBjoern A. Zeeb if (mvm->ftm_initiator.req) 1061bfcc09ddSBjoern A. Zeeb return -EBUSY; 1062bfcc09ddSBjoern A. Zeeb 1063bfcc09ddSBjoern A. Zeeb if (new_api) { 1064d9836fb4SBjoern A. Zeeb u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, 1065d9836fb4SBjoern A. Zeeb WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), 1066bfcc09ddSBjoern A. Zeeb IWL_FW_CMD_VER_UNKNOWN); 1067bfcc09ddSBjoern A. Zeeb 1068bfcc09ddSBjoern A. Zeeb switch (cmd_ver) { 1069*a4128aadSBjoern A. Zeeb case 14: 1070*a4128aadSBjoern A. Zeeb err = iwl_mvm_ftm_start_v14(mvm, vif, req); 1071*a4128aadSBjoern A. Zeeb break; 1072bfcc09ddSBjoern A. Zeeb case 13: 1073bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v13(mvm, vif, req); 1074bfcc09ddSBjoern A. Zeeb break; 1075bfcc09ddSBjoern A. Zeeb case 12: 1076bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v12(mvm, vif, req); 1077bfcc09ddSBjoern A. Zeeb break; 1078bfcc09ddSBjoern A. Zeeb case 11: 1079bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v11(mvm, vif, req); 1080bfcc09ddSBjoern A. Zeeb break; 1081bfcc09ddSBjoern A. Zeeb case 9: 1082bfcc09ddSBjoern A. Zeeb case 10: 1083bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v9(mvm, vif, req); 1084bfcc09ddSBjoern A. Zeeb break; 1085bfcc09ddSBjoern A. Zeeb case 8: 1086bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v8(mvm, vif, req); 1087bfcc09ddSBjoern A. Zeeb break; 1088bfcc09ddSBjoern A. Zeeb default: 1089bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v7(mvm, vif, req); 1090bfcc09ddSBjoern A. Zeeb break; 1091bfcc09ddSBjoern A. Zeeb } 1092bfcc09ddSBjoern A. Zeeb } else { 1093bfcc09ddSBjoern A. Zeeb err = iwl_mvm_ftm_start_v5(mvm, vif, req); 1094bfcc09ddSBjoern A. Zeeb } 1095bfcc09ddSBjoern A. Zeeb 1096bfcc09ddSBjoern A. Zeeb if (!err) { 1097bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req = req; 1098bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif); 1099bfcc09ddSBjoern A. Zeeb } 1100bfcc09ddSBjoern A. Zeeb 1101bfcc09ddSBjoern A. Zeeb return err; 1102bfcc09ddSBjoern A. Zeeb } 1103bfcc09ddSBjoern A. Zeeb 1104bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req) 1105bfcc09ddSBjoern A. Zeeb { 1106bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_abort_cmd cmd = { 1107bfcc09ddSBjoern A. Zeeb .request_id = req->cookie, 1108bfcc09ddSBjoern A. Zeeb }; 1109bfcc09ddSBjoern A. Zeeb 1110bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1111bfcc09ddSBjoern A. Zeeb 1112bfcc09ddSBjoern A. Zeeb if (req != mvm->ftm_initiator.req) 1113bfcc09ddSBjoern A. Zeeb return; 1114bfcc09ddSBjoern A. Zeeb 1115bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_reset(mvm); 1116bfcc09ddSBjoern A. Zeeb 1117d9836fb4SBjoern A. Zeeb if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(LOCATION_GROUP, TOF_RANGE_ABORT_CMD), 1118bfcc09ddSBjoern A. Zeeb 0, sizeof(cmd), &cmd)) 1119bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "failed to abort FTM process\n"); 1120bfcc09ddSBjoern A. Zeeb } 1121bfcc09ddSBjoern A. Zeeb 1122bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req, 1123bfcc09ddSBjoern A. Zeeb const u8 *addr) 1124bfcc09ddSBjoern A. Zeeb { 1125bfcc09ddSBjoern A. Zeeb int i; 1126bfcc09ddSBjoern A. Zeeb 1127bfcc09ddSBjoern A. Zeeb for (i = 0; i < req->n_peers; i++) { 1128bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 1129bfcc09ddSBjoern A. Zeeb 1130bfcc09ddSBjoern A. Zeeb if (ether_addr_equal_unaligned(peer->addr, addr)) 1131bfcc09ddSBjoern A. Zeeb return i; 1132bfcc09ddSBjoern A. Zeeb } 1133bfcc09ddSBjoern A. Zeeb 1134bfcc09ddSBjoern A. Zeeb return -ENOENT; 1135bfcc09ddSBjoern A. Zeeb } 1136bfcc09ddSBjoern A. Zeeb 1137bfcc09ddSBjoern A. Zeeb static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts) 1138bfcc09ddSBjoern A. Zeeb { 1139bfcc09ddSBjoern A. Zeeb u32 gp2_ts = le32_to_cpu(fw_gp2_ts); 1140bfcc09ddSBjoern A. Zeeb u32 curr_gp2, diff; 1141bfcc09ddSBjoern A. Zeeb u64 now_from_boot_ns; 1142bfcc09ddSBjoern A. Zeeb 1143bfcc09ddSBjoern A. Zeeb iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2, 1144bfcc09ddSBjoern A. Zeeb &now_from_boot_ns, NULL); 1145bfcc09ddSBjoern A. Zeeb 1146bfcc09ddSBjoern A. Zeeb if (curr_gp2 >= gp2_ts) 1147bfcc09ddSBjoern A. Zeeb diff = curr_gp2 - gp2_ts; 1148bfcc09ddSBjoern A. Zeeb else 1149bfcc09ddSBjoern A. Zeeb diff = curr_gp2 + (U32_MAX - gp2_ts + 1); 1150bfcc09ddSBjoern A. Zeeb 1151bfcc09ddSBjoern A. Zeeb return now_from_boot_ns - (u64)diff * 1000; 1152bfcc09ddSBjoern A. Zeeb } 1153bfcc09ddSBjoern A. Zeeb 1154bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm, 1155bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_result *res) 1156bfcc09ddSBjoern A. Zeeb { 1157bfcc09ddSBjoern A. Zeeb struct iwl_mvm_loc_entry *entry; 1158bfcc09ddSBjoern A. Zeeb 1159bfcc09ddSBjoern A. Zeeb list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) { 1160bfcc09ddSBjoern A. Zeeb if (!ether_addr_equal_unaligned(res->addr, entry->addr)) 1161bfcc09ddSBjoern A. Zeeb continue; 1162bfcc09ddSBjoern A. Zeeb 1163bfcc09ddSBjoern A. Zeeb if (entry->lci_len) { 1164bfcc09ddSBjoern A. Zeeb res->ftm.lci_len = entry->lci_len; 1165bfcc09ddSBjoern A. Zeeb res->ftm.lci = entry->buf; 1166bfcc09ddSBjoern A. Zeeb } 1167bfcc09ddSBjoern A. Zeeb 1168bfcc09ddSBjoern A. Zeeb if (entry->civic_len) { 1169bfcc09ddSBjoern A. Zeeb res->ftm.civicloc_len = entry->civic_len; 1170bfcc09ddSBjoern A. Zeeb res->ftm.civicloc = entry->buf + entry->lci_len; 1171bfcc09ddSBjoern A. Zeeb } 1172bfcc09ddSBjoern A. Zeeb 1173bfcc09ddSBjoern A. Zeeb /* we found the entry we needed */ 1174bfcc09ddSBjoern A. Zeeb break; 1175bfcc09ddSBjoern A. Zeeb } 1176bfcc09ddSBjoern A. Zeeb } 1177bfcc09ddSBjoern A. Zeeb 1178bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id, 1179bfcc09ddSBjoern A. Zeeb u8 num_of_aps) 1180bfcc09ddSBjoern A. Zeeb { 1181bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1182bfcc09ddSBjoern A. Zeeb 1183bfcc09ddSBjoern A. Zeeb if (request_id != (u8)mvm->ftm_initiator.req->cookie) { 1184bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n", 1185bfcc09ddSBjoern A. Zeeb request_id, (u8)mvm->ftm_initiator.req->cookie); 1186bfcc09ddSBjoern A. Zeeb return -EINVAL; 1187bfcc09ddSBjoern A. Zeeb } 1188bfcc09ddSBjoern A. Zeeb 1189bfcc09ddSBjoern A. Zeeb if (num_of_aps > mvm->ftm_initiator.req->n_peers) { 1190bfcc09ddSBjoern A. Zeeb IWL_ERR(mvm, "FTM range response invalid\n"); 1191bfcc09ddSBjoern A. Zeeb return -EINVAL; 1192bfcc09ddSBjoern A. Zeeb } 1193bfcc09ddSBjoern A. Zeeb 1194bfcc09ddSBjoern A. Zeeb return 0; 1195bfcc09ddSBjoern A. Zeeb } 1196bfcc09ddSBjoern A. Zeeb 1197bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm, 1198bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_result *res) 1199bfcc09ddSBjoern A. Zeeb { 12009af1bba4SBjoern A. Zeeb struct iwl_mvm_smooth_entry *resp = NULL, *iter; 1201bfcc09ddSBjoern A. Zeeb s64 rtt_avg, rtt = res->ftm.rtt_avg; 1202bfcc09ddSBjoern A. Zeeb u32 undershoot, overshoot; 1203bfcc09ddSBjoern A. Zeeb u8 alpha; 1204bfcc09ddSBjoern A. Zeeb 1205bfcc09ddSBjoern A. Zeeb if (!IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH) 1206bfcc09ddSBjoern A. Zeeb return; 1207bfcc09ddSBjoern A. Zeeb 1208bfcc09ddSBjoern A. Zeeb WARN_ON(rtt < 0); 1209bfcc09ddSBjoern A. Zeeb 1210bfcc09ddSBjoern A. Zeeb if (res->status != NL80211_PMSR_STATUS_SUCCESS) { 1211bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1212bfcc09ddSBjoern A. Zeeb ": %pM: ignore failed measurement. Status=%u\n", 1213bfcc09ddSBjoern A. Zeeb res->addr, res->status); 1214bfcc09ddSBjoern A. Zeeb return; 1215bfcc09ddSBjoern A. Zeeb } 1216bfcc09ddSBjoern A. Zeeb 12179af1bba4SBjoern A. Zeeb list_for_each_entry(iter, &mvm->ftm_initiator.smooth.resp, list) { 12189af1bba4SBjoern A. Zeeb if (!memcmp(res->addr, iter->addr, ETH_ALEN)) { 12199af1bba4SBjoern A. Zeeb resp = iter; 1220bfcc09ddSBjoern A. Zeeb break; 1221bfcc09ddSBjoern A. Zeeb } 1222bfcc09ddSBjoern A. Zeeb } 1223bfcc09ddSBjoern A. Zeeb 12249af1bba4SBjoern A. Zeeb if (!resp) { 1225bfcc09ddSBjoern A. Zeeb resp = kzalloc(sizeof(*resp), GFP_KERNEL); 1226bfcc09ddSBjoern A. Zeeb if (!resp) 1227bfcc09ddSBjoern A. Zeeb return; 1228bfcc09ddSBjoern A. Zeeb 1229bfcc09ddSBjoern A. Zeeb memcpy(resp->addr, res->addr, ETH_ALEN); 1230bfcc09ddSBjoern A. Zeeb list_add_tail(&resp->list, &mvm->ftm_initiator.smooth.resp); 1231bfcc09ddSBjoern A. Zeeb 1232bfcc09ddSBjoern A. Zeeb resp->rtt_avg = rtt; 1233bfcc09ddSBjoern A. Zeeb 1234bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "new: %pM: rtt_avg=%lld\n", 1235bfcc09ddSBjoern A. Zeeb resp->addr, resp->rtt_avg); 1236bfcc09ddSBjoern A. Zeeb goto update_time; 1237bfcc09ddSBjoern A. Zeeb } 1238bfcc09ddSBjoern A. Zeeb 1239bfcc09ddSBjoern A. Zeeb if (res->host_time - resp->host_time > 1240bfcc09ddSBjoern A. Zeeb IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * 1000000000) { 1241bfcc09ddSBjoern A. Zeeb resp->rtt_avg = rtt; 1242bfcc09ddSBjoern A. Zeeb 1243bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "expired: %pM: rtt_avg=%lld\n", 1244bfcc09ddSBjoern A. Zeeb resp->addr, resp->rtt_avg); 1245bfcc09ddSBjoern A. Zeeb goto update_time; 1246bfcc09ddSBjoern A. Zeeb } 1247bfcc09ddSBjoern A. Zeeb 1248bfcc09ddSBjoern A. Zeeb /* Smooth the results based on the tracked RTT average */ 1249bfcc09ddSBjoern A. Zeeb undershoot = IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT; 1250bfcc09ddSBjoern A. Zeeb overshoot = IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT; 1251bfcc09ddSBjoern A. Zeeb alpha = IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA; 1252bfcc09ddSBjoern A. Zeeb 1253d9836fb4SBjoern A. Zeeb rtt_avg = div_s64(alpha * rtt + (100 - alpha) * resp->rtt_avg, 100); 1254bfcc09ddSBjoern A. Zeeb 1255bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1256bfcc09ddSBjoern A. Zeeb "%pM: prev rtt_avg=%lld, new rtt_avg=%lld, rtt=%lld\n", 1257bfcc09ddSBjoern A. Zeeb resp->addr, resp->rtt_avg, rtt_avg, rtt); 1258bfcc09ddSBjoern A. Zeeb 1259bfcc09ddSBjoern A. Zeeb /* 1260bfcc09ddSBjoern A. Zeeb * update the responder's average RTT results regardless of 1261bfcc09ddSBjoern A. Zeeb * the under/over shoot logic below 1262bfcc09ddSBjoern A. Zeeb */ 1263bfcc09ddSBjoern A. Zeeb resp->rtt_avg = rtt_avg; 1264bfcc09ddSBjoern A. Zeeb 1265bfcc09ddSBjoern A. Zeeb /* smooth the results */ 1266bfcc09ddSBjoern A. Zeeb if (rtt_avg > rtt && (rtt_avg - rtt) > undershoot) { 1267bfcc09ddSBjoern A. Zeeb res->ftm.rtt_avg = rtt_avg; 1268bfcc09ddSBjoern A. Zeeb 1269bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1270bfcc09ddSBjoern A. Zeeb "undershoot: val=%lld\n", 1271bfcc09ddSBjoern A. Zeeb (rtt_avg - rtt)); 1272bfcc09ddSBjoern A. Zeeb } else if (rtt_avg < rtt && (rtt - rtt_avg) > 1273bfcc09ddSBjoern A. Zeeb overshoot) { 1274bfcc09ddSBjoern A. Zeeb res->ftm.rtt_avg = rtt_avg; 1275bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, 1276bfcc09ddSBjoern A. Zeeb "overshoot: val=%lld\n", 1277bfcc09ddSBjoern A. Zeeb (rtt - rtt_avg)); 1278bfcc09ddSBjoern A. Zeeb } 1279bfcc09ddSBjoern A. Zeeb 1280bfcc09ddSBjoern A. Zeeb update_time: 1281bfcc09ddSBjoern A. Zeeb resp->host_time = res->host_time; 1282bfcc09ddSBjoern A. Zeeb } 1283bfcc09ddSBjoern A. Zeeb 1284bfcc09ddSBjoern A. Zeeb static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index, 1285bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_result *res) 1286bfcc09ddSBjoern A. Zeeb { 1287bfcc09ddSBjoern A. Zeeb s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666); 1288bfcc09ddSBjoern A. Zeeb 1289bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "entry %d\n", index); 1290bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status); 1291bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr); 1292bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time); 12939af1bba4SBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\tburst index: %d\n", res->ftm.burst_index); 1294bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes); 1295bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg); 12969af1bba4SBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\trssi spread: %d\n", res->ftm.rssi_spread); 1297bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg); 1298bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance); 1299bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread); 1300bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg); 1301bfcc09ddSBjoern A. Zeeb } 1302bfcc09ddSBjoern A. Zeeb 1303bfcc09ddSBjoern A. Zeeb static void 1304bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm, 1305bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap) 1306bfcc09ddSBjoern A. Zeeb { 1307bfcc09ddSBjoern A. Zeeb struct iwl_mvm_ftm_pasn_entry *entry; 1308bfcc09ddSBjoern A. Zeeb 1309bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1310bfcc09ddSBjoern A. Zeeb 1311bfcc09ddSBjoern A. Zeeb list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) { 1312bfcc09ddSBjoern A. Zeeb if (memcmp(fw_ap->bssid, entry->addr, sizeof(entry->addr))) 1313bfcc09ddSBjoern A. Zeeb continue; 1314bfcc09ddSBjoern A. Zeeb 1315bfcc09ddSBjoern A. Zeeb memcpy(entry->rx_pn, fw_ap->rx_pn, sizeof(entry->rx_pn)); 1316bfcc09ddSBjoern A. Zeeb memcpy(entry->tx_pn, fw_ap->tx_pn, sizeof(entry->tx_pn)); 1317bfcc09ddSBjoern A. Zeeb return; 1318bfcc09ddSBjoern A. Zeeb } 1319bfcc09ddSBjoern A. Zeeb } 1320bfcc09ddSBjoern A. Zeeb 1321bfcc09ddSBjoern A. Zeeb static u8 iwl_mvm_ftm_get_range_resp_ver(struct iwl_mvm *mvm) 1322bfcc09ddSBjoern A. Zeeb { 1323bfcc09ddSBjoern A. Zeeb if (!fw_has_api(&mvm->fw->ucode_capa, 1324bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ)) 1325bfcc09ddSBjoern A. Zeeb return 5; 1326bfcc09ddSBjoern A. Zeeb 1327bfcc09ddSBjoern A. Zeeb /* Starting from version 8, the FW advertises the version */ 1328bfcc09ddSBjoern A. Zeeb if (mvm->cmd_ver.range_resp >= 8) 1329bfcc09ddSBjoern A. Zeeb return mvm->cmd_ver.range_resp; 1330bfcc09ddSBjoern A. Zeeb else if (fw_has_api(&mvm->fw->ucode_capa, 1331bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) 1332bfcc09ddSBjoern A. Zeeb return 7; 1333bfcc09ddSBjoern A. Zeeb 1334bfcc09ddSBjoern A. Zeeb /* The first version of the new range request API */ 1335bfcc09ddSBjoern A. Zeeb return 6; 1336bfcc09ddSBjoern A. Zeeb } 1337bfcc09ddSBjoern A. Zeeb 1338bfcc09ddSBjoern A. Zeeb static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len) 1339bfcc09ddSBjoern A. Zeeb { 1340bfcc09ddSBjoern A. Zeeb switch (ver) { 1341bfcc09ddSBjoern A. Zeeb case 9: 1342bfcc09ddSBjoern A. Zeeb case 8: 1343bfcc09ddSBjoern A. Zeeb return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8); 1344bfcc09ddSBjoern A. Zeeb case 7: 1345bfcc09ddSBjoern A. Zeeb return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7); 1346bfcc09ddSBjoern A. Zeeb case 6: 1347bfcc09ddSBjoern A. Zeeb return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v6); 1348bfcc09ddSBjoern A. Zeeb case 5: 1349bfcc09ddSBjoern A. Zeeb return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v5); 1350bfcc09ddSBjoern A. Zeeb default: 1351bfcc09ddSBjoern A. Zeeb WARN_ONCE(1, "FTM: unsupported range response version %u", ver); 1352bfcc09ddSBjoern A. Zeeb return false; 1353bfcc09ddSBjoern A. Zeeb } 1354bfcc09ddSBjoern A. Zeeb } 1355bfcc09ddSBjoern A. Zeeb 1356bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 1357bfcc09ddSBjoern A. Zeeb { 1358bfcc09ddSBjoern A. Zeeb struct iwl_rx_packet *pkt = rxb_addr(rxb); 1359bfcc09ddSBjoern A. Zeeb unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); 1360bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data; 1361bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data; 1362bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data; 1363bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data; 1364bfcc09ddSBjoern A. Zeeb int i; 1365bfcc09ddSBjoern A. Zeeb bool new_api = fw_has_api(&mvm->fw->ucode_capa, 1366bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ); 1367bfcc09ddSBjoern A. Zeeb u8 num_of_aps, last_in_batch; 1368bfcc09ddSBjoern A. Zeeb u8 notif_ver = iwl_mvm_ftm_get_range_resp_ver(mvm); 1369bfcc09ddSBjoern A. Zeeb 1370bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1371bfcc09ddSBjoern A. Zeeb 1372bfcc09ddSBjoern A. Zeeb if (!mvm->ftm_initiator.req) { 1373bfcc09ddSBjoern A. Zeeb return; 1374bfcc09ddSBjoern A. Zeeb } 1375bfcc09ddSBjoern A. Zeeb 1376bfcc09ddSBjoern A. Zeeb if (unlikely(!iwl_mvm_ftm_resp_size_validation(notif_ver, pkt_len))) 1377bfcc09ddSBjoern A. Zeeb return; 1378bfcc09ddSBjoern A. Zeeb 1379bfcc09ddSBjoern A. Zeeb if (new_api) { 1380bfcc09ddSBjoern A. Zeeb if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v8->request_id, 1381bfcc09ddSBjoern A. Zeeb fw_resp_v8->num_of_aps)) 1382bfcc09ddSBjoern A. Zeeb return; 1383bfcc09ddSBjoern A. Zeeb 1384bfcc09ddSBjoern A. Zeeb num_of_aps = fw_resp_v8->num_of_aps; 1385bfcc09ddSBjoern A. Zeeb last_in_batch = fw_resp_v8->last_report; 1386bfcc09ddSBjoern A. Zeeb } else { 1387bfcc09ddSBjoern A. Zeeb if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id, 1388bfcc09ddSBjoern A. Zeeb fw_resp_v5->num_of_aps)) 1389bfcc09ddSBjoern A. Zeeb return; 1390bfcc09ddSBjoern A. Zeeb 1391bfcc09ddSBjoern A. Zeeb num_of_aps = fw_resp_v5->num_of_aps; 1392bfcc09ddSBjoern A. Zeeb last_in_batch = fw_resp_v5->last_in_batch; 1393bfcc09ddSBjoern A. Zeeb } 1394bfcc09ddSBjoern A. Zeeb 1395bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "Range response received\n"); 13969af1bba4SBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n", 1397bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req->cookie, num_of_aps); 1398bfcc09ddSBjoern A. Zeeb 1399bfcc09ddSBjoern A. Zeeb for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) { 1400bfcc09ddSBjoern A. Zeeb struct cfg80211_pmsr_result result = {}; 1401bfcc09ddSBjoern A. Zeeb struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap; 1402bfcc09ddSBjoern A. Zeeb int peer_idx; 1403bfcc09ddSBjoern A. Zeeb 1404bfcc09ddSBjoern A. Zeeb if (new_api) { 1405bfcc09ddSBjoern A. Zeeb if (notif_ver >= 8) { 1406bfcc09ddSBjoern A. Zeeb fw_ap = &fw_resp_v8->ap[i]; 1407bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_pasn_update_pn(mvm, fw_ap); 1408bfcc09ddSBjoern A. Zeeb } else if (notif_ver == 7) { 1409bfcc09ddSBjoern A. Zeeb fw_ap = (void *)&fw_resp_v7->ap[i]; 1410bfcc09ddSBjoern A. Zeeb } else { 1411bfcc09ddSBjoern A. Zeeb fw_ap = (void *)&fw_resp_v6->ap[i]; 1412bfcc09ddSBjoern A. Zeeb } 1413bfcc09ddSBjoern A. Zeeb 1414bfcc09ddSBjoern A. Zeeb result.final = fw_ap->last_burst; 1415bfcc09ddSBjoern A. Zeeb result.ap_tsf = le32_to_cpu(fw_ap->start_tsf); 1416bfcc09ddSBjoern A. Zeeb result.ap_tsf_valid = 1; 1417bfcc09ddSBjoern A. Zeeb } else { 1418bfcc09ddSBjoern A. Zeeb /* the first part is the same for old and new APIs */ 1419bfcc09ddSBjoern A. Zeeb fw_ap = (void *)&fw_resp_v5->ap[i]; 1420bfcc09ddSBjoern A. Zeeb /* 1421bfcc09ddSBjoern A. Zeeb * FIXME: the firmware needs to report this, we don't 1422bfcc09ddSBjoern A. Zeeb * even know the number of bursts the responder picked 1423bfcc09ddSBjoern A. Zeeb * (if we asked it to) 1424bfcc09ddSBjoern A. Zeeb */ 1425bfcc09ddSBjoern A. Zeeb result.final = 0; 1426bfcc09ddSBjoern A. Zeeb } 1427bfcc09ddSBjoern A. Zeeb 1428bfcc09ddSBjoern A. Zeeb peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req, 1429bfcc09ddSBjoern A. Zeeb fw_ap->bssid); 1430bfcc09ddSBjoern A. Zeeb if (peer_idx < 0) { 1431bfcc09ddSBjoern A. Zeeb IWL_WARN(mvm, 1432bfcc09ddSBjoern A. Zeeb "Unknown address (%pM, target #%d) in FTM response\n", 1433bfcc09ddSBjoern A. Zeeb fw_ap->bssid, i); 1434bfcc09ddSBjoern A. Zeeb continue; 1435bfcc09ddSBjoern A. Zeeb } 1436bfcc09ddSBjoern A. Zeeb 1437bfcc09ddSBjoern A. Zeeb switch (fw_ap->measure_status) { 1438bfcc09ddSBjoern A. Zeeb case IWL_TOF_ENTRY_SUCCESS: 1439bfcc09ddSBjoern A. Zeeb result.status = NL80211_PMSR_STATUS_SUCCESS; 1440bfcc09ddSBjoern A. Zeeb break; 1441bfcc09ddSBjoern A. Zeeb case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT: 1442bfcc09ddSBjoern A. Zeeb result.status = NL80211_PMSR_STATUS_TIMEOUT; 1443bfcc09ddSBjoern A. Zeeb break; 1444bfcc09ddSBjoern A. Zeeb case IWL_TOF_ENTRY_NO_RESPONSE: 1445bfcc09ddSBjoern A. Zeeb result.status = NL80211_PMSR_STATUS_FAILURE; 1446bfcc09ddSBjoern A. Zeeb result.ftm.failure_reason = 1447bfcc09ddSBjoern A. Zeeb NL80211_PMSR_FTM_FAILURE_NO_RESPONSE; 1448bfcc09ddSBjoern A. Zeeb break; 1449bfcc09ddSBjoern A. Zeeb case IWL_TOF_ENTRY_REQUEST_REJECTED: 1450bfcc09ddSBjoern A. Zeeb result.status = NL80211_PMSR_STATUS_FAILURE; 1451bfcc09ddSBjoern A. Zeeb result.ftm.failure_reason = 1452bfcc09ddSBjoern A. Zeeb NL80211_PMSR_FTM_FAILURE_PEER_BUSY; 1453bfcc09ddSBjoern A. Zeeb result.ftm.busy_retry_time = fw_ap->refusal_period; 1454bfcc09ddSBjoern A. Zeeb break; 1455bfcc09ddSBjoern A. Zeeb default: 1456bfcc09ddSBjoern A. Zeeb result.status = NL80211_PMSR_STATUS_FAILURE; 1457bfcc09ddSBjoern A. Zeeb result.ftm.failure_reason = 1458bfcc09ddSBjoern A. Zeeb NL80211_PMSR_FTM_FAILURE_UNSPECIFIED; 1459bfcc09ddSBjoern A. Zeeb break; 1460bfcc09ddSBjoern A. Zeeb } 1461bfcc09ddSBjoern A. Zeeb memcpy(result.addr, fw_ap->bssid, ETH_ALEN); 1462bfcc09ddSBjoern A. Zeeb result.host_time = iwl_mvm_ftm_get_host_time(mvm, 1463bfcc09ddSBjoern A. Zeeb fw_ap->timestamp); 1464bfcc09ddSBjoern A. Zeeb result.type = NL80211_PMSR_TYPE_FTM; 1465bfcc09ddSBjoern A. Zeeb result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx]; 1466bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.responses[peer_idx]++; 1467bfcc09ddSBjoern A. Zeeb result.ftm.rssi_avg = fw_ap->rssi; 1468bfcc09ddSBjoern A. Zeeb result.ftm.rssi_avg_valid = 1; 1469bfcc09ddSBjoern A. Zeeb result.ftm.rssi_spread = fw_ap->rssi_spread; 1470bfcc09ddSBjoern A. Zeeb result.ftm.rssi_spread_valid = 1; 1471bfcc09ddSBjoern A. Zeeb result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt); 1472bfcc09ddSBjoern A. Zeeb result.ftm.rtt_avg_valid = 1; 1473bfcc09ddSBjoern A. Zeeb result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance); 1474bfcc09ddSBjoern A. Zeeb result.ftm.rtt_variance_valid = 1; 1475bfcc09ddSBjoern A. Zeeb result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread); 1476bfcc09ddSBjoern A. Zeeb result.ftm.rtt_spread_valid = 1; 1477bfcc09ddSBjoern A. Zeeb 1478bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_get_lci_civic(mvm, &result); 1479bfcc09ddSBjoern A. Zeeb 1480bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_rtt_smoothing(mvm, &result); 1481bfcc09ddSBjoern A. Zeeb 1482bfcc09ddSBjoern A. Zeeb cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev, 1483bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req, 1484bfcc09ddSBjoern A. Zeeb &result, GFP_KERNEL); 1485bfcc09ddSBjoern A. Zeeb 1486bfcc09ddSBjoern A. Zeeb if (fw_has_api(&mvm->fw->ucode_capa, 1487bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) 14889af1bba4SBjoern A. Zeeb IWL_DEBUG_INFO(mvm, "RTT confidence: %u\n", 1489bfcc09ddSBjoern A. Zeeb fw_ap->rttConfidence); 1490bfcc09ddSBjoern A. Zeeb 1491bfcc09ddSBjoern A. Zeeb iwl_mvm_debug_range_resp(mvm, i, &result); 1492bfcc09ddSBjoern A. Zeeb } 1493bfcc09ddSBjoern A. Zeeb 1494bfcc09ddSBjoern A. Zeeb if (last_in_batch) { 1495bfcc09ddSBjoern A. Zeeb cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev, 1496bfcc09ddSBjoern A. Zeeb mvm->ftm_initiator.req, 1497bfcc09ddSBjoern A. Zeeb GFP_KERNEL); 1498bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_reset(mvm); 1499bfcc09ddSBjoern A. Zeeb } 1500bfcc09ddSBjoern A. Zeeb } 1501bfcc09ddSBjoern A. Zeeb 1502bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 1503bfcc09ddSBjoern A. Zeeb { 1504bfcc09ddSBjoern A. Zeeb struct iwl_rx_packet *pkt = rxb_addr(rxb); 1505bfcc09ddSBjoern A. Zeeb const struct ieee80211_mgmt *mgmt = (void *)pkt->data; 1506bfcc09ddSBjoern A. Zeeb size_t len = iwl_rx_packet_payload_len(pkt); 1507bfcc09ddSBjoern A. Zeeb struct iwl_mvm_loc_entry *entry; 1508bfcc09ddSBjoern A. Zeeb const u8 *ies, *lci, *civic, *msr_ie; 1509bfcc09ddSBjoern A. Zeeb size_t ies_len, lci_len = 0, civic_len = 0; 1510bfcc09ddSBjoern A. Zeeb size_t baselen = IEEE80211_MIN_ACTION_SIZE + 1511bfcc09ddSBjoern A. Zeeb sizeof(mgmt->u.action.u.ftm); 1512bfcc09ddSBjoern A. Zeeb static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI; 1513bfcc09ddSBjoern A. Zeeb static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC; 1514bfcc09ddSBjoern A. Zeeb 1515bfcc09ddSBjoern A. Zeeb if (len <= baselen) 1516bfcc09ddSBjoern A. Zeeb return; 1517bfcc09ddSBjoern A. Zeeb 1518bfcc09ddSBjoern A. Zeeb lockdep_assert_held(&mvm->mutex); 1519bfcc09ddSBjoern A. Zeeb 1520bfcc09ddSBjoern A. Zeeb ies = mgmt->u.action.u.ftm.variable; 1521bfcc09ddSBjoern A. Zeeb ies_len = len - baselen; 1522bfcc09ddSBjoern A. Zeeb 1523bfcc09ddSBjoern A. Zeeb msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len, 1524bfcc09ddSBjoern A. Zeeb &rprt_type_lci, 1, 4); 1525bfcc09ddSBjoern A. Zeeb if (msr_ie) { 1526bfcc09ddSBjoern A. Zeeb lci = msr_ie + 2; 1527bfcc09ddSBjoern A. Zeeb lci_len = msr_ie[1]; 1528bfcc09ddSBjoern A. Zeeb } 1529bfcc09ddSBjoern A. Zeeb 1530bfcc09ddSBjoern A. Zeeb msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len, 1531bfcc09ddSBjoern A. Zeeb &rprt_type_civic, 1, 4); 1532bfcc09ddSBjoern A. Zeeb if (msr_ie) { 1533bfcc09ddSBjoern A. Zeeb civic = msr_ie + 2; 1534bfcc09ddSBjoern A. Zeeb civic_len = msr_ie[1]; 1535bfcc09ddSBjoern A. Zeeb } 1536bfcc09ddSBjoern A. Zeeb 1537bfcc09ddSBjoern A. Zeeb entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL); 1538bfcc09ddSBjoern A. Zeeb if (!entry) 1539bfcc09ddSBjoern A. Zeeb return; 1540bfcc09ddSBjoern A. Zeeb 1541bfcc09ddSBjoern A. Zeeb memcpy(entry->addr, mgmt->bssid, ETH_ALEN); 1542bfcc09ddSBjoern A. Zeeb 1543bfcc09ddSBjoern A. Zeeb entry->lci_len = lci_len; 1544bfcc09ddSBjoern A. Zeeb if (lci_len) 1545bfcc09ddSBjoern A. Zeeb memcpy(entry->buf, lci, lci_len); 1546bfcc09ddSBjoern A. Zeeb 1547bfcc09ddSBjoern A. Zeeb entry->civic_len = civic_len; 1548bfcc09ddSBjoern A. Zeeb if (civic_len) 1549bfcc09ddSBjoern A. Zeeb memcpy(entry->buf + lci_len, civic, civic_len); 1550bfcc09ddSBjoern A. Zeeb 1551bfcc09ddSBjoern A. Zeeb list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list); 1552bfcc09ddSBjoern A. Zeeb } 1553