Path: blob/main/sys/contrib/dev/iwlwifi/mvm/ftm-initiator.c
48286 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2015-2017 Intel Deutschland GmbH3* Copyright (C) 2018-2025 Intel Corporation4*/5#include <linux/etherdevice.h>6#include <linux/math64.h>7#include <net/cfg80211.h>8#include "mvm.h"9#include "iwl-io.h"10#include "iwl-prph.h"11#include "constants.h"1213struct iwl_mvm_loc_entry {14struct list_head list;15u8 addr[ETH_ALEN];16u8 lci_len, civic_len;17u8 buf[];18};1920struct iwl_mvm_smooth_entry {21struct list_head list;22u8 addr[ETH_ALEN];23s64 rtt_avg;24u64 host_time;25};2627enum iwl_mvm_pasn_flags {28IWL_MVM_PASN_FLAG_HAS_HLTK = BIT(0),29};3031struct iwl_mvm_ftm_pasn_entry {32struct list_head list;33u8 addr[ETH_ALEN];34u8 hltk[HLTK_11AZ_LEN];35u8 tk[TK_11AZ_LEN];36u8 cipher;37u8 tx_pn[IEEE80211_CCMP_PN_LEN];38u8 rx_pn[IEEE80211_CCMP_PN_LEN];39u32 flags;40};4142struct iwl_mvm_ftm_iter_data {43u8 *cipher;44u8 *bssid;45u8 *tk;46};4748static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm)49{50struct iwl_mvm_loc_entry *e, *t;5152mvm->ftm_initiator.req = NULL;53mvm->ftm_initiator.req_wdev = NULL;54memset(mvm->ftm_initiator.responses, 0,55sizeof(mvm->ftm_initiator.responses));5657list_for_each_entry_safe(e, t, &mvm->ftm_initiator.loc_list, list) {58list_del(&e->list);59kfree(e);60}61}6263void iwl_mvm_ftm_restart(struct iwl_mvm *mvm)64{65struct cfg80211_pmsr_result result = {66.status = NL80211_PMSR_STATUS_FAILURE,67.final = 1,68.host_time = ktime_get_boottime_ns(),69.type = NL80211_PMSR_TYPE_FTM,70};71int i;7273lockdep_assert_held(&mvm->mutex);7475if (!mvm->ftm_initiator.req)76return;7778for (i = 0; i < mvm->ftm_initiator.req->n_peers; i++) {79memcpy(result.addr, mvm->ftm_initiator.req->peers[i].addr,80ETH_ALEN);81result.ftm.burst_index = mvm->ftm_initiator.responses[i];8283cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,84mvm->ftm_initiator.req,85&result, GFP_KERNEL);86}8788cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,89mvm->ftm_initiator.req, GFP_KERNEL);90iwl_mvm_ftm_reset(mvm);91}9293void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm)94{95INIT_LIST_HEAD(&mvm->ftm_initiator.smooth.resp);9697IWL_DEBUG_INFO(mvm,98"enable=%u, alpha=%u, age_jiffies=%u, thresh=(%u:%u)\n",99IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH,100IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA,101IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * HZ,102IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT,103IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT);104}105106void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm)107{108struct iwl_mvm_smooth_entry *se, *st;109110list_for_each_entry_safe(se, st, &mvm->ftm_initiator.smooth.resp,111list) {112list_del(&se->list);113kfree(se);114}115}116117static int118iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s)119{120switch (s) {121case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS:122return 0;123case IWL_TOF_RANGE_REQUEST_STATUS_BUSY:124return -EBUSY;125default:126WARN_ON_ONCE(1);127return -EIO;128}129}130131static void iwl_mvm_ftm_cmd_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,132struct iwl_tof_range_req_cmd_v5 *cmd,133struct cfg80211_pmsr_request *req)134{135int i;136137cmd->request_id = req->cookie;138cmd->num_of_ap = req->n_peers;139140/* use maximum for "no timeout" or bigger than what we can do */141if (!req->timeout || req->timeout > 255 * 100)142cmd->req_timeout = 255;143else144cmd->req_timeout = DIV_ROUND_UP(req->timeout, 100);145146/*147* We treat it always as random, since if not we'll148* have filled our local address there instead.149*/150cmd->macaddr_random = 1;151memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);152for (i = 0; i < ETH_ALEN; i++)153cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];154155if (vif->cfg.assoc)156memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);157else158eth_broadcast_addr(cmd->range_req_bssid);159}160161static void iwl_mvm_ftm_cmd_common(struct iwl_mvm *mvm,162struct ieee80211_vif *vif,163#if defined(__linux__)164struct iwl_tof_range_req_cmd_v9 *cmd,165#elif defined(__FreeBSD__)166struct iwl_tof_range_req_cmd_v9 *cmd, /* XXX-BZ Probably better solved by a common struct in fw for top parts of the struct. */167#endif168struct cfg80211_pmsr_request *req)169{170int i;171172cmd->initiator_flags =173cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM |174IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT);175cmd->request_id = req->cookie;176cmd->num_of_ap = req->n_peers;177178/*179* Use a large value for "no timeout". Don't use the maximum value180* because of fw limitations.181*/182if (req->timeout)183cmd->req_timeout_ms = cpu_to_le32(req->timeout);184else185cmd->req_timeout_ms = cpu_to_le32(0xfffff);186187memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);188for (i = 0; i < ETH_ALEN; i++)189cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];190191if (vif->cfg.assoc) {192memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);193194/* AP's TSF is only relevant if associated */195for (i = 0; i < req->n_peers; i++) {196if (req->peers[i].report_ap_tsf) {197struct iwl_mvm_vif *mvmvif =198iwl_mvm_vif_from_mac80211(vif);199200cmd->tsf_mac_id = cpu_to_le32(mvmvif->id);201return;202}203}204} else {205eth_broadcast_addr(cmd->range_req_bssid);206}207208/* Don't report AP's TSF */209cmd->tsf_mac_id = cpu_to_le32(0xff);210}211212static void iwl_mvm_ftm_cmd_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,213struct iwl_tof_range_req_cmd_v8 *cmd,214struct cfg80211_pmsr_request *req)215{216iwl_mvm_ftm_cmd_common(mvm, vif, (void *)cmd, req);217}218219static int220iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm *mvm,221struct cfg80211_pmsr_request_peer *peer,222u8 *channel, u8 *bandwidth,223u8 *ctrl_ch_position)224{225u32 freq = peer->chandef.chan->center_freq;226227*channel = ieee80211_frequency_to_channel(freq);228229switch (peer->chandef.width) {230case NL80211_CHAN_WIDTH_20_NOHT:231*bandwidth = IWL_TOF_BW_20_LEGACY;232break;233case NL80211_CHAN_WIDTH_20:234*bandwidth = IWL_TOF_BW_20_HT;235break;236case NL80211_CHAN_WIDTH_40:237*bandwidth = IWL_TOF_BW_40;238break;239case NL80211_CHAN_WIDTH_80:240*bandwidth = IWL_TOF_BW_80;241break;242default:243IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",244peer->chandef.width);245return -EINVAL;246}247248*ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?249iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;250251return 0;252}253254static int255iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm,256struct cfg80211_pmsr_request_peer *peer,257u8 *channel, u8 *format_bw,258u8 *ctrl_ch_position)259{260u32 freq = peer->chandef.chan->center_freq;261u8 cmd_ver;262263*channel = ieee80211_frequency_to_channel(freq);264265switch (peer->chandef.width) {266case NL80211_CHAN_WIDTH_20_NOHT:267*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;268*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;269break;270case NL80211_CHAN_WIDTH_20:271*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;272*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;273break;274case NL80211_CHAN_WIDTH_40:275*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;276*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;277break;278case NL80211_CHAN_WIDTH_80:279*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;280*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;281break;282case NL80211_CHAN_WIDTH_160:283cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,284WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),285IWL_FW_CMD_VER_UNKNOWN);286287if (cmd_ver >= 13) {288*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;289*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;290break;291}292fallthrough;293default:294IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",295peer->chandef.width);296return -EINVAL;297}298299/* non EDCA based measurement must use HE preamble */300if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)301*format_bw |= IWL_LOCATION_FRAME_FORMAT_HE;302303*ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?304iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;305306return 0;307}308309static int310iwl_mvm_ftm_put_target_v2(struct iwl_mvm *mvm,311struct cfg80211_pmsr_request_peer *peer,312struct iwl_tof_range_req_ap_entry_v2 *target)313{314int ret;315316ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,317&target->bandwidth,318&target->ctrl_ch_position);319if (ret)320return ret;321322memcpy(target->bssid, peer->addr, ETH_ALEN);323target->burst_period =324cpu_to_le16(peer->ftm.burst_period);325target->samples_per_burst = peer->ftm.ftms_per_burst;326target->num_of_bursts = peer->ftm.num_bursts_exp;327target->measure_type = 0; /* regular two-sided FTM */328target->retries_per_sample = peer->ftm.ftmr_retries;329target->asap_mode = peer->ftm.asap;330target->enable_dyn_ack = IWL_MVM_FTM_INITIATOR_DYNACK;331332if (peer->ftm.request_lci)333target->location_req |= IWL_TOF_LOC_LCI;334if (peer->ftm.request_civicloc)335target->location_req |= IWL_TOF_LOC_CIVIC;336337target->algo_type = IWL_MVM_FTM_INITIATOR_ALGO;338339return 0;340}341342#define FTM_SET_FLAG(flag) (*flags |= \343cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))344345static void346iwl_mvm_ftm_set_target_flags(struct iwl_mvm *mvm,347struct cfg80211_pmsr_request_peer *peer,348__le32 *flags)349{350*flags = cpu_to_le32(0);351352if (peer->ftm.asap)353FTM_SET_FLAG(ASAP);354355if (peer->ftm.request_lci)356FTM_SET_FLAG(LCI_REQUEST);357358if (peer->ftm.request_civicloc)359FTM_SET_FLAG(CIVIC_REQUEST);360361if (IWL_MVM_FTM_INITIATOR_DYNACK)362FTM_SET_FLAG(DYN_ACK);363364if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG)365FTM_SET_FLAG(ALGO_LR);366else if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT)367FTM_SET_FLAG(ALGO_FFT);368369if (peer->ftm.trigger_based)370FTM_SET_FLAG(TB);371else if (peer->ftm.non_trigger_based)372FTM_SET_FLAG(NON_TB);373374if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) &&375peer->ftm.lmr_feedback)376FTM_SET_FLAG(LMR_FEEDBACK);377}378379static void380iwl_mvm_ftm_put_target_common(struct iwl_mvm *mvm,381struct cfg80211_pmsr_request_peer *peer,382struct iwl_tof_range_req_ap_entry_v6 *target)383{384memcpy(target->bssid, peer->addr, ETH_ALEN);385target->burst_period =386cpu_to_le16(peer->ftm.burst_period);387target->samples_per_burst = peer->ftm.ftms_per_burst;388target->num_of_bursts = peer->ftm.num_bursts_exp;389target->ftmr_max_retries = peer->ftm.ftmr_retries;390iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags);391}392393static int394iwl_mvm_ftm_put_target_v3(struct iwl_mvm *mvm,395struct cfg80211_pmsr_request_peer *peer,396struct iwl_tof_range_req_ap_entry_v3 *target)397{398int ret;399400ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,401&target->bandwidth,402&target->ctrl_ch_position);403if (ret)404return ret;405406/*407* Versions 3 and 4 has some common fields, so408* iwl_mvm_ftm_put_target_common() can be used for version 7 too.409*/410iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);411412return 0;413}414415static int416iwl_mvm_ftm_put_target_v4(struct iwl_mvm *mvm,417struct cfg80211_pmsr_request_peer *peer,418struct iwl_tof_range_req_ap_entry_v4 *target)419{420int ret;421422ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,423&target->format_bw,424&target->ctrl_ch_position);425if (ret)426return ret;427428iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);429430return 0;431}432433static int iwl_mvm_ftm_set_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,434struct cfg80211_pmsr_request_peer *peer,435u8 *sta_id, __le32 *flags)436{437if (vif->cfg.assoc) {438struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);439struct ieee80211_sta *sta;440struct ieee80211_bss_conf *link_conf;441unsigned int link_id;442443rcu_read_lock();444for_each_vif_active_link(vif, link_conf, link_id) {445if (memcmp(peer->addr, link_conf->bssid, ETH_ALEN))446continue;447448*sta_id = mvmvif->link[link_id]->ap_sta_id;449sta = rcu_dereference(mvm->fw_id_to_mac_id[*sta_id]);450if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {451rcu_read_unlock();452return PTR_ERR_OR_ZERO(sta);453}454455if (sta->mfp && (peer->ftm.trigger_based ||456peer->ftm.non_trigger_based))457FTM_SET_FLAG(PMF);458break;459}460rcu_read_unlock();461462#ifdef CONFIG_IWLWIFI_DEBUGFS463if (mvmvif->ftm_unprotected) {464*sta_id = IWL_INVALID_STA;465*flags &= ~cpu_to_le32(IWL_INITIATOR_AP_FLAGS_PMF);466}467#endif468} else {469*sta_id = IWL_INVALID_STA;470}471472return 0;473}474475static int476iwl_mvm_ftm_put_target(struct iwl_mvm *mvm, struct ieee80211_vif *vif,477struct cfg80211_pmsr_request_peer *peer,478struct iwl_tof_range_req_ap_entry_v6 *target)479{480int ret;481482ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,483&target->format_bw,484&target->ctrl_ch_position);485if (ret)486return ret;487488iwl_mvm_ftm_put_target_common(mvm, peer, target);489490iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id,491&target->initiator_ap_flags);492493/*494* TODO: Beacon interval is currently unknown, so use the common value495* of 100 TUs.496*/497target->beacon_interval = cpu_to_le16(100);498return 0;499}500501static int iwl_mvm_ftm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *hcmd)502{503u32 status;504int err = iwl_mvm_send_cmd_status(mvm, hcmd, &status);505506if (!err && status) {507IWL_ERR(mvm, "FTM range request command failure, status: %u\n",508status);509err = iwl_ftm_range_request_status_to_err(status);510}511512return err;513}514515static int iwl_mvm_ftm_start_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,516struct cfg80211_pmsr_request *req)517{518struct iwl_tof_range_req_cmd_v5 cmd_v5;519struct iwl_host_cmd hcmd = {520.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),521.dataflags[0] = IWL_HCMD_DFL_DUP,522.data[0] = &cmd_v5,523.len[0] = sizeof(cmd_v5),524};525u8 i;526int err;527528iwl_mvm_ftm_cmd_v5(mvm, vif, &cmd_v5, req);529530for (i = 0; i < cmd_v5.num_of_ap; i++) {531struct cfg80211_pmsr_request_peer *peer = &req->peers[i];532533err = iwl_mvm_ftm_put_target_v2(mvm, peer, &cmd_v5.ap[i]);534if (err)535return err;536}537538return iwl_mvm_ftm_send_cmd(mvm, &hcmd);539}540541static int iwl_mvm_ftm_start_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,542struct cfg80211_pmsr_request *req)543{544struct iwl_tof_range_req_cmd_v7 cmd_v7;545struct iwl_host_cmd hcmd = {546.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),547.dataflags[0] = IWL_HCMD_DFL_DUP,548.data[0] = &cmd_v7,549.len[0] = sizeof(cmd_v7),550};551u8 i;552int err;553554/*555* Versions 7 and 8 has the same structure except from the responders556* list, so iwl_mvm_ftm_cmd() can be used for version 7 too.557*/558iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd_v7, req);559560for (i = 0; i < cmd_v7.num_of_ap; i++) {561struct cfg80211_pmsr_request_peer *peer = &req->peers[i];562563err = iwl_mvm_ftm_put_target_v3(mvm, peer, &cmd_v7.ap[i]);564if (err)565return err;566}567568return iwl_mvm_ftm_send_cmd(mvm, &hcmd);569}570571static int iwl_mvm_ftm_start_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,572struct cfg80211_pmsr_request *req)573{574struct iwl_tof_range_req_cmd_v8 cmd;575struct iwl_host_cmd hcmd = {576.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),577.dataflags[0] = IWL_HCMD_DFL_DUP,578.data[0] = &cmd,579.len[0] = sizeof(cmd),580};581u8 i;582int err;583584iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd, req);585586for (i = 0; i < cmd.num_of_ap; i++) {587struct cfg80211_pmsr_request_peer *peer = &req->peers[i];588589err = iwl_mvm_ftm_put_target_v4(mvm, peer, &cmd.ap[i]);590if (err)591return err;592}593594return iwl_mvm_ftm_send_cmd(mvm, &hcmd);595}596597static int iwl_mvm_ftm_start_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif,598struct cfg80211_pmsr_request *req)599{600struct iwl_tof_range_req_cmd_v9 cmd;601struct iwl_host_cmd hcmd = {602.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),603.dataflags[0] = IWL_HCMD_DFL_DUP,604.data[0] = &cmd,605.len[0] = sizeof(cmd),606};607u8 i;608int err;609610iwl_mvm_ftm_cmd_common(mvm, vif, &cmd, req);611612for (i = 0; i < cmd.num_of_ap; i++) {613struct cfg80211_pmsr_request_peer *peer = &req->peers[i];614struct iwl_tof_range_req_ap_entry_v6 *target = &cmd.ap[i];615616err = iwl_mvm_ftm_put_target(mvm, vif, peer, target);617if (err)618return err;619}620621return iwl_mvm_ftm_send_cmd(mvm, &hcmd);622}623624static void iter(struct ieee80211_hw *hw,625struct ieee80211_vif *vif,626struct ieee80211_sta *sta,627struct ieee80211_key_conf *key,628void *data)629{630struct iwl_mvm_ftm_iter_data *target = data;631632if (!sta || memcmp(sta->addr, target->bssid, ETH_ALEN))633return;634635WARN_ON(!sta->mfp);636637target->tk = key->key;638*target->cipher = iwl_mvm_cipher_to_location_cipher(key->cipher);639WARN_ON(*target->cipher == IWL_LOCATION_CIPHER_INVALID);640}641642static void643iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,644u8 *bssid, u8 *cipher, u8 *hltk, u8 *tk,645u8 *rx_pn, u8 *tx_pn, __le32 *flags)646{647struct iwl_mvm_ftm_pasn_entry *entry;648#ifdef CONFIG_IWLWIFI_DEBUGFS649struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);650651if (mvmvif->ftm_unprotected)652return;653#endif654655if (!(le32_to_cpu(*flags) & (IWL_INITIATOR_AP_FLAGS_NON_TB |656IWL_INITIATOR_AP_FLAGS_TB)))657return;658659lockdep_assert_held(&mvm->mutex);660661list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {662if (memcmp(entry->addr, bssid, sizeof(entry->addr)))663continue;664665*cipher = entry->cipher;666667if (entry->flags & IWL_MVM_PASN_FLAG_HAS_HLTK)668memcpy(hltk, entry->hltk, sizeof(entry->hltk));669else670memset(hltk, 0, sizeof(entry->hltk));671672if (vif->cfg.assoc &&673!memcmp(vif->bss_conf.bssid, bssid, ETH_ALEN)) {674struct iwl_mvm_ftm_iter_data target;675676target.bssid = bssid;677target.cipher = cipher;678target.tk = NULL;679ieee80211_iter_keys(mvm->hw, vif, iter, &target);680681if (!WARN_ON(!target.tk))682memcpy(tk, target.tk, TK_11AZ_LEN);683} else {684memcpy(tk, entry->tk, sizeof(entry->tk));685}686687memcpy(rx_pn, entry->rx_pn, sizeof(entry->rx_pn));688memcpy(tx_pn, entry->tx_pn, sizeof(entry->tx_pn));689690FTM_SET_FLAG(SECURED);691return;692}693}694695static int696iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,697struct cfg80211_pmsr_request_peer *peer,698struct iwl_tof_range_req_ap_entry_v7 *target)699{700int err = iwl_mvm_ftm_put_target(mvm, vif, peer, (void *)target);701if (err)702return err;703704iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,705&target->cipher, target->hltk,706target->tk, target->rx_pn,707target->tx_pn,708&target->initiator_ap_flags);709return err;710}711712static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm,713struct ieee80211_vif *vif,714struct cfg80211_pmsr_request *req)715{716struct iwl_tof_range_req_cmd_v11 cmd;717struct iwl_host_cmd hcmd = {718.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),719.dataflags[0] = IWL_HCMD_DFL_DUP,720.data[0] = &cmd,721.len[0] = sizeof(cmd),722};723u8 i;724int err;725726iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);727728for (i = 0; i < cmd.num_of_ap; i++) {729struct cfg80211_pmsr_request_peer *peer = &req->peers[i];730struct iwl_tof_range_req_ap_entry_v7 *target = &cmd.ap[i];731732err = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, target);733if (err)734return err;735}736737return iwl_mvm_ftm_send_cmd(mvm, &hcmd);738}739740static void741iwl_mvm_ftm_set_ndp_params(struct iwl_mvm *mvm,742struct iwl_tof_range_req_ap_entry_v8 *target)743{744/* Only 2 STS are supported on Tx */745u32 i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 :746IWL_MVM_FTM_I2R_MAX_STS;747748target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |749(IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS);750target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |751(i2r_max_sts << IWL_LOCATION_MAX_STS_POS);752target->r2i_max_total_ltf = IWL_MVM_FTM_R2I_MAX_TOTAL_LTF;753target->i2r_max_total_ltf = IWL_MVM_FTM_I2R_MAX_TOTAL_LTF;754}755756static int757iwl_mvm_ftm_put_target_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,758struct cfg80211_pmsr_request_peer *peer,759struct iwl_tof_range_req_ap_entry_v8 *target)760{761u32 flags;762int ret = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, (void *)target);763764if (ret)765return ret;766767iwl_mvm_ftm_set_ndp_params(mvm, target);768769/*770* If secure LTF is turned off, replace the flag with PMF only771*/772flags = le32_to_cpu(target->initiator_ap_flags);773if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) {774if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF)775flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED;776777flags |= IWL_INITIATOR_AP_FLAGS_PMF;778target->initiator_ap_flags = cpu_to_le32(flags);779}780781return 0;782}783784static int iwl_mvm_ftm_start_v12(struct iwl_mvm *mvm,785struct ieee80211_vif *vif,786struct cfg80211_pmsr_request *req)787{788struct iwl_tof_range_req_cmd_v12 cmd;789struct iwl_host_cmd hcmd = {790.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),791.dataflags[0] = IWL_HCMD_DFL_DUP,792.data[0] = &cmd,793.len[0] = sizeof(cmd),794};795u8 i;796int err;797798iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);799800for (i = 0; i < cmd.num_of_ap; i++) {801struct cfg80211_pmsr_request_peer *peer = &req->peers[i];802struct iwl_tof_range_req_ap_entry_v8 *target = &cmd.ap[i];803804err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, target);805if (err)806return err;807}808809return iwl_mvm_ftm_send_cmd(mvm, &hcmd);810}811812static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm,813struct ieee80211_vif *vif,814struct cfg80211_pmsr_request *req)815{816struct iwl_tof_range_req_cmd_v13 cmd;817struct iwl_host_cmd hcmd = {818.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),819.dataflags[0] = IWL_HCMD_DFL_DUP,820.data[0] = &cmd,821.len[0] = sizeof(cmd),822};823u8 i;824int err;825826iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);827828for (i = 0; i < cmd.num_of_ap; i++) {829struct cfg80211_pmsr_request_peer *peer = &req->peers[i];830struct iwl_tof_range_req_ap_entry_v9 *target = &cmd.ap[i];831832err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, (void *)target);833if (err)834return err;835836if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)837target->bss_color = peer->ftm.bss_color;838839if (peer->ftm.non_trigger_based) {840target->min_time_between_msr =841cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);842target->burst_period =843cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);844} else {845target->min_time_between_msr = cpu_to_le16(0);846}847848target->band =849iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band);850}851852return iwl_mvm_ftm_send_cmd(mvm, &hcmd);853}854855static int856iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif,857struct cfg80211_pmsr_request_peer *peer,858struct iwl_tof_range_req_ap_entry *target)859{860u32 i2r_max_sts, flags;861int ret;862863ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,864&target->format_bw,865&target->ctrl_ch_position);866if (ret)867return ret;868869memcpy(target->bssid, peer->addr, ETH_ALEN);870target->burst_period =871cpu_to_le16(peer->ftm.burst_period);872target->samples_per_burst = peer->ftm.ftms_per_burst;873target->num_of_bursts = peer->ftm.num_bursts_exp;874iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags);875iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id,876&target->initiator_ap_flags);877iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,878&target->cipher, target->hltk,879target->tk, target->rx_pn,880target->tx_pn,881&target->initiator_ap_flags);882883i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 :884IWL_MVM_FTM_I2R_MAX_STS;885886target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |887(IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS) |888(IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS);889target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |890(i2r_max_sts << IWL_LOCATION_MAX_STS_POS) |891(IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS);892893if (peer->ftm.non_trigger_based) {894target->min_time_between_msr =895cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);896target->burst_period =897cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);898} else {899target->min_time_between_msr = cpu_to_le16(0);900}901902target->band =903iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band);904905/*906* TODO: Beacon interval is currently unknown, so use the common value907* of 100 TUs.908*/909target->beacon_interval = cpu_to_le16(100);910911/*912* If secure LTF is turned off, replace the flag with PMF only913*/914flags = le32_to_cpu(target->initiator_ap_flags);915if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) {916if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF)917flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED;918919flags |= IWL_INITIATOR_AP_FLAGS_PMF;920target->initiator_ap_flags = cpu_to_le32(flags);921}922923return 0;924}925926static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm,927struct ieee80211_vif *vif,928struct cfg80211_pmsr_request *req)929{930struct iwl_tof_range_req_cmd cmd;931struct iwl_host_cmd hcmd = {932.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),933.dataflags[0] = IWL_HCMD_DFL_DUP,934.data[0] = &cmd,935.len[0] = sizeof(cmd),936};937u8 i;938int err;939940iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);941942for (i = 0; i < cmd.num_of_ap; i++) {943struct cfg80211_pmsr_request_peer *peer = &req->peers[i];944struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i];945946err = iwl_mvm_ftm_put_target_v10(mvm, vif, peer, target);947if (err)948return err;949}950951return iwl_mvm_ftm_send_cmd(mvm, &hcmd);952}953954int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,955struct cfg80211_pmsr_request *req)956{957bool new_api = fw_has_api(&mvm->fw->ucode_capa,958IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);959int err;960961lockdep_assert_held(&mvm->mutex);962963if (mvm->ftm_initiator.req)964return -EBUSY;965966if (new_api) {967u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,968WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),969IWL_FW_CMD_VER_UNKNOWN);970971switch (cmd_ver) {972case 15:973/* Version 15 has the same struct as 14 */974case 14:975err = iwl_mvm_ftm_start_v14(mvm, vif, req);976break;977case 13:978err = iwl_mvm_ftm_start_v13(mvm, vif, req);979break;980case 12:981err = iwl_mvm_ftm_start_v12(mvm, vif, req);982break;983case 11:984err = iwl_mvm_ftm_start_v11(mvm, vif, req);985break;986case 9:987case 10:988err = iwl_mvm_ftm_start_v9(mvm, vif, req);989break;990case 8:991err = iwl_mvm_ftm_start_v8(mvm, vif, req);992break;993default:994err = iwl_mvm_ftm_start_v7(mvm, vif, req);995break;996}997} else {998err = iwl_mvm_ftm_start_v5(mvm, vif, req);999}10001001if (!err) {1002mvm->ftm_initiator.req = req;1003mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif);1004}10051006return err;1007}10081009void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req)1010{1011struct iwl_tof_range_abort_cmd cmd = {1012.request_id = req->cookie,1013};10141015lockdep_assert_held(&mvm->mutex);10161017if (req != mvm->ftm_initiator.req)1018return;10191020iwl_mvm_ftm_reset(mvm);10211022if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(LOCATION_GROUP, TOF_RANGE_ABORT_CMD),10230, sizeof(cmd), &cmd))1024IWL_ERR(mvm, "failed to abort FTM process\n");1025}10261027static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req,1028const u8 *addr)1029{1030int i;10311032for (i = 0; i < req->n_peers; i++) {1033struct cfg80211_pmsr_request_peer *peer = &req->peers[i];10341035if (ether_addr_equal_unaligned(peer->addr, addr))1036return i;1037}10381039return -ENOENT;1040}10411042static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts)1043{1044u32 gp2_ts = le32_to_cpu(fw_gp2_ts);1045u32 curr_gp2, diff;1046u64 now_from_boot_ns;10471048iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2,1049&now_from_boot_ns, NULL);10501051if (curr_gp2 >= gp2_ts)1052diff = curr_gp2 - gp2_ts;1053else1054diff = curr_gp2 + (U32_MAX - gp2_ts + 1);10551056return now_from_boot_ns - (u64)diff * 1000;1057}10581059static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm,1060struct cfg80211_pmsr_result *res)1061{1062struct iwl_mvm_loc_entry *entry;10631064list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) {1065if (!ether_addr_equal_unaligned(res->addr, entry->addr))1066continue;10671068if (entry->lci_len) {1069res->ftm.lci_len = entry->lci_len;1070res->ftm.lci = entry->buf;1071}10721073if (entry->civic_len) {1074res->ftm.civicloc_len = entry->civic_len;1075res->ftm.civicloc = entry->buf + entry->lci_len;1076}10771078/* we found the entry we needed */1079break;1080}1081}10821083static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id,1084u8 num_of_aps)1085{1086lockdep_assert_held(&mvm->mutex);10871088if (request_id != (u8)mvm->ftm_initiator.req->cookie) {1089IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n",1090request_id, (u8)mvm->ftm_initiator.req->cookie);1091return -EINVAL;1092}10931094if (num_of_aps > mvm->ftm_initiator.req->n_peers) {1095IWL_ERR(mvm, "FTM range response invalid\n");1096return -EINVAL;1097}10981099return 0;1100}11011102static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm,1103struct cfg80211_pmsr_result *res)1104{1105struct iwl_mvm_smooth_entry *resp = NULL, *iter;1106s64 rtt_avg, rtt = res->ftm.rtt_avg;1107u32 undershoot, overshoot;1108u8 alpha;11091110if (!IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH)1111return;11121113WARN_ON(rtt < 0);11141115if (res->status != NL80211_PMSR_STATUS_SUCCESS) {1116IWL_DEBUG_INFO(mvm,1117": %pM: ignore failed measurement. Status=%u\n",1118res->addr, res->status);1119return;1120}11211122list_for_each_entry(iter, &mvm->ftm_initiator.smooth.resp, list) {1123if (!memcmp(res->addr, iter->addr, ETH_ALEN)) {1124resp = iter;1125break;1126}1127}11281129if (!resp) {1130resp = kzalloc(sizeof(*resp), GFP_KERNEL);1131if (!resp)1132return;11331134memcpy(resp->addr, res->addr, ETH_ALEN);1135list_add_tail(&resp->list, &mvm->ftm_initiator.smooth.resp);11361137resp->rtt_avg = rtt;11381139IWL_DEBUG_INFO(mvm, "new: %pM: rtt_avg=%lld\n",1140resp->addr, resp->rtt_avg);1141goto update_time;1142}11431144if (res->host_time - resp->host_time >1145IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * 1000000000) {1146resp->rtt_avg = rtt;11471148IWL_DEBUG_INFO(mvm, "expired: %pM: rtt_avg=%lld\n",1149resp->addr, resp->rtt_avg);1150goto update_time;1151}11521153/* Smooth the results based on the tracked RTT average */1154undershoot = IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT;1155overshoot = IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT;1156alpha = IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA;11571158rtt_avg = div_s64(alpha * rtt + (100 - alpha) * resp->rtt_avg, 100);11591160IWL_DEBUG_INFO(mvm,1161"%pM: prev rtt_avg=%lld, new rtt_avg=%lld, rtt=%lld\n",1162resp->addr, resp->rtt_avg, rtt_avg, rtt);11631164/*1165* update the responder's average RTT results regardless of1166* the under/over shoot logic below1167*/1168resp->rtt_avg = rtt_avg;11691170/* smooth the results */1171if (rtt_avg > rtt && (rtt_avg - rtt) > undershoot) {1172res->ftm.rtt_avg = rtt_avg;11731174IWL_DEBUG_INFO(mvm,1175"undershoot: val=%lld\n",1176(rtt_avg - rtt));1177} else if (rtt_avg < rtt && (rtt - rtt_avg) >1178overshoot) {1179res->ftm.rtt_avg = rtt_avg;1180IWL_DEBUG_INFO(mvm,1181"overshoot: val=%lld\n",1182(rtt - rtt_avg));1183}11841185update_time:1186resp->host_time = res->host_time;1187}11881189static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,1190struct cfg80211_pmsr_result *res)1191{1192s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);11931194IWL_DEBUG_INFO(mvm, "entry %d\n", index);1195IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status);1196IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr);1197IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time);1198IWL_DEBUG_INFO(mvm, "\tburst index: %d\n", res->ftm.burst_index);1199IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes);1200IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg);1201IWL_DEBUG_INFO(mvm, "\trssi spread: %d\n", res->ftm.rssi_spread);1202IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg);1203IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance);1204IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread);1205IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg);1206}12071208static void1209iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm,1210struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap)1211{1212struct iwl_mvm_ftm_pasn_entry *entry;12131214lockdep_assert_held(&mvm->mutex);12151216list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {1217if (memcmp(fw_ap->bssid, entry->addr, sizeof(entry->addr)))1218continue;12191220memcpy(entry->rx_pn, fw_ap->rx_pn, sizeof(entry->rx_pn));1221memcpy(entry->tx_pn, fw_ap->tx_pn, sizeof(entry->tx_pn));1222return;1223}1224}12251226static u8 iwl_mvm_ftm_get_range_resp_ver(struct iwl_mvm *mvm)1227{1228if (!fw_has_api(&mvm->fw->ucode_capa,1229IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ))1230return 5;12311232/* Starting from version 8, the FW advertises the version */1233if (mvm->cmd_ver.range_resp >= 8)1234return mvm->cmd_ver.range_resp;1235else if (fw_has_api(&mvm->fw->ucode_capa,1236IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))1237return 7;12381239/* The first version of the new range request API */1240return 6;1241}12421243static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len)1244{1245switch (ver) {1246case 9:1247case 8:1248return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy);1249case 7:1250return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7);1251case 6:1252return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v6);1253case 5:1254return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v5);1255default:1256WARN_ONCE(1, "FTM: unsupported range response version %u", ver);1257return false;1258}1259}12601261void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)1262{1263struct iwl_rx_packet *pkt = rxb_addr(rxb);1264unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);1265struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data;1266struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data;1267struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data;1268struct iwl_tof_range_rsp_ntfy *fw_resp_v8 = (void *)pkt->data;1269int i;1270bool new_api = fw_has_api(&mvm->fw->ucode_capa,1271IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);1272u8 num_of_aps, last_in_batch;1273u8 notif_ver = iwl_mvm_ftm_get_range_resp_ver(mvm);12741275lockdep_assert_held(&mvm->mutex);12761277if (!mvm->ftm_initiator.req) {1278return;1279}12801281if (unlikely(!iwl_mvm_ftm_resp_size_validation(notif_ver, pkt_len)))1282return;12831284if (new_api) {1285if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v8->request_id,1286fw_resp_v8->num_of_aps))1287return;12881289num_of_aps = fw_resp_v8->num_of_aps;1290last_in_batch = fw_resp_v8->last_report;1291} else {1292if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id,1293fw_resp_v5->num_of_aps))1294return;12951296num_of_aps = fw_resp_v5->num_of_aps;1297last_in_batch = fw_resp_v5->last_in_batch;1298}12991300IWL_DEBUG_INFO(mvm, "Range response received\n");1301IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n",1302mvm->ftm_initiator.req->cookie, num_of_aps);13031304for (i = 0; i < num_of_aps && i < IWL_TOF_MAX_APS; i++) {1305struct cfg80211_pmsr_result result = {};1306struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap;1307int peer_idx;13081309if (new_api) {1310if (notif_ver >= 8) {1311fw_ap = &fw_resp_v8->ap[i];1312iwl_mvm_ftm_pasn_update_pn(mvm, fw_ap);1313} else if (notif_ver == 7) {1314fw_ap = (void *)&fw_resp_v7->ap[i];1315} else {1316fw_ap = (void *)&fw_resp_v6->ap[i];1317}13181319result.final = fw_ap->last_burst;1320result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);1321result.ap_tsf_valid = 1;1322} else {1323/* the first part is the same for old and new APIs */1324fw_ap = (void *)&fw_resp_v5->ap[i];1325/*1326* FIXME: the firmware needs to report this, we don't1327* even know the number of bursts the responder picked1328* (if we asked it to)1329*/1330result.final = 0;1331}13321333peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req,1334fw_ap->bssid);1335if (peer_idx < 0) {1336IWL_WARN(mvm,1337"Unknown address (%pM, target #%d) in FTM response\n",1338fw_ap->bssid, i);1339continue;1340}13411342switch (fw_ap->measure_status) {1343case IWL_TOF_ENTRY_SUCCESS:1344result.status = NL80211_PMSR_STATUS_SUCCESS;1345break;1346case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT:1347result.status = NL80211_PMSR_STATUS_TIMEOUT;1348break;1349case IWL_TOF_ENTRY_NO_RESPONSE:1350result.status = NL80211_PMSR_STATUS_FAILURE;1351result.ftm.failure_reason =1352NL80211_PMSR_FTM_FAILURE_NO_RESPONSE;1353break;1354case IWL_TOF_ENTRY_REQUEST_REJECTED:1355result.status = NL80211_PMSR_STATUS_FAILURE;1356result.ftm.failure_reason =1357NL80211_PMSR_FTM_FAILURE_PEER_BUSY;1358result.ftm.busy_retry_time = fw_ap->refusal_period;1359break;1360default:1361result.status = NL80211_PMSR_STATUS_FAILURE;1362result.ftm.failure_reason =1363NL80211_PMSR_FTM_FAILURE_UNSPECIFIED;1364break;1365}1366memcpy(result.addr, fw_ap->bssid, ETH_ALEN);1367result.host_time = iwl_mvm_ftm_get_host_time(mvm,1368fw_ap->timestamp);1369result.type = NL80211_PMSR_TYPE_FTM;1370result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx];1371mvm->ftm_initiator.responses[peer_idx]++;1372result.ftm.rssi_avg = fw_ap->rssi;1373result.ftm.rssi_avg_valid = 1;1374result.ftm.rssi_spread = fw_ap->rssi_spread;1375result.ftm.rssi_spread_valid = 1;1376result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt);1377result.ftm.rtt_avg_valid = 1;1378result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance);1379result.ftm.rtt_variance_valid = 1;1380result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread);1381result.ftm.rtt_spread_valid = 1;13821383iwl_mvm_ftm_get_lci_civic(mvm, &result);13841385iwl_mvm_ftm_rtt_smoothing(mvm, &result);13861387cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,1388mvm->ftm_initiator.req,1389&result, GFP_KERNEL);13901391if (fw_has_api(&mvm->fw->ucode_capa,1392IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))1393IWL_DEBUG_INFO(mvm, "RTT confidence: %u\n",1394fw_ap->rttConfidence);13951396iwl_mvm_debug_range_resp(mvm, i, &result);1397}13981399if (last_in_batch) {1400cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,1401mvm->ftm_initiator.req,1402GFP_KERNEL);1403iwl_mvm_ftm_reset(mvm);1404}1405}14061407void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)1408{1409struct iwl_rx_packet *pkt = rxb_addr(rxb);1410const struct ieee80211_mgmt *mgmt = (void *)pkt->data;1411size_t len = iwl_rx_packet_payload_len(pkt);1412struct iwl_mvm_loc_entry *entry;1413const u8 *ies, *lci, *civic, *msr_ie;1414size_t ies_len, lci_len = 0, civic_len = 0;1415size_t baselen = IEEE80211_MIN_ACTION_SIZE +1416sizeof(mgmt->u.action.u.ftm);1417static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI;1418static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC;14191420if (len <= baselen)1421return;14221423lockdep_assert_held(&mvm->mutex);14241425ies = mgmt->u.action.u.ftm.variable;1426ies_len = len - baselen;14271428msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,1429&rprt_type_lci, 1, 4);1430if (msr_ie) {1431lci = msr_ie + 2;1432lci_len = msr_ie[1];1433}14341435msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,1436&rprt_type_civic, 1, 4);1437if (msr_ie) {1438civic = msr_ie + 2;1439civic_len = msr_ie[1];1440}14411442entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL);1443if (!entry)1444return;14451446memcpy(entry->addr, mgmt->bssid, ETH_ALEN);14471448entry->lci_len = lci_len;1449if (lci_len)1450memcpy(entry->buf, lci, lci_len);14511452entry->civic_len = civic_len;1453if (civic_len)1454memcpy(entry->buf + lci_len, civic, civic_len);14551456list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list);1457}145814591460