gpio: rcar: Fix runtime PM imbalance on error
[linux/fpc-iii.git] / drivers / net / wireless / intel / iwlwifi / mvm / ftm-initiator.c
blob9e21f5e5d36498b2d96575b83e0e74b6af30f175
1 /******************************************************************************
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.
6 * GPL LICENSE SUMMARY
8 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
9 * Copyright (C) 2018 Intel Corporation
10 * Copyright (C) 2019 Intel Corporation
11 * Copyright (C) 2020 Intel Corporation
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of version 2 of the GNU General Public License as
15 * published by the Free Software Foundation.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * The full GNU General Public License is included in this distribution
23 * in the file called COPYING.
25 * Contact Information:
26 * Intel Linux Wireless <linuxwifi@intel.com>
27 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
29 * BSD LICENSE
31 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
32 * Copyright (C) 2018 Intel Corporation
33 * Copyright (C) 2019 Intel Corporation
34 * Copyright (C) 2020 Intel Corporation
35 * All rights reserved.
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
41 * * Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * * Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in
45 * the documentation and/or other materials provided with the
46 * distribution.
47 * * Neither the name Intel Corporation nor the names of its
48 * contributors may be used to endorse or promote products derived
49 * from this software without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
52 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
53 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
54 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
55 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
56 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
57 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
59 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
61 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63 *****************************************************************************/
64 #include <linux/etherdevice.h>
65 #include <linux/math64.h>
66 #include <net/cfg80211.h>
67 #include "mvm.h"
68 #include "iwl-io.h"
69 #include "iwl-prph.h"
70 #include "constants.h"
72 struct iwl_mvm_loc_entry {
73 struct list_head list;
74 u8 addr[ETH_ALEN];
75 u8 lci_len, civic_len;
76 u8 buf[];
79 static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm)
81 struct iwl_mvm_loc_entry *e, *t;
83 mvm->ftm_initiator.req = NULL;
84 mvm->ftm_initiator.req_wdev = NULL;
85 memset(mvm->ftm_initiator.responses, 0,
86 sizeof(mvm->ftm_initiator.responses));
87 list_for_each_entry_safe(e, t, &mvm->ftm_initiator.loc_list, list) {
88 list_del(&e->list);
89 kfree(e);
93 void iwl_mvm_ftm_restart(struct iwl_mvm *mvm)
95 struct cfg80211_pmsr_result result = {
96 .status = NL80211_PMSR_STATUS_FAILURE,
97 .final = 1,
98 .host_time = ktime_get_boottime_ns(),
99 .type = NL80211_PMSR_TYPE_FTM,
101 int i;
103 lockdep_assert_held(&mvm->mutex);
105 if (!mvm->ftm_initiator.req)
106 return;
108 for (i = 0; i < mvm->ftm_initiator.req->n_peers; i++) {
109 memcpy(result.addr, mvm->ftm_initiator.req->peers[i].addr,
110 ETH_ALEN);
111 result.ftm.burst_index = mvm->ftm_initiator.responses[i];
113 cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
114 mvm->ftm_initiator.req,
115 &result, GFP_KERNEL);
118 cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
119 mvm->ftm_initiator.req, GFP_KERNEL);
120 iwl_mvm_ftm_reset(mvm);
123 static int
124 iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s)
126 switch (s) {
127 case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS:
128 return 0;
129 case IWL_TOF_RANGE_REQUEST_STATUS_BUSY:
130 return -EBUSY;
131 default:
132 WARN_ON_ONCE(1);
133 return -EIO;
137 static void iwl_mvm_ftm_cmd_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
138 struct iwl_tof_range_req_cmd_v5 *cmd,
139 struct cfg80211_pmsr_request *req)
141 int i;
143 cmd->request_id = req->cookie;
144 cmd->num_of_ap = req->n_peers;
146 /* use maximum for "no timeout" or bigger than what we can do */
147 if (!req->timeout || req->timeout > 255 * 100)
148 cmd->req_timeout = 255;
149 else
150 cmd->req_timeout = DIV_ROUND_UP(req->timeout, 100);
153 * We treat it always as random, since if not we'll
154 * have filled our local address there instead.
156 cmd->macaddr_random = 1;
157 memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
158 for (i = 0; i < ETH_ALEN; i++)
159 cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
161 if (vif->bss_conf.assoc)
162 memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
163 else
164 eth_broadcast_addr(cmd->range_req_bssid);
167 static void iwl_mvm_ftm_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
168 struct iwl_tof_range_req_cmd *cmd,
169 struct cfg80211_pmsr_request *req)
171 int i;
173 cmd->initiator_flags =
174 cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM |
175 IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT);
176 cmd->request_id = req->cookie;
177 cmd->num_of_ap = req->n_peers;
180 * Use a large value for "no timeout". Don't use the maximum value
181 * because of fw limitations.
183 if (req->timeout)
184 cmd->req_timeout_ms = cpu_to_le32(req->timeout);
185 else
186 cmd->req_timeout_ms = cpu_to_le32(0xfffff);
188 memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
189 for (i = 0; i < ETH_ALEN; i++)
190 cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
192 if (vif->bss_conf.assoc) {
193 memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
195 /* AP's TSF is only relevant if associated */
196 for (i = 0; i < req->n_peers; i++) {
197 if (req->peers[i].report_ap_tsf) {
198 struct iwl_mvm_vif *mvmvif =
199 iwl_mvm_vif_from_mac80211(vif);
201 cmd->tsf_mac_id = cpu_to_le32(mvmvif->id);
202 return;
205 } else {
206 eth_broadcast_addr(cmd->range_req_bssid);
209 /* Don't report AP's TSF */
210 cmd->tsf_mac_id = cpu_to_le32(0xff);
213 static int
214 iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm *mvm,
215 struct cfg80211_pmsr_request_peer *peer,
216 u8 *channel, u8 *bandwidth,
217 u8 *ctrl_ch_position)
219 u32 freq = peer->chandef.chan->center_freq;
221 *channel = ieee80211_frequency_to_channel(freq);
223 switch (peer->chandef.width) {
224 case NL80211_CHAN_WIDTH_20_NOHT:
225 *bandwidth = IWL_TOF_BW_20_LEGACY;
226 break;
227 case NL80211_CHAN_WIDTH_20:
228 *bandwidth = IWL_TOF_BW_20_HT;
229 break;
230 case NL80211_CHAN_WIDTH_40:
231 *bandwidth = IWL_TOF_BW_40;
232 break;
233 case NL80211_CHAN_WIDTH_80:
234 *bandwidth = IWL_TOF_BW_80;
235 break;
236 default:
237 IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",
238 peer->chandef.width);
239 return -EINVAL;
242 *ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
243 iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;
245 return 0;
248 static int
249 iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm,
250 struct cfg80211_pmsr_request_peer *peer,
251 u8 *channel, u8 *format_bw,
252 u8 *ctrl_ch_position)
254 u32 freq = peer->chandef.chan->center_freq;
256 *channel = ieee80211_frequency_to_channel(freq);
258 switch (peer->chandef.width) {
259 case NL80211_CHAN_WIDTH_20_NOHT:
260 *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
261 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
262 break;
263 case NL80211_CHAN_WIDTH_20:
264 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
265 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
266 break;
267 case NL80211_CHAN_WIDTH_40:
268 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
269 *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
270 break;
271 case NL80211_CHAN_WIDTH_80:
272 *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
273 *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
274 break;
275 default:
276 IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",
277 peer->chandef.width);
278 return -EINVAL;
281 /* non EDCA based measurement must use HE preamble */
282 if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
283 *format_bw |= IWL_LOCATION_FRAME_FORMAT_HE;
285 *ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
286 iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;
288 return 0;
291 static int
292 iwl_mvm_ftm_put_target_v2(struct iwl_mvm *mvm,
293 struct cfg80211_pmsr_request_peer *peer,
294 struct iwl_tof_range_req_ap_entry_v2 *target)
296 int ret;
298 ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,
299 &target->bandwidth,
300 &target->ctrl_ch_position);
301 if (ret)
302 return ret;
304 memcpy(target->bssid, peer->addr, ETH_ALEN);
305 target->burst_period =
306 cpu_to_le16(peer->ftm.burst_period);
307 target->samples_per_burst = peer->ftm.ftms_per_burst;
308 target->num_of_bursts = peer->ftm.num_bursts_exp;
309 target->measure_type = 0; /* regular two-sided FTM */
310 target->retries_per_sample = peer->ftm.ftmr_retries;
311 target->asap_mode = peer->ftm.asap;
312 target->enable_dyn_ack = IWL_MVM_FTM_INITIATOR_DYNACK;
314 if (peer->ftm.request_lci)
315 target->location_req |= IWL_TOF_LOC_LCI;
316 if (peer->ftm.request_civicloc)
317 target->location_req |= IWL_TOF_LOC_CIVIC;
319 target->algo_type = IWL_MVM_FTM_INITIATOR_ALGO;
321 return 0;
324 #define FTM_PUT_FLAG(flag) (target->initiator_ap_flags |= \
325 cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))
327 static void
328 iwl_mvm_ftm_put_target_common(struct iwl_mvm *mvm,
329 struct cfg80211_pmsr_request_peer *peer,
330 struct iwl_tof_range_req_ap_entry *target)
332 memcpy(target->bssid, peer->addr, ETH_ALEN);
333 target->burst_period =
334 cpu_to_le16(peer->ftm.burst_period);
335 target->samples_per_burst = peer->ftm.ftms_per_burst;
336 target->num_of_bursts = peer->ftm.num_bursts_exp;
337 target->ftmr_max_retries = peer->ftm.ftmr_retries;
338 target->initiator_ap_flags = cpu_to_le32(0);
340 if (peer->ftm.asap)
341 FTM_PUT_FLAG(ASAP);
343 if (peer->ftm.request_lci)
344 FTM_PUT_FLAG(LCI_REQUEST);
346 if (peer->ftm.request_civicloc)
347 FTM_PUT_FLAG(CIVIC_REQUEST);
349 if (IWL_MVM_FTM_INITIATOR_DYNACK)
350 FTM_PUT_FLAG(DYN_ACK);
352 if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG)
353 FTM_PUT_FLAG(ALGO_LR);
354 else if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT)
355 FTM_PUT_FLAG(ALGO_FFT);
357 if (peer->ftm.trigger_based)
358 FTM_PUT_FLAG(TB);
359 else if (peer->ftm.non_trigger_based)
360 FTM_PUT_FLAG(NON_TB);
363 static int
364 iwl_mvm_ftm_put_target_v3(struct iwl_mvm *mvm,
365 struct cfg80211_pmsr_request_peer *peer,
366 struct iwl_tof_range_req_ap_entry_v3 *target)
368 int ret;
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;
377 * Versions 3 and 4 has some common fields, so
378 * iwl_mvm_ftm_put_target_common() can be used for version 7 too.
380 iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);
382 return 0;
385 static int iwl_mvm_ftm_put_target_v4(struct iwl_mvm *mvm,
386 struct cfg80211_pmsr_request_peer *peer,
387 struct iwl_tof_range_req_ap_entry *target)
389 int ret;
391 ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,
392 &target->format_bw,
393 &target->ctrl_ch_position);
394 if (ret)
395 return ret;
397 iwl_mvm_ftm_put_target_common(mvm, peer, target);
399 return 0;
402 static int iwl_mvm_ftm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *hcmd)
404 u32 status;
405 int err = iwl_mvm_send_cmd_status(mvm, hcmd, &status);
407 if (!err && status) {
408 IWL_ERR(mvm, "FTM range request command failure, status: %u\n",
409 status);
410 err = iwl_ftm_range_request_status_to_err(status);
413 return err;
416 static int iwl_mvm_ftm_start_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
417 struct cfg80211_pmsr_request *req)
419 struct iwl_tof_range_req_cmd_v5 cmd_v5;
420 struct iwl_host_cmd hcmd = {
421 .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0),
422 .dataflags[0] = IWL_HCMD_DFL_DUP,
423 .data[0] = &cmd_v5,
424 .len[0] = sizeof(cmd_v5),
426 u8 i;
427 int err;
429 iwl_mvm_ftm_cmd_v5(mvm, vif, &cmd_v5, req);
431 for (i = 0; i < cmd_v5.num_of_ap; i++) {
432 struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
434 err = iwl_mvm_ftm_put_target_v2(mvm, peer, &cmd_v5.ap[i]);
435 if (err)
436 return err;
439 return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
442 static int iwl_mvm_ftm_start_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
443 struct cfg80211_pmsr_request *req)
445 struct iwl_tof_range_req_cmd_v7 cmd_v7;
446 struct iwl_host_cmd hcmd = {
447 .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0),
448 .dataflags[0] = IWL_HCMD_DFL_DUP,
449 .data[0] = &cmd_v7,
450 .len[0] = sizeof(cmd_v7),
452 u8 i;
453 int err;
456 * Versions 7 and 8 has the same structure except from the responders
457 * list, so iwl_mvm_ftm_cmd() can be used for version 7 too.
459 iwl_mvm_ftm_cmd(mvm, vif, (void *)&cmd_v7, req);
461 for (i = 0; i < cmd_v7.num_of_ap; i++) {
462 struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
464 err = iwl_mvm_ftm_put_target_v3(mvm, peer, &cmd_v7.ap[i]);
465 if (err)
466 return err;
469 return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
472 static int iwl_mvm_ftm_start_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
473 struct cfg80211_pmsr_request *req)
475 struct iwl_tof_range_req_cmd cmd;
476 struct iwl_host_cmd hcmd = {
477 .id = iwl_cmd_id(TOF_RANGE_REQ_CMD, LOCATION_GROUP, 0),
478 .dataflags[0] = IWL_HCMD_DFL_DUP,
479 .data[0] = &cmd,
480 .len[0] = sizeof(cmd),
482 u8 i;
483 int err;
485 iwl_mvm_ftm_cmd(mvm, vif, &cmd, req);
487 for (i = 0; i < cmd.num_of_ap; i++) {
488 struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
490 err = iwl_mvm_ftm_put_target_v4(mvm, peer, &cmd.ap[i]);
491 if (err)
492 return err;
495 return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
498 int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
499 struct cfg80211_pmsr_request *req)
501 bool new_api = fw_has_api(&mvm->fw->ucode_capa,
502 IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
503 int err;
505 lockdep_assert_held(&mvm->mutex);
507 if (mvm->ftm_initiator.req)
508 return -EBUSY;
510 if (new_api) {
511 u8 cmd_ver = iwl_mvm_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
512 TOF_RANGE_REQ_CMD);
514 if (cmd_ver == 8)
515 err = iwl_mvm_ftm_start_v8(mvm, vif, req);
516 else
517 err = iwl_mvm_ftm_start_v7(mvm, vif, req);
519 } else {
520 err = iwl_mvm_ftm_start_v5(mvm, vif, req);
523 if (!err) {
524 mvm->ftm_initiator.req = req;
525 mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif);
528 return err;
531 void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req)
533 struct iwl_tof_range_abort_cmd cmd = {
534 .request_id = req->cookie,
537 lockdep_assert_held(&mvm->mutex);
539 if (req != mvm->ftm_initiator.req)
540 return;
542 iwl_mvm_ftm_reset(mvm);
544 if (iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RANGE_ABORT_CMD,
545 LOCATION_GROUP, 0),
546 0, sizeof(cmd), &cmd))
547 IWL_ERR(mvm, "failed to abort FTM process\n");
550 static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req,
551 const u8 *addr)
553 int i;
555 for (i = 0; i < req->n_peers; i++) {
556 struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
558 if (ether_addr_equal_unaligned(peer->addr, addr))
559 return i;
562 return -ENOENT;
565 static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts)
567 u32 gp2_ts = le32_to_cpu(fw_gp2_ts);
568 u32 curr_gp2, diff;
569 u64 now_from_boot_ns;
571 iwl_mvm_get_sync_time(mvm, &curr_gp2, &now_from_boot_ns);
573 if (curr_gp2 >= gp2_ts)
574 diff = curr_gp2 - gp2_ts;
575 else
576 diff = curr_gp2 + (U32_MAX - gp2_ts + 1);
578 return now_from_boot_ns - (u64)diff * 1000;
581 static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm,
582 struct cfg80211_pmsr_result *res)
584 struct iwl_mvm_loc_entry *entry;
586 list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) {
587 if (!ether_addr_equal_unaligned(res->addr, entry->addr))
588 continue;
590 if (entry->lci_len) {
591 res->ftm.lci_len = entry->lci_len;
592 res->ftm.lci = entry->buf;
595 if (entry->civic_len) {
596 res->ftm.civicloc_len = entry->civic_len;
597 res->ftm.civicloc = entry->buf + entry->lci_len;
600 /* we found the entry we needed */
601 break;
605 static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id,
606 u8 num_of_aps)
608 lockdep_assert_held(&mvm->mutex);
610 if (request_id != (u8)mvm->ftm_initiator.req->cookie) {
611 IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n",
612 request_id, (u8)mvm->ftm_initiator.req->cookie);
613 return -EINVAL;
616 if (num_of_aps > mvm->ftm_initiator.req->n_peers) {
617 IWL_ERR(mvm, "FTM range response invalid\n");
618 return -EINVAL;
621 return 0;
624 static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
625 struct cfg80211_pmsr_result *res)
627 s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);
629 IWL_DEBUG_INFO(mvm, "entry %d\n", index);
630 IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status);
631 IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr);
632 IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time);
633 IWL_DEBUG_INFO(mvm, "\tburst index: %hhu\n", res->ftm.burst_index);
634 IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes);
635 IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg);
636 IWL_DEBUG_INFO(mvm, "\trssi spread: %hhu\n", res->ftm.rssi_spread);
637 IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg);
638 IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance);
639 IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread);
640 IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg);
643 void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
645 struct iwl_rx_packet *pkt = rxb_addr(rxb);
646 struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data;
647 struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data;
648 struct iwl_tof_range_rsp_ntfy *fw_resp = (void *)pkt->data;
649 int i;
650 bool new_api = fw_has_api(&mvm->fw->ucode_capa,
651 IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
652 u8 num_of_aps, last_in_batch;
654 lockdep_assert_held(&mvm->mutex);
656 if (!mvm->ftm_initiator.req) {
657 return;
660 if (new_api) {
661 if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp->request_id,
662 fw_resp->num_of_aps))
663 return;
665 num_of_aps = fw_resp->num_of_aps;
666 last_in_batch = fw_resp->last_report;
667 } else {
668 if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id,
669 fw_resp_v5->num_of_aps))
670 return;
672 num_of_aps = fw_resp_v5->num_of_aps;
673 last_in_batch = fw_resp_v5->last_in_batch;
676 IWL_DEBUG_INFO(mvm, "Range response received\n");
677 IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %hhu\n",
678 mvm->ftm_initiator.req->cookie, num_of_aps);
680 for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) {
681 struct cfg80211_pmsr_result result = {};
682 struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap;
683 int peer_idx;
685 if (new_api) {
686 if (fw_has_api(&mvm->fw->ucode_capa,
687 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
688 fw_ap = &fw_resp->ap[i];
689 else
690 fw_ap = (void *)&fw_resp_v6->ap[i];
692 result.final = fw_resp->ap[i].last_burst;
693 result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);
694 result.ap_tsf_valid = 1;
695 } else {
696 /* the first part is the same for old and new APIs */
697 fw_ap = (void *)&fw_resp_v5->ap[i];
699 * FIXME: the firmware needs to report this, we don't
700 * even know the number of bursts the responder picked
701 * (if we asked it to)
703 result.final = 0;
706 peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req,
707 fw_ap->bssid);
708 if (peer_idx < 0) {
709 IWL_WARN(mvm,
710 "Unknown address (%pM, target #%d) in FTM response\n",
711 fw_ap->bssid, i);
712 continue;
715 switch (fw_ap->measure_status) {
716 case IWL_TOF_ENTRY_SUCCESS:
717 result.status = NL80211_PMSR_STATUS_SUCCESS;
718 break;
719 case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT:
720 result.status = NL80211_PMSR_STATUS_TIMEOUT;
721 break;
722 case IWL_TOF_ENTRY_NO_RESPONSE:
723 result.status = NL80211_PMSR_STATUS_FAILURE;
724 result.ftm.failure_reason =
725 NL80211_PMSR_FTM_FAILURE_NO_RESPONSE;
726 break;
727 case IWL_TOF_ENTRY_REQUEST_REJECTED:
728 result.status = NL80211_PMSR_STATUS_FAILURE;
729 result.ftm.failure_reason =
730 NL80211_PMSR_FTM_FAILURE_PEER_BUSY;
731 result.ftm.busy_retry_time = fw_ap->refusal_period;
732 break;
733 default:
734 result.status = NL80211_PMSR_STATUS_FAILURE;
735 result.ftm.failure_reason =
736 NL80211_PMSR_FTM_FAILURE_UNSPECIFIED;
737 break;
739 memcpy(result.addr, fw_ap->bssid, ETH_ALEN);
740 result.host_time = iwl_mvm_ftm_get_host_time(mvm,
741 fw_ap->timestamp);
742 result.type = NL80211_PMSR_TYPE_FTM;
743 result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx];
744 mvm->ftm_initiator.responses[peer_idx]++;
745 result.ftm.rssi_avg = fw_ap->rssi;
746 result.ftm.rssi_avg_valid = 1;
747 result.ftm.rssi_spread = fw_ap->rssi_spread;
748 result.ftm.rssi_spread_valid = 1;
749 result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt);
750 result.ftm.rtt_avg_valid = 1;
751 result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance);
752 result.ftm.rtt_variance_valid = 1;
753 result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread);
754 result.ftm.rtt_spread_valid = 1;
756 iwl_mvm_ftm_get_lci_civic(mvm, &result);
758 cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
759 mvm->ftm_initiator.req,
760 &result, GFP_KERNEL);
762 if (fw_has_api(&mvm->fw->ucode_capa,
763 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
764 IWL_DEBUG_INFO(mvm, "RTT confidence: %hhu\n",
765 fw_ap->rttConfidence);
767 iwl_mvm_debug_range_resp(mvm, i, &result);
770 if (last_in_batch) {
771 cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
772 mvm->ftm_initiator.req,
773 GFP_KERNEL);
774 iwl_mvm_ftm_reset(mvm);
778 void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
780 struct iwl_rx_packet *pkt = rxb_addr(rxb);
781 const struct ieee80211_mgmt *mgmt = (void *)pkt->data;
782 size_t len = iwl_rx_packet_payload_len(pkt);
783 struct iwl_mvm_loc_entry *entry;
784 const u8 *ies, *lci, *civic, *msr_ie;
785 size_t ies_len, lci_len = 0, civic_len = 0;
786 size_t baselen = IEEE80211_MIN_ACTION_SIZE +
787 sizeof(mgmt->u.action.u.ftm);
788 static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI;
789 static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC;
791 if (len <= baselen)
792 return;
794 lockdep_assert_held(&mvm->mutex);
796 ies = mgmt->u.action.u.ftm.variable;
797 ies_len = len - baselen;
799 msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
800 &rprt_type_lci, 1, 4);
801 if (msr_ie) {
802 lci = msr_ie + 2;
803 lci_len = msr_ie[1];
806 msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
807 &rprt_type_civic, 1, 4);
808 if (msr_ie) {
809 civic = msr_ie + 2;
810 civic_len = msr_ie[1];
813 entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL);
814 if (!entry)
815 return;
817 memcpy(entry->addr, mgmt->bssid, ETH_ALEN);
819 entry->lci_len = lci_len;
820 if (lci_len)
821 memcpy(entry->buf, lci, lci_len);
823 entry->civic_len = civic_len;
824 if (civic_len)
825 memcpy(entry->buf + lci_len, civic, civic_len);
827 list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list);