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