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.
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
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
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
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>
70 #include "constants.h"
72 struct iwl_mvm_loc_entry
{
73 struct list_head list
;
75 u8 lci_len
, civic_len
;
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
) {
93 void iwl_mvm_ftm_restart(struct iwl_mvm
*mvm
)
95 struct cfg80211_pmsr_result result
= {
96 .status
= NL80211_PMSR_STATUS_FAILURE
,
98 .host_time
= ktime_get_boottime_ns(),
99 .type
= NL80211_PMSR_TYPE_FTM
,
103 lockdep_assert_held(&mvm
->mutex
);
105 if (!mvm
->ftm_initiator
.req
)
108 for (i
= 0; i
< mvm
->ftm_initiator
.req
->n_peers
; i
++) {
109 memcpy(result
.addr
, mvm
->ftm_initiator
.req
->peers
[i
].addr
,
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
);
124 iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s
)
127 case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS
:
129 case IWL_TOF_RANGE_REQUEST_STATUS_BUSY
:
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
)
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;
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
);
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
)
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.
184 cmd
->req_timeout_ms
= cpu_to_le32(req
->timeout
);
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
);
206 eth_broadcast_addr(cmd
->range_req_bssid
);
209 /* Don't report AP's TSF */
210 cmd
->tsf_mac_id
= cpu_to_le32(0xff);
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
;
227 case NL80211_CHAN_WIDTH_20
:
228 *bandwidth
= IWL_TOF_BW_20_HT
;
230 case NL80211_CHAN_WIDTH_40
:
231 *bandwidth
= IWL_TOF_BW_40
;
233 case NL80211_CHAN_WIDTH_80
:
234 *bandwidth
= IWL_TOF_BW_80
;
237 IWL_ERR(mvm
, "Unsupported BW in FTM request (%d)\n",
238 peer
->chandef
.width
);
242 *ctrl_ch_position
= (peer
->chandef
.width
> NL80211_CHAN_WIDTH_20
) ?
243 iwl_mvm_get_ctrl_pos(&peer
->chandef
) : 0;
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
;
263 case NL80211_CHAN_WIDTH_20
:
264 *format_bw
= IWL_LOCATION_FRAME_FORMAT_HT
;
265 *format_bw
|= IWL_LOCATION_BW_20MHZ
<< LOCATION_BW_POS
;
267 case NL80211_CHAN_WIDTH_40
:
268 *format_bw
= IWL_LOCATION_FRAME_FORMAT_HT
;
269 *format_bw
|= IWL_LOCATION_BW_40MHZ
<< LOCATION_BW_POS
;
271 case NL80211_CHAN_WIDTH_80
:
272 *format_bw
= IWL_LOCATION_FRAME_FORMAT_VHT
;
273 *format_bw
|= IWL_LOCATION_BW_80MHZ
<< LOCATION_BW_POS
;
276 IWL_ERR(mvm
, "Unsupported BW in FTM request (%d)\n",
277 peer
->chandef
.width
);
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;
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
)
298 ret
= iwl_mvm_ftm_target_chandef_v1(mvm
, peer
, &target
->channel_num
,
300 &target
->ctrl_ch_position
);
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
;
324 #define FTM_PUT_FLAG(flag) (target->initiator_ap_flags |= \
325 cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))
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);
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
)
359 else if (peer
->ftm
.non_trigger_based
)
360 FTM_PUT_FLAG(NON_TB
);
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
)
370 ret
= iwl_mvm_ftm_target_chandef_v1(mvm
, peer
, &target
->channel_num
,
372 &target
->ctrl_ch_position
);
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
);
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
)
391 ret
= iwl_mvm_ftm_target_chandef_v2(mvm
, peer
, &target
->channel_num
,
393 &target
->ctrl_ch_position
);
397 iwl_mvm_ftm_put_target_common(mvm
, peer
, target
);
402 static int iwl_mvm_ftm_send_cmd(struct iwl_mvm
*mvm
, struct iwl_host_cmd
*hcmd
)
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",
410 err
= iwl_ftm_range_request_status_to_err(status
);
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
,
424 .len
[0] = sizeof(cmd_v5
),
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
]);
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
,
450 .len
[0] = sizeof(cmd_v7
),
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
]);
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
,
480 .len
[0] = sizeof(cmd
),
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
]);
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
);
505 lockdep_assert_held(&mvm
->mutex
);
507 if (mvm
->ftm_initiator
.req
)
511 u8 cmd_ver
= iwl_mvm_lookup_cmd_ver(mvm
->fw
, LOCATION_GROUP
,
515 err
= iwl_mvm_ftm_start_v8(mvm
, vif
, req
);
517 err
= iwl_mvm_ftm_start_v7(mvm
, vif
, req
);
520 err
= iwl_mvm_ftm_start_v5(mvm
, vif
, req
);
524 mvm
->ftm_initiator
.req
= req
;
525 mvm
->ftm_initiator
.req_wdev
= ieee80211_vif_to_wdev(vif
);
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
)
542 iwl_mvm_ftm_reset(mvm
);
544 if (iwl_mvm_send_cmd_pdu(mvm
, iwl_cmd_id(TOF_RANGE_ABORT_CMD
,
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
,
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
))
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
);
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
;
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
))
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 */
605 static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm
*mvm
, u8 request_id
,
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
);
616 if (num_of_aps
> mvm
->ftm_initiator
.req
->n_peers
) {
617 IWL_ERR(mvm
, "FTM range response invalid\n");
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
;
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
) {
661 if (iwl_mvm_ftm_range_resp_valid(mvm
, fw_resp
->request_id
,
662 fw_resp
->num_of_aps
))
665 num_of_aps
= fw_resp
->num_of_aps
;
666 last_in_batch
= fw_resp
->last_report
;
668 if (iwl_mvm_ftm_range_resp_valid(mvm
, fw_resp_v5
->request_id
,
669 fw_resp_v5
->num_of_aps
))
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
;
686 if (fw_has_api(&mvm
->fw
->ucode_capa
,
687 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY
))
688 fw_ap
= &fw_resp
->ap
[i
];
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;
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)
706 peer_idx
= iwl_mvm_ftm_find_peer(mvm
->ftm_initiator
.req
,
710 "Unknown address (%pM, target #%d) in FTM response\n",
715 switch (fw_ap
->measure_status
) {
716 case IWL_TOF_ENTRY_SUCCESS
:
717 result
.status
= NL80211_PMSR_STATUS_SUCCESS
;
719 case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT
:
720 result
.status
= NL80211_PMSR_STATUS_TIMEOUT
;
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
;
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
;
734 result
.status
= NL80211_PMSR_STATUS_FAILURE
;
735 result
.ftm
.failure_reason
=
736 NL80211_PMSR_FTM_FAILURE_UNSPECIFIED
;
739 memcpy(result
.addr
, fw_ap
->bssid
, ETH_ALEN
);
740 result
.host_time
= iwl_mvm_ftm_get_host_time(mvm
,
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
);
771 cfg80211_pmsr_complete(mvm
->ftm_initiator
.req_wdev
,
772 mvm
->ftm_initiator
.req
,
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
;
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);
806 msr_ie
= cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT
, ies
, ies_len
,
807 &rprt_type_civic
, 1, 4);
810 civic_len
= msr_ie
[1];
813 entry
= kmalloc(sizeof(*entry
) + lci_len
+ civic_len
, GFP_KERNEL
);
817 memcpy(entry
->addr
, mgmt
->bssid
, ETH_ALEN
);
819 entry
->lci_len
= lci_len
;
821 memcpy(entry
->buf
, lci
, lci_len
);
823 entry
->civic_len
= civic_len
;
825 memcpy(entry
->buf
+ lci_len
, civic
, civic_len
);
827 list_add_tail(&entry
->list
, &mvm
->ftm_initiator
.loc_list
);