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
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.
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.
21 * The full GNU General Public License is included in this distribution
22 * in the file called COPYING.
24 * Contact Information:
25 * Intel Linux Wireless <linuxwifi@intel.com>
26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
31 * Copyright (C) 2018 Intel Corporation
32 * Copyright (C) 2019 Intel Corporation
33 * All rights reserved.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
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
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.
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.
61 *****************************************************************************/
62 #include <linux/etherdevice.h>
63 #include <linux/math64.h>
64 #include <net/cfg80211.h>
68 #include "constants.h"
70 struct iwl_mvm_loc_entry
{
71 struct list_head list
;
73 u8 lci_len
, civic_len
;
77 static void iwl_mvm_ftm_reset(struct iwl_mvm
*mvm
)
79 struct iwl_mvm_loc_entry
*e
, *t
;
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
) {
91 void iwl_mvm_ftm_restart(struct iwl_mvm
*mvm
)
93 struct cfg80211_pmsr_result result
= {
94 .status
= NL80211_PMSR_STATUS_FAILURE
,
96 .host_time
= ktime_get_boottime_ns(),
97 .type
= NL80211_PMSR_TYPE_FTM
,
101 lockdep_assert_held(&mvm
->mutex
);
103 if (!mvm
->ftm_initiator
.req
)
106 for (i
= 0; i
< mvm
->ftm_initiator
.req
->n_peers
; i
++) {
107 memcpy(result
.addr
, mvm
->ftm_initiator
.req
->peers
[i
].addr
,
109 result
.ftm
.burst_index
= mvm
->ftm_initiator
.responses
[i
];
111 cfg80211_pmsr_report(mvm
->ftm_initiator
.req_wdev
,
112 mvm
->ftm_initiator
.req
,
113 &result
, GFP_KERNEL
);
116 cfg80211_pmsr_complete(mvm
->ftm_initiator
.req_wdev
,
117 mvm
->ftm_initiator
.req
, GFP_KERNEL
);
118 iwl_mvm_ftm_reset(mvm
);
122 iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s
)
125 case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS
:
127 case IWL_TOF_RANGE_REQUEST_STATUS_BUSY
:
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
)
141 cmd
->request_id
= req
->cookie
;
142 cmd
->num_of_ap
= req
->n_peers
;
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;
148 cmd
->req_timeout
= DIV_ROUND_UP(req
->timeout
, 100);
151 * We treat it always as random, since if not we'll
152 * have filled our local address there instead.
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
];
159 if (vif
->bss_conf
.assoc
)
160 memcpy(cmd
->range_req_bssid
, vif
->bss_conf
.bssid
, ETH_ALEN
);
162 eth_broadcast_addr(cmd
->range_req_bssid
);
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
)
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
;
178 * Use a large value for "no timeout". Don't use the maximum value
179 * because of fw limitations.
182 cmd
->req_timeout_ms
= cpu_to_le32(req
->timeout
);
184 cmd
->req_timeout_ms
= cpu_to_le32(0xfffff);
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
];
190 if (vif
->bss_conf
.assoc
) {
191 memcpy(cmd
->range_req_bssid
, vif
->bss_conf
.bssid
, ETH_ALEN
);
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
);
199 cmd
->tsf_mac_id
= cpu_to_le32(mvmvif
->id
);
204 eth_broadcast_addr(cmd
->range_req_bssid
);
207 /* Don't report AP's TSF */
208 cmd
->tsf_mac_id
= cpu_to_le32(0xff);
212 iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm
*mvm
,
213 struct cfg80211_pmsr_request_peer
*peer
,
214 u8
*channel
, u8
*bandwidth
,
215 u8
*ctrl_ch_position
)
217 u32 freq
= peer
->chandef
.chan
->center_freq
;
219 *channel
= ieee80211_frequency_to_channel(freq
);
221 switch (peer
->chandef
.width
) {
222 case NL80211_CHAN_WIDTH_20_NOHT
:
223 *bandwidth
= IWL_TOF_BW_20_LEGACY
;
225 case NL80211_CHAN_WIDTH_20
:
226 *bandwidth
= IWL_TOF_BW_20_HT
;
228 case NL80211_CHAN_WIDTH_40
:
229 *bandwidth
= IWL_TOF_BW_40
;
231 case NL80211_CHAN_WIDTH_80
:
232 *bandwidth
= IWL_TOF_BW_80
;
235 IWL_ERR(mvm
, "Unsupported BW in FTM request (%d)\n",
236 peer
->chandef
.width
);
240 *ctrl_ch_position
= (peer
->chandef
.width
> NL80211_CHAN_WIDTH_20
) ?
241 iwl_mvm_get_ctrl_pos(&peer
->chandef
) : 0;
247 iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm
*mvm
,
248 struct cfg80211_pmsr_request_peer
*peer
,
249 u8
*channel
, u8
*format_bw
,
250 u8
*ctrl_ch_position
)
252 u32 freq
= peer
->chandef
.chan
->center_freq
;
254 *channel
= ieee80211_frequency_to_channel(freq
);
256 switch (peer
->chandef
.width
) {
257 case NL80211_CHAN_WIDTH_20_NOHT
:
258 *format_bw
= IWL_LOCATION_FRAME_FORMAT_LEGACY
;
259 *format_bw
|= IWL_LOCATION_BW_20MHZ
<< LOCATION_BW_POS
;
261 case NL80211_CHAN_WIDTH_20
:
262 *format_bw
= IWL_LOCATION_FRAME_FORMAT_HT
;
263 *format_bw
|= IWL_LOCATION_BW_20MHZ
<< LOCATION_BW_POS
;
265 case NL80211_CHAN_WIDTH_40
:
266 *format_bw
= IWL_LOCATION_FRAME_FORMAT_HT
;
267 *format_bw
|= IWL_LOCATION_BW_40MHZ
<< LOCATION_BW_POS
;
269 case NL80211_CHAN_WIDTH_80
:
270 *format_bw
= IWL_LOCATION_FRAME_FORMAT_VHT
;
271 *format_bw
|= IWL_LOCATION_BW_80MHZ
<< LOCATION_BW_POS
;
274 IWL_ERR(mvm
, "Unsupported BW in FTM request (%d)\n",
275 peer
->chandef
.width
);
279 *ctrl_ch_position
= (peer
->chandef
.width
> NL80211_CHAN_WIDTH_20
) ?
280 iwl_mvm_get_ctrl_pos(&peer
->chandef
) : 0;
286 iwl_mvm_ftm_put_target_v2(struct iwl_mvm
*mvm
,
287 struct cfg80211_pmsr_request_peer
*peer
,
288 struct iwl_tof_range_req_ap_entry_v2
*target
)
292 ret
= iwl_mvm_ftm_target_chandef_v1(mvm
, peer
, &target
->channel_num
,
294 &target
->ctrl_ch_position
);
298 memcpy(target
->bssid
, peer
->addr
, ETH_ALEN
);
299 target
->burst_period
=
300 cpu_to_le16(peer
->ftm
.burst_period
);
301 target
->samples_per_burst
= peer
->ftm
.ftms_per_burst
;
302 target
->num_of_bursts
= peer
->ftm
.num_bursts_exp
;
303 target
->measure_type
= 0; /* regular two-sided FTM */
304 target
->retries_per_sample
= peer
->ftm
.ftmr_retries
;
305 target
->asap_mode
= peer
->ftm
.asap
;
306 target
->enable_dyn_ack
= IWL_MVM_FTM_INITIATOR_DYNACK
;
308 if (peer
->ftm
.request_lci
)
309 target
->location_req
|= IWL_TOF_LOC_LCI
;
310 if (peer
->ftm
.request_civicloc
)
311 target
->location_req
|= IWL_TOF_LOC_CIVIC
;
313 target
->algo_type
= IWL_MVM_FTM_INITIATOR_ALGO
;
318 #define FTM_PUT_FLAG(flag) (target->initiator_ap_flags |= \
319 cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))
322 iwl_mvm_ftm_put_target_common(struct iwl_mvm
*mvm
,
323 struct cfg80211_pmsr_request_peer
*peer
,
324 struct iwl_tof_range_req_ap_entry
*target
)
326 memcpy(target
->bssid
, peer
->addr
, ETH_ALEN
);
327 target
->burst_period
=
328 cpu_to_le16(peer
->ftm
.burst_period
);
329 target
->samples_per_burst
= peer
->ftm
.ftms_per_burst
;
330 target
->num_of_bursts
= peer
->ftm
.num_bursts_exp
;
331 target
->ftmr_max_retries
= peer
->ftm
.ftmr_retries
;
332 target
->initiator_ap_flags
= cpu_to_le32(0);
337 if (peer
->ftm
.request_lci
)
338 FTM_PUT_FLAG(LCI_REQUEST
);
340 if (peer
->ftm
.request_civicloc
)
341 FTM_PUT_FLAG(CIVIC_REQUEST
);
343 if (IWL_MVM_FTM_INITIATOR_DYNACK
)
344 FTM_PUT_FLAG(DYN_ACK
);
346 if (IWL_MVM_FTM_INITIATOR_ALGO
== IWL_TOF_ALGO_TYPE_LINEAR_REG
)
347 FTM_PUT_FLAG(ALGO_LR
);
348 else if (IWL_MVM_FTM_INITIATOR_ALGO
== IWL_TOF_ALGO_TYPE_FFT
)
349 FTM_PUT_FLAG(ALGO_FFT
);
353 iwl_mvm_ftm_put_target_v3(struct iwl_mvm
*mvm
,
354 struct cfg80211_pmsr_request_peer
*peer
,
355 struct iwl_tof_range_req_ap_entry_v3
*target
)
359 ret
= iwl_mvm_ftm_target_chandef_v1(mvm
, peer
, &target
->channel_num
,
361 &target
->ctrl_ch_position
);
366 * Versions 3 and 4 has some common fields, so
367 * iwl_mvm_ftm_put_target_common() can be used for version 7 too.
369 iwl_mvm_ftm_put_target_common(mvm
, peer
, (void *)target
);
374 static int iwl_mvm_ftm_put_target_v4(struct iwl_mvm
*mvm
,
375 struct cfg80211_pmsr_request_peer
*peer
,
376 struct iwl_tof_range_req_ap_entry
*target
)
380 ret
= iwl_mvm_ftm_target_chandef_v2(mvm
, peer
, &target
->channel_num
,
382 &target
->ctrl_ch_position
);
386 iwl_mvm_ftm_put_target_common(mvm
, peer
, target
);
391 static int iwl_mvm_ftm_send_cmd(struct iwl_mvm
*mvm
, struct iwl_host_cmd
*hcmd
)
394 int err
= iwl_mvm_send_cmd_status(mvm
, hcmd
, &status
);
396 if (!err
&& status
) {
397 IWL_ERR(mvm
, "FTM range request command failure, status: %u\n",
399 err
= iwl_ftm_range_request_status_to_err(status
);
405 static int iwl_mvm_ftm_start_v5(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
406 struct cfg80211_pmsr_request
*req
)
408 struct iwl_tof_range_req_cmd_v5 cmd_v5
;
409 struct iwl_host_cmd hcmd
= {
410 .id
= iwl_cmd_id(TOF_RANGE_REQ_CMD
, LOCATION_GROUP
, 0),
411 .dataflags
[0] = IWL_HCMD_DFL_DUP
,
413 .len
[0] = sizeof(cmd_v5
),
418 iwl_mvm_ftm_cmd_v5(mvm
, vif
, &cmd_v5
, req
);
420 for (i
= 0; i
< cmd_v5
.num_of_ap
; i
++) {
421 struct cfg80211_pmsr_request_peer
*peer
= &req
->peers
[i
];
423 err
= iwl_mvm_ftm_put_target_v2(mvm
, peer
, &cmd_v5
.ap
[i
]);
428 return iwl_mvm_ftm_send_cmd(mvm
, &hcmd
);
431 static int iwl_mvm_ftm_start_v7(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
432 struct cfg80211_pmsr_request
*req
)
434 struct iwl_tof_range_req_cmd_v7 cmd_v7
;
435 struct iwl_host_cmd hcmd
= {
436 .id
= iwl_cmd_id(TOF_RANGE_REQ_CMD
, LOCATION_GROUP
, 0),
437 .dataflags
[0] = IWL_HCMD_DFL_DUP
,
439 .len
[0] = sizeof(cmd_v7
),
445 * Versions 7 and 8 has the same structure except from the responders
446 * list, so iwl_mvm_ftm_cmd() can be used for version 7 too.
448 iwl_mvm_ftm_cmd(mvm
, vif
, (void *)&cmd_v7
, req
);
450 for (i
= 0; i
< cmd_v7
.num_of_ap
; i
++) {
451 struct cfg80211_pmsr_request_peer
*peer
= &req
->peers
[i
];
453 err
= iwl_mvm_ftm_put_target_v3(mvm
, peer
, &cmd_v7
.ap
[i
]);
458 return iwl_mvm_ftm_send_cmd(mvm
, &hcmd
);
461 static int iwl_mvm_ftm_start_v8(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
462 struct cfg80211_pmsr_request
*req
)
464 struct iwl_tof_range_req_cmd cmd
;
465 struct iwl_host_cmd hcmd
= {
466 .id
= iwl_cmd_id(TOF_RANGE_REQ_CMD
, LOCATION_GROUP
, 0),
467 .dataflags
[0] = IWL_HCMD_DFL_DUP
,
469 .len
[0] = sizeof(cmd
),
474 iwl_mvm_ftm_cmd(mvm
, vif
, &cmd
, req
);
476 for (i
= 0; i
< cmd
.num_of_ap
; i
++) {
477 struct cfg80211_pmsr_request_peer
*peer
= &req
->peers
[i
];
479 err
= iwl_mvm_ftm_put_target_v4(mvm
, peer
, &cmd
.ap
[i
]);
484 return iwl_mvm_ftm_send_cmd(mvm
, &hcmd
);
487 int iwl_mvm_ftm_start(struct iwl_mvm
*mvm
, struct ieee80211_vif
*vif
,
488 struct cfg80211_pmsr_request
*req
)
490 bool new_api
= fw_has_api(&mvm
->fw
->ucode_capa
,
491 IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ
);
494 lockdep_assert_held(&mvm
->mutex
);
496 if (mvm
->ftm_initiator
.req
)
500 u8 cmd_ver
= iwl_mvm_lookup_cmd_ver(mvm
->fw
, LOCATION_GROUP
,
504 err
= iwl_mvm_ftm_start_v8(mvm
, vif
, req
);
506 err
= iwl_mvm_ftm_start_v7(mvm
, vif
, req
);
509 err
= iwl_mvm_ftm_start_v5(mvm
, vif
, req
);
513 mvm
->ftm_initiator
.req
= req
;
514 mvm
->ftm_initiator
.req_wdev
= ieee80211_vif_to_wdev(vif
);
520 void iwl_mvm_ftm_abort(struct iwl_mvm
*mvm
, struct cfg80211_pmsr_request
*req
)
522 struct iwl_tof_range_abort_cmd cmd
= {
523 .request_id
= req
->cookie
,
526 lockdep_assert_held(&mvm
->mutex
);
528 if (req
!= mvm
->ftm_initiator
.req
)
531 if (iwl_mvm_send_cmd_pdu(mvm
, iwl_cmd_id(TOF_RANGE_ABORT_CMD
,
533 0, sizeof(cmd
), &cmd
))
534 IWL_ERR(mvm
, "failed to abort FTM process\n");
537 static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request
*req
,
542 for (i
= 0; i
< req
->n_peers
; i
++) {
543 struct cfg80211_pmsr_request_peer
*peer
= &req
->peers
[i
];
545 if (ether_addr_equal_unaligned(peer
->addr
, addr
))
552 static u64
iwl_mvm_ftm_get_host_time(struct iwl_mvm
*mvm
, __le32 fw_gp2_ts
)
554 u32 gp2_ts
= le32_to_cpu(fw_gp2_ts
);
556 u64 now_from_boot_ns
;
558 iwl_mvm_get_sync_time(mvm
, &curr_gp2
, &now_from_boot_ns
);
560 if (curr_gp2
>= gp2_ts
)
561 diff
= curr_gp2
- gp2_ts
;
563 diff
= curr_gp2
+ (U32_MAX
- gp2_ts
+ 1);
565 return now_from_boot_ns
- (u64
)diff
* 1000;
568 static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm
*mvm
,
569 struct cfg80211_pmsr_result
*res
)
571 struct iwl_mvm_loc_entry
*entry
;
573 list_for_each_entry(entry
, &mvm
->ftm_initiator
.loc_list
, list
) {
574 if (!ether_addr_equal_unaligned(res
->addr
, entry
->addr
))
577 if (entry
->lci_len
) {
578 res
->ftm
.lci_len
= entry
->lci_len
;
579 res
->ftm
.lci
= entry
->buf
;
582 if (entry
->civic_len
) {
583 res
->ftm
.civicloc_len
= entry
->civic_len
;
584 res
->ftm
.civicloc
= entry
->buf
+ entry
->lci_len
;
587 /* we found the entry we needed */
592 static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm
*mvm
, u8 request_id
,
595 lockdep_assert_held(&mvm
->mutex
);
597 if (request_id
!= (u8
)mvm
->ftm_initiator
.req
->cookie
) {
598 IWL_ERR(mvm
, "Request ID mismatch, got %u, active %u\n",
599 request_id
, (u8
)mvm
->ftm_initiator
.req
->cookie
);
603 if (num_of_aps
> mvm
->ftm_initiator
.req
->n_peers
) {
604 IWL_ERR(mvm
, "FTM range response invalid\n");
611 static void iwl_mvm_debug_range_resp(struct iwl_mvm
*mvm
, u8 index
,
612 struct cfg80211_pmsr_result
*res
)
614 s64 rtt_avg
= div_s64(res
->ftm
.rtt_avg
* 100, 6666);
616 IWL_DEBUG_INFO(mvm
, "entry %d\n", index
);
617 IWL_DEBUG_INFO(mvm
, "\tstatus: %d\n", res
->status
);
618 IWL_DEBUG_INFO(mvm
, "\tBSSID: %pM\n", res
->addr
);
619 IWL_DEBUG_INFO(mvm
, "\thost time: %llu\n", res
->host_time
);
620 IWL_DEBUG_INFO(mvm
, "\tburst index: %hhu\n", res
->ftm
.burst_index
);
621 IWL_DEBUG_INFO(mvm
, "\tsuccess num: %u\n", res
->ftm
.num_ftmr_successes
);
622 IWL_DEBUG_INFO(mvm
, "\trssi: %d\n", res
->ftm
.rssi_avg
);
623 IWL_DEBUG_INFO(mvm
, "\trssi spread: %hhu\n", res
->ftm
.rssi_spread
);
624 IWL_DEBUG_INFO(mvm
, "\trtt: %lld\n", res
->ftm
.rtt_avg
);
625 IWL_DEBUG_INFO(mvm
, "\trtt var: %llu\n", res
->ftm
.rtt_variance
);
626 IWL_DEBUG_INFO(mvm
, "\trtt spread: %llu\n", res
->ftm
.rtt_spread
);
627 IWL_DEBUG_INFO(mvm
, "\tdistance: %lld\n", rtt_avg
);
630 void iwl_mvm_ftm_range_resp(struct iwl_mvm
*mvm
, struct iwl_rx_cmd_buffer
*rxb
)
632 struct iwl_rx_packet
*pkt
= rxb_addr(rxb
);
633 struct iwl_tof_range_rsp_ntfy_v5
*fw_resp_v5
= (void *)pkt
->data
;
634 struct iwl_tof_range_rsp_ntfy_v6
*fw_resp_v6
= (void *)pkt
->data
;
635 struct iwl_tof_range_rsp_ntfy
*fw_resp
= (void *)pkt
->data
;
637 bool new_api
= fw_has_api(&mvm
->fw
->ucode_capa
,
638 IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ
);
639 u8 num_of_aps
, last_in_batch
;
641 lockdep_assert_held(&mvm
->mutex
);
643 if (!mvm
->ftm_initiator
.req
) {
644 IWL_ERR(mvm
, "Got FTM response but have no request?\n");
649 if (iwl_mvm_ftm_range_resp_valid(mvm
, fw_resp
->request_id
,
650 fw_resp
->num_of_aps
))
653 num_of_aps
= fw_resp
->num_of_aps
;
654 last_in_batch
= fw_resp
->last_report
;
656 if (iwl_mvm_ftm_range_resp_valid(mvm
, fw_resp_v5
->request_id
,
657 fw_resp_v5
->num_of_aps
))
660 num_of_aps
= fw_resp_v5
->num_of_aps
;
661 last_in_batch
= fw_resp_v5
->last_in_batch
;
664 IWL_DEBUG_INFO(mvm
, "Range response received\n");
665 IWL_DEBUG_INFO(mvm
, "request id: %lld, num of entries: %hhu\n",
666 mvm
->ftm_initiator
.req
->cookie
, num_of_aps
);
668 for (i
= 0; i
< num_of_aps
&& i
< IWL_MVM_TOF_MAX_APS
; i
++) {
669 struct cfg80211_pmsr_result result
= {};
670 struct iwl_tof_range_rsp_ap_entry_ntfy
*fw_ap
;
674 if (fw_has_api(&mvm
->fw
->ucode_capa
,
675 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY
))
676 fw_ap
= &fw_resp
->ap
[i
];
678 fw_ap
= (void *)&fw_resp_v6
->ap
[i
];
680 result
.final
= fw_resp
->ap
[i
].last_burst
;
681 result
.ap_tsf
= le32_to_cpu(fw_ap
->start_tsf
);
682 result
.ap_tsf_valid
= 1;
684 /* the first part is the same for old and new APIs */
685 fw_ap
= (void *)&fw_resp_v5
->ap
[i
];
687 * FIXME: the firmware needs to report this, we don't
688 * even know the number of bursts the responder picked
689 * (if we asked it to)
694 peer_idx
= iwl_mvm_ftm_find_peer(mvm
->ftm_initiator
.req
,
698 "Unknown address (%pM, target #%d) in FTM response\n",
703 switch (fw_ap
->measure_status
) {
704 case IWL_TOF_ENTRY_SUCCESS
:
705 result
.status
= NL80211_PMSR_STATUS_SUCCESS
;
707 case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT
:
708 result
.status
= NL80211_PMSR_STATUS_TIMEOUT
;
710 case IWL_TOF_ENTRY_NO_RESPONSE
:
711 result
.status
= NL80211_PMSR_STATUS_FAILURE
;
712 result
.ftm
.failure_reason
=
713 NL80211_PMSR_FTM_FAILURE_NO_RESPONSE
;
715 case IWL_TOF_ENTRY_REQUEST_REJECTED
:
716 result
.status
= NL80211_PMSR_STATUS_FAILURE
;
717 result
.ftm
.failure_reason
=
718 NL80211_PMSR_FTM_FAILURE_PEER_BUSY
;
719 result
.ftm
.busy_retry_time
= fw_ap
->refusal_period
;
722 result
.status
= NL80211_PMSR_STATUS_FAILURE
;
723 result
.ftm
.failure_reason
=
724 NL80211_PMSR_FTM_FAILURE_UNSPECIFIED
;
727 memcpy(result
.addr
, fw_ap
->bssid
, ETH_ALEN
);
728 result
.host_time
= iwl_mvm_ftm_get_host_time(mvm
,
730 result
.type
= NL80211_PMSR_TYPE_FTM
;
731 result
.ftm
.burst_index
= mvm
->ftm_initiator
.responses
[peer_idx
];
732 mvm
->ftm_initiator
.responses
[peer_idx
]++;
733 result
.ftm
.rssi_avg
= fw_ap
->rssi
;
734 result
.ftm
.rssi_avg_valid
= 1;
735 result
.ftm
.rssi_spread
= fw_ap
->rssi_spread
;
736 result
.ftm
.rssi_spread_valid
= 1;
737 result
.ftm
.rtt_avg
= (s32
)le32_to_cpu(fw_ap
->rtt
);
738 result
.ftm
.rtt_avg_valid
= 1;
739 result
.ftm
.rtt_variance
= le32_to_cpu(fw_ap
->rtt_variance
);
740 result
.ftm
.rtt_variance_valid
= 1;
741 result
.ftm
.rtt_spread
= le32_to_cpu(fw_ap
->rtt_spread
);
742 result
.ftm
.rtt_spread_valid
= 1;
744 iwl_mvm_ftm_get_lci_civic(mvm
, &result
);
746 cfg80211_pmsr_report(mvm
->ftm_initiator
.req_wdev
,
747 mvm
->ftm_initiator
.req
,
748 &result
, GFP_KERNEL
);
750 if (fw_has_api(&mvm
->fw
->ucode_capa
,
751 IWL_UCODE_TLV_API_FTM_RTT_ACCURACY
))
752 IWL_DEBUG_INFO(mvm
, "RTT confidence: %hhu\n",
753 fw_ap
->rttConfidence
);
755 iwl_mvm_debug_range_resp(mvm
, i
, &result
);
759 cfg80211_pmsr_complete(mvm
->ftm_initiator
.req_wdev
,
760 mvm
->ftm_initiator
.req
,
762 iwl_mvm_ftm_reset(mvm
);
766 void iwl_mvm_ftm_lc_notif(struct iwl_mvm
*mvm
, struct iwl_rx_cmd_buffer
*rxb
)
768 struct iwl_rx_packet
*pkt
= rxb_addr(rxb
);
769 const struct ieee80211_mgmt
*mgmt
= (void *)pkt
->data
;
770 size_t len
= iwl_rx_packet_payload_len(pkt
);
771 struct iwl_mvm_loc_entry
*entry
;
772 const u8
*ies
, *lci
, *civic
, *msr_ie
;
773 size_t ies_len
, lci_len
= 0, civic_len
= 0;
774 size_t baselen
= IEEE80211_MIN_ACTION_SIZE
+
775 sizeof(mgmt
->u
.action
.u
.ftm
);
776 static const u8 rprt_type_lci
= IEEE80211_SPCT_MSR_RPRT_TYPE_LCI
;
777 static const u8 rprt_type_civic
= IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC
;
782 lockdep_assert_held(&mvm
->mutex
);
784 ies
= mgmt
->u
.action
.u
.ftm
.variable
;
785 ies_len
= len
- baselen
;
787 msr_ie
= cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT
, ies
, ies_len
,
788 &rprt_type_lci
, 1, 4);
794 msr_ie
= cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT
, ies
, ies_len
,
795 &rprt_type_civic
, 1, 4);
798 civic_len
= msr_ie
[1];
801 entry
= kmalloc(sizeof(*entry
) + lci_len
+ civic_len
, GFP_KERNEL
);
805 memcpy(entry
->addr
, mgmt
->bssid
, ETH_ALEN
);
807 entry
->lci_len
= lci_len
;
809 memcpy(entry
->buf
, lci
, lci_len
);
811 entry
->civic_len
= civic_len
;
813 memcpy(entry
->buf
+ lci_len
, civic
, civic_len
);
815 list_add_tail(&entry
->list
, &mvm
->ftm_initiator
.loc_list
);