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