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
 758iwl_mvm_ftm_put_target_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 759                          struct cfg80211_pmsr_request_peer *peer,
 760                          struct iwl_tof_range_req_ap_entry_v8 *target)
 761{
 762        u32 flags;
 763        int ret = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, (void *)target);
 764
 765        if (ret)
 766                return ret;
 767
 768        iwl_mvm_ftm_set_ndp_params(mvm, target);
 769
 770        /*
 771         * If secure LTF is turned off, replace the flag with PMF only
 772         */
 773        flags = le32_to_cpu(target->initiator_ap_flags);
 774        if ((flags & IWL_INITIATOR_AP_FLAGS_SECURED) &&
 775            !IWL_MVM_FTM_INITIATOR_SECURE_LTF) {
 776                flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED;
 777                flags |= IWL_INITIATOR_AP_FLAGS_PMF;
 778                target->initiator_ap_flags = cpu_to_le32(flags);
 779        }
 780
 781        return 0;
 782}
 783
 784static int iwl_mvm_ftm_start_v12(struct iwl_mvm *mvm,
 785                                 struct ieee80211_vif *vif,
 786                                 struct cfg80211_pmsr_request *req)
 787{
 788        struct iwl_tof_range_req_cmd_v12 cmd;
 789        struct iwl_host_cmd hcmd = {
 790                .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0),
 791                .dataflags[0] = IWL_HCMD_DFL_DUP,
 792                .data[0] = &cmd,
 793                .len[0] = sizeof(cmd),
 794        };
 795        u8 i;
 796        int err;
 797
 798        iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
 799
 800        for (i = 0; i < cmd.num_of_ap; i++) {
 801                struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
 802                struct iwl_tof_range_req_ap_entry_v8 *target = &cmd.ap[i];
 803
 804                err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, target);
 805                if (err)
 806                        return err;
 807        }
 808
 809        return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
 810}
 811
 812static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm,
 813                                 struct ieee80211_vif *vif,
 814                                 struct cfg80211_pmsr_request *req)
 815{
 816        struct iwl_tof_range_req_cmd_v13 cmd;
 817        struct iwl_host_cmd hcmd = {
 818                .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0),
 819                .dataflags[0] = IWL_HCMD_DFL_DUP,
 820                .data[0] = &cmd,
 821                .len[0] = sizeof(cmd),
 822        };
 823        u8 i;
 824        int err;
 825
 826        iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
 827
 828        for (i = 0; i < cmd.num_of_ap; i++) {
 829                struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
 830                struct iwl_tof_range_req_ap_entry_v9 *target = &cmd.ap[i];
 831
 832                err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, (void *)target);
 833                if (err)
 834                        return err;
 835
 836                if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
 837                        target->bss_color = peer->ftm.bss_color;
 838
 839                if (peer->ftm.non_trigger_based) {
 840                        target->min_time_between_msr =
 841                                cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
 842                        target->burst_period =
 843                                cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
 844                } else {
 845                        target->min_time_between_msr = cpu_to_le16(0);
 846                }
 847
 848                target->band =
 849                        iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band);
 850        }
 851
 852        return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
 853}
 854
 855int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 856                      struct cfg80211_pmsr_request *req)
 857{
 858        bool new_api = fw_has_api(&mvm->fw->ucode_capa,
 859                                  IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
 860        int err;
 861
 862        lockdep_assert_held(&mvm->mutex);
 863
 864        if (mvm->ftm_initiator.req)
 865                return -EBUSY;
 866
 867        if (new_api) {
 868                u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
 869                                                   TOF_RANGE_REQ_CMD,
 870                                                   IWL_FW_CMD_VER_UNKNOWN);
 871
 872                switch (cmd_ver) {
 873                case 13:
 874                        err = iwl_mvm_ftm_start_v13(mvm, vif, req);
 875                        break;
 876                case 12:
 877                        err = iwl_mvm_ftm_start_v12(mvm, vif, req);
 878                        break;
 879                case 11:
 880                        err = iwl_mvm_ftm_start_v11(mvm, vif, req);
 881                        break;
 882                case 9:
 883                case 10:
 884                        err = iwl_mvm_ftm_start_v9(mvm, vif, req);
 885                        break;
 886                case 8:
 887                        err = iwl_mvm_ftm_start_v8(mvm, vif, req);
 888                        break;
 889                default:
 890                        err = iwl_mvm_ftm_start_v7(mvm, vif, req);
 891                        break;
 892                }
 893        } else {
 894                err = iwl_mvm_ftm_start_v5(mvm, vif, req);
 895        }
 896
 897        if (!err) {
 898                mvm->ftm_initiator.req = req;
 899                mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif);
 900        }
 901
 902        return err;
 903}
 904
 905void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req)
 906{
 907        struct iwl_tof_range_abort_cmd cmd = {
 908                .request_id = req->cookie,
 909        };
 910
 911        lockdep_assert_held(&mvm->mutex);
 912
 913        if (req != mvm->ftm_initiator.req)
 914                return;
 915
 916        iwl_mvm_ftm_reset(mvm);
 917
 918        if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RANGE_ABORT_CMD,
 919                                                 LOCATION_GROUP, 0),
 920                                 0, sizeof(cmd), &cmd))
 921                IWL_ERR(mvm, "failed to abort FTM process\n");
 922}
 923
 924static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req,
 925                                 const u8 *addr)
 926{
 927        int i;
 928
 929        for (i = 0; i < req->n_peers; i++) {
 930                struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
 931
 932                if (ether_addr_equal_unaligned(peer->addr, addr))
 933                        return i;
 934        }
 935
 936        return -ENOENT;
 937}
 938
 939static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts)
 940{
 941        u32 gp2_ts = le32_to_cpu(fw_gp2_ts);
 942        u32 curr_gp2, diff;
 943        u64 now_from_boot_ns;
 944
 945        iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2,
 946                              &now_from_boot_ns, NULL);
 947
 948        if (curr_gp2 >= gp2_ts)
 949                diff = curr_gp2 - gp2_ts;
 950        else
 951                diff = curr_gp2 + (U32_MAX - gp2_ts + 1);
 952
 953        return now_from_boot_ns - (u64)diff * 1000;
 954}
 955
 956static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm,
 957                                      struct cfg80211_pmsr_result *res)
 958{
 959        struct iwl_mvm_loc_entry *entry;
 960
 961        list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) {
 962                if (!ether_addr_equal_unaligned(res->addr, entry->addr))
 963                        continue;
 964
 965                if (entry->lci_len) {
 966                        res->ftm.lci_len = entry->lci_len;
 967                        res->ftm.lci = entry->buf;
 968                }
 969
 970                if (entry->civic_len) {
 971                        res->ftm.civicloc_len = entry->civic_len;
 972                        res->ftm.civicloc = entry->buf + entry->lci_len;
 973                }
 974
 975                /* we found the entry we needed */
 976                break;
 977        }
 978}
 979
 980static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id,
 981                                        u8 num_of_aps)
 982{
 983        lockdep_assert_held(&mvm->mutex);
 984
 985        if (request_id != (u8)mvm->ftm_initiator.req->cookie) {
 986                IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n",
 987                        request_id, (u8)mvm->ftm_initiator.req->cookie);
 988                return -EINVAL;
 989        }
 990
 991        if (num_of_aps > mvm->ftm_initiator.req->n_peers) {
 992                IWL_ERR(mvm, "FTM range response invalid\n");
 993                return -EINVAL;
 994        }
 995
 996        return 0;
 997}
 998
 999static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm,
1000                                      struct cfg80211_pmsr_result *res)
1001{
1002        struct iwl_mvm_smooth_entry *resp;
1003        s64 rtt_avg, rtt = res->ftm.rtt_avg;
1004        u32 undershoot, overshoot;
1005        u8 alpha;
1006        bool found;
1007
1008        if (!IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH)
1009                return;
1010
1011        WARN_ON(rtt < 0);
1012
1013        if (res->status != NL80211_PMSR_STATUS_SUCCESS) {
1014                IWL_DEBUG_INFO(mvm,
1015                               ": %pM: ignore failed measurement. Status=%u\n",
1016                               res->addr, res->status);
1017                return;
1018        }
1019
1020        found = false;
1021        list_for_each_entry(resp, &mvm->ftm_initiator.smooth.resp, list) {
1022                if (!memcmp(res->addr, resp->addr, ETH_ALEN)) {
1023                        found = true;
1024                        break;
1025                }
1026        }
1027
1028        if (!found) {
1029                resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1030                if (!resp)
1031                        return;
1032
1033                memcpy(resp->addr, res->addr, ETH_ALEN);
1034                list_add_tail(&resp->list, &mvm->ftm_initiator.smooth.resp);
1035
1036                resp->rtt_avg = rtt;
1037
1038                IWL_DEBUG_INFO(mvm, "new: %pM: rtt_avg=%lld\n",
1039                               resp->addr, resp->rtt_avg);
1040                goto update_time;
1041        }
1042
1043        if (res->host_time - resp->host_time >
1044            IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * 1000000000) {
1045                resp->rtt_avg = rtt;
1046
1047                IWL_DEBUG_INFO(mvm, "expired: %pM: rtt_avg=%lld\n",
1048                               resp->addr, resp->rtt_avg);
1049                goto update_time;
1050        }
1051
1052        /* Smooth the results based on the tracked RTT average */
1053        undershoot = IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT;
1054        overshoot = IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT;
1055        alpha = IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA;
1056
1057        rtt_avg = (alpha * rtt + (100 - alpha) * resp->rtt_avg) / 100;
1058
1059        IWL_DEBUG_INFO(mvm,
1060                       "%pM: prev rtt_avg=%lld, new rtt_avg=%lld, rtt=%lld\n",
1061                       resp->addr, resp->rtt_avg, rtt_avg, rtt);
1062
1063        /*
1064         * update the responder's average RTT results regardless of
1065         * the under/over shoot logic below
1066         */
1067        resp->rtt_avg = rtt_avg;
1068
1069        /* smooth the results */
1070        if (rtt_avg > rtt && (rtt_avg - rtt) > undershoot) {
1071                res->ftm.rtt_avg = rtt_avg;
1072
1073                IWL_DEBUG_INFO(mvm,
1074                               "undershoot: val=%lld\n",
1075                               (rtt_avg - rtt));
1076        } else if (rtt_avg < rtt && (rtt - rtt_avg) >
1077                   overshoot) {
1078                res->ftm.rtt_avg = rtt_avg;
1079                IWL_DEBUG_INFO(mvm,
1080                               "overshoot: val=%lld\n",
1081                               (rtt - rtt_avg));
1082        }
1083
1084update_time:
1085        resp->host_time = res->host_time;
1086}
1087
1088static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
1089                                     struct cfg80211_pmsr_result *res)
1090{
1091        s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);
1092
1093        IWL_DEBUG_INFO(mvm, "entry %d\n", index);
1094        IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status);
1095        IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr);
1096        IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time);
1097        IWL_DEBUG_INFO(mvm, "\tburst index: %hhu\n", res->ftm.burst_index);
1098        IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes);
1099        IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg);
1100        IWL_DEBUG_INFO(mvm, "\trssi spread: %hhu\n", res->ftm.rssi_spread);
1101        IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg);
1102        IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance);
1103        IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread);
1104        IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg);
1105}
1106
1107static void
1108iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm,
1109                           struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap)
1110{
1111        struct iwl_mvm_ftm_pasn_entry *entry;
1112
1113        lockdep_assert_held(&mvm->mutex);
1114
1115        list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
1116                if (memcmp(fw_ap->bssid, entry->addr, sizeof(entry->addr)))
1117                        continue;
1118
1119                memcpy(entry->rx_pn, fw_ap->rx_pn, sizeof(entry->rx_pn));
1120                memcpy(entry->tx_pn, fw_ap->tx_pn, sizeof(entry->tx_pn));
1121                return;
1122        }
1123}
1124
1125static u8 iwl_mvm_ftm_get_range_resp_ver(struct iwl_mvm *mvm)
1126{
1127        if (!fw_has_api(&mvm->fw->ucode_capa,
1128                        IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ))
1129                return 5;
1130
1131        /* Starting from version 8, the FW advertises the version */
1132        if (mvm->cmd_ver.range_resp >= 8)
1133                return mvm->cmd_ver.range_resp;
1134        else if (fw_has_api(&mvm->fw->ucode_capa,
1135                            IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
1136                return 7;
1137
1138        /* The first version of the new range request API */
1139        return 6;
1140}
1141
1142static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len)
1143{
1144        switch (ver) {
1145        case 8:
1146                return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8);
1147        case 7:
1148                return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7);
1149        case 6:
1150                return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v6);
1151        case 5:
1152                return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v5);
1153        default:
1154                WARN_ONCE(1, "FTM: unsupported range response version %u", ver);
1155                return false;
1156        }
1157}
1158
1159void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1160{
1161        struct iwl_rx_packet *pkt = rxb_addr(rxb);
1162        unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
1163        struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data;
1164        struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data;
1165        struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data;
1166        struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data;
1167        int i;
1168        bool new_api = fw_has_api(&mvm->fw->ucode_capa,
1169                                  IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
1170        u8 num_of_aps, last_in_batch;
1171        u8 notif_ver = iwl_mvm_ftm_get_range_resp_ver(mvm);
1172
1173        lockdep_assert_held(&mvm->mutex);
1174
1175        if (!mvm->ftm_initiator.req) {
1176                return;
1177        }
1178
1179        if (unlikely(!iwl_mvm_ftm_resp_size_validation(notif_ver, pkt_len)))
1180                return;
1181
1182        if (new_api) {
1183                if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v8->request_id,
1184                                                 fw_resp_v8->num_of_aps))
1185                        return;
1186
1187                num_of_aps = fw_resp_v8->num_of_aps;
1188                last_in_batch = fw_resp_v8->last_report;
1189        } else {
1190                if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id,
1191                                                 fw_resp_v5->num_of_aps))
1192                        return;
1193
1194                num_of_aps = fw_resp_v5->num_of_aps;
1195                last_in_batch = fw_resp_v5->last_in_batch;
1196        }
1197
1198        IWL_DEBUG_INFO(mvm, "Range response received\n");
1199        IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %hhu\n",
1200                       mvm->ftm_initiator.req->cookie, num_of_aps);
1201
1202        for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) {
1203                struct cfg80211_pmsr_result result = {};
1204                struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap;
1205                int peer_idx;
1206
1207                if (new_api) {
1208                        if (notif_ver == 8) {
1209                                fw_ap = &fw_resp_v8->ap[i];
1210                                iwl_mvm_ftm_pasn_update_pn(mvm, fw_ap);
1211                        } else if (notif_ver == 7) {
1212                                fw_ap = (void *)&fw_resp_v7->ap[i];
1213                        } else {
1214                                fw_ap = (void *)&fw_resp_v6->ap[i];
1215                        }
1216
1217                        result.final = fw_ap->last_burst;
1218                        result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);
1219                        result.ap_tsf_valid = 1;
1220                } else {
1221                        /* the first part is the same for old and new APIs */
1222                        fw_ap = (void *)&fw_resp_v5->ap[i];
1223                        /*
1224                         * FIXME: the firmware needs to report this, we don't
1225                         * even know the number of bursts the responder picked
1226                         * (if we asked it to)
1227                         */
1228                        result.final = 0;
1229                }
1230
1231                peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req,
1232                                                 fw_ap->bssid);
1233                if (peer_idx < 0) {
1234                        IWL_WARN(mvm,
1235                                 "Unknown address (%pM, target #%d) in FTM response\n",
1236                                 fw_ap->bssid, i);
1237                        continue;
1238                }
1239
1240                switch (fw_ap->measure_status) {
1241                case IWL_TOF_ENTRY_SUCCESS:
1242                        result.status = NL80211_PMSR_STATUS_SUCCESS;
1243                        break;
1244                case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT:
1245                        result.status = NL80211_PMSR_STATUS_TIMEOUT;
1246                        break;
1247                case IWL_TOF_ENTRY_NO_RESPONSE:
1248                        result.status = NL80211_PMSR_STATUS_FAILURE;
1249                        result.ftm.failure_reason =
1250                                NL80211_PMSR_FTM_FAILURE_NO_RESPONSE;
1251                        break;
1252                case IWL_TOF_ENTRY_REQUEST_REJECTED:
1253                        result.status = NL80211_PMSR_STATUS_FAILURE;
1254                        result.ftm.failure_reason =
1255                                NL80211_PMSR_FTM_FAILURE_PEER_BUSY;
1256                        result.ftm.busy_retry_time = fw_ap->refusal_period;
1257                        break;
1258                default:
1259                        result.status = NL80211_PMSR_STATUS_FAILURE;
1260                        result.ftm.failure_reason =
1261                                NL80211_PMSR_FTM_FAILURE_UNSPECIFIED;
1262                        break;
1263                }
1264                memcpy(result.addr, fw_ap->bssid, ETH_ALEN);
1265                result.host_time = iwl_mvm_ftm_get_host_time(mvm,
1266                                                             fw_ap->timestamp);
1267                result.type = NL80211_PMSR_TYPE_FTM;
1268                result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx];
1269                mvm->ftm_initiator.responses[peer_idx]++;
1270                result.ftm.rssi_avg = fw_ap->rssi;
1271                result.ftm.rssi_avg_valid = 1;
1272                result.ftm.rssi_spread = fw_ap->rssi_spread;
1273                result.ftm.rssi_spread_valid = 1;
1274                result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt);
1275                result.ftm.rtt_avg_valid = 1;
1276                result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance);
1277                result.ftm.rtt_variance_valid = 1;
1278                result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread);
1279                result.ftm.rtt_spread_valid = 1;
1280
1281                iwl_mvm_ftm_get_lci_civic(mvm, &result);
1282
1283                iwl_mvm_ftm_rtt_smoothing(mvm, &result);
1284
1285                cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
1286                                     mvm->ftm_initiator.req,
1287                                     &result, GFP_KERNEL);
1288
1289                if (fw_has_api(&mvm->fw->ucode_capa,
1290                               IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
1291                        IWL_DEBUG_INFO(mvm, "RTT confidence: %hhu\n",
1292                                       fw_ap->rttConfidence);
1293
1294                iwl_mvm_debug_range_resp(mvm, i, &result);
1295        }
1296
1297        if (last_in_batch) {
1298                cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
1299                                       mvm->ftm_initiator.req,
1300                                       GFP_KERNEL);
1301                iwl_mvm_ftm_reset(mvm);
1302        }
1303}
1304
1305void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1306{
1307        struct iwl_rx_packet *pkt = rxb_addr(rxb);
1308        const struct ieee80211_mgmt *mgmt = (void *)pkt->data;
1309        size_t len = iwl_rx_packet_payload_len(pkt);
1310        struct iwl_mvm_loc_entry *entry;
1311        const u8 *ies, *lci, *civic, *msr_ie;
1312        size_t ies_len, lci_len = 0, civic_len = 0;
1313        size_t baselen = IEEE80211_MIN_ACTION_SIZE +
1314                         sizeof(mgmt->u.action.u.ftm);
1315        static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI;
1316        static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC;
1317
1318        if (len <= baselen)
1319                return;
1320
1321        lockdep_assert_held(&mvm->mutex);
1322
1323        ies = mgmt->u.action.u.ftm.variable;
1324        ies_len = len - baselen;
1325
1326        msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
1327                                        &rprt_type_lci, 1, 4);
1328        if (msr_ie) {
1329                lci = msr_ie + 2;
1330                lci_len = msr_ie[1];
1331        }
1332
1333        msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
1334                                        &rprt_type_civic, 1, 4);
1335        if (msr_ie) {
1336                civic = msr_ie + 2;
1337                civic_len = msr_ie[1];
1338        }
1339
1340        entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL);
1341        if (!entry)
1342                return;
1343
1344        memcpy(entry->addr, mgmt->bssid, ETH_ALEN);
1345
1346        entry->lci_len = lci_len;
1347        if (lci_len)
1348                memcpy(entry->buf, lci, lci_len);
1349
1350        entry->civic_len = civic_len;
1351        if (civic_len)
1352                memcpy(entry->buf + lci_len, civic, civic_len);
1353
1354        list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list);
1355}
1356