1 /****************************************************************************** 2 * 3 * This file is provided under a dual BSD/GPLv2 license. When using or 4 * redistributing this file, you may do so under either license. 5 * 6 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH 9 * Copyright (C) 2018 Intel Corporation 10 * Copyright (C) 2019 Intel Corporation 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of version 2 of the GNU General Public License as 14 * published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * General Public License for more details. 20 * 21 * The full GNU General Public License is included in this distribution 22 * in the file called COPYING. 23 * 24 * Contact Information: 25 * Intel Linux Wireless <linuxwifi@intel.com> 26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 27 * 28 * BSD LICENSE 29 * 30 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH 31 * Copyright (C) 2018 Intel Corporation 32 * Copyright (C) 2019 Intel Corporation 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 39 * * Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * * Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in 43 * the documentation and/or other materials provided with the 44 * distribution. 45 * * Neither the name Intel Corporation nor the names of its 46 * contributors may be used to endorse or promote products derived 47 * from this software without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 50 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 51 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 52 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 53 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 54 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 55 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 59 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 * 61 *****************************************************************************/ 62 #include <linux/etherdevice.h> 63 #include <linux/math64.h> 64 #include <net/cfg80211.h> 65 #include "mvm.h" 66 #include "iwl-io.h" 67 #include "iwl-prph.h" 68 #include "constants.h" 69 70 struct iwl_mvm_loc_entry { 71 struct list_head list; 72 u8 addr[ETH_ALEN]; 73 u8 lci_len, civic_len; 74 u8 buf[]; 75 }; 76 77 static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm) 78 { 79 struct iwl_mvm_loc_entry *e, *t; 80 81 mvm->ftm_initiator.req = NULL; 82 mvm->ftm_initiator.req_wdev = NULL; 83 memset(mvm->ftm_initiator.responses, 0, 84 sizeof(mvm->ftm_initiator.responses)); 85 list_for_each_entry_safe(e, t, &mvm->ftm_initiator.loc_list, list) { 86 list_del(&e->list); 87 kfree(e); 88 } 89 } 90 91 void iwl_mvm_ftm_restart(struct iwl_mvm *mvm) 92 { 93 struct cfg80211_pmsr_result result = { 94 .status = NL80211_PMSR_STATUS_FAILURE, 95 .final = 1, 96 .host_time = ktime_get_boottime_ns(), 97 .type = NL80211_PMSR_TYPE_FTM, 98 }; 99 int i; 100 101 lockdep_assert_held(&mvm->mutex); 102 103 if (!mvm->ftm_initiator.req) 104 return; 105 106 for (i = 0; i < mvm->ftm_initiator.req->n_peers; i++) { 107 memcpy(result.addr, mvm->ftm_initiator.req->peers[i].addr, 108 ETH_ALEN); 109 result.ftm.burst_index = mvm->ftm_initiator.responses[i]; 110 111 cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev, 112 mvm->ftm_initiator.req, 113 &result, GFP_KERNEL); 114 } 115 116 cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev, 117 mvm->ftm_initiator.req, GFP_KERNEL); 118 iwl_mvm_ftm_reset(mvm); 119 } 120 121 static int 122 iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s) 123 { 124 switch (s) { 125 case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS: 126 return 0; 127 case IWL_TOF_RANGE_REQUEST_STATUS_BUSY: 128 return -EBUSY; 129 default: 130 WARN_ON_ONCE(1); 131 return -EIO; 132 } 133 } 134 135 static void iwl_mvm_ftm_cmd_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 136 struct iwl_tof_range_req_cmd_v5 *cmd, 137 struct cfg80211_pmsr_request *req) 138 { 139 int i; 140 141 cmd->request_id = req->cookie; 142 cmd->num_of_ap = req->n_peers; 143 144 /* use maximum for "no timeout" or bigger than what we can do */ 145 if (!req->timeout || req->timeout > 255 * 100) 146 cmd->req_timeout = 255; 147 else 148 cmd->req_timeout = DIV_ROUND_UP(req->timeout, 100); 149 150 /* 151 * We treat it always as random, since if not we'll 152 * have filled our local address there instead. 153 */ 154 cmd->macaddr_random = 1; 155 memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN); 156 for (i = 0; i < ETH_ALEN; i++) 157 cmd->macaddr_mask[i] = ~req->mac_addr_mask[i]; 158 159 if (vif->bss_conf.assoc) 160 memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN); 161 else 162 eth_broadcast_addr(cmd->range_req_bssid); 163 } 164 165 static void iwl_mvm_ftm_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 166 struct iwl_tof_range_req_cmd *cmd, 167 struct cfg80211_pmsr_request *req) 168 { 169 int i; 170 171 cmd->initiator_flags = 172 cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM | 173 IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT); 174 cmd->request_id = req->cookie; 175 cmd->num_of_ap = req->n_peers; 176 177 /* 178 * Use a large value for "no timeout". Don't use the maximum value 179 * because of fw limitations. 180 */ 181 if (req->timeout) 182 cmd->req_timeout_ms = cpu_to_le32(req->timeout); 183 else 184 cmd->req_timeout_ms = cpu_to_le32(0xfffff); 185 186 memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN); 187 for (i = 0; i < ETH_ALEN; i++) 188 cmd->macaddr_mask[i] = ~req->mac_addr_mask[i]; 189 190 if (vif->bss_conf.assoc) { 191 memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN); 192 193 /* AP's TSF is only relevant if associated */ 194 for (i = 0; i < req->n_peers; i++) { 195 if (req->peers[i].report_ap_tsf) { 196 struct iwl_mvm_vif *mvmvif = 197 iwl_mvm_vif_from_mac80211(vif); 198 199 cmd->tsf_mac_id = cpu_to_le32(mvmvif->id); 200 return; 201 } 202 } 203 } else { 204 eth_broadcast_addr(cmd->range_req_bssid); 205 } 206 207 /* Don't report AP's TSF */ 208 cmd->tsf_mac_id = cpu_to_le32(0xff); 209 } 210 211 static int iwl_mvm_ftm_target_chandef(struct iwl_mvm *mvm, 212 struct cfg80211_pmsr_request_peer *peer, 213 u8 *channel, u8 *bandwidth, 214 u8 *ctrl_ch_position) 215 { 216 u32 freq = peer->chandef.chan->center_freq; 217 218 *channel = ieee80211_frequency_to_channel(freq); 219 220 switch (peer->chandef.width) { 221 case NL80211_CHAN_WIDTH_20_NOHT: 222 *bandwidth = IWL_TOF_BW_20_LEGACY; 223 break; 224 case NL80211_CHAN_WIDTH_20: 225 *bandwidth = IWL_TOF_BW_20_HT; 226 break; 227 case NL80211_CHAN_WIDTH_40: 228 *bandwidth = IWL_TOF_BW_40; 229 break; 230 case NL80211_CHAN_WIDTH_80: 231 *bandwidth = IWL_TOF_BW_80; 232 break; 233 default: 234 IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n", 235 peer->chandef.width); 236 return -EINVAL; 237 } 238 239 *ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ? 240 iwl_mvm_get_ctrl_pos(&peer->chandef) : 0; 241 242 return 0; 243 } 244 245 static int 246 iwl_mvm_ftm_put_target_v2(struct iwl_mvm *mvm, 247 struct cfg80211_pmsr_request_peer *peer, 248 struct iwl_tof_range_req_ap_entry_v2 *target) 249 { 250 int ret; 251 252 ret = iwl_mvm_ftm_target_chandef(mvm, peer, &target->channel_num, 253 &target->bandwidth, 254 &target->ctrl_ch_position); 255 if (ret) 256 return ret; 257 258 memcpy(target->bssid, peer->addr, ETH_ALEN); 259 target->burst_period = 260 cpu_to_le16(peer->ftm.burst_period); 261 target->samples_per_burst = peer->ftm.ftms_per_burst; 262 target->num_of_bursts = peer->ftm.num_bursts_exp; 263 target->measure_type = 0; /* regular two-sided FTM */ 264 target->retries_per_sample = peer->ftm.ftmr_retries; 265 target->asap_mode = peer->ftm.asap; 266 target->enable_dyn_ack = IWL_MVM_FTM_INITIATOR_DYNACK; 267 268 if (peer->ftm.request_lci) 269 target->location_req |= IWL_TOF_LOC_LCI; 270 if (peer->ftm.request_civicloc) 271 target->location_req |= IWL_TOF_LOC_CIVIC; 272 273 target->algo_type = IWL_MVM_FTM_INITIATOR_ALGO; 274 275 return 0; 276 } 277 278 #define FTM_PUT_FLAG(flag) (target->initiator_ap_flags |= \ 279 cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag)) 280 281 static int iwl_mvm_ftm_put_target(struct iwl_mvm *mvm, 282 struct cfg80211_pmsr_request_peer *peer, 283 struct iwl_tof_range_req_ap_entry *target) 284 { 285 int ret; 286 287 ret = iwl_mvm_ftm_target_chandef(mvm, peer, &target->channel_num, 288 &target->bandwidth, 289 &target->ctrl_ch_position); 290 if (ret) 291 return ret; 292 293 memcpy(target->bssid, peer->addr, ETH_ALEN); 294 target->burst_period = 295 cpu_to_le16(peer->ftm.burst_period); 296 target->samples_per_burst = peer->ftm.ftms_per_burst; 297 target->num_of_bursts = peer->ftm.num_bursts_exp; 298 target->ftmr_max_retries = peer->ftm.ftmr_retries; 299 target->initiator_ap_flags = cpu_to_le32(0); 300 301 if (peer->ftm.asap) 302 FTM_PUT_FLAG(ASAP); 303 304 if (peer->ftm.request_lci) 305 FTM_PUT_FLAG(LCI_REQUEST); 306 307 if (peer->ftm.request_civicloc) 308 FTM_PUT_FLAG(CIVIC_REQUEST); 309 310 if (IWL_MVM_FTM_INITIATOR_DYNACK) 311 FTM_PUT_FLAG(DYN_ACK); 312 313 if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG) 314 FTM_PUT_FLAG(ALGO_LR); 315 else if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT) 316 FTM_PUT_FLAG(ALGO_FFT); 317 318 return 0; 319 } 320 321 int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 322 struct cfg80211_pmsr_request *req) 323 { 324 struct iwl_tof_range_req_cmd_v5 cmd_v5; 325 struct iwl_tof_range_req_cmd cmd; 326 bool new_api = fw_has_api(&mvm->fw->ucode_capa, 327 IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ); 328 u8 num_of_ap; 329 struct iwl_host_cmd hcmd = { 330 .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0), 331 .dataflags[0] = IWL_HCMD_DFL_DUP, 332 }; 333 u32 status = 0; 334 int err, i; 335 336 lockdep_assert_held(&mvm->mutex); 337 338 if (mvm->ftm_initiator.req) 339 return -EBUSY; 340 341 if (new_api) { 342 iwl_mvm_ftm_cmd(mvm, vif, &cmd, req); 343 hcmd.data[0] = &cmd; 344 hcmd.len[0] = sizeof(cmd); 345 num_of_ap = cmd.num_of_ap; 346 } else { 347 iwl_mvm_ftm_cmd_v5(mvm, vif, &cmd_v5, req); 348 hcmd.data[0] = &cmd_v5; 349 hcmd.len[0] = sizeof(cmd_v5); 350 num_of_ap = cmd_v5.num_of_ap; 351 } 352 353 for (i = 0; i < num_of_ap; i++) { 354 struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 355 356 if (new_api) 357 err = iwl_mvm_ftm_put_target(mvm, peer, &cmd.ap[i]); 358 else 359 err = iwl_mvm_ftm_put_target_v2(mvm, peer, 360 &cmd_v5.ap[i]); 361 362 if (err) 363 return err; 364 } 365 366 err = iwl_mvm_send_cmd_status(mvm, &hcmd, &status); 367 if (!err && status) { 368 IWL_ERR(mvm, "FTM range request command failure, status: %u\n", 369 status); 370 err = iwl_ftm_range_request_status_to_err(status); 371 } 372 373 if (!err) { 374 mvm->ftm_initiator.req = req; 375 mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif); 376 } 377 378 return err; 379 } 380 381 void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req) 382 { 383 struct iwl_tof_range_abort_cmd cmd = { 384 .request_id = req->cookie, 385 }; 386 387 lockdep_assert_held(&mvm->mutex); 388 389 if (req != mvm->ftm_initiator.req) 390 return; 391 392 if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RANGE_ABORT_CMD, 393 LOCATION_GROUP, 0), 394 0, sizeof(cmd), &cmd)) 395 IWL_ERR(mvm, "failed to abort FTM process\n"); 396 } 397 398 static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req, 399 const u8 *addr) 400 { 401 int i; 402 403 for (i = 0; i < req->n_peers; i++) { 404 struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; 405 406 if (ether_addr_equal_unaligned(peer->addr, addr)) 407 return i; 408 } 409 410 return -ENOENT; 411 } 412 413 static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts) 414 { 415 u32 gp2_ts = le32_to_cpu(fw_gp2_ts); 416 u32 curr_gp2, diff; 417 u64 now_from_boot_ns; 418 419 iwl_mvm_get_sync_time(mvm, &curr_gp2, &now_from_boot_ns); 420 421 if (curr_gp2 >= gp2_ts) 422 diff = curr_gp2 - gp2_ts; 423 else 424 diff = curr_gp2 + (U32_MAX - gp2_ts + 1); 425 426 return now_from_boot_ns - (u64)diff * 1000; 427 } 428 429 static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm, 430 struct cfg80211_pmsr_result *res) 431 { 432 struct iwl_mvm_loc_entry *entry; 433 434 list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) { 435 if (!ether_addr_equal_unaligned(res->addr, entry->addr)) 436 continue; 437 438 if (entry->lci_len) { 439 res->ftm.lci_len = entry->lci_len; 440 res->ftm.lci = entry->buf; 441 } 442 443 if (entry->civic_len) { 444 res->ftm.civicloc_len = entry->civic_len; 445 res->ftm.civicloc = entry->buf + entry->lci_len; 446 } 447 448 /* we found the entry we needed */ 449 break; 450 } 451 } 452 453 static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id, 454 u8 num_of_aps) 455 { 456 lockdep_assert_held(&mvm->mutex); 457 458 if (request_id != (u8)mvm->ftm_initiator.req->cookie) { 459 IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n", 460 request_id, (u8)mvm->ftm_initiator.req->cookie); 461 return -EINVAL; 462 } 463 464 if (num_of_aps > mvm->ftm_initiator.req->n_peers) { 465 IWL_ERR(mvm, "FTM range response invalid\n"); 466 return -EINVAL; 467 } 468 469 return 0; 470 } 471 472 static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index, 473 struct cfg80211_pmsr_result *res) 474 { 475 s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666); 476 477 IWL_DEBUG_INFO(mvm, "entry %d\n", index); 478 IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status); 479 IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr); 480 IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time); 481 IWL_DEBUG_INFO(mvm, "\tburst index: %hhu\n", res->ftm.burst_index); 482 IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes); 483 IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg); 484 IWL_DEBUG_INFO(mvm, "\trssi spread: %hhu\n", res->ftm.rssi_spread); 485 IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg); 486 IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance); 487 IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread); 488 IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg); 489 } 490 491 void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 492 { 493 struct iwl_rx_packet *pkt = rxb_addr(rxb); 494 struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data; 495 struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data; 496 struct iwl_tof_range_rsp_ntfy *fw_resp = (void *)pkt->data; 497 int i; 498 bool new_api = fw_has_api(&mvm->fw->ucode_capa, 499 IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ); 500 u8 num_of_aps, last_in_batch; 501 502 lockdep_assert_held(&mvm->mutex); 503 504 if (!mvm->ftm_initiator.req) { 505 IWL_ERR(mvm, "Got FTM response but have no request?\n"); 506 return; 507 } 508 509 if (new_api) { 510 if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp->request_id, 511 fw_resp->num_of_aps)) 512 return; 513 514 num_of_aps = fw_resp->num_of_aps; 515 last_in_batch = fw_resp->last_report; 516 } else { 517 if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id, 518 fw_resp_v5->num_of_aps)) 519 return; 520 521 num_of_aps = fw_resp_v5->num_of_aps; 522 last_in_batch = fw_resp_v5->last_in_batch; 523 } 524 525 IWL_DEBUG_INFO(mvm, "Range response received\n"); 526 IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %hhu\n", 527 mvm->ftm_initiator.req->cookie, num_of_aps); 528 529 for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) { 530 struct cfg80211_pmsr_result result = {}; 531 struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap; 532 int peer_idx; 533 534 if (new_api) { 535 if (fw_has_api(&mvm->fw->ucode_capa, 536 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) 537 fw_ap = &fw_resp->ap[i]; 538 else 539 fw_ap = (void *)&fw_resp_v6->ap[i]; 540 541 result.final = fw_resp->ap[i].last_burst; 542 result.ap_tsf = le32_to_cpu(fw_ap->start_tsf); 543 result.ap_tsf_valid = 1; 544 } else { 545 /* the first part is the same for old and new APIs */ 546 fw_ap = (void *)&fw_resp_v5->ap[i]; 547 /* 548 * FIXME: the firmware needs to report this, we don't 549 * even know the number of bursts the responder picked 550 * (if we asked it to) 551 */ 552 result.final = 0; 553 } 554 555 peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req, 556 fw_ap->bssid); 557 if (peer_idx < 0) { 558 IWL_WARN(mvm, 559 "Unknown address (%pM, target #%d) in FTM response\n", 560 fw_ap->bssid, i); 561 continue; 562 } 563 564 switch (fw_ap->measure_status) { 565 case IWL_TOF_ENTRY_SUCCESS: 566 result.status = NL80211_PMSR_STATUS_SUCCESS; 567 break; 568 case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT: 569 result.status = NL80211_PMSR_STATUS_TIMEOUT; 570 break; 571 case IWL_TOF_ENTRY_NO_RESPONSE: 572 result.status = NL80211_PMSR_STATUS_FAILURE; 573 result.ftm.failure_reason = 574 NL80211_PMSR_FTM_FAILURE_NO_RESPONSE; 575 break; 576 case IWL_TOF_ENTRY_REQUEST_REJECTED: 577 result.status = NL80211_PMSR_STATUS_FAILURE; 578 result.ftm.failure_reason = 579 NL80211_PMSR_FTM_FAILURE_PEER_BUSY; 580 result.ftm.busy_retry_time = fw_ap->refusal_period; 581 break; 582 default: 583 result.status = NL80211_PMSR_STATUS_FAILURE; 584 result.ftm.failure_reason = 585 NL80211_PMSR_FTM_FAILURE_UNSPECIFIED; 586 break; 587 } 588 memcpy(result.addr, fw_ap->bssid, ETH_ALEN); 589 result.host_time = iwl_mvm_ftm_get_host_time(mvm, 590 fw_ap->timestamp); 591 result.type = NL80211_PMSR_TYPE_FTM; 592 result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx]; 593 mvm->ftm_initiator.responses[peer_idx]++; 594 result.ftm.rssi_avg = fw_ap->rssi; 595 result.ftm.rssi_avg_valid = 1; 596 result.ftm.rssi_spread = fw_ap->rssi_spread; 597 result.ftm.rssi_spread_valid = 1; 598 result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt); 599 result.ftm.rtt_avg_valid = 1; 600 result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance); 601 result.ftm.rtt_variance_valid = 1; 602 result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread); 603 result.ftm.rtt_spread_valid = 1; 604 605 iwl_mvm_ftm_get_lci_civic(mvm, &result); 606 607 cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev, 608 mvm->ftm_initiator.req, 609 &result, GFP_KERNEL); 610 611 if (fw_has_api(&mvm->fw->ucode_capa, 612 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) 613 IWL_DEBUG_INFO(mvm, "RTT confidence: %hhu\n", 614 fw_ap->rttConfidence); 615 616 iwl_mvm_debug_range_resp(mvm, i, &result); 617 } 618 619 if (last_in_batch) { 620 cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev, 621 mvm->ftm_initiator.req, 622 GFP_KERNEL); 623 iwl_mvm_ftm_reset(mvm); 624 } 625 } 626 627 void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 628 { 629 struct iwl_rx_packet *pkt = rxb_addr(rxb); 630 const struct ieee80211_mgmt *mgmt = (void *)pkt->data; 631 size_t len = iwl_rx_packet_payload_len(pkt); 632 struct iwl_mvm_loc_entry *entry; 633 const u8 *ies, *lci, *civic, *msr_ie; 634 size_t ies_len, lci_len = 0, civic_len = 0; 635 size_t baselen = IEEE80211_MIN_ACTION_SIZE + 636 sizeof(mgmt->u.action.u.ftm); 637 static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI; 638 static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC; 639 640 if (len <= baselen) 641 return; 642 643 lockdep_assert_held(&mvm->mutex); 644 645 ies = mgmt->u.action.u.ftm.variable; 646 ies_len = len - baselen; 647 648 msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len, 649 &rprt_type_lci, 1, 4); 650 if (msr_ie) { 651 lci = msr_ie + 2; 652 lci_len = msr_ie[1]; 653 } 654 655 msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len, 656 &rprt_type_civic, 1, 4); 657 if (msr_ie) { 658 civic = msr_ie + 2; 659 civic_len = msr_ie[1]; 660 } 661 662 entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL); 663 if (!entry) 664 return; 665 666 memcpy(entry->addr, mgmt->bssid, ETH_ALEN); 667 668 entry->lci_len = lci_len; 669 if (lci_len) 670 memcpy(entry->buf, lci, lci_len); 671 672 entry->civic_len = civic_len; 673 if (civic_len) 674 memcpy(entry->buf + lci_len, civic, civic_len); 675 676 list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list); 677 } 678