xref: /openbmc/linux/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c (revision c1e01cdbe0312d95b8c1542abd67fe786b534f57)
18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
38e99ea8dSJohannes Berg  * Copyright (C) 2015-2017 Intel Deutschland GmbH
4f276e20bSJohannes Berg  * Copyright (C) 2018-2022 Intel Corporation
58e99ea8dSJohannes Berg  */
6fc36ffdaSJohannes Berg #include <linux/etherdevice.h>
7fc36ffdaSJohannes Berg #include <linux/math64.h>
8fc36ffdaSJohannes Berg #include <net/cfg80211.h>
9fc36ffdaSJohannes Berg #include "mvm.h"
10fc36ffdaSJohannes Berg #include "iwl-io.h"
11fc36ffdaSJohannes Berg #include "iwl-prph.h"
12fc36ffdaSJohannes Berg #include "constants.h"
13fc36ffdaSJohannes Berg 
14fc36ffdaSJohannes Berg struct iwl_mvm_loc_entry {
15fc36ffdaSJohannes Berg 	struct list_head list;
16fc36ffdaSJohannes Berg 	u8 addr[ETH_ALEN];
17fc36ffdaSJohannes Berg 	u8 lci_len, civic_len;
18fc36ffdaSJohannes Berg 	u8 buf[];
19fc36ffdaSJohannes Berg };
20fc36ffdaSJohannes Berg 
21b68bd2e3SIlan Peer struct iwl_mvm_smooth_entry {
22b68bd2e3SIlan Peer 	struct list_head list;
23b68bd2e3SIlan Peer 	u8 addr[ETH_ALEN];
24b68bd2e3SIlan Peer 	s64 rtt_avg;
25b68bd2e3SIlan Peer 	u64 host_time;
26b68bd2e3SIlan Peer };
27b68bd2e3SIlan Peer 
2826c680b7SAvraham Stern enum iwl_mvm_pasn_flags {
2926c680b7SAvraham Stern 	IWL_MVM_PASN_FLAG_HAS_HLTK = BIT(0),
3026c680b7SAvraham Stern };
3126c680b7SAvraham Stern 
320739a7d7SAvraham Stern struct iwl_mvm_ftm_pasn_entry {
330739a7d7SAvraham Stern 	struct list_head list;
340739a7d7SAvraham Stern 	u8 addr[ETH_ALEN];
350739a7d7SAvraham Stern 	u8 hltk[HLTK_11AZ_LEN];
360739a7d7SAvraham Stern 	u8 tk[TK_11AZ_LEN];
370739a7d7SAvraham Stern 	u8 cipher;
380739a7d7SAvraham Stern 	u8 tx_pn[IEEE80211_CCMP_PN_LEN];
390739a7d7SAvraham Stern 	u8 rx_pn[IEEE80211_CCMP_PN_LEN];
4026c680b7SAvraham Stern 	u32 flags;
410739a7d7SAvraham Stern };
420739a7d7SAvraham Stern 
iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u8 * addr,u32 cipher,u8 * tk,u32 tk_len,u8 * hltk,u32 hltk_len)430739a7d7SAvraham Stern int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
440739a7d7SAvraham Stern 			     u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
450739a7d7SAvraham Stern 			     u8 *hltk, u32 hltk_len)
460739a7d7SAvraham Stern {
470739a7d7SAvraham Stern 	struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn),
480739a7d7SAvraham Stern 						      GFP_KERNEL);
490739a7d7SAvraham Stern 	u32 expected_tk_len;
500739a7d7SAvraham Stern 
510739a7d7SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
520739a7d7SAvraham Stern 
530739a7d7SAvraham Stern 	if (!pasn)
540739a7d7SAvraham Stern 		return -ENOBUFS;
550739a7d7SAvraham Stern 
56*2565820dSAvraham Stern 	iwl_mvm_ftm_remove_pasn_sta(mvm, addr);
57*2565820dSAvraham Stern 
580739a7d7SAvraham Stern 	pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher);
590739a7d7SAvraham Stern 
600739a7d7SAvraham Stern 	switch (pasn->cipher) {
610739a7d7SAvraham Stern 	case IWL_LOCATION_CIPHER_CCMP_128:
620739a7d7SAvraham Stern 	case IWL_LOCATION_CIPHER_GCMP_128:
630739a7d7SAvraham Stern 		expected_tk_len = WLAN_KEY_LEN_CCMP;
640739a7d7SAvraham Stern 		break;
650739a7d7SAvraham Stern 	case IWL_LOCATION_CIPHER_GCMP_256:
660739a7d7SAvraham Stern 		expected_tk_len = WLAN_KEY_LEN_GCMP_256;
670739a7d7SAvraham Stern 		break;
680739a7d7SAvraham Stern 	default:
690739a7d7SAvraham Stern 		goto out;
700739a7d7SAvraham Stern 	}
710739a7d7SAvraham Stern 
720739a7d7SAvraham Stern 	/*
730739a7d7SAvraham Stern 	 * If associated to this AP and already have security context,
740739a7d7SAvraham Stern 	 * the TK is already configured for this station, so it
750739a7d7SAvraham Stern 	 * shouldn't be set again here.
760739a7d7SAvraham Stern 	 */
7744fa698cSAvraham Stern 	if (vif->cfg.assoc) {
780739a7d7SAvraham Stern 		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
7944fa698cSAvraham Stern 		struct ieee80211_bss_conf *link_conf;
8044fa698cSAvraham Stern 		unsigned int link_id;
810739a7d7SAvraham Stern 		struct ieee80211_sta *sta;
8244fa698cSAvraham Stern 		u8 sta_id;
830739a7d7SAvraham Stern 
840739a7d7SAvraham Stern 		rcu_read_lock();
8544fa698cSAvraham Stern 		for_each_vif_active_link(vif, link_conf, link_id) {
8644fa698cSAvraham Stern 			if (memcmp(addr, link_conf->bssid, ETH_ALEN))
8744fa698cSAvraham Stern 				continue;
8844fa698cSAvraham Stern 
8944fa698cSAvraham Stern 			sta_id = mvmvif->link[link_id]->ap_sta_id;
9044fa698cSAvraham Stern 			sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
910739a7d7SAvraham Stern 			if (!IS_ERR_OR_NULL(sta) && sta->mfp)
920739a7d7SAvraham Stern 				expected_tk_len = 0;
9344fa698cSAvraham Stern 			break;
9444fa698cSAvraham Stern 		}
950739a7d7SAvraham Stern 		rcu_read_unlock();
960739a7d7SAvraham Stern 	}
970739a7d7SAvraham Stern 
9826c680b7SAvraham Stern 	if (tk_len != expected_tk_len ||
9926c680b7SAvraham Stern 	    (hltk_len && hltk_len != sizeof(pasn->hltk))) {
1000739a7d7SAvraham Stern 		IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n",
1010739a7d7SAvraham Stern 			tk_len, hltk_len);
1020739a7d7SAvraham Stern 		goto out;
1030739a7d7SAvraham Stern 	}
1040739a7d7SAvraham Stern 
10526c680b7SAvraham Stern 	if (!expected_tk_len && !hltk_len) {
10626c680b7SAvraham Stern 		IWL_ERR(mvm, "TK and HLTK not set\n");
10726c680b7SAvraham Stern 		goto out;
10826c680b7SAvraham Stern 	}
10926c680b7SAvraham Stern 
1100739a7d7SAvraham Stern 	memcpy(pasn->addr, addr, sizeof(pasn->addr));
11126c680b7SAvraham Stern 
11226c680b7SAvraham Stern 	if (hltk_len) {
1130739a7d7SAvraham Stern 		memcpy(pasn->hltk, hltk, sizeof(pasn->hltk));
11426c680b7SAvraham Stern 		pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK;
11526c680b7SAvraham Stern 	}
1160739a7d7SAvraham Stern 
1170739a7d7SAvraham Stern 	if (tk && tk_len)
1180739a7d7SAvraham Stern 		memcpy(pasn->tk, tk, sizeof(pasn->tk));
1190739a7d7SAvraham Stern 
1200739a7d7SAvraham Stern 	list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list);
1210739a7d7SAvraham Stern 	return 0;
1220739a7d7SAvraham Stern out:
1230739a7d7SAvraham Stern 	kfree(pasn);
1240739a7d7SAvraham Stern 	return -EINVAL;
1250739a7d7SAvraham Stern }
1260739a7d7SAvraham Stern 
iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm * mvm,u8 * addr)1270739a7d7SAvraham Stern void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr)
1280739a7d7SAvraham Stern {
1290739a7d7SAvraham Stern 	struct iwl_mvm_ftm_pasn_entry *entry, *prev;
1300739a7d7SAvraham Stern 
1310739a7d7SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
1320739a7d7SAvraham Stern 
1330739a7d7SAvraham Stern 	list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list,
1340739a7d7SAvraham Stern 				 list) {
1350739a7d7SAvraham Stern 		if (memcmp(entry->addr, addr, sizeof(entry->addr)))
1360739a7d7SAvraham Stern 			continue;
1370739a7d7SAvraham Stern 
1380739a7d7SAvraham Stern 		list_del(&entry->list);
1390739a7d7SAvraham Stern 		kfree(entry);
1400739a7d7SAvraham Stern 		return;
1410739a7d7SAvraham Stern 	}
1420739a7d7SAvraham Stern }
1430739a7d7SAvraham Stern 
iwl_mvm_ftm_reset(struct iwl_mvm * mvm)144fc36ffdaSJohannes Berg static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm)
145fc36ffdaSJohannes Berg {
146fc36ffdaSJohannes Berg 	struct iwl_mvm_loc_entry *e, *t;
147fc36ffdaSJohannes Berg 
148fc36ffdaSJohannes Berg 	mvm->ftm_initiator.req = NULL;
149fc36ffdaSJohannes Berg 	mvm->ftm_initiator.req_wdev = NULL;
150fc36ffdaSJohannes Berg 	memset(mvm->ftm_initiator.responses, 0,
151fc36ffdaSJohannes Berg 	       sizeof(mvm->ftm_initiator.responses));
152b68bd2e3SIlan Peer 
153fc36ffdaSJohannes Berg 	list_for_each_entry_safe(e, t, &mvm->ftm_initiator.loc_list, list) {
154fc36ffdaSJohannes Berg 		list_del(&e->list);
155fc36ffdaSJohannes Berg 		kfree(e);
156fc36ffdaSJohannes Berg 	}
157fc36ffdaSJohannes Berg }
158fc36ffdaSJohannes Berg 
iwl_mvm_ftm_restart(struct iwl_mvm * mvm)159fc36ffdaSJohannes Berg void iwl_mvm_ftm_restart(struct iwl_mvm *mvm)
160fc36ffdaSJohannes Berg {
161fc36ffdaSJohannes Berg 	struct cfg80211_pmsr_result result = {
162fc36ffdaSJohannes Berg 		.status = NL80211_PMSR_STATUS_FAILURE,
163fc36ffdaSJohannes Berg 		.final = 1,
1649285ec4cSJason A. Donenfeld 		.host_time = ktime_get_boottime_ns(),
165fc36ffdaSJohannes Berg 		.type = NL80211_PMSR_TYPE_FTM,
166fc36ffdaSJohannes Berg 	};
167fc36ffdaSJohannes Berg 	int i;
168fc36ffdaSJohannes Berg 
169fc36ffdaSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
170fc36ffdaSJohannes Berg 
171fc36ffdaSJohannes Berg 	if (!mvm->ftm_initiator.req)
172fc36ffdaSJohannes Berg 		return;
173fc36ffdaSJohannes Berg 
174fc36ffdaSJohannes Berg 	for (i = 0; i < mvm->ftm_initiator.req->n_peers; i++) {
175fc36ffdaSJohannes Berg 		memcpy(result.addr, mvm->ftm_initiator.req->peers[i].addr,
176fc36ffdaSJohannes Berg 		       ETH_ALEN);
177fc36ffdaSJohannes Berg 		result.ftm.burst_index = mvm->ftm_initiator.responses[i];
178fc36ffdaSJohannes Berg 
179fc36ffdaSJohannes Berg 		cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
180fc36ffdaSJohannes Berg 				     mvm->ftm_initiator.req,
181fc36ffdaSJohannes Berg 				     &result, GFP_KERNEL);
182fc36ffdaSJohannes Berg 	}
183fc36ffdaSJohannes Berg 
184fc36ffdaSJohannes Berg 	cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
185fc36ffdaSJohannes Berg 			       mvm->ftm_initiator.req, GFP_KERNEL);
186fc36ffdaSJohannes Berg 	iwl_mvm_ftm_reset(mvm);
187fc36ffdaSJohannes Berg }
188fc36ffdaSJohannes Berg 
iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm * mvm)189b68bd2e3SIlan Peer void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm)
190b68bd2e3SIlan Peer {
191b68bd2e3SIlan Peer 	INIT_LIST_HEAD(&mvm->ftm_initiator.smooth.resp);
192b68bd2e3SIlan Peer 
193b68bd2e3SIlan Peer 	IWL_DEBUG_INFO(mvm,
194b68bd2e3SIlan Peer 		       "enable=%u, alpha=%u, age_jiffies=%u, thresh=(%u:%u)\n",
195b68bd2e3SIlan Peer 			IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH,
196b68bd2e3SIlan Peer 			IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA,
197b68bd2e3SIlan Peer 			IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * HZ,
198b68bd2e3SIlan Peer 			IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT,
199b68bd2e3SIlan Peer 			IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT);
200b68bd2e3SIlan Peer }
201b68bd2e3SIlan Peer 
iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm * mvm)202b68bd2e3SIlan Peer void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm)
203b68bd2e3SIlan Peer {
204b68bd2e3SIlan Peer 	struct iwl_mvm_smooth_entry *se, *st;
205b68bd2e3SIlan Peer 
206b68bd2e3SIlan Peer 	list_for_each_entry_safe(se, st, &mvm->ftm_initiator.smooth.resp,
207b68bd2e3SIlan Peer 				 list) {
208b68bd2e3SIlan Peer 		list_del(&se->list);
209b68bd2e3SIlan Peer 		kfree(se);
210b68bd2e3SIlan Peer 	}
211b68bd2e3SIlan Peer }
212b68bd2e3SIlan Peer 
213fc36ffdaSJohannes Berg static int
iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s)214fc36ffdaSJohannes Berg iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s)
215fc36ffdaSJohannes Berg {
216fc36ffdaSJohannes Berg 	switch (s) {
217fc36ffdaSJohannes Berg 	case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS:
218fc36ffdaSJohannes Berg 		return 0;
219fc36ffdaSJohannes Berg 	case IWL_TOF_RANGE_REQUEST_STATUS_BUSY:
220fc36ffdaSJohannes Berg 		return -EBUSY;
221fc36ffdaSJohannes Berg 	default:
222fc36ffdaSJohannes Berg 		WARN_ON_ONCE(1);
223fc36ffdaSJohannes Berg 		return -EIO;
224fc36ffdaSJohannes Berg 	}
225fc36ffdaSJohannes Berg }
226fc36ffdaSJohannes Berg 
iwl_mvm_ftm_cmd_v5(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_tof_range_req_cmd_v5 * cmd,struct cfg80211_pmsr_request * req)227ff418feeSAvraham Stern static void iwl_mvm_ftm_cmd_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
228ff418feeSAvraham Stern 			       struct iwl_tof_range_req_cmd_v5 *cmd,
229fc36ffdaSJohannes Berg 			       struct cfg80211_pmsr_request *req)
230fc36ffdaSJohannes Berg {
231ff418feeSAvraham Stern 	int i;
232ff418feeSAvraham Stern 
233ff418feeSAvraham Stern 	cmd->request_id = req->cookie;
234ff418feeSAvraham Stern 	cmd->num_of_ap = req->n_peers;
235ff418feeSAvraham Stern 
236ff418feeSAvraham Stern 	/* use maximum for "no timeout" or bigger than what we can do */
237ff418feeSAvraham Stern 	if (!req->timeout || req->timeout > 255 * 100)
238ff418feeSAvraham Stern 		cmd->req_timeout = 255;
239ff418feeSAvraham Stern 	else
240ff418feeSAvraham Stern 		cmd->req_timeout = DIV_ROUND_UP(req->timeout, 100);
241ff418feeSAvraham Stern 
242fc36ffdaSJohannes Berg 	/*
243fc36ffdaSJohannes Berg 	 * We treat it always as random, since if not we'll
244fc36ffdaSJohannes Berg 	 * have filled our local address there instead.
245fc36ffdaSJohannes Berg 	 */
246ff418feeSAvraham Stern 	cmd->macaddr_random = 1;
247ff418feeSAvraham Stern 	memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
248fc36ffdaSJohannes Berg 	for (i = 0; i < ETH_ALEN; i++)
249ff418feeSAvraham Stern 		cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
250fc36ffdaSJohannes Berg 
251f276e20bSJohannes Berg 	if (vif->cfg.assoc)
252ff418feeSAvraham Stern 		memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
253ff418feeSAvraham Stern 	else
254ff418feeSAvraham Stern 		eth_broadcast_addr(cmd->range_req_bssid);
255ff418feeSAvraham Stern }
256ff418feeSAvraham Stern 
iwl_mvm_ftm_cmd_common(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_tof_range_req_cmd_v9 * cmd,struct cfg80211_pmsr_request * req)257f092e4e3SAvraham Stern static void iwl_mvm_ftm_cmd_common(struct iwl_mvm *mvm,
258f092e4e3SAvraham Stern 				   struct ieee80211_vif *vif,
2591c096d89SAvraham Stern 				   struct iwl_tof_range_req_cmd_v9 *cmd,
260ff418feeSAvraham Stern 				   struct cfg80211_pmsr_request *req)
261ff418feeSAvraham Stern {
262ff418feeSAvraham Stern 	int i;
263ff418feeSAvraham Stern 
264ff418feeSAvraham Stern 	cmd->initiator_flags =
265ff418feeSAvraham Stern 		cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM |
266ff418feeSAvraham Stern 			    IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT);
267ff418feeSAvraham Stern 	cmd->request_id = req->cookie;
268ff418feeSAvraham Stern 	cmd->num_of_ap = req->n_peers;
269ff418feeSAvraham Stern 
270ff418feeSAvraham Stern 	/*
271ff418feeSAvraham Stern 	 * Use a large value for "no timeout". Don't use the maximum value
272ff418feeSAvraham Stern 	 * because of fw limitations.
273ff418feeSAvraham Stern 	 */
274ff418feeSAvraham Stern 	if (req->timeout)
275ff418feeSAvraham Stern 		cmd->req_timeout_ms = cpu_to_le32(req->timeout);
276ff418feeSAvraham Stern 	else
277ff418feeSAvraham Stern 		cmd->req_timeout_ms = cpu_to_le32(0xfffff);
278ff418feeSAvraham Stern 
279ff418feeSAvraham Stern 	memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
280ff418feeSAvraham Stern 	for (i = 0; i < ETH_ALEN; i++)
281ff418feeSAvraham Stern 		cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
282ff418feeSAvraham Stern 
283f276e20bSJohannes Berg 	if (vif->cfg.assoc) {
284ff418feeSAvraham Stern 		memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
285ff418feeSAvraham Stern 
286cec2d4f6SAvraham Stern 		/* AP's TSF is only relevant if associated */
287cec2d4f6SAvraham Stern 		for (i = 0; i < req->n_peers; i++) {
288cec2d4f6SAvraham Stern 			if (req->peers[i].report_ap_tsf) {
289cec2d4f6SAvraham Stern 				struct iwl_mvm_vif *mvmvif =
290cec2d4f6SAvraham Stern 					iwl_mvm_vif_from_mac80211(vif);
291cec2d4f6SAvraham Stern 
292cec2d4f6SAvraham Stern 				cmd->tsf_mac_id = cpu_to_le32(mvmvif->id);
293cec2d4f6SAvraham Stern 				return;
294cec2d4f6SAvraham Stern 			}
295cec2d4f6SAvraham Stern 		}
296cec2d4f6SAvraham Stern 	} else {
297cec2d4f6SAvraham Stern 		eth_broadcast_addr(cmd->range_req_bssid);
298cec2d4f6SAvraham Stern 	}
299cec2d4f6SAvraham Stern 
300cec2d4f6SAvraham Stern 	/* Don't report AP's TSF */
301ff418feeSAvraham Stern 	cmd->tsf_mac_id = cpu_to_le32(0xff);
302ff418feeSAvraham Stern }
303ff418feeSAvraham Stern 
iwl_mvm_ftm_cmd_v8(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_tof_range_req_cmd_v8 * cmd,struct cfg80211_pmsr_request * req)304f092e4e3SAvraham Stern static void iwl_mvm_ftm_cmd_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
305f092e4e3SAvraham Stern 			       struct iwl_tof_range_req_cmd_v8 *cmd,
306f092e4e3SAvraham Stern 			       struct cfg80211_pmsr_request *req)
307f092e4e3SAvraham Stern {
308f092e4e3SAvraham Stern 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)cmd, req);
309f092e4e3SAvraham Stern }
310f092e4e3SAvraham Stern 
311b59ec4caSAvraham Stern static int
iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm * mvm,struct cfg80211_pmsr_request_peer * peer,u8 * channel,u8 * bandwidth,u8 * ctrl_ch_position)312b59ec4caSAvraham Stern iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm *mvm,
313ff418feeSAvraham Stern 			      struct cfg80211_pmsr_request_peer *peer,
314ff418feeSAvraham Stern 			      u8 *channel, u8 *bandwidth,
315ff418feeSAvraham Stern 			      u8 *ctrl_ch_position)
316ff418feeSAvraham Stern {
317fc36ffdaSJohannes Berg 	u32 freq = peer->chandef.chan->center_freq;
318fc36ffdaSJohannes Berg 
319ff418feeSAvraham Stern 	*channel = ieee80211_frequency_to_channel(freq);
320ff418feeSAvraham Stern 
321fc36ffdaSJohannes Berg 	switch (peer->chandef.width) {
322fc36ffdaSJohannes Berg 	case NL80211_CHAN_WIDTH_20_NOHT:
323ff418feeSAvraham Stern 		*bandwidth = IWL_TOF_BW_20_LEGACY;
324fc36ffdaSJohannes Berg 		break;
325fc36ffdaSJohannes Berg 	case NL80211_CHAN_WIDTH_20:
326ff418feeSAvraham Stern 		*bandwidth = IWL_TOF_BW_20_HT;
327fc36ffdaSJohannes Berg 		break;
328fc36ffdaSJohannes Berg 	case NL80211_CHAN_WIDTH_40:
329ff418feeSAvraham Stern 		*bandwidth = IWL_TOF_BW_40;
330fc36ffdaSJohannes Berg 		break;
331fc36ffdaSJohannes Berg 	case NL80211_CHAN_WIDTH_80:
332ff418feeSAvraham Stern 		*bandwidth = IWL_TOF_BW_80;
333fc36ffdaSJohannes Berg 		break;
334fc36ffdaSJohannes Berg 	default:
335fc36ffdaSJohannes Berg 		IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",
336fc36ffdaSJohannes Berg 			peer->chandef.width);
337fc36ffdaSJohannes Berg 		return -EINVAL;
338fc36ffdaSJohannes Berg 	}
339ff418feeSAvraham Stern 
340ff418feeSAvraham Stern 	*ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
341fc36ffdaSJohannes Berg 		iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;
342fc36ffdaSJohannes Berg 
343ff418feeSAvraham Stern 	return 0;
344fc36ffdaSJohannes Berg }
345fc36ffdaSJohannes Berg 
346ff418feeSAvraham Stern static int
iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm * mvm,struct cfg80211_pmsr_request_peer * peer,u8 * channel,u8 * format_bw,u8 * ctrl_ch_position)347b59ec4caSAvraham Stern iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm,
348b59ec4caSAvraham Stern 			      struct cfg80211_pmsr_request_peer *peer,
349b59ec4caSAvraham Stern 			      u8 *channel, u8 *format_bw,
350b59ec4caSAvraham Stern 			      u8 *ctrl_ch_position)
351b59ec4caSAvraham Stern {
352b59ec4caSAvraham Stern 	u32 freq = peer->chandef.chan->center_freq;
3538a2c1516SAvraham Stern 	u8 cmd_ver;
354b59ec4caSAvraham Stern 
355b59ec4caSAvraham Stern 	*channel = ieee80211_frequency_to_channel(freq);
356b59ec4caSAvraham Stern 
357b59ec4caSAvraham Stern 	switch (peer->chandef.width) {
358b59ec4caSAvraham Stern 	case NL80211_CHAN_WIDTH_20_NOHT:
359b59ec4caSAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
360b59ec4caSAvraham Stern 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
361b59ec4caSAvraham Stern 		break;
362b59ec4caSAvraham Stern 	case NL80211_CHAN_WIDTH_20:
363b59ec4caSAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
364b59ec4caSAvraham Stern 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
365b59ec4caSAvraham Stern 		break;
366b59ec4caSAvraham Stern 	case NL80211_CHAN_WIDTH_40:
367b59ec4caSAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
368b59ec4caSAvraham Stern 		*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
369b59ec4caSAvraham Stern 		break;
370b59ec4caSAvraham Stern 	case NL80211_CHAN_WIDTH_80:
371b59ec4caSAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
372b59ec4caSAvraham Stern 		*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
373b59ec4caSAvraham Stern 		break;
3748a2c1516SAvraham Stern 	case NL80211_CHAN_WIDTH_160:
375971cbe50SJohannes Berg 		cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
376971cbe50SJohannes Berg 						WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
3778a2c1516SAvraham Stern 						IWL_FW_CMD_VER_UNKNOWN);
3788a2c1516SAvraham Stern 
3798a2c1516SAvraham Stern 		if (cmd_ver >= 13) {
3808a2c1516SAvraham Stern 			*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
3818a2c1516SAvraham Stern 			*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
3828a2c1516SAvraham Stern 			break;
3838a2c1516SAvraham Stern 		}
3848a2c1516SAvraham Stern 		fallthrough;
385b59ec4caSAvraham Stern 	default:
386b59ec4caSAvraham Stern 		IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",
387b59ec4caSAvraham Stern 			peer->chandef.width);
388b59ec4caSAvraham Stern 		return -EINVAL;
389b59ec4caSAvraham Stern 	}
390b59ec4caSAvraham Stern 
3916815e3d0SAvraham Stern 	/* non EDCA based measurement must use HE preamble */
3926815e3d0SAvraham Stern 	if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
3936815e3d0SAvraham Stern 		*format_bw |= IWL_LOCATION_FRAME_FORMAT_HE;
3946815e3d0SAvraham Stern 
395b59ec4caSAvraham Stern 	*ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
396b59ec4caSAvraham Stern 		iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;
397b59ec4caSAvraham Stern 
398b59ec4caSAvraham Stern 	return 0;
399b59ec4caSAvraham Stern }
400b59ec4caSAvraham Stern 
401b59ec4caSAvraham Stern static int
iwl_mvm_ftm_put_target_v2(struct iwl_mvm * mvm,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v2 * target)402ff418feeSAvraham Stern iwl_mvm_ftm_put_target_v2(struct iwl_mvm *mvm,
403ff418feeSAvraham Stern 			  struct cfg80211_pmsr_request_peer *peer,
404ff418feeSAvraham Stern 			  struct iwl_tof_range_req_ap_entry_v2 *target)
405ff418feeSAvraham Stern {
406ff418feeSAvraham Stern 	int ret;
407ff418feeSAvraham Stern 
408b59ec4caSAvraham Stern 	ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,
409ff418feeSAvraham Stern 					    &target->bandwidth,
410ff418feeSAvraham Stern 					    &target->ctrl_ch_position);
411ff418feeSAvraham Stern 	if (ret)
412ff418feeSAvraham Stern 		return ret;
413ff418feeSAvraham Stern 
414ff418feeSAvraham Stern 	memcpy(target->bssid, peer->addr, ETH_ALEN);
415ff418feeSAvraham Stern 	target->burst_period =
416ff418feeSAvraham Stern 		cpu_to_le16(peer->ftm.burst_period);
417ff418feeSAvraham Stern 	target->samples_per_burst = peer->ftm.ftms_per_burst;
418ff418feeSAvraham Stern 	target->num_of_bursts = peer->ftm.num_bursts_exp;
419ff418feeSAvraham Stern 	target->measure_type = 0; /* regular two-sided FTM */
420ff418feeSAvraham Stern 	target->retries_per_sample = peer->ftm.ftmr_retries;
421ff418feeSAvraham Stern 	target->asap_mode = peer->ftm.asap;
422ff418feeSAvraham Stern 	target->enable_dyn_ack = IWL_MVM_FTM_INITIATOR_DYNACK;
423ff418feeSAvraham Stern 
424ff418feeSAvraham Stern 	if (peer->ftm.request_lci)
425ff418feeSAvraham Stern 		target->location_req |= IWL_TOF_LOC_LCI;
426ff418feeSAvraham Stern 	if (peer->ftm.request_civicloc)
427ff418feeSAvraham Stern 		target->location_req |= IWL_TOF_LOC_CIVIC;
428ff418feeSAvraham Stern 
429ff418feeSAvraham Stern 	target->algo_type = IWL_MVM_FTM_INITIATOR_ALGO;
430ff418feeSAvraham Stern 
431ff418feeSAvraham Stern 	return 0;
432ff418feeSAvraham Stern }
433ff418feeSAvraham Stern 
434ff418feeSAvraham Stern #define FTM_PUT_FLAG(flag)	(target->initiator_ap_flags |= \
435ff418feeSAvraham Stern 				 cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))
436ff418feeSAvraham Stern 
437b59ec4caSAvraham Stern static void
iwl_mvm_ftm_put_target_common(struct iwl_mvm * mvm,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v6 * target)438b59ec4caSAvraham Stern iwl_mvm_ftm_put_target_common(struct iwl_mvm *mvm,
439ff418feeSAvraham Stern 			      struct cfg80211_pmsr_request_peer *peer,
4401c096d89SAvraham Stern 			      struct iwl_tof_range_req_ap_entry_v6 *target)
441ff418feeSAvraham Stern {
442ff418feeSAvraham Stern 	memcpy(target->bssid, peer->addr, ETH_ALEN);
443ff418feeSAvraham Stern 	target->burst_period =
444ff418feeSAvraham Stern 		cpu_to_le16(peer->ftm.burst_period);
445ff418feeSAvraham Stern 	target->samples_per_burst = peer->ftm.ftms_per_burst;
446ff418feeSAvraham Stern 	target->num_of_bursts = peer->ftm.num_bursts_exp;
447ff418feeSAvraham Stern 	target->ftmr_max_retries = peer->ftm.ftmr_retries;
448ff418feeSAvraham Stern 	target->initiator_ap_flags = cpu_to_le32(0);
449ff418feeSAvraham Stern 
450ff418feeSAvraham Stern 	if (peer->ftm.asap)
451ff418feeSAvraham Stern 		FTM_PUT_FLAG(ASAP);
452ff418feeSAvraham Stern 
453ff418feeSAvraham Stern 	if (peer->ftm.request_lci)
454ff418feeSAvraham Stern 		FTM_PUT_FLAG(LCI_REQUEST);
455ff418feeSAvraham Stern 
456ff418feeSAvraham Stern 	if (peer->ftm.request_civicloc)
457ff418feeSAvraham Stern 		FTM_PUT_FLAG(CIVIC_REQUEST);
458ff418feeSAvraham Stern 
459ff418feeSAvraham Stern 	if (IWL_MVM_FTM_INITIATOR_DYNACK)
460ff418feeSAvraham Stern 		FTM_PUT_FLAG(DYN_ACK);
461ff418feeSAvraham Stern 
462ff418feeSAvraham Stern 	if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG)
463ff418feeSAvraham Stern 		FTM_PUT_FLAG(ALGO_LR);
464ff418feeSAvraham Stern 	else if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT)
465ff418feeSAvraham Stern 		FTM_PUT_FLAG(ALGO_FFT);
4666815e3d0SAvraham Stern 
4676815e3d0SAvraham Stern 	if (peer->ftm.trigger_based)
4686815e3d0SAvraham Stern 		FTM_PUT_FLAG(TB);
4696815e3d0SAvraham Stern 	else if (peer->ftm.non_trigger_based)
4706815e3d0SAvraham Stern 		FTM_PUT_FLAG(NON_TB);
4715c1f0942SAvraham Stern 
4725c1f0942SAvraham Stern 	if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) &&
4735c1f0942SAvraham Stern 	    peer->ftm.lmr_feedback)
4745c1f0942SAvraham Stern 		FTM_PUT_FLAG(LMR_FEEDBACK);
475b59ec4caSAvraham Stern }
476b59ec4caSAvraham Stern 
477b59ec4caSAvraham Stern static int
iwl_mvm_ftm_put_target_v3(struct iwl_mvm * mvm,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v3 * target)478b59ec4caSAvraham Stern iwl_mvm_ftm_put_target_v3(struct iwl_mvm *mvm,
479b59ec4caSAvraham Stern 			  struct cfg80211_pmsr_request_peer *peer,
480b59ec4caSAvraham Stern 			  struct iwl_tof_range_req_ap_entry_v3 *target)
481b59ec4caSAvraham Stern {
482b59ec4caSAvraham Stern 	int ret;
483b59ec4caSAvraham Stern 
484b59ec4caSAvraham Stern 	ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,
485b59ec4caSAvraham Stern 					    &target->bandwidth,
486b59ec4caSAvraham Stern 					    &target->ctrl_ch_position);
487b59ec4caSAvraham Stern 	if (ret)
488b59ec4caSAvraham Stern 		return ret;
489b59ec4caSAvraham Stern 
490b59ec4caSAvraham Stern 	/*
491b59ec4caSAvraham Stern 	 * Versions 3 and 4 has some common fields, so
492b59ec4caSAvraham Stern 	 * iwl_mvm_ftm_put_target_common() can be used for version 7 too.
493b59ec4caSAvraham Stern 	 */
494b59ec4caSAvraham Stern 	iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);
495ff418feeSAvraham Stern 
496ff418feeSAvraham Stern 	return 0;
497ff418feeSAvraham Stern }
498ff418feeSAvraham Stern 
499f092e4e3SAvraham Stern static int
iwl_mvm_ftm_put_target_v4(struct iwl_mvm * mvm,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v4 * target)500018971b1SAvraham Stern iwl_mvm_ftm_put_target_v4(struct iwl_mvm *mvm,
501b59ec4caSAvraham Stern 			  struct cfg80211_pmsr_request_peer *peer,
502f092e4e3SAvraham Stern 			  struct iwl_tof_range_req_ap_entry_v4 *target)
503b59ec4caSAvraham Stern {
504b59ec4caSAvraham Stern 	int ret;
505b59ec4caSAvraham Stern 
506b59ec4caSAvraham Stern 	ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,
507b59ec4caSAvraham Stern 					    &target->format_bw,
508b59ec4caSAvraham Stern 					    &target->ctrl_ch_position);
509b59ec4caSAvraham Stern 	if (ret)
510b59ec4caSAvraham Stern 		return ret;
511b59ec4caSAvraham Stern 
512f092e4e3SAvraham Stern 	iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);
513b59ec4caSAvraham Stern 
514b59ec4caSAvraham Stern 	return 0;
515b59ec4caSAvraham Stern }
516b59ec4caSAvraham Stern 
517018971b1SAvraham Stern static int
iwl_mvm_ftm_put_target(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v6 * target)518018971b1SAvraham Stern iwl_mvm_ftm_put_target(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
519018971b1SAvraham Stern 		       struct cfg80211_pmsr_request_peer *peer,
5201c096d89SAvraham Stern 		       struct iwl_tof_range_req_ap_entry_v6 *target)
521018971b1SAvraham Stern {
522018971b1SAvraham Stern 	int ret;
523018971b1SAvraham Stern 
524018971b1SAvraham Stern 	ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,
525018971b1SAvraham Stern 					    &target->format_bw,
526018971b1SAvraham Stern 					    &target->ctrl_ch_position);
527018971b1SAvraham Stern 	if (ret)
528018971b1SAvraham Stern 		return ret;
529018971b1SAvraham Stern 
5301c096d89SAvraham Stern 	iwl_mvm_ftm_put_target_common(mvm, peer, target);
531018971b1SAvraham Stern 
53244fa698cSAvraham Stern 	if (vif->cfg.assoc) {
533018971b1SAvraham Stern 		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
534517a5eb9SAvraham Stern 		struct ieee80211_sta *sta;
53544fa698cSAvraham Stern 		struct ieee80211_bss_conf *link_conf;
53644fa698cSAvraham Stern 		unsigned int link_id;
537517a5eb9SAvraham Stern 
538517a5eb9SAvraham Stern 		rcu_read_lock();
53944fa698cSAvraham Stern 		for_each_vif_active_link(vif, link_conf, link_id) {
54044fa698cSAvraham Stern 			if (memcmp(peer->addr, link_conf->bssid, ETH_ALEN))
54144fa698cSAvraham Stern 				continue;
542517a5eb9SAvraham Stern 
54344fa698cSAvraham Stern 			target->sta_id = mvmvif->link[link_id]->ap_sta_id;
54444fa698cSAvraham Stern 			sta = rcu_dereference(mvm->fw_id_to_mac_id[target->sta_id]);
54568182662SGregory Greenman 			if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
54668182662SGregory Greenman 				rcu_read_unlock();
54768182662SGregory Greenman 				return PTR_ERR_OR_ZERO(sta);
54868182662SGregory Greenman 			}
54968182662SGregory Greenman 
55044fa698cSAvraham Stern 			if (sta->mfp && (peer->ftm.trigger_based ||
55144fa698cSAvraham Stern 					 peer->ftm.non_trigger_based))
552517a5eb9SAvraham Stern 				FTM_PUT_FLAG(PMF);
55344fa698cSAvraham Stern 			break;
55444fa698cSAvraham Stern 		}
555517a5eb9SAvraham Stern 		rcu_read_unlock();
556018971b1SAvraham Stern 	} else {
557018971b1SAvraham Stern 		target->sta_id = IWL_MVM_INVALID_STA;
558018971b1SAvraham Stern 	}
559018971b1SAvraham Stern 
560018971b1SAvraham Stern 	/*
561018971b1SAvraham Stern 	 * TODO: Beacon interval is currently unknown, so use the common value
562018971b1SAvraham Stern 	 * of 100 TUs.
563018971b1SAvraham Stern 	 */
564018971b1SAvraham Stern 	target->beacon_interval = cpu_to_le16(100);
565018971b1SAvraham Stern 	return 0;
566018971b1SAvraham Stern }
567018971b1SAvraham Stern 
iwl_mvm_ftm_send_cmd(struct iwl_mvm * mvm,struct iwl_host_cmd * hcmd)568b59ec4caSAvraham Stern static int iwl_mvm_ftm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *hcmd)
569b59ec4caSAvraham Stern {
570b59ec4caSAvraham Stern 	u32 status;
571b59ec4caSAvraham Stern 	int err = iwl_mvm_send_cmd_status(mvm, hcmd, &status);
572b59ec4caSAvraham Stern 
573b59ec4caSAvraham Stern 	if (!err && status) {
574b59ec4caSAvraham Stern 		IWL_ERR(mvm, "FTM range request command failure, status: %u\n",
575b59ec4caSAvraham Stern 			status);
576b59ec4caSAvraham Stern 		err = iwl_ftm_range_request_status_to_err(status);
577b59ec4caSAvraham Stern 	}
578b59ec4caSAvraham Stern 
579b59ec4caSAvraham Stern 	return err;
580b59ec4caSAvraham Stern }
581b59ec4caSAvraham Stern 
iwl_mvm_ftm_start_v5(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)582b59ec4caSAvraham Stern static int iwl_mvm_ftm_start_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
583b59ec4caSAvraham Stern 				struct cfg80211_pmsr_request *req)
584b59ec4caSAvraham Stern {
585b59ec4caSAvraham Stern 	struct iwl_tof_range_req_cmd_v5 cmd_v5;
586b59ec4caSAvraham Stern 	struct iwl_host_cmd hcmd = {
587f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
588b59ec4caSAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
589b59ec4caSAvraham Stern 		.data[0] = &cmd_v5,
590b59ec4caSAvraham Stern 		.len[0] = sizeof(cmd_v5),
591b59ec4caSAvraham Stern 	};
592b59ec4caSAvraham Stern 	u8 i;
593b59ec4caSAvraham Stern 	int err;
594b59ec4caSAvraham Stern 
595b59ec4caSAvraham Stern 	iwl_mvm_ftm_cmd_v5(mvm, vif, &cmd_v5, req);
596b59ec4caSAvraham Stern 
597b59ec4caSAvraham Stern 	for (i = 0; i < cmd_v5.num_of_ap; i++) {
598b59ec4caSAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
599b59ec4caSAvraham Stern 
600b59ec4caSAvraham Stern 		err = iwl_mvm_ftm_put_target_v2(mvm, peer, &cmd_v5.ap[i]);
601b59ec4caSAvraham Stern 		if (err)
602b59ec4caSAvraham Stern 			return err;
603b59ec4caSAvraham Stern 	}
604b59ec4caSAvraham Stern 
605b59ec4caSAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
606b59ec4caSAvraham Stern }
607b59ec4caSAvraham Stern 
iwl_mvm_ftm_start_v7(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)608b59ec4caSAvraham Stern static int iwl_mvm_ftm_start_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
609b59ec4caSAvraham Stern 				struct cfg80211_pmsr_request *req)
610b59ec4caSAvraham Stern {
611b59ec4caSAvraham Stern 	struct iwl_tof_range_req_cmd_v7 cmd_v7;
612b59ec4caSAvraham Stern 	struct iwl_host_cmd hcmd = {
613f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
614b59ec4caSAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
615b59ec4caSAvraham Stern 		.data[0] = &cmd_v7,
616b59ec4caSAvraham Stern 		.len[0] = sizeof(cmd_v7),
617b59ec4caSAvraham Stern 	};
618b59ec4caSAvraham Stern 	u8 i;
619b59ec4caSAvraham Stern 	int err;
620b59ec4caSAvraham Stern 
621b59ec4caSAvraham Stern 	/*
622b59ec4caSAvraham Stern 	 * Versions 7 and 8 has the same structure except from the responders
623b59ec4caSAvraham Stern 	 * list, so iwl_mvm_ftm_cmd() can be used for version 7 too.
624b59ec4caSAvraham Stern 	 */
625f092e4e3SAvraham Stern 	iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd_v7, req);
626b59ec4caSAvraham Stern 
627b59ec4caSAvraham Stern 	for (i = 0; i < cmd_v7.num_of_ap; i++) {
628b59ec4caSAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
629b59ec4caSAvraham Stern 
630b59ec4caSAvraham Stern 		err = iwl_mvm_ftm_put_target_v3(mvm, peer, &cmd_v7.ap[i]);
631b59ec4caSAvraham Stern 		if (err)
632b59ec4caSAvraham Stern 			return err;
633b59ec4caSAvraham Stern 	}
634b59ec4caSAvraham Stern 
635b59ec4caSAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
636b59ec4caSAvraham Stern }
637b59ec4caSAvraham Stern 
iwl_mvm_ftm_start_v8(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)638b59ec4caSAvraham Stern static int iwl_mvm_ftm_start_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
639b59ec4caSAvraham Stern 				struct cfg80211_pmsr_request *req)
640b59ec4caSAvraham Stern {
641f092e4e3SAvraham Stern 	struct iwl_tof_range_req_cmd_v8 cmd;
642f092e4e3SAvraham Stern 	struct iwl_host_cmd hcmd = {
643f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
644f092e4e3SAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
645f092e4e3SAvraham Stern 		.data[0] = &cmd,
646f092e4e3SAvraham Stern 		.len[0] = sizeof(cmd),
647f092e4e3SAvraham Stern 	};
648f092e4e3SAvraham Stern 	u8 i;
649f092e4e3SAvraham Stern 	int err;
650f092e4e3SAvraham Stern 
651f092e4e3SAvraham Stern 	iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd, req);
652f092e4e3SAvraham Stern 
653f092e4e3SAvraham Stern 	for (i = 0; i < cmd.num_of_ap; i++) {
654f092e4e3SAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
655f092e4e3SAvraham Stern 
656018971b1SAvraham Stern 		err = iwl_mvm_ftm_put_target_v4(mvm, peer, &cmd.ap[i]);
657f092e4e3SAvraham Stern 		if (err)
658f092e4e3SAvraham Stern 			return err;
659f092e4e3SAvraham Stern 	}
660f092e4e3SAvraham Stern 
661f092e4e3SAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
662f092e4e3SAvraham Stern }
663f092e4e3SAvraham Stern 
iwl_mvm_ftm_start_v9(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)664f092e4e3SAvraham Stern static int iwl_mvm_ftm_start_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
665f092e4e3SAvraham Stern 				struct cfg80211_pmsr_request *req)
666f092e4e3SAvraham Stern {
6671c096d89SAvraham Stern 	struct iwl_tof_range_req_cmd_v9 cmd;
668b59ec4caSAvraham Stern 	struct iwl_host_cmd hcmd = {
669f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
670b59ec4caSAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
671b59ec4caSAvraham Stern 		.data[0] = &cmd,
672b59ec4caSAvraham Stern 		.len[0] = sizeof(cmd),
673b59ec4caSAvraham Stern 	};
674b59ec4caSAvraham Stern 	u8 i;
675b59ec4caSAvraham Stern 	int err;
676b59ec4caSAvraham Stern 
677f092e4e3SAvraham Stern 	iwl_mvm_ftm_cmd_common(mvm, vif, &cmd, req);
678b59ec4caSAvraham Stern 
679b59ec4caSAvraham Stern 	for (i = 0; i < cmd.num_of_ap; i++) {
680b59ec4caSAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
6811c096d89SAvraham Stern 		struct iwl_tof_range_req_ap_entry_v6 *target = &cmd.ap[i];
682b59ec4caSAvraham Stern 
683018971b1SAvraham Stern 		err = iwl_mvm_ftm_put_target(mvm, vif, peer, target);
684b59ec4caSAvraham Stern 		if (err)
685b59ec4caSAvraham Stern 			return err;
686b59ec4caSAvraham Stern 	}
687b59ec4caSAvraham Stern 
688b59ec4caSAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
689b59ec4caSAvraham Stern }
690b59ec4caSAvraham Stern 
iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * data)6910739a7d7SAvraham Stern static void iter(struct ieee80211_hw *hw,
6920739a7d7SAvraham Stern 		 struct ieee80211_vif *vif,
6930739a7d7SAvraham Stern 		 struct ieee80211_sta *sta,
6940739a7d7SAvraham Stern 		 struct ieee80211_key_conf *key,
6950739a7d7SAvraham Stern 		 void *data)
6960739a7d7SAvraham Stern {
6970739a7d7SAvraham Stern 	struct iwl_tof_range_req_ap_entry_v6 *target = data;
6980739a7d7SAvraham Stern 
6990739a7d7SAvraham Stern 	if (!sta || memcmp(sta->addr, target->bssid, ETH_ALEN))
7000739a7d7SAvraham Stern 		return;
7010739a7d7SAvraham Stern 
7020739a7d7SAvraham Stern 	WARN_ON(!sta->mfp);
7030739a7d7SAvraham Stern 
7040739a7d7SAvraham Stern 	if (WARN_ON(key->keylen > sizeof(target->tk)))
7050739a7d7SAvraham Stern 		return;
7060739a7d7SAvraham Stern 
7070739a7d7SAvraham Stern 	memcpy(target->tk, key->key, key->keylen);
7080739a7d7SAvraham Stern 	target->cipher = iwl_mvm_cipher_to_location_cipher(key->cipher);
7090739a7d7SAvraham Stern 	WARN_ON(target->cipher == IWL_LOCATION_CIPHER_INVALID);
7100739a7d7SAvraham Stern }
7110739a7d7SAvraham Stern 
7120739a7d7SAvraham Stern static void
iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_tof_range_req_ap_entry_v7 * target)7130739a7d7SAvraham Stern iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
7140739a7d7SAvraham Stern 				struct iwl_tof_range_req_ap_entry_v7 *target)
7150739a7d7SAvraham Stern {
7160739a7d7SAvraham Stern 	struct iwl_mvm_ftm_pasn_entry *entry;
7170739a7d7SAvraham Stern 	u32 flags = le32_to_cpu(target->initiator_ap_flags);
7180739a7d7SAvraham Stern 
7190739a7d7SAvraham Stern 	if (!(flags & (IWL_INITIATOR_AP_FLAGS_NON_TB |
7200739a7d7SAvraham Stern 		       IWL_INITIATOR_AP_FLAGS_TB)))
7210739a7d7SAvraham Stern 		return;
7220739a7d7SAvraham Stern 
7230739a7d7SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
7240739a7d7SAvraham Stern 
7250739a7d7SAvraham Stern 	list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
7260739a7d7SAvraham Stern 		if (memcmp(entry->addr, target->bssid, sizeof(entry->addr)))
7270739a7d7SAvraham Stern 			continue;
7280739a7d7SAvraham Stern 
7290739a7d7SAvraham Stern 		target->cipher = entry->cipher;
73026c680b7SAvraham Stern 
73126c680b7SAvraham Stern 		if (entry->flags & IWL_MVM_PASN_FLAG_HAS_HLTK)
7320739a7d7SAvraham Stern 			memcpy(target->hltk, entry->hltk, sizeof(target->hltk));
73326c680b7SAvraham Stern 		else
73426c680b7SAvraham Stern 			memset(target->hltk, 0, sizeof(target->hltk));
7350739a7d7SAvraham Stern 
736f276e20bSJohannes Berg 		if (vif->cfg.assoc &&
7370739a7d7SAvraham Stern 		    !memcmp(vif->bss_conf.bssid, target->bssid,
7380739a7d7SAvraham Stern 			    sizeof(target->bssid)))
7390739a7d7SAvraham Stern 			ieee80211_iter_keys(mvm->hw, vif, iter, target);
7400739a7d7SAvraham Stern 		else
7410739a7d7SAvraham Stern 			memcpy(target->tk, entry->tk, sizeof(target->tk));
7420739a7d7SAvraham Stern 
7430739a7d7SAvraham Stern 		memcpy(target->rx_pn, entry->rx_pn, sizeof(target->rx_pn));
7440739a7d7SAvraham Stern 		memcpy(target->tx_pn, entry->tx_pn, sizeof(target->tx_pn));
7450739a7d7SAvraham Stern 
7460739a7d7SAvraham Stern 		target->initiator_ap_flags |=
7470739a7d7SAvraham Stern 			cpu_to_le32(IWL_INITIATOR_AP_FLAGS_SECURED);
7480739a7d7SAvraham Stern 		return;
7490739a7d7SAvraham Stern 	}
7500739a7d7SAvraham Stern }
7510739a7d7SAvraham Stern 
7529896b0b9SAvraham Stern static int
iwl_mvm_ftm_put_target_v7(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v7 * target)7539896b0b9SAvraham Stern iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
7549896b0b9SAvraham Stern 			  struct cfg80211_pmsr_request_peer *peer,
7559896b0b9SAvraham Stern 			  struct iwl_tof_range_req_ap_entry_v7 *target)
7569896b0b9SAvraham Stern {
7579896b0b9SAvraham Stern 	int err = iwl_mvm_ftm_put_target(mvm, vif, peer, (void *)target);
7589896b0b9SAvraham Stern 	if (err)
7599896b0b9SAvraham Stern 		return err;
7609896b0b9SAvraham Stern 
7619896b0b9SAvraham Stern 	iwl_mvm_ftm_set_secured_ranging(mvm, vif, target);
7629896b0b9SAvraham Stern 	return err;
7639896b0b9SAvraham Stern }
7649896b0b9SAvraham Stern 
iwl_mvm_ftm_start_v11(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)7651c096d89SAvraham Stern static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm,
7661c096d89SAvraham Stern 				 struct ieee80211_vif *vif,
7671c096d89SAvraham Stern 				 struct cfg80211_pmsr_request *req)
7681c096d89SAvraham Stern {
7691c096d89SAvraham Stern 	struct iwl_tof_range_req_cmd_v11 cmd;
7701c096d89SAvraham Stern 	struct iwl_host_cmd hcmd = {
771f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
7721c096d89SAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
7731c096d89SAvraham Stern 		.data[0] = &cmd,
7741c096d89SAvraham Stern 		.len[0] = sizeof(cmd),
7751c096d89SAvraham Stern 	};
7761c096d89SAvraham Stern 	u8 i;
7771c096d89SAvraham Stern 	int err;
7781c096d89SAvraham Stern 
7791c096d89SAvraham Stern 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
7801c096d89SAvraham Stern 
7811c096d89SAvraham Stern 	for (i = 0; i < cmd.num_of_ap; i++) {
7821c096d89SAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
7831c096d89SAvraham Stern 		struct iwl_tof_range_req_ap_entry_v7 *target = &cmd.ap[i];
7841c096d89SAvraham Stern 
7859896b0b9SAvraham Stern 		err = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, target);
7869896b0b9SAvraham Stern 		if (err)
7879896b0b9SAvraham Stern 			return err;
7889896b0b9SAvraham Stern 	}
7899896b0b9SAvraham Stern 
7909896b0b9SAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
7919896b0b9SAvraham Stern }
7929896b0b9SAvraham Stern 
7939896b0b9SAvraham Stern static void
iwl_mvm_ftm_set_ndp_params(struct iwl_mvm * mvm,struct iwl_tof_range_req_ap_entry_v8 * target)7949896b0b9SAvraham Stern iwl_mvm_ftm_set_ndp_params(struct iwl_mvm *mvm,
7959896b0b9SAvraham Stern 			   struct iwl_tof_range_req_ap_entry_v8 *target)
7969896b0b9SAvraham Stern {
7979896b0b9SAvraham Stern 	/* Only 2 STS are supported on Tx */
7989896b0b9SAvraham Stern 	u32 i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 :
7999896b0b9SAvraham Stern 		IWL_MVM_FTM_I2R_MAX_STS;
8009896b0b9SAvraham Stern 
8019896b0b9SAvraham Stern 	target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |
8029896b0b9SAvraham Stern 		(IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS);
8039896b0b9SAvraham Stern 	target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |
8049896b0b9SAvraham Stern 		(i2r_max_sts << IWL_LOCATION_MAX_STS_POS);
8059896b0b9SAvraham Stern 	target->r2i_max_total_ltf = IWL_MVM_FTM_R2I_MAX_TOTAL_LTF;
8069896b0b9SAvraham Stern 	target->i2r_max_total_ltf = IWL_MVM_FTM_I2R_MAX_TOTAL_LTF;
8079896b0b9SAvraham Stern }
8089896b0b9SAvraham Stern 
809830aa3e7SAvraham Stern static int
iwl_mvm_ftm_put_target_v8(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request_peer * peer,struct iwl_tof_range_req_ap_entry_v8 * target)810830aa3e7SAvraham Stern iwl_mvm_ftm_put_target_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
811830aa3e7SAvraham Stern 			  struct cfg80211_pmsr_request_peer *peer,
812830aa3e7SAvraham Stern 			  struct iwl_tof_range_req_ap_entry_v8 *target)
813830aa3e7SAvraham Stern {
814830aa3e7SAvraham Stern 	u32 flags;
815830aa3e7SAvraham Stern 	int ret = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, (void *)target);
816830aa3e7SAvraham Stern 
817830aa3e7SAvraham Stern 	if (ret)
818830aa3e7SAvraham Stern 		return ret;
819830aa3e7SAvraham Stern 
820830aa3e7SAvraham Stern 	iwl_mvm_ftm_set_ndp_params(mvm, target);
821830aa3e7SAvraham Stern 
822830aa3e7SAvraham Stern 	/*
823830aa3e7SAvraham Stern 	 * If secure LTF is turned off, replace the flag with PMF only
824830aa3e7SAvraham Stern 	 */
825830aa3e7SAvraham Stern 	flags = le32_to_cpu(target->initiator_ap_flags);
826830aa3e7SAvraham Stern 	if ((flags & IWL_INITIATOR_AP_FLAGS_SECURED) &&
827830aa3e7SAvraham Stern 	    !IWL_MVM_FTM_INITIATOR_SECURE_LTF) {
828830aa3e7SAvraham Stern 		flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED;
829830aa3e7SAvraham Stern 		flags |= IWL_INITIATOR_AP_FLAGS_PMF;
830830aa3e7SAvraham Stern 		target->initiator_ap_flags = cpu_to_le32(flags);
831830aa3e7SAvraham Stern 	}
832830aa3e7SAvraham Stern 
833830aa3e7SAvraham Stern 	return 0;
834830aa3e7SAvraham Stern }
835830aa3e7SAvraham Stern 
iwl_mvm_ftm_start_v12(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)8369896b0b9SAvraham Stern static int iwl_mvm_ftm_start_v12(struct iwl_mvm *mvm,
8379896b0b9SAvraham Stern 				 struct ieee80211_vif *vif,
8389896b0b9SAvraham Stern 				 struct cfg80211_pmsr_request *req)
8399896b0b9SAvraham Stern {
8409896b0b9SAvraham Stern 	struct iwl_tof_range_req_cmd_v12 cmd;
8419896b0b9SAvraham Stern 	struct iwl_host_cmd hcmd = {
842f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
8439896b0b9SAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
8449896b0b9SAvraham Stern 		.data[0] = &cmd,
8459896b0b9SAvraham Stern 		.len[0] = sizeof(cmd),
8469896b0b9SAvraham Stern 	};
8479896b0b9SAvraham Stern 	u8 i;
8489896b0b9SAvraham Stern 	int err;
8499896b0b9SAvraham Stern 
8509896b0b9SAvraham Stern 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
8519896b0b9SAvraham Stern 
8529896b0b9SAvraham Stern 	for (i = 0; i < cmd.num_of_ap; i++) {
8539896b0b9SAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
8549896b0b9SAvraham Stern 		struct iwl_tof_range_req_ap_entry_v8 *target = &cmd.ap[i];
8559896b0b9SAvraham Stern 
856830aa3e7SAvraham Stern 		err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, target);
857830aa3e7SAvraham Stern 		if (err)
858830aa3e7SAvraham Stern 			return err;
859830aa3e7SAvraham Stern 	}
860830aa3e7SAvraham Stern 
861830aa3e7SAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
862830aa3e7SAvraham Stern }
863830aa3e7SAvraham Stern 
iwl_mvm_ftm_start_v13(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)864830aa3e7SAvraham Stern static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm,
865830aa3e7SAvraham Stern 				 struct ieee80211_vif *vif,
866830aa3e7SAvraham Stern 				 struct cfg80211_pmsr_request *req)
867830aa3e7SAvraham Stern {
868830aa3e7SAvraham Stern 	struct iwl_tof_range_req_cmd_v13 cmd;
869830aa3e7SAvraham Stern 	struct iwl_host_cmd hcmd = {
870f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
871830aa3e7SAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
872830aa3e7SAvraham Stern 		.data[0] = &cmd,
873830aa3e7SAvraham Stern 		.len[0] = sizeof(cmd),
874830aa3e7SAvraham Stern 	};
875830aa3e7SAvraham Stern 	u8 i;
876830aa3e7SAvraham Stern 	int err;
877830aa3e7SAvraham Stern 
878830aa3e7SAvraham Stern 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
879830aa3e7SAvraham Stern 
880830aa3e7SAvraham Stern 	for (i = 0; i < cmd.num_of_ap; i++) {
881830aa3e7SAvraham Stern 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
882830aa3e7SAvraham Stern 		struct iwl_tof_range_req_ap_entry_v9 *target = &cmd.ap[i];
883830aa3e7SAvraham Stern 
884830aa3e7SAvraham Stern 		err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, (void *)target);
8851c096d89SAvraham Stern 		if (err)
8861c096d89SAvraham Stern 			return err;
8870739a7d7SAvraham Stern 
888830aa3e7SAvraham Stern 		if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
889830aa3e7SAvraham Stern 			target->bss_color = peer->ftm.bss_color;
8909896b0b9SAvraham Stern 
891830aa3e7SAvraham Stern 		if (peer->ftm.non_trigger_based) {
892830aa3e7SAvraham Stern 			target->min_time_between_msr =
893830aa3e7SAvraham Stern 				cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
894830aa3e7SAvraham Stern 			target->burst_period =
895830aa3e7SAvraham Stern 				cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
896830aa3e7SAvraham Stern 		} else {
897830aa3e7SAvraham Stern 			target->min_time_between_msr = cpu_to_le16(0);
8989896b0b9SAvraham Stern 		}
899830aa3e7SAvraham Stern 
900830aa3e7SAvraham Stern 		target->band =
901830aa3e7SAvraham Stern 			iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band);
9021c096d89SAvraham Stern 	}
9031c096d89SAvraham Stern 
9041c096d89SAvraham Stern 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
9051c096d89SAvraham Stern }
9061c096d89SAvraham Stern 
iwl_mvm_ftm_start(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_pmsr_request * req)907ff418feeSAvraham Stern int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
908ff418feeSAvraham Stern 		      struct cfg80211_pmsr_request *req)
909ff418feeSAvraham Stern {
910ff418feeSAvraham Stern 	bool new_api = fw_has_api(&mvm->fw->ucode_capa,
911ff418feeSAvraham Stern 				  IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
912b59ec4caSAvraham Stern 	int err;
913ff418feeSAvraham Stern 
914ff418feeSAvraham Stern 	lockdep_assert_held(&mvm->mutex);
915ff418feeSAvraham Stern 
916ff418feeSAvraham Stern 	if (mvm->ftm_initiator.req)
917ff418feeSAvraham Stern 		return -EBUSY;
918ff418feeSAvraham Stern 
919ff418feeSAvraham Stern 	if (new_api) {
920971cbe50SJohannes Berg 		u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
921971cbe50SJohannes Berg 						   WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
922e80bfd11SMordechay Goodstein 						   IWL_FW_CMD_VER_UNKNOWN);
923ff418feeSAvraham Stern 
924f092e4e3SAvraham Stern 		switch (cmd_ver) {
925830aa3e7SAvraham Stern 		case 13:
926830aa3e7SAvraham Stern 			err = iwl_mvm_ftm_start_v13(mvm, vif, req);
927830aa3e7SAvraham Stern 			break;
9289896b0b9SAvraham Stern 		case 12:
9299896b0b9SAvraham Stern 			err = iwl_mvm_ftm_start_v12(mvm, vif, req);
9309896b0b9SAvraham Stern 			break;
9311c096d89SAvraham Stern 		case 11:
9321c096d89SAvraham Stern 			err = iwl_mvm_ftm_start_v11(mvm, vif, req);
9331c096d89SAvraham Stern 			break;
934f092e4e3SAvraham Stern 		case 9:
935018971b1SAvraham Stern 		case 10:
936f092e4e3SAvraham Stern 			err = iwl_mvm_ftm_start_v9(mvm, vif, req);
937f092e4e3SAvraham Stern 			break;
938f092e4e3SAvraham Stern 		case 8:
939b59ec4caSAvraham Stern 			err = iwl_mvm_ftm_start_v8(mvm, vif, req);
940f092e4e3SAvraham Stern 			break;
941f092e4e3SAvraham Stern 		default:
942b59ec4caSAvraham Stern 			err = iwl_mvm_ftm_start_v7(mvm, vif, req);
943f092e4e3SAvraham Stern 			break;
944f092e4e3SAvraham Stern 		}
945b59ec4caSAvraham Stern 	} else {
946b59ec4caSAvraham Stern 		err = iwl_mvm_ftm_start_v5(mvm, vif, req);
947fc36ffdaSJohannes Berg 	}
948fc36ffdaSJohannes Berg 
949fc36ffdaSJohannes Berg 	if (!err) {
950fc36ffdaSJohannes Berg 		mvm->ftm_initiator.req = req;
951fc36ffdaSJohannes Berg 		mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif);
952fc36ffdaSJohannes Berg 	}
953fc36ffdaSJohannes Berg 
954fc36ffdaSJohannes Berg 	return err;
955fc36ffdaSJohannes Berg }
956fc36ffdaSJohannes Berg 
iwl_mvm_ftm_abort(struct iwl_mvm * mvm,struct cfg80211_pmsr_request * req)957fc36ffdaSJohannes Berg void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req)
958fc36ffdaSJohannes Berg {
959fc36ffdaSJohannes Berg 	struct iwl_tof_range_abort_cmd cmd = {
960fc36ffdaSJohannes Berg 		.request_id = req->cookie,
961fc36ffdaSJohannes Berg 	};
962fc36ffdaSJohannes Berg 
963fc36ffdaSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
964fc36ffdaSJohannes Berg 
965fc36ffdaSJohannes Berg 	if (req != mvm->ftm_initiator.req)
966fc36ffdaSJohannes Berg 		return;
967fc36ffdaSJohannes Berg 
968cc4255efSAvraham Stern 	iwl_mvm_ftm_reset(mvm);
969cc4255efSAvraham Stern 
970f0c86427SJohannes Berg 	if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(LOCATION_GROUP, TOF_RANGE_ABORT_CMD),
971fc36ffdaSJohannes Berg 				 0, sizeof(cmd), &cmd))
972fc36ffdaSJohannes Berg 		IWL_ERR(mvm, "failed to abort FTM process\n");
973fc36ffdaSJohannes Berg }
974fc36ffdaSJohannes Berg 
iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request * req,const u8 * addr)975fc36ffdaSJohannes Berg static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req,
976fc36ffdaSJohannes Berg 				 const u8 *addr)
977fc36ffdaSJohannes Berg {
978fc36ffdaSJohannes Berg 	int i;
979fc36ffdaSJohannes Berg 
980fc36ffdaSJohannes Berg 	for (i = 0; i < req->n_peers; i++) {
981fc36ffdaSJohannes Berg 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
982fc36ffdaSJohannes Berg 
983fc36ffdaSJohannes Berg 		if (ether_addr_equal_unaligned(peer->addr, addr))
984fc36ffdaSJohannes Berg 			return i;
985fc36ffdaSJohannes Berg 	}
986fc36ffdaSJohannes Berg 
987fc36ffdaSJohannes Berg 	return -ENOENT;
988fc36ffdaSJohannes Berg }
989fc36ffdaSJohannes Berg 
iwl_mvm_ftm_get_host_time(struct iwl_mvm * mvm,__le32 fw_gp2_ts)990fc36ffdaSJohannes Berg static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts)
991fc36ffdaSJohannes Berg {
992fc36ffdaSJohannes Berg 	u32 gp2_ts = le32_to_cpu(fw_gp2_ts);
993fc36ffdaSJohannes Berg 	u32 curr_gp2, diff;
994fc36ffdaSJohannes Berg 	u64 now_from_boot_ns;
995fc36ffdaSJohannes Berg 
996c4ae8b9dSLuca Coelho 	iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2,
997c4ae8b9dSLuca Coelho 			      &now_from_boot_ns, NULL);
998fc36ffdaSJohannes Berg 
999fc36ffdaSJohannes Berg 	if (curr_gp2 >= gp2_ts)
1000fc36ffdaSJohannes Berg 		diff = curr_gp2 - gp2_ts;
1001fc36ffdaSJohannes Berg 	else
1002fc36ffdaSJohannes Berg 		diff = curr_gp2 + (U32_MAX - gp2_ts + 1);
1003fc36ffdaSJohannes Berg 
1004fc36ffdaSJohannes Berg 	return now_from_boot_ns - (u64)diff * 1000;
1005fc36ffdaSJohannes Berg }
1006fc36ffdaSJohannes Berg 
iwl_mvm_ftm_get_lci_civic(struct iwl_mvm * mvm,struct cfg80211_pmsr_result * res)1007fc36ffdaSJohannes Berg static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm,
1008fc36ffdaSJohannes Berg 				      struct cfg80211_pmsr_result *res)
1009fc36ffdaSJohannes Berg {
1010fc36ffdaSJohannes Berg 	struct iwl_mvm_loc_entry *entry;
1011fc36ffdaSJohannes Berg 
1012fc36ffdaSJohannes Berg 	list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) {
1013fc36ffdaSJohannes Berg 		if (!ether_addr_equal_unaligned(res->addr, entry->addr))
1014fc36ffdaSJohannes Berg 			continue;
1015fc36ffdaSJohannes Berg 
1016fc36ffdaSJohannes Berg 		if (entry->lci_len) {
1017fc36ffdaSJohannes Berg 			res->ftm.lci_len = entry->lci_len;
1018fc36ffdaSJohannes Berg 			res->ftm.lci = entry->buf;
1019fc36ffdaSJohannes Berg 		}
1020fc36ffdaSJohannes Berg 
1021fc36ffdaSJohannes Berg 		if (entry->civic_len) {
1022fc36ffdaSJohannes Berg 			res->ftm.civicloc_len = entry->civic_len;
1023fc36ffdaSJohannes Berg 			res->ftm.civicloc = entry->buf + entry->lci_len;
1024fc36ffdaSJohannes Berg 		}
1025fc36ffdaSJohannes Berg 
1026fc36ffdaSJohannes Berg 		/* we found the entry we needed */
1027fc36ffdaSJohannes Berg 		break;
1028fc36ffdaSJohannes Berg 	}
1029fc36ffdaSJohannes Berg }
1030fc36ffdaSJohannes Berg 
iwl_mvm_ftm_range_resp_valid(struct iwl_mvm * mvm,u8 request_id,u8 num_of_aps)1031ff418feeSAvraham Stern static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id,
1032ff418feeSAvraham Stern 					u8 num_of_aps)
1033ff418feeSAvraham Stern {
1034ff418feeSAvraham Stern 	lockdep_assert_held(&mvm->mutex);
1035ff418feeSAvraham Stern 
1036ff418feeSAvraham Stern 	if (request_id != (u8)mvm->ftm_initiator.req->cookie) {
1037ff418feeSAvraham Stern 		IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n",
1038ff418feeSAvraham Stern 			request_id, (u8)mvm->ftm_initiator.req->cookie);
1039ff418feeSAvraham Stern 		return -EINVAL;
1040ff418feeSAvraham Stern 	}
1041ff418feeSAvraham Stern 
1042ff418feeSAvraham Stern 	if (num_of_aps > mvm->ftm_initiator.req->n_peers) {
1043ff418feeSAvraham Stern 		IWL_ERR(mvm, "FTM range response invalid\n");
1044ff418feeSAvraham Stern 		return -EINVAL;
1045ff418feeSAvraham Stern 	}
1046ff418feeSAvraham Stern 
1047ff418feeSAvraham Stern 	return 0;
1048ff418feeSAvraham Stern }
1049ff418feeSAvraham Stern 
iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm * mvm,struct cfg80211_pmsr_result * res)1050b68bd2e3SIlan Peer static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm,
1051b68bd2e3SIlan Peer 				      struct cfg80211_pmsr_result *res)
1052b68bd2e3SIlan Peer {
10536d7cb4a6SJakob Koschel 	struct iwl_mvm_smooth_entry *resp = NULL, *iter;
1054b68bd2e3SIlan Peer 	s64 rtt_avg, rtt = res->ftm.rtt_avg;
1055b68bd2e3SIlan Peer 	u32 undershoot, overshoot;
1056b68bd2e3SIlan Peer 	u8 alpha;
1057b68bd2e3SIlan Peer 
1058b68bd2e3SIlan Peer 	if (!IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH)
1059b68bd2e3SIlan Peer 		return;
1060b68bd2e3SIlan Peer 
1061b68bd2e3SIlan Peer 	WARN_ON(rtt < 0);
1062b68bd2e3SIlan Peer 
1063b68bd2e3SIlan Peer 	if (res->status != NL80211_PMSR_STATUS_SUCCESS) {
1064b68bd2e3SIlan Peer 		IWL_DEBUG_INFO(mvm,
1065b68bd2e3SIlan Peer 			       ": %pM: ignore failed measurement. Status=%u\n",
1066b68bd2e3SIlan Peer 			       res->addr, res->status);
1067b68bd2e3SIlan Peer 		return;
1068b68bd2e3SIlan Peer 	}
1069b68bd2e3SIlan Peer 
10706d7cb4a6SJakob Koschel 	list_for_each_entry(iter, &mvm->ftm_initiator.smooth.resp, list) {
10716d7cb4a6SJakob Koschel 		if (!memcmp(res->addr, iter->addr, ETH_ALEN)) {
10726d7cb4a6SJakob Koschel 			resp = iter;
1073b68bd2e3SIlan Peer 			break;
1074b68bd2e3SIlan Peer 		}
1075b68bd2e3SIlan Peer 	}
1076b68bd2e3SIlan Peer 
10776d7cb4a6SJakob Koschel 	if (!resp) {
1078b68bd2e3SIlan Peer 		resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1079b68bd2e3SIlan Peer 		if (!resp)
1080b68bd2e3SIlan Peer 			return;
1081b68bd2e3SIlan Peer 
1082b68bd2e3SIlan Peer 		memcpy(resp->addr, res->addr, ETH_ALEN);
1083b68bd2e3SIlan Peer 		list_add_tail(&resp->list, &mvm->ftm_initiator.smooth.resp);
1084b68bd2e3SIlan Peer 
1085b68bd2e3SIlan Peer 		resp->rtt_avg = rtt;
1086b68bd2e3SIlan Peer 
1087b68bd2e3SIlan Peer 		IWL_DEBUG_INFO(mvm, "new: %pM: rtt_avg=%lld\n",
1088b68bd2e3SIlan Peer 			       resp->addr, resp->rtt_avg);
1089b68bd2e3SIlan Peer 		goto update_time;
1090b68bd2e3SIlan Peer 	}
1091b68bd2e3SIlan Peer 
1092b68bd2e3SIlan Peer 	if (res->host_time - resp->host_time >
1093b68bd2e3SIlan Peer 	    IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * 1000000000) {
1094b68bd2e3SIlan Peer 		resp->rtt_avg = rtt;
1095b68bd2e3SIlan Peer 
1096b68bd2e3SIlan Peer 		IWL_DEBUG_INFO(mvm, "expired: %pM: rtt_avg=%lld\n",
1097b68bd2e3SIlan Peer 			       resp->addr, resp->rtt_avg);
1098b68bd2e3SIlan Peer 		goto update_time;
1099b68bd2e3SIlan Peer 	}
1100b68bd2e3SIlan Peer 
1101b68bd2e3SIlan Peer 	/* Smooth the results based on the tracked RTT average */
1102b68bd2e3SIlan Peer 	undershoot = IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT;
1103b68bd2e3SIlan Peer 	overshoot = IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT;
1104b68bd2e3SIlan Peer 	alpha = IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA;
1105b68bd2e3SIlan Peer 
11064ccdcc8fSNathan Chancellor 	rtt_avg = div_s64(alpha * rtt + (100 - alpha) * resp->rtt_avg, 100);
1107b68bd2e3SIlan Peer 
1108b68bd2e3SIlan Peer 	IWL_DEBUG_INFO(mvm,
1109b68bd2e3SIlan Peer 		       "%pM: prev rtt_avg=%lld, new rtt_avg=%lld, rtt=%lld\n",
1110b68bd2e3SIlan Peer 		       resp->addr, resp->rtt_avg, rtt_avg, rtt);
1111b68bd2e3SIlan Peer 
1112b68bd2e3SIlan Peer 	/*
1113b68bd2e3SIlan Peer 	 * update the responder's average RTT results regardless of
1114b68bd2e3SIlan Peer 	 * the under/over shoot logic below
1115b68bd2e3SIlan Peer 	 */
1116b68bd2e3SIlan Peer 	resp->rtt_avg = rtt_avg;
1117b68bd2e3SIlan Peer 
1118b68bd2e3SIlan Peer 	/* smooth the results */
1119b68bd2e3SIlan Peer 	if (rtt_avg > rtt && (rtt_avg - rtt) > undershoot) {
1120b68bd2e3SIlan Peer 		res->ftm.rtt_avg = rtt_avg;
1121b68bd2e3SIlan Peer 
1122b68bd2e3SIlan Peer 		IWL_DEBUG_INFO(mvm,
1123b68bd2e3SIlan Peer 			       "undershoot: val=%lld\n",
1124b68bd2e3SIlan Peer 			       (rtt_avg - rtt));
1125b68bd2e3SIlan Peer 	} else if (rtt_avg < rtt && (rtt - rtt_avg) >
1126b68bd2e3SIlan Peer 		   overshoot) {
1127b68bd2e3SIlan Peer 		res->ftm.rtt_avg = rtt_avg;
1128b68bd2e3SIlan Peer 		IWL_DEBUG_INFO(mvm,
1129b68bd2e3SIlan Peer 			       "overshoot: val=%lld\n",
1130b68bd2e3SIlan Peer 			       (rtt - rtt_avg));
1131b68bd2e3SIlan Peer 	}
1132b68bd2e3SIlan Peer 
1133b68bd2e3SIlan Peer update_time:
1134b68bd2e3SIlan Peer 	resp->host_time = res->host_time;
1135b68bd2e3SIlan Peer }
1136b68bd2e3SIlan Peer 
iwl_mvm_debug_range_resp(struct iwl_mvm * mvm,u8 index,struct cfg80211_pmsr_result * res)1137937b10c0SAvraham Stern static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
1138937b10c0SAvraham Stern 				     struct cfg80211_pmsr_result *res)
1139937b10c0SAvraham Stern {
1140688cd8bdSArnd Bergmann 	s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);
1141937b10c0SAvraham Stern 
1142937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "entry %d\n", index);
1143937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status);
1144937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr);
1145937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time);
11467819b3d1SJustin Stitt 	IWL_DEBUG_INFO(mvm, "\tburst index: %d\n", res->ftm.burst_index);
1147937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes);
1148937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg);
11497819b3d1SJustin Stitt 	IWL_DEBUG_INFO(mvm, "\trssi spread: %d\n", res->ftm.rssi_spread);
1150937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg);
1151937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance);
1152937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread);
1153937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg);
1154937b10c0SAvraham Stern }
1155937b10c0SAvraham Stern 
11560739a7d7SAvraham Stern static void
iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm * mvm,struct iwl_tof_range_rsp_ap_entry_ntfy_v6 * fw_ap)11570739a7d7SAvraham Stern iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm,
11580739a7d7SAvraham Stern 			   struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap)
11590739a7d7SAvraham Stern {
11600739a7d7SAvraham Stern 	struct iwl_mvm_ftm_pasn_entry *entry;
11610739a7d7SAvraham Stern 
11620739a7d7SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
11630739a7d7SAvraham Stern 
11640739a7d7SAvraham Stern 	list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
11650739a7d7SAvraham Stern 		if (memcmp(fw_ap->bssid, entry->addr, sizeof(entry->addr)))
11660739a7d7SAvraham Stern 			continue;
11670739a7d7SAvraham Stern 
11680739a7d7SAvraham Stern 		memcpy(entry->rx_pn, fw_ap->rx_pn, sizeof(entry->rx_pn));
11690739a7d7SAvraham Stern 		memcpy(entry->tx_pn, fw_ap->tx_pn, sizeof(entry->tx_pn));
11700739a7d7SAvraham Stern 		return;
11710739a7d7SAvraham Stern 	}
11720739a7d7SAvraham Stern }
11730739a7d7SAvraham Stern 
iwl_mvm_ftm_get_range_resp_ver(struct iwl_mvm * mvm)117427eeb037SAvraham Stern static u8 iwl_mvm_ftm_get_range_resp_ver(struct iwl_mvm *mvm)
117527eeb037SAvraham Stern {
117627eeb037SAvraham Stern 	if (!fw_has_api(&mvm->fw->ucode_capa,
117727eeb037SAvraham Stern 			IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ))
117827eeb037SAvraham Stern 		return 5;
117927eeb037SAvraham Stern 
118027eeb037SAvraham Stern 	/* Starting from version 8, the FW advertises the version */
118127eeb037SAvraham Stern 	if (mvm->cmd_ver.range_resp >= 8)
118227eeb037SAvraham Stern 		return mvm->cmd_ver.range_resp;
118327eeb037SAvraham Stern 	else if (fw_has_api(&mvm->fw->ucode_capa,
118427eeb037SAvraham Stern 			    IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
118527eeb037SAvraham Stern 		return 7;
118627eeb037SAvraham Stern 
118727eeb037SAvraham Stern 	/* The first version of the new range request API */
118827eeb037SAvraham Stern 	return 6;
118927eeb037SAvraham Stern }
119027eeb037SAvraham Stern 
iwl_mvm_ftm_resp_size_validation(u8 ver,unsigned int pkt_len)119127eeb037SAvraham Stern static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len)
119227eeb037SAvraham Stern {
119327eeb037SAvraham Stern 	switch (ver) {
1194bd8b5f30SMiri Korenblit 	case 9:
119527eeb037SAvraham Stern 	case 8:
119627eeb037SAvraham Stern 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8);
119727eeb037SAvraham Stern 	case 7:
119827eeb037SAvraham Stern 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7);
119927eeb037SAvraham Stern 	case 6:
120027eeb037SAvraham Stern 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v6);
120127eeb037SAvraham Stern 	case 5:
120227eeb037SAvraham Stern 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v5);
120327eeb037SAvraham Stern 	default:
120427eeb037SAvraham Stern 		WARN_ONCE(1, "FTM: unsupported range response version %u", ver);
120527eeb037SAvraham Stern 		return false;
120627eeb037SAvraham Stern 	}
120727eeb037SAvraham Stern }
120827eeb037SAvraham Stern 
iwl_mvm_ftm_range_resp(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)1209fc36ffdaSJohannes Berg void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1210fc36ffdaSJohannes Berg {
1211fc36ffdaSJohannes Berg 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
121227eeb037SAvraham Stern 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
1213ff418feeSAvraham Stern 	struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data;
1214957a67c8SAvraham Stern 	struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data;
12151c096d89SAvraham Stern 	struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data;
12161c096d89SAvraham Stern 	struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data;
1217fc36ffdaSJohannes Berg 	int i;
1218ff418feeSAvraham Stern 	bool new_api = fw_has_api(&mvm->fw->ucode_capa,
1219ff418feeSAvraham Stern 				  IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
1220ff418feeSAvraham Stern 	u8 num_of_aps, last_in_batch;
122127eeb037SAvraham Stern 	u8 notif_ver = iwl_mvm_ftm_get_range_resp_ver(mvm);
1222fc36ffdaSJohannes Berg 
1223fc36ffdaSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
1224fc36ffdaSJohannes Berg 
1225fc36ffdaSJohannes Berg 	if (!mvm->ftm_initiator.req) {
1226fc36ffdaSJohannes Berg 		return;
1227fc36ffdaSJohannes Berg 	}
1228fc36ffdaSJohannes Berg 
122927eeb037SAvraham Stern 	if (unlikely(!iwl_mvm_ftm_resp_size_validation(notif_ver, pkt_len)))
123027eeb037SAvraham Stern 		return;
123127eeb037SAvraham Stern 
1232ff418feeSAvraham Stern 	if (new_api) {
12331c096d89SAvraham Stern 		if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v8->request_id,
12341c096d89SAvraham Stern 						 fw_resp_v8->num_of_aps))
1235fc36ffdaSJohannes Berg 			return;
1236ff418feeSAvraham Stern 
12371c096d89SAvraham Stern 		num_of_aps = fw_resp_v8->num_of_aps;
12381c096d89SAvraham Stern 		last_in_batch = fw_resp_v8->last_report;
1239ff418feeSAvraham Stern 	} else {
1240ff418feeSAvraham Stern 		if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id,
1241ff418feeSAvraham Stern 						 fw_resp_v5->num_of_aps))
1242ff418feeSAvraham Stern 			return;
1243ff418feeSAvraham Stern 
1244ff418feeSAvraham Stern 		num_of_aps = fw_resp_v5->num_of_aps;
1245ff418feeSAvraham Stern 		last_in_batch = fw_resp_v5->last_in_batch;
1246fc36ffdaSJohannes Berg 	}
1247fc36ffdaSJohannes Berg 
1248937b10c0SAvraham Stern 	IWL_DEBUG_INFO(mvm, "Range response received\n");
12494de5ceefSTom Rix 	IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n",
1250937b10c0SAvraham Stern 		       mvm->ftm_initiator.req->cookie, num_of_aps);
1251937b10c0SAvraham Stern 
1252ff418feeSAvraham Stern 	for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) {
1253fc36ffdaSJohannes Berg 		struct cfg80211_pmsr_result result = {};
12541c096d89SAvraham Stern 		struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap;
1255fc36ffdaSJohannes Berg 		int peer_idx;
1256fc36ffdaSJohannes Berg 
1257ff418feeSAvraham Stern 		if (new_api) {
1258bd8b5f30SMiri Korenblit 			if (notif_ver >= 8) {
12591c096d89SAvraham Stern 				fw_ap = &fw_resp_v8->ap[i];
12600739a7d7SAvraham Stern 				iwl_mvm_ftm_pasn_update_pn(mvm, fw_ap);
126127eeb037SAvraham Stern 			} else if (notif_ver == 7) {
12621c096d89SAvraham Stern 				fw_ap = (void *)&fw_resp_v7->ap[i];
12630739a7d7SAvraham Stern 			} else {
1264957a67c8SAvraham Stern 				fw_ap = (void *)&fw_resp_v6->ap[i];
12650739a7d7SAvraham Stern 			}
1266957a67c8SAvraham Stern 
12671c096d89SAvraham Stern 			result.final = fw_ap->last_burst;
1268cec2d4f6SAvraham Stern 			result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);
1269cec2d4f6SAvraham Stern 			result.ap_tsf_valid = 1;
1270ff418feeSAvraham Stern 		} else {
1271ff418feeSAvraham Stern 			/* the first part is the same for old and new APIs */
1272ff418feeSAvraham Stern 			fw_ap = (void *)&fw_resp_v5->ap[i];
1273ff418feeSAvraham Stern 			/*
1274ff418feeSAvraham Stern 			 * FIXME: the firmware needs to report this, we don't
1275ff418feeSAvraham Stern 			 * even know the number of bursts the responder picked
1276ff418feeSAvraham Stern 			 * (if we asked it to)
1277ff418feeSAvraham Stern 			 */
1278ff418feeSAvraham Stern 			result.final = 0;
1279ff418feeSAvraham Stern 		}
1280ff418feeSAvraham Stern 
1281fc36ffdaSJohannes Berg 		peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req,
1282fc36ffdaSJohannes Berg 						 fw_ap->bssid);
1283fc36ffdaSJohannes Berg 		if (peer_idx < 0) {
1284fc36ffdaSJohannes Berg 			IWL_WARN(mvm,
1285ff418feeSAvraham Stern 				 "Unknown address (%pM, target #%d) in FTM response\n",
1286fc36ffdaSJohannes Berg 				 fw_ap->bssid, i);
1287fc36ffdaSJohannes Berg 			continue;
1288fc36ffdaSJohannes Berg 		}
1289fc36ffdaSJohannes Berg 
1290fc36ffdaSJohannes Berg 		switch (fw_ap->measure_status) {
1291fc36ffdaSJohannes Berg 		case IWL_TOF_ENTRY_SUCCESS:
1292fc36ffdaSJohannes Berg 			result.status = NL80211_PMSR_STATUS_SUCCESS;
1293fc36ffdaSJohannes Berg 			break;
1294fc36ffdaSJohannes Berg 		case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT:
1295fc36ffdaSJohannes Berg 			result.status = NL80211_PMSR_STATUS_TIMEOUT;
1296fc36ffdaSJohannes Berg 			break;
1297fc36ffdaSJohannes Berg 		case IWL_TOF_ENTRY_NO_RESPONSE:
1298fc36ffdaSJohannes Berg 			result.status = NL80211_PMSR_STATUS_FAILURE;
1299fc36ffdaSJohannes Berg 			result.ftm.failure_reason =
1300fc36ffdaSJohannes Berg 				NL80211_PMSR_FTM_FAILURE_NO_RESPONSE;
1301fc36ffdaSJohannes Berg 			break;
1302fc36ffdaSJohannes Berg 		case IWL_TOF_ENTRY_REQUEST_REJECTED:
1303fc36ffdaSJohannes Berg 			result.status = NL80211_PMSR_STATUS_FAILURE;
1304fc36ffdaSJohannes Berg 			result.ftm.failure_reason =
1305fc36ffdaSJohannes Berg 				NL80211_PMSR_FTM_FAILURE_PEER_BUSY;
1306fc36ffdaSJohannes Berg 			result.ftm.busy_retry_time = fw_ap->refusal_period;
1307fc36ffdaSJohannes Berg 			break;
1308fc36ffdaSJohannes Berg 		default:
1309fc36ffdaSJohannes Berg 			result.status = NL80211_PMSR_STATUS_FAILURE;
1310fc36ffdaSJohannes Berg 			result.ftm.failure_reason =
1311fc36ffdaSJohannes Berg 				NL80211_PMSR_FTM_FAILURE_UNSPECIFIED;
1312fc36ffdaSJohannes Berg 			break;
1313fc36ffdaSJohannes Berg 		}
1314fc36ffdaSJohannes Berg 		memcpy(result.addr, fw_ap->bssid, ETH_ALEN);
1315fc36ffdaSJohannes Berg 		result.host_time = iwl_mvm_ftm_get_host_time(mvm,
1316fc36ffdaSJohannes Berg 							     fw_ap->timestamp);
1317fc36ffdaSJohannes Berg 		result.type = NL80211_PMSR_TYPE_FTM;
1318fc36ffdaSJohannes Berg 		result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx];
1319fc36ffdaSJohannes Berg 		mvm->ftm_initiator.responses[peer_idx]++;
1320fc36ffdaSJohannes Berg 		result.ftm.rssi_avg = fw_ap->rssi;
1321fc36ffdaSJohannes Berg 		result.ftm.rssi_avg_valid = 1;
1322fc36ffdaSJohannes Berg 		result.ftm.rssi_spread = fw_ap->rssi_spread;
1323fc36ffdaSJohannes Berg 		result.ftm.rssi_spread_valid = 1;
1324fc36ffdaSJohannes Berg 		result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt);
1325fc36ffdaSJohannes Berg 		result.ftm.rtt_avg_valid = 1;
1326fc36ffdaSJohannes Berg 		result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance);
1327fc36ffdaSJohannes Berg 		result.ftm.rtt_variance_valid = 1;
1328fc36ffdaSJohannes Berg 		result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread);
1329fc36ffdaSJohannes Berg 		result.ftm.rtt_spread_valid = 1;
1330fc36ffdaSJohannes Berg 
1331fc36ffdaSJohannes Berg 		iwl_mvm_ftm_get_lci_civic(mvm, &result);
1332fc36ffdaSJohannes Berg 
1333b68bd2e3SIlan Peer 		iwl_mvm_ftm_rtt_smoothing(mvm, &result);
1334b68bd2e3SIlan Peer 
1335fc36ffdaSJohannes Berg 		cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
1336fc36ffdaSJohannes Berg 				     mvm->ftm_initiator.req,
1337fc36ffdaSJohannes Berg 				     &result, GFP_KERNEL);
1338937b10c0SAvraham Stern 
1339957a67c8SAvraham Stern 		if (fw_has_api(&mvm->fw->ucode_capa,
1340957a67c8SAvraham Stern 			       IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
13414de5ceefSTom Rix 			IWL_DEBUG_INFO(mvm, "RTT confidence: %u\n",
1342957a67c8SAvraham Stern 				       fw_ap->rttConfidence);
1343957a67c8SAvraham Stern 
1344937b10c0SAvraham Stern 		iwl_mvm_debug_range_resp(mvm, i, &result);
1345fc36ffdaSJohannes Berg 	}
1346fc36ffdaSJohannes Berg 
1347ff418feeSAvraham Stern 	if (last_in_batch) {
1348fc36ffdaSJohannes Berg 		cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
1349fc36ffdaSJohannes Berg 				       mvm->ftm_initiator.req,
1350fc36ffdaSJohannes Berg 				       GFP_KERNEL);
1351fc36ffdaSJohannes Berg 		iwl_mvm_ftm_reset(mvm);
1352fc36ffdaSJohannes Berg 	}
1353fc36ffdaSJohannes Berg }
1354fc36ffdaSJohannes Berg 
iwl_mvm_ftm_lc_notif(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)1355fc36ffdaSJohannes Berg void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1356fc36ffdaSJohannes Berg {
1357fc36ffdaSJohannes Berg 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
1358fc36ffdaSJohannes Berg 	const struct ieee80211_mgmt *mgmt = (void *)pkt->data;
1359fc36ffdaSJohannes Berg 	size_t len = iwl_rx_packet_payload_len(pkt);
1360fc36ffdaSJohannes Berg 	struct iwl_mvm_loc_entry *entry;
1361fc36ffdaSJohannes Berg 	const u8 *ies, *lci, *civic, *msr_ie;
1362fc36ffdaSJohannes Berg 	size_t ies_len, lci_len = 0, civic_len = 0;
1363fc36ffdaSJohannes Berg 	size_t baselen = IEEE80211_MIN_ACTION_SIZE +
1364fc36ffdaSJohannes Berg 			 sizeof(mgmt->u.action.u.ftm);
1365fc36ffdaSJohannes Berg 	static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI;
1366fc36ffdaSJohannes Berg 	static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC;
1367fc36ffdaSJohannes Berg 
1368fc36ffdaSJohannes Berg 	if (len <= baselen)
1369fc36ffdaSJohannes Berg 		return;
1370fc36ffdaSJohannes Berg 
1371fc36ffdaSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
1372fc36ffdaSJohannes Berg 
1373fc36ffdaSJohannes Berg 	ies = mgmt->u.action.u.ftm.variable;
1374fc36ffdaSJohannes Berg 	ies_len = len - baselen;
1375fc36ffdaSJohannes Berg 
1376fc36ffdaSJohannes Berg 	msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
1377fc36ffdaSJohannes Berg 					&rprt_type_lci, 1, 4);
1378fc36ffdaSJohannes Berg 	if (msr_ie) {
1379fc36ffdaSJohannes Berg 		lci = msr_ie + 2;
1380fc36ffdaSJohannes Berg 		lci_len = msr_ie[1];
1381fc36ffdaSJohannes Berg 	}
1382fc36ffdaSJohannes Berg 
1383fc36ffdaSJohannes Berg 	msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
1384fc36ffdaSJohannes Berg 					&rprt_type_civic, 1, 4);
1385fc36ffdaSJohannes Berg 	if (msr_ie) {
1386fc36ffdaSJohannes Berg 		civic = msr_ie + 2;
1387fc36ffdaSJohannes Berg 		civic_len = msr_ie[1];
1388fc36ffdaSJohannes Berg 	}
1389fc36ffdaSJohannes Berg 
1390fc36ffdaSJohannes Berg 	entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL);
1391fc36ffdaSJohannes Berg 	if (!entry)
1392fc36ffdaSJohannes Berg 		return;
1393fc36ffdaSJohannes Berg 
1394fc36ffdaSJohannes Berg 	memcpy(entry->addr, mgmt->bssid, ETH_ALEN);
1395fc36ffdaSJohannes Berg 
1396fc36ffdaSJohannes Berg 	entry->lci_len = lci_len;
1397fc36ffdaSJohannes Berg 	if (lci_len)
1398fc36ffdaSJohannes Berg 		memcpy(entry->buf, lci, lci_len);
1399fc36ffdaSJohannes Berg 
1400fc36ffdaSJohannes Berg 	entry->civic_len = civic_len;
1401fc36ffdaSJohannes Berg 	if (civic_len)
1402fc36ffdaSJohannes Berg 		memcpy(entry->buf + lci_len, civic, civic_len);
1403fc36ffdaSJohannes Berg 
1404fc36ffdaSJohannes Berg 	list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list);
1405fc36ffdaSJohannes Berg }
1406