xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/ftm-initiator.c (revision a4128aad8503277614f2d214011ef60a19447b83)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2015-2017 Intel Deutschland GmbH
4*a4128aadSBjoern A. Zeeb  * Copyright (C) 2018-2024 Intel Corporation
5bfcc09ddSBjoern A. Zeeb  */
6bfcc09ddSBjoern A. Zeeb #include <linux/etherdevice.h>
7bfcc09ddSBjoern A. Zeeb #include <linux/math64.h>
8bfcc09ddSBjoern A. Zeeb #include <net/cfg80211.h>
9bfcc09ddSBjoern A. Zeeb #include "mvm.h"
10bfcc09ddSBjoern A. Zeeb #include "iwl-io.h"
11bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h"
12bfcc09ddSBjoern A. Zeeb #include "constants.h"
13bfcc09ddSBjoern A. Zeeb 
14bfcc09ddSBjoern A. Zeeb struct iwl_mvm_loc_entry {
15bfcc09ddSBjoern A. Zeeb 	struct list_head list;
16bfcc09ddSBjoern A. Zeeb 	u8 addr[ETH_ALEN];
17bfcc09ddSBjoern A. Zeeb 	u8 lci_len, civic_len;
18bfcc09ddSBjoern A. Zeeb 	u8 buf[];
19bfcc09ddSBjoern A. Zeeb };
20bfcc09ddSBjoern A. Zeeb 
21bfcc09ddSBjoern A. Zeeb struct iwl_mvm_smooth_entry {
22bfcc09ddSBjoern A. Zeeb 	struct list_head list;
23bfcc09ddSBjoern A. Zeeb 	u8 addr[ETH_ALEN];
24bfcc09ddSBjoern A. Zeeb 	s64 rtt_avg;
25bfcc09ddSBjoern A. Zeeb 	u64 host_time;
26bfcc09ddSBjoern A. Zeeb };
27bfcc09ddSBjoern A. Zeeb 
289af1bba4SBjoern A. Zeeb enum iwl_mvm_pasn_flags {
299af1bba4SBjoern A. Zeeb 	IWL_MVM_PASN_FLAG_HAS_HLTK = BIT(0),
309af1bba4SBjoern A. Zeeb };
319af1bba4SBjoern A. Zeeb 
32bfcc09ddSBjoern A. Zeeb struct iwl_mvm_ftm_pasn_entry {
33bfcc09ddSBjoern A. Zeeb 	struct list_head list;
34bfcc09ddSBjoern A. Zeeb 	u8 addr[ETH_ALEN];
35bfcc09ddSBjoern A. Zeeb 	u8 hltk[HLTK_11AZ_LEN];
36bfcc09ddSBjoern A. Zeeb 	u8 tk[TK_11AZ_LEN];
37bfcc09ddSBjoern A. Zeeb 	u8 cipher;
38bfcc09ddSBjoern A. Zeeb 	u8 tx_pn[IEEE80211_CCMP_PN_LEN];
39bfcc09ddSBjoern A. Zeeb 	u8 rx_pn[IEEE80211_CCMP_PN_LEN];
409af1bba4SBjoern A. Zeeb 	u32 flags;
41bfcc09ddSBjoern A. Zeeb };
42bfcc09ddSBjoern A. Zeeb 
43*a4128aadSBjoern A. Zeeb struct iwl_mvm_ftm_iter_data {
44*a4128aadSBjoern A. Zeeb 	u8 *cipher;
45*a4128aadSBjoern A. Zeeb 	u8 *bssid;
46*a4128aadSBjoern A. Zeeb 	u8 *tk;
47*a4128aadSBjoern A. Zeeb };
48*a4128aadSBjoern A. Zeeb 
49bfcc09ddSBjoern A. Zeeb int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
50bfcc09ddSBjoern A. Zeeb 			     u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
51bfcc09ddSBjoern A. Zeeb 			     u8 *hltk, u32 hltk_len)
52bfcc09ddSBjoern A. Zeeb {
53bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn),
54bfcc09ddSBjoern A. Zeeb 						      GFP_KERNEL);
55bfcc09ddSBjoern A. Zeeb 	u32 expected_tk_len;
56bfcc09ddSBjoern A. Zeeb 
57bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
58bfcc09ddSBjoern A. Zeeb 
59bfcc09ddSBjoern A. Zeeb 	if (!pasn)
60bfcc09ddSBjoern A. Zeeb 		return -ENOBUFS;
61bfcc09ddSBjoern A. Zeeb 
62*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_remove_pasn_sta(mvm, addr);
63*a4128aadSBjoern A. Zeeb 
64bfcc09ddSBjoern A. Zeeb 	pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher);
65bfcc09ddSBjoern A. Zeeb 
66bfcc09ddSBjoern A. Zeeb 	switch (pasn->cipher) {
67bfcc09ddSBjoern A. Zeeb 	case IWL_LOCATION_CIPHER_CCMP_128:
68bfcc09ddSBjoern A. Zeeb 	case IWL_LOCATION_CIPHER_GCMP_128:
69bfcc09ddSBjoern A. Zeeb 		expected_tk_len = WLAN_KEY_LEN_CCMP;
70bfcc09ddSBjoern A. Zeeb 		break;
71bfcc09ddSBjoern A. Zeeb 	case IWL_LOCATION_CIPHER_GCMP_256:
72bfcc09ddSBjoern A. Zeeb 		expected_tk_len = WLAN_KEY_LEN_GCMP_256;
73bfcc09ddSBjoern A. Zeeb 		break;
74bfcc09ddSBjoern A. Zeeb 	default:
75bfcc09ddSBjoern A. Zeeb 		goto out;
76bfcc09ddSBjoern A. Zeeb 	}
77bfcc09ddSBjoern A. Zeeb 
78bfcc09ddSBjoern A. Zeeb 	/*
79bfcc09ddSBjoern A. Zeeb 	 * If associated to this AP and already have security context,
80bfcc09ddSBjoern A. Zeeb 	 * the TK is already configured for this station, so it
81bfcc09ddSBjoern A. Zeeb 	 * shouldn't be set again here.
82bfcc09ddSBjoern A. Zeeb 	 */
839af1bba4SBjoern A. Zeeb 	if (vif->cfg.assoc) {
84bfcc09ddSBjoern A. Zeeb 		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
859af1bba4SBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf;
869af1bba4SBjoern A. Zeeb 		unsigned int link_id;
87bfcc09ddSBjoern A. Zeeb 		struct ieee80211_sta *sta;
889af1bba4SBjoern A. Zeeb 		u8 sta_id;
89bfcc09ddSBjoern A. Zeeb 
90bfcc09ddSBjoern A. Zeeb 		rcu_read_lock();
919af1bba4SBjoern A. Zeeb 		for_each_vif_active_link(vif, link_conf, link_id) {
929af1bba4SBjoern A. Zeeb 			if (memcmp(addr, link_conf->bssid, ETH_ALEN))
939af1bba4SBjoern A. Zeeb 				continue;
949af1bba4SBjoern A. Zeeb 
959af1bba4SBjoern A. Zeeb 			sta_id = mvmvif->link[link_id]->ap_sta_id;
969af1bba4SBjoern A. Zeeb 			sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
97bfcc09ddSBjoern A. Zeeb 			if (!IS_ERR_OR_NULL(sta) && sta->mfp)
98bfcc09ddSBjoern A. Zeeb 				expected_tk_len = 0;
999af1bba4SBjoern A. Zeeb 			break;
1009af1bba4SBjoern A. Zeeb 		}
101bfcc09ddSBjoern A. Zeeb 		rcu_read_unlock();
102bfcc09ddSBjoern A. Zeeb 	}
103bfcc09ddSBjoern A. Zeeb 
1049af1bba4SBjoern A. Zeeb 	if (tk_len != expected_tk_len ||
1059af1bba4SBjoern A. Zeeb 	    (hltk_len && hltk_len != sizeof(pasn->hltk))) {
106bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n",
107bfcc09ddSBjoern A. Zeeb 			tk_len, hltk_len);
108bfcc09ddSBjoern A. Zeeb 		goto out;
109bfcc09ddSBjoern A. Zeeb 	}
110bfcc09ddSBjoern A. Zeeb 
1119af1bba4SBjoern A. Zeeb 	if (!expected_tk_len && !hltk_len) {
1129af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "TK and HLTK not set\n");
1139af1bba4SBjoern A. Zeeb 		goto out;
1149af1bba4SBjoern A. Zeeb 	}
1159af1bba4SBjoern A. Zeeb 
116bfcc09ddSBjoern A. Zeeb 	memcpy(pasn->addr, addr, sizeof(pasn->addr));
1179af1bba4SBjoern A. Zeeb 
1189af1bba4SBjoern A. Zeeb 	if (hltk_len) {
119bfcc09ddSBjoern A. Zeeb 		memcpy(pasn->hltk, hltk, sizeof(pasn->hltk));
1209af1bba4SBjoern A. Zeeb 		pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK;
1219af1bba4SBjoern A. Zeeb 	}
122bfcc09ddSBjoern A. Zeeb 
123bfcc09ddSBjoern A. Zeeb 	if (tk && tk_len)
124bfcc09ddSBjoern A. Zeeb 		memcpy(pasn->tk, tk, sizeof(pasn->tk));
125bfcc09ddSBjoern A. Zeeb 
126bfcc09ddSBjoern A. Zeeb 	list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list);
127bfcc09ddSBjoern A. Zeeb 	return 0;
128bfcc09ddSBjoern A. Zeeb out:
129bfcc09ddSBjoern A. Zeeb 	kfree(pasn);
130bfcc09ddSBjoern A. Zeeb 	return -EINVAL;
131bfcc09ddSBjoern A. Zeeb }
132bfcc09ddSBjoern A. Zeeb 
133bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr)
134bfcc09ddSBjoern A. Zeeb {
135bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_ftm_pasn_entry *entry, *prev;
136bfcc09ddSBjoern A. Zeeb 
137bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
138bfcc09ddSBjoern A. Zeeb 
139bfcc09ddSBjoern A. Zeeb 	list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list,
140bfcc09ddSBjoern A. Zeeb 				 list) {
141bfcc09ddSBjoern A. Zeeb 		if (memcmp(entry->addr, addr, sizeof(entry->addr)))
142bfcc09ddSBjoern A. Zeeb 			continue;
143bfcc09ddSBjoern A. Zeeb 
144bfcc09ddSBjoern A. Zeeb 		list_del(&entry->list);
145bfcc09ddSBjoern A. Zeeb 		kfree(entry);
146bfcc09ddSBjoern A. Zeeb 		return;
147bfcc09ddSBjoern A. Zeeb 	}
148bfcc09ddSBjoern A. Zeeb }
149bfcc09ddSBjoern A. Zeeb 
150bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm)
151bfcc09ddSBjoern A. Zeeb {
152bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_loc_entry *e, *t;
153bfcc09ddSBjoern A. Zeeb 
154bfcc09ddSBjoern A. Zeeb 	mvm->ftm_initiator.req = NULL;
155bfcc09ddSBjoern A. Zeeb 	mvm->ftm_initiator.req_wdev = NULL;
156bfcc09ddSBjoern A. Zeeb 	memset(mvm->ftm_initiator.responses, 0,
157bfcc09ddSBjoern A. Zeeb 	       sizeof(mvm->ftm_initiator.responses));
158bfcc09ddSBjoern A. Zeeb 
159bfcc09ddSBjoern A. Zeeb 	list_for_each_entry_safe(e, t, &mvm->ftm_initiator.loc_list, list) {
160bfcc09ddSBjoern A. Zeeb 		list_del(&e->list);
161bfcc09ddSBjoern A. Zeeb 		kfree(e);
162bfcc09ddSBjoern A. Zeeb 	}
163bfcc09ddSBjoern A. Zeeb }
164bfcc09ddSBjoern A. Zeeb 
165bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_restart(struct iwl_mvm *mvm)
166bfcc09ddSBjoern A. Zeeb {
167bfcc09ddSBjoern A. Zeeb 	struct cfg80211_pmsr_result result = {
168bfcc09ddSBjoern A. Zeeb 		.status = NL80211_PMSR_STATUS_FAILURE,
169bfcc09ddSBjoern A. Zeeb 		.final = 1,
170bfcc09ddSBjoern A. Zeeb 		.host_time = ktime_get_boottime_ns(),
171bfcc09ddSBjoern A. Zeeb 		.type = NL80211_PMSR_TYPE_FTM,
172bfcc09ddSBjoern A. Zeeb 	};
173bfcc09ddSBjoern A. Zeeb 	int i;
174bfcc09ddSBjoern A. Zeeb 
175bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
176bfcc09ddSBjoern A. Zeeb 
177bfcc09ddSBjoern A. Zeeb 	if (!mvm->ftm_initiator.req)
178bfcc09ddSBjoern A. Zeeb 		return;
179bfcc09ddSBjoern A. Zeeb 
180bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < mvm->ftm_initiator.req->n_peers; i++) {
181bfcc09ddSBjoern A. Zeeb 		memcpy(result.addr, mvm->ftm_initiator.req->peers[i].addr,
182bfcc09ddSBjoern A. Zeeb 		       ETH_ALEN);
183bfcc09ddSBjoern A. Zeeb 		result.ftm.burst_index = mvm->ftm_initiator.responses[i];
184bfcc09ddSBjoern A. Zeeb 
185bfcc09ddSBjoern A. Zeeb 		cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
186bfcc09ddSBjoern A. Zeeb 				     mvm->ftm_initiator.req,
187bfcc09ddSBjoern A. Zeeb 				     &result, GFP_KERNEL);
188bfcc09ddSBjoern A. Zeeb 	}
189bfcc09ddSBjoern A. Zeeb 
190bfcc09ddSBjoern A. Zeeb 	cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
191bfcc09ddSBjoern A. Zeeb 			       mvm->ftm_initiator.req, GFP_KERNEL);
192bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_reset(mvm);
193bfcc09ddSBjoern A. Zeeb }
194bfcc09ddSBjoern A. Zeeb 
195bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm)
196bfcc09ddSBjoern A. Zeeb {
197bfcc09ddSBjoern A. Zeeb 	INIT_LIST_HEAD(&mvm->ftm_initiator.smooth.resp);
198bfcc09ddSBjoern A. Zeeb 
199bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
200bfcc09ddSBjoern A. Zeeb 		       "enable=%u, alpha=%u, age_jiffies=%u, thresh=(%u:%u)\n",
201bfcc09ddSBjoern A. Zeeb 			IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH,
202bfcc09ddSBjoern A. Zeeb 			IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA,
203bfcc09ddSBjoern A. Zeeb 			IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * HZ,
204bfcc09ddSBjoern A. Zeeb 			IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT,
205bfcc09ddSBjoern A. Zeeb 			IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT);
206bfcc09ddSBjoern A. Zeeb }
207bfcc09ddSBjoern A. Zeeb 
208bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm)
209bfcc09ddSBjoern A. Zeeb {
210bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_smooth_entry *se, *st;
211bfcc09ddSBjoern A. Zeeb 
212bfcc09ddSBjoern A. Zeeb 	list_for_each_entry_safe(se, st, &mvm->ftm_initiator.smooth.resp,
213bfcc09ddSBjoern A. Zeeb 				 list) {
214bfcc09ddSBjoern A. Zeeb 		list_del(&se->list);
215bfcc09ddSBjoern A. Zeeb 		kfree(se);
216bfcc09ddSBjoern A. Zeeb 	}
217bfcc09ddSBjoern A. Zeeb }
218bfcc09ddSBjoern A. Zeeb 
219bfcc09ddSBjoern A. Zeeb static int
220bfcc09ddSBjoern A. Zeeb iwl_ftm_range_request_status_to_err(enum iwl_tof_range_request_status s)
221bfcc09ddSBjoern A. Zeeb {
222bfcc09ddSBjoern A. Zeeb 	switch (s) {
223bfcc09ddSBjoern A. Zeeb 	case IWL_TOF_RANGE_REQUEST_STATUS_SUCCESS:
224bfcc09ddSBjoern A. Zeeb 		return 0;
225bfcc09ddSBjoern A. Zeeb 	case IWL_TOF_RANGE_REQUEST_STATUS_BUSY:
226bfcc09ddSBjoern A. Zeeb 		return -EBUSY;
227bfcc09ddSBjoern A. Zeeb 	default:
228bfcc09ddSBjoern A. Zeeb 		WARN_ON_ONCE(1);
229bfcc09ddSBjoern A. Zeeb 		return -EIO;
230bfcc09ddSBjoern A. Zeeb 	}
231bfcc09ddSBjoern A. Zeeb }
232bfcc09ddSBjoern A. Zeeb 
233bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_cmd_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
234bfcc09ddSBjoern A. Zeeb 			       struct iwl_tof_range_req_cmd_v5 *cmd,
235bfcc09ddSBjoern A. Zeeb 			       struct cfg80211_pmsr_request *req)
236bfcc09ddSBjoern A. Zeeb {
237bfcc09ddSBjoern A. Zeeb 	int i;
238bfcc09ddSBjoern A. Zeeb 
239bfcc09ddSBjoern A. Zeeb 	cmd->request_id = req->cookie;
240bfcc09ddSBjoern A. Zeeb 	cmd->num_of_ap = req->n_peers;
241bfcc09ddSBjoern A. Zeeb 
242bfcc09ddSBjoern A. Zeeb 	/* use maximum for "no timeout" or bigger than what we can do */
243bfcc09ddSBjoern A. Zeeb 	if (!req->timeout || req->timeout > 255 * 100)
244bfcc09ddSBjoern A. Zeeb 		cmd->req_timeout = 255;
245bfcc09ddSBjoern A. Zeeb 	else
246bfcc09ddSBjoern A. Zeeb 		cmd->req_timeout = DIV_ROUND_UP(req->timeout, 100);
247bfcc09ddSBjoern A. Zeeb 
248bfcc09ddSBjoern A. Zeeb 	/*
249bfcc09ddSBjoern A. Zeeb 	 * We treat it always as random, since if not we'll
250bfcc09ddSBjoern A. Zeeb 	 * have filled our local address there instead.
251bfcc09ddSBjoern A. Zeeb 	 */
252bfcc09ddSBjoern A. Zeeb 	cmd->macaddr_random = 1;
253bfcc09ddSBjoern A. Zeeb 	memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
254bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < ETH_ALEN; i++)
255bfcc09ddSBjoern A. Zeeb 		cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
256bfcc09ddSBjoern A. Zeeb 
2579af1bba4SBjoern A. Zeeb 	if (vif->cfg.assoc)
258bfcc09ddSBjoern A. Zeeb 		memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
259bfcc09ddSBjoern A. Zeeb 	else
260bfcc09ddSBjoern A. Zeeb 		eth_broadcast_addr(cmd->range_req_bssid);
261bfcc09ddSBjoern A. Zeeb }
262bfcc09ddSBjoern A. Zeeb 
263bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_cmd_common(struct iwl_mvm *mvm,
264bfcc09ddSBjoern A. Zeeb 				   struct ieee80211_vif *vif,
2657db7bfe1SBjoern A. Zeeb #if defined(__linux__)
266bfcc09ddSBjoern A. Zeeb 				   struct iwl_tof_range_req_cmd_v9 *cmd,
2677db7bfe1SBjoern A. Zeeb #elif defined(__FreeBSD__)
2687db7bfe1SBjoern A. Zeeb 				   struct iwl_tof_range_req_cmd_v9 *cmd,	/* XXX-BZ Probably better solved by a common struct in fw for top parts of the struct. */
2697db7bfe1SBjoern A. Zeeb #endif
270bfcc09ddSBjoern A. Zeeb 				   struct cfg80211_pmsr_request *req)
271bfcc09ddSBjoern A. Zeeb {
272bfcc09ddSBjoern A. Zeeb 	int i;
273bfcc09ddSBjoern A. Zeeb 
274bfcc09ddSBjoern A. Zeeb 	cmd->initiator_flags =
275bfcc09ddSBjoern A. Zeeb 		cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM |
276bfcc09ddSBjoern A. Zeeb 			    IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT);
277bfcc09ddSBjoern A. Zeeb 	cmd->request_id = req->cookie;
278bfcc09ddSBjoern A. Zeeb 	cmd->num_of_ap = req->n_peers;
279bfcc09ddSBjoern A. Zeeb 
280bfcc09ddSBjoern A. Zeeb 	/*
281bfcc09ddSBjoern A. Zeeb 	 * Use a large value for "no timeout". Don't use the maximum value
282bfcc09ddSBjoern A. Zeeb 	 * because of fw limitations.
283bfcc09ddSBjoern A. Zeeb 	 */
284bfcc09ddSBjoern A. Zeeb 	if (req->timeout)
285bfcc09ddSBjoern A. Zeeb 		cmd->req_timeout_ms = cpu_to_le32(req->timeout);
286bfcc09ddSBjoern A. Zeeb 	else
287bfcc09ddSBjoern A. Zeeb 		cmd->req_timeout_ms = cpu_to_le32(0xfffff);
288bfcc09ddSBjoern A. Zeeb 
289bfcc09ddSBjoern A. Zeeb 	memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN);
290bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < ETH_ALEN; i++)
291bfcc09ddSBjoern A. Zeeb 		cmd->macaddr_mask[i] = ~req->mac_addr_mask[i];
292bfcc09ddSBjoern A. Zeeb 
2939af1bba4SBjoern A. Zeeb 	if (vif->cfg.assoc) {
294bfcc09ddSBjoern A. Zeeb 		memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN);
295bfcc09ddSBjoern A. Zeeb 
296bfcc09ddSBjoern A. Zeeb 		/* AP's TSF is only relevant if associated */
297bfcc09ddSBjoern A. Zeeb 		for (i = 0; i < req->n_peers; i++) {
298bfcc09ddSBjoern A. Zeeb 			if (req->peers[i].report_ap_tsf) {
299bfcc09ddSBjoern A. Zeeb 				struct iwl_mvm_vif *mvmvif =
300bfcc09ddSBjoern A. Zeeb 					iwl_mvm_vif_from_mac80211(vif);
301bfcc09ddSBjoern A. Zeeb 
302bfcc09ddSBjoern A. Zeeb 				cmd->tsf_mac_id = cpu_to_le32(mvmvif->id);
303bfcc09ddSBjoern A. Zeeb 				return;
304bfcc09ddSBjoern A. Zeeb 			}
305bfcc09ddSBjoern A. Zeeb 		}
306bfcc09ddSBjoern A. Zeeb 	} else {
307bfcc09ddSBjoern A. Zeeb 		eth_broadcast_addr(cmd->range_req_bssid);
308bfcc09ddSBjoern A. Zeeb 	}
309bfcc09ddSBjoern A. Zeeb 
310bfcc09ddSBjoern A. Zeeb 	/* Don't report AP's TSF */
311bfcc09ddSBjoern A. Zeeb 	cmd->tsf_mac_id = cpu_to_le32(0xff);
312bfcc09ddSBjoern A. Zeeb }
313bfcc09ddSBjoern A. Zeeb 
314bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_cmd_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
315bfcc09ddSBjoern A. Zeeb 			       struct iwl_tof_range_req_cmd_v8 *cmd,
316bfcc09ddSBjoern A. Zeeb 			       struct cfg80211_pmsr_request *req)
317bfcc09ddSBjoern A. Zeeb {
318bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)cmd, req);
319bfcc09ddSBjoern A. Zeeb }
320bfcc09ddSBjoern A. Zeeb 
321bfcc09ddSBjoern A. Zeeb static int
322bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_target_chandef_v1(struct iwl_mvm *mvm,
323bfcc09ddSBjoern A. Zeeb 			      struct cfg80211_pmsr_request_peer *peer,
324bfcc09ddSBjoern A. Zeeb 			      u8 *channel, u8 *bandwidth,
325bfcc09ddSBjoern A. Zeeb 			      u8 *ctrl_ch_position)
326bfcc09ddSBjoern A. Zeeb {
327bfcc09ddSBjoern A. Zeeb 	u32 freq = peer->chandef.chan->center_freq;
328bfcc09ddSBjoern A. Zeeb 
329bfcc09ddSBjoern A. Zeeb 	*channel = ieee80211_frequency_to_channel(freq);
330bfcc09ddSBjoern A. Zeeb 
331bfcc09ddSBjoern A. Zeeb 	switch (peer->chandef.width) {
332bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20_NOHT:
333bfcc09ddSBjoern A. Zeeb 		*bandwidth = IWL_TOF_BW_20_LEGACY;
334bfcc09ddSBjoern A. Zeeb 		break;
335bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20:
336bfcc09ddSBjoern A. Zeeb 		*bandwidth = IWL_TOF_BW_20_HT;
337bfcc09ddSBjoern A. Zeeb 		break;
338bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_40:
339bfcc09ddSBjoern A. Zeeb 		*bandwidth = IWL_TOF_BW_40;
340bfcc09ddSBjoern A. Zeeb 		break;
341bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80:
342bfcc09ddSBjoern A. Zeeb 		*bandwidth = IWL_TOF_BW_80;
343bfcc09ddSBjoern A. Zeeb 		break;
344bfcc09ddSBjoern A. Zeeb 	default:
345bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",
346bfcc09ddSBjoern A. Zeeb 			peer->chandef.width);
347bfcc09ddSBjoern A. Zeeb 		return -EINVAL;
348bfcc09ddSBjoern A. Zeeb 	}
349bfcc09ddSBjoern A. Zeeb 
350bfcc09ddSBjoern A. Zeeb 	*ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
351bfcc09ddSBjoern A. Zeeb 		iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;
352bfcc09ddSBjoern A. Zeeb 
353bfcc09ddSBjoern A. Zeeb 	return 0;
354bfcc09ddSBjoern A. Zeeb }
355bfcc09ddSBjoern A. Zeeb 
356bfcc09ddSBjoern A. Zeeb static int
357bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_target_chandef_v2(struct iwl_mvm *mvm,
358bfcc09ddSBjoern A. Zeeb 			      struct cfg80211_pmsr_request_peer *peer,
359bfcc09ddSBjoern A. Zeeb 			      u8 *channel, u8 *format_bw,
360bfcc09ddSBjoern A. Zeeb 			      u8 *ctrl_ch_position)
361bfcc09ddSBjoern A. Zeeb {
362bfcc09ddSBjoern A. Zeeb 	u32 freq = peer->chandef.chan->center_freq;
363bfcc09ddSBjoern A. Zeeb 	u8 cmd_ver;
364bfcc09ddSBjoern A. Zeeb 
365bfcc09ddSBjoern A. Zeeb 	*channel = ieee80211_frequency_to_channel(freq);
366bfcc09ddSBjoern A. Zeeb 
367bfcc09ddSBjoern A. Zeeb 	switch (peer->chandef.width) {
368bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20_NOHT:
369bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
370bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
371bfcc09ddSBjoern A. Zeeb 		break;
372bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20:
373bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
374bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
375bfcc09ddSBjoern A. Zeeb 		break;
376bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_40:
377bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
378bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
379bfcc09ddSBjoern A. Zeeb 		break;
380bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80:
381bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
382bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
383bfcc09ddSBjoern A. Zeeb 		break;
384bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_160:
385d9836fb4SBjoern A. Zeeb 		cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
386d9836fb4SBjoern A. Zeeb 						WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
387bfcc09ddSBjoern A. Zeeb 						IWL_FW_CMD_VER_UNKNOWN);
388bfcc09ddSBjoern A. Zeeb 
389bfcc09ddSBjoern A. Zeeb 		if (cmd_ver >= 13) {
390bfcc09ddSBjoern A. Zeeb 			*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
391bfcc09ddSBjoern A. Zeeb 			*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
392bfcc09ddSBjoern A. Zeeb 			break;
393bfcc09ddSBjoern A. Zeeb 		}
394bfcc09ddSBjoern A. Zeeb 		fallthrough;
395bfcc09ddSBjoern A. Zeeb 	default:
396bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Unsupported BW in FTM request (%d)\n",
397bfcc09ddSBjoern A. Zeeb 			peer->chandef.width);
398bfcc09ddSBjoern A. Zeeb 		return -EINVAL;
399bfcc09ddSBjoern A. Zeeb 	}
400bfcc09ddSBjoern A. Zeeb 
401bfcc09ddSBjoern A. Zeeb 	/* non EDCA based measurement must use HE preamble */
402bfcc09ddSBjoern A. Zeeb 	if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
403bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_FRAME_FORMAT_HE;
404bfcc09ddSBjoern A. Zeeb 
405bfcc09ddSBjoern A. Zeeb 	*ctrl_ch_position = (peer->chandef.width > NL80211_CHAN_WIDTH_20) ?
406bfcc09ddSBjoern A. Zeeb 		iwl_mvm_get_ctrl_pos(&peer->chandef) : 0;
407bfcc09ddSBjoern A. Zeeb 
408bfcc09ddSBjoern A. Zeeb 	return 0;
409bfcc09ddSBjoern A. Zeeb }
410bfcc09ddSBjoern A. Zeeb 
411bfcc09ddSBjoern A. Zeeb static int
412bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v2(struct iwl_mvm *mvm,
413bfcc09ddSBjoern A. Zeeb 			  struct cfg80211_pmsr_request_peer *peer,
414bfcc09ddSBjoern A. Zeeb 			  struct iwl_tof_range_req_ap_entry_v2 *target)
415bfcc09ddSBjoern A. Zeeb {
416bfcc09ddSBjoern A. Zeeb 	int ret;
417bfcc09ddSBjoern A. Zeeb 
418bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,
419bfcc09ddSBjoern A. Zeeb 					    &target->bandwidth,
420bfcc09ddSBjoern A. Zeeb 					    &target->ctrl_ch_position);
421bfcc09ddSBjoern A. Zeeb 	if (ret)
422bfcc09ddSBjoern A. Zeeb 		return ret;
423bfcc09ddSBjoern A. Zeeb 
424bfcc09ddSBjoern A. Zeeb 	memcpy(target->bssid, peer->addr, ETH_ALEN);
425bfcc09ddSBjoern A. Zeeb 	target->burst_period =
426bfcc09ddSBjoern A. Zeeb 		cpu_to_le16(peer->ftm.burst_period);
427bfcc09ddSBjoern A. Zeeb 	target->samples_per_burst = peer->ftm.ftms_per_burst;
428bfcc09ddSBjoern A. Zeeb 	target->num_of_bursts = peer->ftm.num_bursts_exp;
429bfcc09ddSBjoern A. Zeeb 	target->measure_type = 0; /* regular two-sided FTM */
430bfcc09ddSBjoern A. Zeeb 	target->retries_per_sample = peer->ftm.ftmr_retries;
431bfcc09ddSBjoern A. Zeeb 	target->asap_mode = peer->ftm.asap;
432bfcc09ddSBjoern A. Zeeb 	target->enable_dyn_ack = IWL_MVM_FTM_INITIATOR_DYNACK;
433bfcc09ddSBjoern A. Zeeb 
434bfcc09ddSBjoern A. Zeeb 	if (peer->ftm.request_lci)
435bfcc09ddSBjoern A. Zeeb 		target->location_req |= IWL_TOF_LOC_LCI;
436bfcc09ddSBjoern A. Zeeb 	if (peer->ftm.request_civicloc)
437bfcc09ddSBjoern A. Zeeb 		target->location_req |= IWL_TOF_LOC_CIVIC;
438bfcc09ddSBjoern A. Zeeb 
439bfcc09ddSBjoern A. Zeeb 	target->algo_type = IWL_MVM_FTM_INITIATOR_ALGO;
440bfcc09ddSBjoern A. Zeeb 
441bfcc09ddSBjoern A. Zeeb 	return 0;
442bfcc09ddSBjoern A. Zeeb }
443bfcc09ddSBjoern A. Zeeb 
444*a4128aadSBjoern A. Zeeb #define FTM_SET_FLAG(flag)	(*flags |= \
445bfcc09ddSBjoern A. Zeeb 				 cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag))
446bfcc09ddSBjoern A. Zeeb 
447bfcc09ddSBjoern A. Zeeb static void
448*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_set_target_flags(struct iwl_mvm *mvm,
449*a4128aadSBjoern A. Zeeb 			     struct cfg80211_pmsr_request_peer *peer,
450*a4128aadSBjoern A. Zeeb 			     __le32 *flags)
451*a4128aadSBjoern A. Zeeb {
452*a4128aadSBjoern A. Zeeb 	*flags = cpu_to_le32(0);
453*a4128aadSBjoern A. Zeeb 
454*a4128aadSBjoern A. Zeeb 	if (peer->ftm.asap)
455*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(ASAP);
456*a4128aadSBjoern A. Zeeb 
457*a4128aadSBjoern A. Zeeb 	if (peer->ftm.request_lci)
458*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(LCI_REQUEST);
459*a4128aadSBjoern A. Zeeb 
460*a4128aadSBjoern A. Zeeb 	if (peer->ftm.request_civicloc)
461*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(CIVIC_REQUEST);
462*a4128aadSBjoern A. Zeeb 
463*a4128aadSBjoern A. Zeeb 	if (IWL_MVM_FTM_INITIATOR_DYNACK)
464*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(DYN_ACK);
465*a4128aadSBjoern A. Zeeb 
466*a4128aadSBjoern A. Zeeb 	if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG)
467*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(ALGO_LR);
468*a4128aadSBjoern A. Zeeb 	else if (IWL_MVM_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT)
469*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(ALGO_FFT);
470*a4128aadSBjoern A. Zeeb 
471*a4128aadSBjoern A. Zeeb 	if (peer->ftm.trigger_based)
472*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(TB);
473*a4128aadSBjoern A. Zeeb 	else if (peer->ftm.non_trigger_based)
474*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(NON_TB);
475*a4128aadSBjoern A. Zeeb 
476*a4128aadSBjoern A. Zeeb 	if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) &&
477*a4128aadSBjoern A. Zeeb 	    peer->ftm.lmr_feedback)
478*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(LMR_FEEDBACK);
479*a4128aadSBjoern A. Zeeb }
480*a4128aadSBjoern A. Zeeb 
481*a4128aadSBjoern A. Zeeb static void
482bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_common(struct iwl_mvm *mvm,
483bfcc09ddSBjoern A. Zeeb 			      struct cfg80211_pmsr_request_peer *peer,
484bfcc09ddSBjoern A. Zeeb 			      struct iwl_tof_range_req_ap_entry_v6 *target)
485bfcc09ddSBjoern A. Zeeb {
486bfcc09ddSBjoern A. Zeeb 	memcpy(target->bssid, peer->addr, ETH_ALEN);
487bfcc09ddSBjoern A. Zeeb 	target->burst_period =
488bfcc09ddSBjoern A. Zeeb 		cpu_to_le16(peer->ftm.burst_period);
489bfcc09ddSBjoern A. Zeeb 	target->samples_per_burst = peer->ftm.ftms_per_burst;
490bfcc09ddSBjoern A. Zeeb 	target->num_of_bursts = peer->ftm.num_bursts_exp;
491bfcc09ddSBjoern A. Zeeb 	target->ftmr_max_retries = peer->ftm.ftmr_retries;
492*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags);
493bfcc09ddSBjoern A. Zeeb }
494bfcc09ddSBjoern A. Zeeb 
495bfcc09ddSBjoern A. Zeeb static int
496bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v3(struct iwl_mvm *mvm,
497bfcc09ddSBjoern A. Zeeb 			  struct cfg80211_pmsr_request_peer *peer,
498bfcc09ddSBjoern A. Zeeb 			  struct iwl_tof_range_req_ap_entry_v3 *target)
499bfcc09ddSBjoern A. Zeeb {
500bfcc09ddSBjoern A. Zeeb 	int ret;
501bfcc09ddSBjoern A. Zeeb 
502bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_ftm_target_chandef_v1(mvm, peer, &target->channel_num,
503bfcc09ddSBjoern A. Zeeb 					    &target->bandwidth,
504bfcc09ddSBjoern A. Zeeb 					    &target->ctrl_ch_position);
505bfcc09ddSBjoern A. Zeeb 	if (ret)
506bfcc09ddSBjoern A. Zeeb 		return ret;
507bfcc09ddSBjoern A. Zeeb 
508bfcc09ddSBjoern A. Zeeb 	/*
509bfcc09ddSBjoern A. Zeeb 	 * Versions 3 and 4 has some common fields, so
510bfcc09ddSBjoern A. Zeeb 	 * iwl_mvm_ftm_put_target_common() can be used for version 7 too.
511bfcc09ddSBjoern A. Zeeb 	 */
512bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);
513bfcc09ddSBjoern A. Zeeb 
514bfcc09ddSBjoern A. Zeeb 	return 0;
515bfcc09ddSBjoern A. Zeeb }
516bfcc09ddSBjoern A. Zeeb 
517bfcc09ddSBjoern A. Zeeb static int
518bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v4(struct iwl_mvm *mvm,
519bfcc09ddSBjoern A. Zeeb 			  struct cfg80211_pmsr_request_peer *peer,
520bfcc09ddSBjoern A. Zeeb 			  struct iwl_tof_range_req_ap_entry_v4 *target)
521bfcc09ddSBjoern A. Zeeb {
522bfcc09ddSBjoern A. Zeeb 	int ret;
523bfcc09ddSBjoern A. Zeeb 
524bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,
525bfcc09ddSBjoern A. Zeeb 					    &target->format_bw,
526bfcc09ddSBjoern A. Zeeb 					    &target->ctrl_ch_position);
527bfcc09ddSBjoern A. Zeeb 	if (ret)
528bfcc09ddSBjoern A. Zeeb 		return ret;
529bfcc09ddSBjoern A. Zeeb 
530bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_put_target_common(mvm, peer, (void *)target);
531bfcc09ddSBjoern A. Zeeb 
532bfcc09ddSBjoern A. Zeeb 	return 0;
533bfcc09ddSBjoern A. Zeeb }
534bfcc09ddSBjoern A. Zeeb 
535*a4128aadSBjoern A. Zeeb static int iwl_mvm_ftm_set_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
536*a4128aadSBjoern A. Zeeb 			       struct cfg80211_pmsr_request_peer *peer,
537*a4128aadSBjoern A. Zeeb 			       u8 *sta_id, __le32 *flags)
538*a4128aadSBjoern A. Zeeb {
539*a4128aadSBjoern A. Zeeb 	if (vif->cfg.assoc) {
540*a4128aadSBjoern A. Zeeb 		struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
541*a4128aadSBjoern A. Zeeb 		struct ieee80211_sta *sta;
542*a4128aadSBjoern A. Zeeb 		struct ieee80211_bss_conf *link_conf;
543*a4128aadSBjoern A. Zeeb 		unsigned int link_id;
544*a4128aadSBjoern A. Zeeb 
545*a4128aadSBjoern A. Zeeb 		rcu_read_lock();
546*a4128aadSBjoern A. Zeeb 		for_each_vif_active_link(vif, link_conf, link_id) {
547*a4128aadSBjoern A. Zeeb 			if (memcmp(peer->addr, link_conf->bssid, ETH_ALEN))
548*a4128aadSBjoern A. Zeeb 				continue;
549*a4128aadSBjoern A. Zeeb 
550*a4128aadSBjoern A. Zeeb 			*sta_id = mvmvif->link[link_id]->ap_sta_id;
551*a4128aadSBjoern A. Zeeb 			sta = rcu_dereference(mvm->fw_id_to_mac_id[*sta_id]);
552*a4128aadSBjoern A. Zeeb 			if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
553*a4128aadSBjoern A. Zeeb 				rcu_read_unlock();
554*a4128aadSBjoern A. Zeeb 				return PTR_ERR_OR_ZERO(sta);
555*a4128aadSBjoern A. Zeeb 			}
556*a4128aadSBjoern A. Zeeb 
557*a4128aadSBjoern A. Zeeb 			if (sta->mfp && (peer->ftm.trigger_based ||
558*a4128aadSBjoern A. Zeeb 					 peer->ftm.non_trigger_based))
559*a4128aadSBjoern A. Zeeb 				FTM_SET_FLAG(PMF);
560*a4128aadSBjoern A. Zeeb 			break;
561*a4128aadSBjoern A. Zeeb 		}
562*a4128aadSBjoern A. Zeeb 		rcu_read_unlock();
563*a4128aadSBjoern A. Zeeb 
564*a4128aadSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS
565*a4128aadSBjoern A. Zeeb 		if (mvmvif->ftm_unprotected) {
566*a4128aadSBjoern A. Zeeb 			*sta_id = IWL_MVM_INVALID_STA;
567*a4128aadSBjoern A. Zeeb 			*flags &= ~cpu_to_le32(IWL_INITIATOR_AP_FLAGS_PMF);
568*a4128aadSBjoern A. Zeeb 		}
569*a4128aadSBjoern A. Zeeb #endif
570*a4128aadSBjoern A. Zeeb 	} else {
571*a4128aadSBjoern A. Zeeb 		*sta_id = IWL_MVM_INVALID_STA;
572*a4128aadSBjoern A. Zeeb 	}
573*a4128aadSBjoern A. Zeeb 
574*a4128aadSBjoern A. Zeeb 	return 0;
575*a4128aadSBjoern A. Zeeb }
576*a4128aadSBjoern A. Zeeb 
577bfcc09ddSBjoern A. Zeeb static int
578bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
579bfcc09ddSBjoern A. Zeeb 		       struct cfg80211_pmsr_request_peer *peer,
580bfcc09ddSBjoern A. Zeeb 		       struct iwl_tof_range_req_ap_entry_v6 *target)
581bfcc09ddSBjoern A. Zeeb {
582bfcc09ddSBjoern A. Zeeb 	int ret;
583bfcc09ddSBjoern A. Zeeb 
584bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,
585bfcc09ddSBjoern A. Zeeb 					    &target->format_bw,
586bfcc09ddSBjoern A. Zeeb 					    &target->ctrl_ch_position);
587bfcc09ddSBjoern A. Zeeb 	if (ret)
588bfcc09ddSBjoern A. Zeeb 		return ret;
589bfcc09ddSBjoern A. Zeeb 
590bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_put_target_common(mvm, peer, target);
591bfcc09ddSBjoern A. Zeeb 
592*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id,
593*a4128aadSBjoern A. Zeeb 			    &target->initiator_ap_flags);
594bfcc09ddSBjoern A. Zeeb 
595bfcc09ddSBjoern A. Zeeb 	/*
596bfcc09ddSBjoern A. Zeeb 	 * TODO: Beacon interval is currently unknown, so use the common value
597bfcc09ddSBjoern A. Zeeb 	 * of 100 TUs.
598bfcc09ddSBjoern A. Zeeb 	 */
599bfcc09ddSBjoern A. Zeeb 	target->beacon_interval = cpu_to_le16(100);
600bfcc09ddSBjoern A. Zeeb 	return 0;
601bfcc09ddSBjoern A. Zeeb }
602bfcc09ddSBjoern A. Zeeb 
603bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *hcmd)
604bfcc09ddSBjoern A. Zeeb {
605bfcc09ddSBjoern A. Zeeb 	u32 status;
606bfcc09ddSBjoern A. Zeeb 	int err = iwl_mvm_send_cmd_status(mvm, hcmd, &status);
607bfcc09ddSBjoern A. Zeeb 
608bfcc09ddSBjoern A. Zeeb 	if (!err && status) {
609bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "FTM range request command failure, status: %u\n",
610bfcc09ddSBjoern A. Zeeb 			status);
611bfcc09ddSBjoern A. Zeeb 		err = iwl_ftm_range_request_status_to_err(status);
612bfcc09ddSBjoern A. Zeeb 	}
613bfcc09ddSBjoern A. Zeeb 
614bfcc09ddSBjoern A. Zeeb 	return err;
615bfcc09ddSBjoern A. Zeeb }
616bfcc09ddSBjoern A. Zeeb 
617bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v5(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
618bfcc09ddSBjoern A. Zeeb 				struct cfg80211_pmsr_request *req)
619bfcc09ddSBjoern A. Zeeb {
620bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v5 cmd_v5;
621bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
622d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
623bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
624bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd_v5,
625bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd_v5),
626bfcc09ddSBjoern A. Zeeb 	};
627bfcc09ddSBjoern A. Zeeb 	u8 i;
628bfcc09ddSBjoern A. Zeeb 	int err;
629bfcc09ddSBjoern A. Zeeb 
630bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_v5(mvm, vif, &cmd_v5, req);
631bfcc09ddSBjoern A. Zeeb 
632bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd_v5.num_of_ap; i++) {
633bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
634bfcc09ddSBjoern A. Zeeb 
635bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v2(mvm, peer, &cmd_v5.ap[i]);
636bfcc09ddSBjoern A. Zeeb 		if (err)
637bfcc09ddSBjoern A. Zeeb 			return err;
638bfcc09ddSBjoern A. Zeeb 	}
639bfcc09ddSBjoern A. Zeeb 
640bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
641bfcc09ddSBjoern A. Zeeb }
642bfcc09ddSBjoern A. Zeeb 
643bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
644bfcc09ddSBjoern A. Zeeb 				struct cfg80211_pmsr_request *req)
645bfcc09ddSBjoern A. Zeeb {
646bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v7 cmd_v7;
647bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
648d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
649bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
650bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd_v7,
651bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd_v7),
652bfcc09ddSBjoern A. Zeeb 	};
653bfcc09ddSBjoern A. Zeeb 	u8 i;
654bfcc09ddSBjoern A. Zeeb 	int err;
655bfcc09ddSBjoern A. Zeeb 
656bfcc09ddSBjoern A. Zeeb 	/*
657bfcc09ddSBjoern A. Zeeb 	 * Versions 7 and 8 has the same structure except from the responders
658bfcc09ddSBjoern A. Zeeb 	 * list, so iwl_mvm_ftm_cmd() can be used for version 7 too.
659bfcc09ddSBjoern A. Zeeb 	 */
660bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd_v7, req);
661bfcc09ddSBjoern A. Zeeb 
662bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd_v7.num_of_ap; i++) {
663bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
664bfcc09ddSBjoern A. Zeeb 
665bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v3(mvm, peer, &cmd_v7.ap[i]);
666bfcc09ddSBjoern A. Zeeb 		if (err)
667bfcc09ddSBjoern A. Zeeb 			return err;
668bfcc09ddSBjoern A. Zeeb 	}
669bfcc09ddSBjoern A. Zeeb 
670bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
671bfcc09ddSBjoern A. Zeeb }
672bfcc09ddSBjoern A. Zeeb 
673bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
674bfcc09ddSBjoern A. Zeeb 				struct cfg80211_pmsr_request *req)
675bfcc09ddSBjoern A. Zeeb {
676bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v8 cmd;
677bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
678d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
679bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
680bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
681bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
682bfcc09ddSBjoern A. Zeeb 	};
683bfcc09ddSBjoern A. Zeeb 	u8 i;
684bfcc09ddSBjoern A. Zeeb 	int err;
685bfcc09ddSBjoern A. Zeeb 
686bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_v8(mvm, vif, (void *)&cmd, req);
687bfcc09ddSBjoern A. Zeeb 
688bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd.num_of_ap; i++) {
689bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
690bfcc09ddSBjoern A. Zeeb 
691bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v4(mvm, peer, &cmd.ap[i]);
692bfcc09ddSBjoern A. Zeeb 		if (err)
693bfcc09ddSBjoern A. Zeeb 			return err;
694bfcc09ddSBjoern A. Zeeb 	}
695bfcc09ddSBjoern A. Zeeb 
696bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
697bfcc09ddSBjoern A. Zeeb }
698bfcc09ddSBjoern A. Zeeb 
699bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v9(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
700bfcc09ddSBjoern A. Zeeb 				struct cfg80211_pmsr_request *req)
701bfcc09ddSBjoern A. Zeeb {
702bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v9 cmd;
703bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
704d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
705bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
706bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
707bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
708bfcc09ddSBjoern A. Zeeb 	};
709bfcc09ddSBjoern A. Zeeb 	u8 i;
710bfcc09ddSBjoern A. Zeeb 	int err;
711bfcc09ddSBjoern A. Zeeb 
712bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_common(mvm, vif, &cmd, req);
713bfcc09ddSBjoern A. Zeeb 
714bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd.num_of_ap; i++) {
715bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
716bfcc09ddSBjoern A. Zeeb 		struct iwl_tof_range_req_ap_entry_v6 *target = &cmd.ap[i];
717bfcc09ddSBjoern A. Zeeb 
718bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target(mvm, vif, peer, target);
719bfcc09ddSBjoern A. Zeeb 		if (err)
720bfcc09ddSBjoern A. Zeeb 			return err;
721bfcc09ddSBjoern A. Zeeb 	}
722bfcc09ddSBjoern A. Zeeb 
723bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
724bfcc09ddSBjoern A. Zeeb }
725bfcc09ddSBjoern A. Zeeb 
726bfcc09ddSBjoern A. Zeeb static void iter(struct ieee80211_hw *hw,
727bfcc09ddSBjoern A. Zeeb 		 struct ieee80211_vif *vif,
728bfcc09ddSBjoern A. Zeeb 		 struct ieee80211_sta *sta,
729bfcc09ddSBjoern A. Zeeb 		 struct ieee80211_key_conf *key,
730bfcc09ddSBjoern A. Zeeb 		 void *data)
731bfcc09ddSBjoern A. Zeeb {
732*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_ftm_iter_data *target = data;
733bfcc09ddSBjoern A. Zeeb 
734bfcc09ddSBjoern A. Zeeb 	if (!sta || memcmp(sta->addr, target->bssid, ETH_ALEN))
735bfcc09ddSBjoern A. Zeeb 		return;
736bfcc09ddSBjoern A. Zeeb 
737bfcc09ddSBjoern A. Zeeb 	WARN_ON(!sta->mfp);
738bfcc09ddSBjoern A. Zeeb 
739*a4128aadSBjoern A. Zeeb 	target->tk = key->key;
740*a4128aadSBjoern A. Zeeb 	*target->cipher = iwl_mvm_cipher_to_location_cipher(key->cipher);
741*a4128aadSBjoern A. Zeeb 	WARN_ON(*target->cipher == IWL_LOCATION_CIPHER_INVALID);
742bfcc09ddSBjoern A. Zeeb }
743bfcc09ddSBjoern A. Zeeb 
744bfcc09ddSBjoern A. Zeeb static void
745bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
746*a4128aadSBjoern A. Zeeb 				u8 *bssid, u8 *cipher, u8 *hltk, u8 *tk,
747*a4128aadSBjoern A. Zeeb 				u8 *rx_pn, u8 *tx_pn, __le32 *flags)
748bfcc09ddSBjoern A. Zeeb {
749bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_ftm_pasn_entry *entry;
750*a4128aadSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS
751*a4128aadSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
752bfcc09ddSBjoern A. Zeeb 
753*a4128aadSBjoern A. Zeeb 	if (mvmvif->ftm_unprotected)
754*a4128aadSBjoern A. Zeeb 		return;
755*a4128aadSBjoern A. Zeeb #endif
756*a4128aadSBjoern A. Zeeb 
757*a4128aadSBjoern A. Zeeb 	if (!(le32_to_cpu(*flags) & (IWL_INITIATOR_AP_FLAGS_NON_TB |
758bfcc09ddSBjoern A. Zeeb 		       IWL_INITIATOR_AP_FLAGS_TB)))
759bfcc09ddSBjoern A. Zeeb 		return;
760bfcc09ddSBjoern A. Zeeb 
761bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
762bfcc09ddSBjoern A. Zeeb 
763bfcc09ddSBjoern A. Zeeb 	list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
764*a4128aadSBjoern A. Zeeb 		if (memcmp(entry->addr, bssid, sizeof(entry->addr)))
765bfcc09ddSBjoern A. Zeeb 			continue;
766bfcc09ddSBjoern A. Zeeb 
767*a4128aadSBjoern A. Zeeb 		*cipher = entry->cipher;
768bfcc09ddSBjoern A. Zeeb 
7699af1bba4SBjoern A. Zeeb 		if (entry->flags & IWL_MVM_PASN_FLAG_HAS_HLTK)
770*a4128aadSBjoern A. Zeeb 			memcpy(hltk, entry->hltk, sizeof(entry->hltk));
7719af1bba4SBjoern A. Zeeb 		else
772*a4128aadSBjoern A. Zeeb 			memset(hltk, 0, sizeof(entry->hltk));
7739af1bba4SBjoern A. Zeeb 
7749af1bba4SBjoern A. Zeeb 		if (vif->cfg.assoc &&
775*a4128aadSBjoern A. Zeeb 		    !memcmp(vif->bss_conf.bssid, bssid, ETH_ALEN)) {
776*a4128aadSBjoern A. Zeeb 			struct iwl_mvm_ftm_iter_data target;
777bfcc09ddSBjoern A. Zeeb 
778*a4128aadSBjoern A. Zeeb 			target.bssid = bssid;
779*a4128aadSBjoern A. Zeeb 			ieee80211_iter_keys(mvm->hw, vif, iter, &target);
780*a4128aadSBjoern A. Zeeb 		} else {
781*a4128aadSBjoern A. Zeeb 			memcpy(tk, entry->tk, sizeof(entry->tk));
782*a4128aadSBjoern A. Zeeb 		}
783bfcc09ddSBjoern A. Zeeb 
784*a4128aadSBjoern A. Zeeb 		memcpy(rx_pn, entry->rx_pn, sizeof(entry->rx_pn));
785*a4128aadSBjoern A. Zeeb 		memcpy(tx_pn, entry->tx_pn, sizeof(entry->tx_pn));
786*a4128aadSBjoern A. Zeeb 
787*a4128aadSBjoern A. Zeeb 		FTM_SET_FLAG(SECURED);
788bfcc09ddSBjoern A. Zeeb 		return;
789bfcc09ddSBjoern A. Zeeb 	}
790bfcc09ddSBjoern A. Zeeb }
791bfcc09ddSBjoern A. Zeeb 
792bfcc09ddSBjoern A. Zeeb static int
793bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v7(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
794bfcc09ddSBjoern A. Zeeb 			  struct cfg80211_pmsr_request_peer *peer,
795bfcc09ddSBjoern A. Zeeb 			  struct iwl_tof_range_req_ap_entry_v7 *target)
796bfcc09ddSBjoern A. Zeeb {
797bfcc09ddSBjoern A. Zeeb 	int err = iwl_mvm_ftm_put_target(mvm, vif, peer, (void *)target);
798bfcc09ddSBjoern A. Zeeb 	if (err)
799bfcc09ddSBjoern A. Zeeb 		return err;
800bfcc09ddSBjoern A. Zeeb 
801*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,
802*a4128aadSBjoern A. Zeeb 					&target->cipher, target->hltk,
803*a4128aadSBjoern A. Zeeb 					target->tk, target->rx_pn,
804*a4128aadSBjoern A. Zeeb 					target->tx_pn,
805*a4128aadSBjoern A. Zeeb 					&target->initiator_ap_flags);
806bfcc09ddSBjoern A. Zeeb 	return err;
807bfcc09ddSBjoern A. Zeeb }
808bfcc09ddSBjoern A. Zeeb 
809bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v11(struct iwl_mvm *mvm,
810bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
811bfcc09ddSBjoern A. Zeeb 				 struct cfg80211_pmsr_request *req)
812bfcc09ddSBjoern A. Zeeb {
813bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v11 cmd;
814bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
815d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
816bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
817bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
818bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
819bfcc09ddSBjoern A. Zeeb 	};
820bfcc09ddSBjoern A. Zeeb 	u8 i;
821bfcc09ddSBjoern A. Zeeb 	int err;
822bfcc09ddSBjoern A. Zeeb 
823bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
824bfcc09ddSBjoern A. Zeeb 
825bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd.num_of_ap; i++) {
826bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
827bfcc09ddSBjoern A. Zeeb 		struct iwl_tof_range_req_ap_entry_v7 *target = &cmd.ap[i];
828bfcc09ddSBjoern A. Zeeb 
829bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, target);
830bfcc09ddSBjoern A. Zeeb 		if (err)
831bfcc09ddSBjoern A. Zeeb 			return err;
832bfcc09ddSBjoern A. Zeeb 	}
833bfcc09ddSBjoern A. Zeeb 
834bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
835bfcc09ddSBjoern A. Zeeb }
836bfcc09ddSBjoern A. Zeeb 
837bfcc09ddSBjoern A. Zeeb static void
838bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_set_ndp_params(struct iwl_mvm *mvm,
839bfcc09ddSBjoern A. Zeeb 			   struct iwl_tof_range_req_ap_entry_v8 *target)
840bfcc09ddSBjoern A. Zeeb {
841bfcc09ddSBjoern A. Zeeb 	/* Only 2 STS are supported on Tx */
842bfcc09ddSBjoern A. Zeeb 	u32 i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 :
843bfcc09ddSBjoern A. Zeeb 		IWL_MVM_FTM_I2R_MAX_STS;
844bfcc09ddSBjoern A. Zeeb 
845bfcc09ddSBjoern A. Zeeb 	target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |
846bfcc09ddSBjoern A. Zeeb 		(IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS);
847bfcc09ddSBjoern A. Zeeb 	target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |
848bfcc09ddSBjoern A. Zeeb 		(i2r_max_sts << IWL_LOCATION_MAX_STS_POS);
849bfcc09ddSBjoern A. Zeeb 	target->r2i_max_total_ltf = IWL_MVM_FTM_R2I_MAX_TOTAL_LTF;
850bfcc09ddSBjoern A. Zeeb 	target->i2r_max_total_ltf = IWL_MVM_FTM_I2R_MAX_TOTAL_LTF;
851bfcc09ddSBjoern A. Zeeb }
852bfcc09ddSBjoern A. Zeeb 
853bfcc09ddSBjoern A. Zeeb static int
854bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_put_target_v8(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
855bfcc09ddSBjoern A. Zeeb 			  struct cfg80211_pmsr_request_peer *peer,
856bfcc09ddSBjoern A. Zeeb 			  struct iwl_tof_range_req_ap_entry_v8 *target)
857bfcc09ddSBjoern A. Zeeb {
858bfcc09ddSBjoern A. Zeeb 	u32 flags;
859bfcc09ddSBjoern A. Zeeb 	int ret = iwl_mvm_ftm_put_target_v7(mvm, vif, peer, (void *)target);
860bfcc09ddSBjoern A. Zeeb 
861bfcc09ddSBjoern A. Zeeb 	if (ret)
862bfcc09ddSBjoern A. Zeeb 		return ret;
863bfcc09ddSBjoern A. Zeeb 
864bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_set_ndp_params(mvm, target);
865bfcc09ddSBjoern A. Zeeb 
866bfcc09ddSBjoern A. Zeeb 	/*
867bfcc09ddSBjoern A. Zeeb 	 * If secure LTF is turned off, replace the flag with PMF only
868bfcc09ddSBjoern A. Zeeb 	 */
869bfcc09ddSBjoern A. Zeeb 	flags = le32_to_cpu(target->initiator_ap_flags);
870*a4128aadSBjoern A. Zeeb 	if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) {
871*a4128aadSBjoern A. Zeeb 		if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF)
872bfcc09ddSBjoern A. Zeeb 			flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED;
873*a4128aadSBjoern A. Zeeb 
874bfcc09ddSBjoern A. Zeeb 		flags |= IWL_INITIATOR_AP_FLAGS_PMF;
875bfcc09ddSBjoern A. Zeeb 		target->initiator_ap_flags = cpu_to_le32(flags);
876bfcc09ddSBjoern A. Zeeb 	}
877bfcc09ddSBjoern A. Zeeb 
878bfcc09ddSBjoern A. Zeeb 	return 0;
879bfcc09ddSBjoern A. Zeeb }
880bfcc09ddSBjoern A. Zeeb 
881bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v12(struct iwl_mvm *mvm,
882bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
883bfcc09ddSBjoern A. Zeeb 				 struct cfg80211_pmsr_request *req)
884bfcc09ddSBjoern A. Zeeb {
885bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v12 cmd;
886bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
887d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
888bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
889bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
890bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
891bfcc09ddSBjoern A. Zeeb 	};
892bfcc09ddSBjoern A. Zeeb 	u8 i;
893bfcc09ddSBjoern A. Zeeb 	int err;
894bfcc09ddSBjoern A. Zeeb 
895bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
896bfcc09ddSBjoern A. Zeeb 
897bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd.num_of_ap; i++) {
898bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
899bfcc09ddSBjoern A. Zeeb 		struct iwl_tof_range_req_ap_entry_v8 *target = &cmd.ap[i];
900bfcc09ddSBjoern A. Zeeb 
901bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, target);
902bfcc09ddSBjoern A. Zeeb 		if (err)
903bfcc09ddSBjoern A. Zeeb 			return err;
904bfcc09ddSBjoern A. Zeeb 	}
905bfcc09ddSBjoern A. Zeeb 
906bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
907bfcc09ddSBjoern A. Zeeb }
908bfcc09ddSBjoern A. Zeeb 
909bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm,
910bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
911bfcc09ddSBjoern A. Zeeb 				 struct cfg80211_pmsr_request *req)
912bfcc09ddSBjoern A. Zeeb {
913bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v13 cmd;
914bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
915d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
916bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
917bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
918bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
919bfcc09ddSBjoern A. Zeeb 	};
920bfcc09ddSBjoern A. Zeeb 	u8 i;
921bfcc09ddSBjoern A. Zeeb 	int err;
922bfcc09ddSBjoern A. Zeeb 
923bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
924bfcc09ddSBjoern A. Zeeb 
925bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < cmd.num_of_ap; i++) {
926bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
927bfcc09ddSBjoern A. Zeeb 		struct iwl_tof_range_req_ap_entry_v9 *target = &cmd.ap[i];
928bfcc09ddSBjoern A. Zeeb 
929bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v8(mvm, vif, peer, (void *)target);
930bfcc09ddSBjoern A. Zeeb 		if (err)
931bfcc09ddSBjoern A. Zeeb 			return err;
932bfcc09ddSBjoern A. Zeeb 
933bfcc09ddSBjoern A. Zeeb 		if (peer->ftm.trigger_based || peer->ftm.non_trigger_based)
934bfcc09ddSBjoern A. Zeeb 			target->bss_color = peer->ftm.bss_color;
935bfcc09ddSBjoern A. Zeeb 
936bfcc09ddSBjoern A. Zeeb 		if (peer->ftm.non_trigger_based) {
937bfcc09ddSBjoern A. Zeeb 			target->min_time_between_msr =
938bfcc09ddSBjoern A. Zeeb 				cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
939bfcc09ddSBjoern A. Zeeb 			target->burst_period =
940bfcc09ddSBjoern A. Zeeb 				cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
941bfcc09ddSBjoern A. Zeeb 		} else {
942bfcc09ddSBjoern A. Zeeb 			target->min_time_between_msr = cpu_to_le16(0);
943bfcc09ddSBjoern A. Zeeb 		}
944bfcc09ddSBjoern A. Zeeb 
945bfcc09ddSBjoern A. Zeeb 		target->band =
946bfcc09ddSBjoern A. Zeeb 			iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band);
947bfcc09ddSBjoern A. Zeeb 	}
948bfcc09ddSBjoern A. Zeeb 
949bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
950bfcc09ddSBjoern A. Zeeb }
951bfcc09ddSBjoern A. Zeeb 
952*a4128aadSBjoern A. Zeeb static int
953*a4128aadSBjoern A. Zeeb iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
954*a4128aadSBjoern A. Zeeb 			   struct cfg80211_pmsr_request_peer *peer,
955*a4128aadSBjoern A. Zeeb 			   struct iwl_tof_range_req_ap_entry_v10 *target)
956*a4128aadSBjoern A. Zeeb {
957*a4128aadSBjoern A. Zeeb 	u32 i2r_max_sts, flags;
958*a4128aadSBjoern A. Zeeb 	int ret;
959*a4128aadSBjoern A. Zeeb 
960*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_ftm_target_chandef_v2(mvm, peer, &target->channel_num,
961*a4128aadSBjoern A. Zeeb 					    &target->format_bw,
962*a4128aadSBjoern A. Zeeb 					    &target->ctrl_ch_position);
963*a4128aadSBjoern A. Zeeb 	if (ret)
964*a4128aadSBjoern A. Zeeb 		return ret;
965*a4128aadSBjoern A. Zeeb 
966*a4128aadSBjoern A. Zeeb 	memcpy(target->bssid, peer->addr, ETH_ALEN);
967*a4128aadSBjoern A. Zeeb 	target->burst_period =
968*a4128aadSBjoern A. Zeeb 		cpu_to_le16(peer->ftm.burst_period);
969*a4128aadSBjoern A. Zeeb 	target->samples_per_burst = peer->ftm.ftms_per_burst;
970*a4128aadSBjoern A. Zeeb 	target->num_of_bursts = peer->ftm.num_bursts_exp;
971*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_set_target_flags(mvm, peer, &target->initiator_ap_flags);
972*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_set_sta(mvm, vif, peer, &target->sta_id,
973*a4128aadSBjoern A. Zeeb 			    &target->initiator_ap_flags);
974*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_set_secured_ranging(mvm, vif, target->bssid,
975*a4128aadSBjoern A. Zeeb 					&target->cipher, target->hltk,
976*a4128aadSBjoern A. Zeeb 					target->tk, target->rx_pn,
977*a4128aadSBjoern A. Zeeb 					target->tx_pn,
978*a4128aadSBjoern A. Zeeb 					&target->initiator_ap_flags);
979*a4128aadSBjoern A. Zeeb 
980*a4128aadSBjoern A. Zeeb 	i2r_max_sts = IWL_MVM_FTM_I2R_MAX_STS > 1 ? 1 :
981*a4128aadSBjoern A. Zeeb 		IWL_MVM_FTM_I2R_MAX_STS;
982*a4128aadSBjoern A. Zeeb 
983*a4128aadSBjoern A. Zeeb 	target->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |
984*a4128aadSBjoern A. Zeeb 		(IWL_MVM_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS) |
985*a4128aadSBjoern A. Zeeb 		(IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS);
986*a4128aadSBjoern A. Zeeb 	target->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |
987*a4128aadSBjoern A. Zeeb 		(i2r_max_sts << IWL_LOCATION_MAX_STS_POS) |
988*a4128aadSBjoern A. Zeeb 		(IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS);
989*a4128aadSBjoern A. Zeeb 
990*a4128aadSBjoern A. Zeeb 	if (peer->ftm.non_trigger_based) {
991*a4128aadSBjoern A. Zeeb 		target->min_time_between_msr =
992*a4128aadSBjoern A. Zeeb 			cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
993*a4128aadSBjoern A. Zeeb 		target->burst_period =
994*a4128aadSBjoern A. Zeeb 			cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
995*a4128aadSBjoern A. Zeeb 	} else {
996*a4128aadSBjoern A. Zeeb 		target->min_time_between_msr = cpu_to_le16(0);
997*a4128aadSBjoern A. Zeeb 	}
998*a4128aadSBjoern A. Zeeb 
999*a4128aadSBjoern A. Zeeb 	target->band =
1000*a4128aadSBjoern A. Zeeb 		iwl_mvm_phy_band_from_nl80211(peer->chandef.chan->band);
1001*a4128aadSBjoern A. Zeeb 
1002*a4128aadSBjoern A. Zeeb 	/*
1003*a4128aadSBjoern A. Zeeb 	 * TODO: Beacon interval is currently unknown, so use the common value
1004*a4128aadSBjoern A. Zeeb 	 * of 100 TUs.
1005*a4128aadSBjoern A. Zeeb 	 */
1006*a4128aadSBjoern A. Zeeb 	target->beacon_interval = cpu_to_le16(100);
1007*a4128aadSBjoern A. Zeeb 
1008*a4128aadSBjoern A. Zeeb 	/*
1009*a4128aadSBjoern A. Zeeb 	 * If secure LTF is turned off, replace the flag with PMF only
1010*a4128aadSBjoern A. Zeeb 	 */
1011*a4128aadSBjoern A. Zeeb 	flags = le32_to_cpu(target->initiator_ap_flags);
1012*a4128aadSBjoern A. Zeeb 	if (flags & IWL_INITIATOR_AP_FLAGS_SECURED) {
1013*a4128aadSBjoern A. Zeeb 		if (!IWL_MVM_FTM_INITIATOR_SECURE_LTF)
1014*a4128aadSBjoern A. Zeeb 			flags &= ~IWL_INITIATOR_AP_FLAGS_SECURED;
1015*a4128aadSBjoern A. Zeeb 
1016*a4128aadSBjoern A. Zeeb 		flags |= IWL_INITIATOR_AP_FLAGS_PMF;
1017*a4128aadSBjoern A. Zeeb 		target->initiator_ap_flags = cpu_to_le32(flags);
1018*a4128aadSBjoern A. Zeeb 	}
1019*a4128aadSBjoern A. Zeeb 
1020*a4128aadSBjoern A. Zeeb 	return 0;
1021*a4128aadSBjoern A. Zeeb }
1022*a4128aadSBjoern A. Zeeb 
1023*a4128aadSBjoern A. Zeeb static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm,
1024*a4128aadSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
1025*a4128aadSBjoern A. Zeeb 				 struct cfg80211_pmsr_request *req)
1026*a4128aadSBjoern A. Zeeb {
1027*a4128aadSBjoern A. Zeeb 	struct iwl_tof_range_req_cmd_v14 cmd;
1028*a4128aadSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
1029*a4128aadSBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
1030*a4128aadSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
1031*a4128aadSBjoern A. Zeeb 		.data[0] = &cmd,
1032*a4128aadSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
1033*a4128aadSBjoern A. Zeeb 	};
1034*a4128aadSBjoern A. Zeeb 	u8 i;
1035*a4128aadSBjoern A. Zeeb 	int err;
1036*a4128aadSBjoern A. Zeeb 
1037*a4128aadSBjoern A. Zeeb 	iwl_mvm_ftm_cmd_common(mvm, vif, (void *)&cmd, req);
1038*a4128aadSBjoern A. Zeeb 
1039*a4128aadSBjoern A. Zeeb 	for (i = 0; i < cmd.num_of_ap; i++) {
1040*a4128aadSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
1041*a4128aadSBjoern A. Zeeb 		struct iwl_tof_range_req_ap_entry_v10 *target = &cmd.ap[i];
1042*a4128aadSBjoern A. Zeeb 
1043*a4128aadSBjoern A. Zeeb 		err = iwl_mvm_ftm_put_target_v10(mvm, vif, peer, target);
1044*a4128aadSBjoern A. Zeeb 		if (err)
1045*a4128aadSBjoern A. Zeeb 			return err;
1046*a4128aadSBjoern A. Zeeb 	}
1047*a4128aadSBjoern A. Zeeb 
1048*a4128aadSBjoern A. Zeeb 	return iwl_mvm_ftm_send_cmd(mvm, &hcmd);
1049*a4128aadSBjoern A. Zeeb }
1050*a4128aadSBjoern A. Zeeb 
1051bfcc09ddSBjoern A. Zeeb int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1052bfcc09ddSBjoern A. Zeeb 		      struct cfg80211_pmsr_request *req)
1053bfcc09ddSBjoern A. Zeeb {
1054bfcc09ddSBjoern A. Zeeb 	bool new_api = fw_has_api(&mvm->fw->ucode_capa,
1055bfcc09ddSBjoern A. Zeeb 				  IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
1056bfcc09ddSBjoern A. Zeeb 	int err;
1057bfcc09ddSBjoern A. Zeeb 
1058bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1059bfcc09ddSBjoern A. Zeeb 
1060bfcc09ddSBjoern A. Zeeb 	if (mvm->ftm_initiator.req)
1061bfcc09ddSBjoern A. Zeeb 		return -EBUSY;
1062bfcc09ddSBjoern A. Zeeb 
1063bfcc09ddSBjoern A. Zeeb 	if (new_api) {
1064d9836fb4SBjoern A. Zeeb 		u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
1065d9836fb4SBjoern A. Zeeb 						   WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD),
1066bfcc09ddSBjoern A. Zeeb 						   IWL_FW_CMD_VER_UNKNOWN);
1067bfcc09ddSBjoern A. Zeeb 
1068bfcc09ddSBjoern A. Zeeb 		switch (cmd_ver) {
1069*a4128aadSBjoern A. Zeeb 		case 14:
1070*a4128aadSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v14(mvm, vif, req);
1071*a4128aadSBjoern A. Zeeb 			break;
1072bfcc09ddSBjoern A. Zeeb 		case 13:
1073bfcc09ddSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v13(mvm, vif, req);
1074bfcc09ddSBjoern A. Zeeb 			break;
1075bfcc09ddSBjoern A. Zeeb 		case 12:
1076bfcc09ddSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v12(mvm, vif, req);
1077bfcc09ddSBjoern A. Zeeb 			break;
1078bfcc09ddSBjoern A. Zeeb 		case 11:
1079bfcc09ddSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v11(mvm, vif, req);
1080bfcc09ddSBjoern A. Zeeb 			break;
1081bfcc09ddSBjoern A. Zeeb 		case 9:
1082bfcc09ddSBjoern A. Zeeb 		case 10:
1083bfcc09ddSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v9(mvm, vif, req);
1084bfcc09ddSBjoern A. Zeeb 			break;
1085bfcc09ddSBjoern A. Zeeb 		case 8:
1086bfcc09ddSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v8(mvm, vif, req);
1087bfcc09ddSBjoern A. Zeeb 			break;
1088bfcc09ddSBjoern A. Zeeb 		default:
1089bfcc09ddSBjoern A. Zeeb 			err = iwl_mvm_ftm_start_v7(mvm, vif, req);
1090bfcc09ddSBjoern A. Zeeb 			break;
1091bfcc09ddSBjoern A. Zeeb 		}
1092bfcc09ddSBjoern A. Zeeb 	} else {
1093bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_start_v5(mvm, vif, req);
1094bfcc09ddSBjoern A. Zeeb 	}
1095bfcc09ddSBjoern A. Zeeb 
1096bfcc09ddSBjoern A. Zeeb 	if (!err) {
1097bfcc09ddSBjoern A. Zeeb 		mvm->ftm_initiator.req = req;
1098bfcc09ddSBjoern A. Zeeb 		mvm->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif);
1099bfcc09ddSBjoern A. Zeeb 	}
1100bfcc09ddSBjoern A. Zeeb 
1101bfcc09ddSBjoern A. Zeeb 	return err;
1102bfcc09ddSBjoern A. Zeeb }
1103bfcc09ddSBjoern A. Zeeb 
1104bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req)
1105bfcc09ddSBjoern A. Zeeb {
1106bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_abort_cmd cmd = {
1107bfcc09ddSBjoern A. Zeeb 		.request_id = req->cookie,
1108bfcc09ddSBjoern A. Zeeb 	};
1109bfcc09ddSBjoern A. Zeeb 
1110bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1111bfcc09ddSBjoern A. Zeeb 
1112bfcc09ddSBjoern A. Zeeb 	if (req != mvm->ftm_initiator.req)
1113bfcc09ddSBjoern A. Zeeb 		return;
1114bfcc09ddSBjoern A. Zeeb 
1115bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_reset(mvm);
1116bfcc09ddSBjoern A. Zeeb 
1117d9836fb4SBjoern A. Zeeb 	if (iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(LOCATION_GROUP, TOF_RANGE_ABORT_CMD),
1118bfcc09ddSBjoern A. Zeeb 				 0, sizeof(cmd), &cmd))
1119bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "failed to abort FTM process\n");
1120bfcc09ddSBjoern A. Zeeb }
1121bfcc09ddSBjoern A. Zeeb 
1122bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_find_peer(struct cfg80211_pmsr_request *req,
1123bfcc09ddSBjoern A. Zeeb 				 const u8 *addr)
1124bfcc09ddSBjoern A. Zeeb {
1125bfcc09ddSBjoern A. Zeeb 	int i;
1126bfcc09ddSBjoern A. Zeeb 
1127bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < req->n_peers; i++) {
1128bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_request_peer *peer = &req->peers[i];
1129bfcc09ddSBjoern A. Zeeb 
1130bfcc09ddSBjoern A. Zeeb 		if (ether_addr_equal_unaligned(peer->addr, addr))
1131bfcc09ddSBjoern A. Zeeb 			return i;
1132bfcc09ddSBjoern A. Zeeb 	}
1133bfcc09ddSBjoern A. Zeeb 
1134bfcc09ddSBjoern A. Zeeb 	return -ENOENT;
1135bfcc09ddSBjoern A. Zeeb }
1136bfcc09ddSBjoern A. Zeeb 
1137bfcc09ddSBjoern A. Zeeb static u64 iwl_mvm_ftm_get_host_time(struct iwl_mvm *mvm, __le32 fw_gp2_ts)
1138bfcc09ddSBjoern A. Zeeb {
1139bfcc09ddSBjoern A. Zeeb 	u32 gp2_ts = le32_to_cpu(fw_gp2_ts);
1140bfcc09ddSBjoern A. Zeeb 	u32 curr_gp2, diff;
1141bfcc09ddSBjoern A. Zeeb 	u64 now_from_boot_ns;
1142bfcc09ddSBjoern A. Zeeb 
1143bfcc09ddSBjoern A. Zeeb 	iwl_mvm_get_sync_time(mvm, CLOCK_BOOTTIME, &curr_gp2,
1144bfcc09ddSBjoern A. Zeeb 			      &now_from_boot_ns, NULL);
1145bfcc09ddSBjoern A. Zeeb 
1146bfcc09ddSBjoern A. Zeeb 	if (curr_gp2 >= gp2_ts)
1147bfcc09ddSBjoern A. Zeeb 		diff = curr_gp2 - gp2_ts;
1148bfcc09ddSBjoern A. Zeeb 	else
1149bfcc09ddSBjoern A. Zeeb 		diff = curr_gp2 + (U32_MAX - gp2_ts + 1);
1150bfcc09ddSBjoern A. Zeeb 
1151bfcc09ddSBjoern A. Zeeb 	return now_from_boot_ns - (u64)diff * 1000;
1152bfcc09ddSBjoern A. Zeeb }
1153bfcc09ddSBjoern A. Zeeb 
1154bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_get_lci_civic(struct iwl_mvm *mvm,
1155bfcc09ddSBjoern A. Zeeb 				      struct cfg80211_pmsr_result *res)
1156bfcc09ddSBjoern A. Zeeb {
1157bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_loc_entry *entry;
1158bfcc09ddSBjoern A. Zeeb 
1159bfcc09ddSBjoern A. Zeeb 	list_for_each_entry(entry, &mvm->ftm_initiator.loc_list, list) {
1160bfcc09ddSBjoern A. Zeeb 		if (!ether_addr_equal_unaligned(res->addr, entry->addr))
1161bfcc09ddSBjoern A. Zeeb 			continue;
1162bfcc09ddSBjoern A. Zeeb 
1163bfcc09ddSBjoern A. Zeeb 		if (entry->lci_len) {
1164bfcc09ddSBjoern A. Zeeb 			res->ftm.lci_len = entry->lci_len;
1165bfcc09ddSBjoern A. Zeeb 			res->ftm.lci = entry->buf;
1166bfcc09ddSBjoern A. Zeeb 		}
1167bfcc09ddSBjoern A. Zeeb 
1168bfcc09ddSBjoern A. Zeeb 		if (entry->civic_len) {
1169bfcc09ddSBjoern A. Zeeb 			res->ftm.civicloc_len = entry->civic_len;
1170bfcc09ddSBjoern A. Zeeb 			res->ftm.civicloc = entry->buf + entry->lci_len;
1171bfcc09ddSBjoern A. Zeeb 		}
1172bfcc09ddSBjoern A. Zeeb 
1173bfcc09ddSBjoern A. Zeeb 		/* we found the entry we needed */
1174bfcc09ddSBjoern A. Zeeb 		break;
1175bfcc09ddSBjoern A. Zeeb 	}
1176bfcc09ddSBjoern A. Zeeb }
1177bfcc09ddSBjoern A. Zeeb 
1178bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_range_resp_valid(struct iwl_mvm *mvm, u8 request_id,
1179bfcc09ddSBjoern A. Zeeb 					u8 num_of_aps)
1180bfcc09ddSBjoern A. Zeeb {
1181bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1182bfcc09ddSBjoern A. Zeeb 
1183bfcc09ddSBjoern A. Zeeb 	if (request_id != (u8)mvm->ftm_initiator.req->cookie) {
1184bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Request ID mismatch, got %u, active %u\n",
1185bfcc09ddSBjoern A. Zeeb 			request_id, (u8)mvm->ftm_initiator.req->cookie);
1186bfcc09ddSBjoern A. Zeeb 		return -EINVAL;
1187bfcc09ddSBjoern A. Zeeb 	}
1188bfcc09ddSBjoern A. Zeeb 
1189bfcc09ddSBjoern A. Zeeb 	if (num_of_aps > mvm->ftm_initiator.req->n_peers) {
1190bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "FTM range response invalid\n");
1191bfcc09ddSBjoern A. Zeeb 		return -EINVAL;
1192bfcc09ddSBjoern A. Zeeb 	}
1193bfcc09ddSBjoern A. Zeeb 
1194bfcc09ddSBjoern A. Zeeb 	return 0;
1195bfcc09ddSBjoern A. Zeeb }
1196bfcc09ddSBjoern A. Zeeb 
1197bfcc09ddSBjoern A. Zeeb static void iwl_mvm_ftm_rtt_smoothing(struct iwl_mvm *mvm,
1198bfcc09ddSBjoern A. Zeeb 				      struct cfg80211_pmsr_result *res)
1199bfcc09ddSBjoern A. Zeeb {
12009af1bba4SBjoern A. Zeeb 	struct iwl_mvm_smooth_entry *resp = NULL, *iter;
1201bfcc09ddSBjoern A. Zeeb 	s64 rtt_avg, rtt = res->ftm.rtt_avg;
1202bfcc09ddSBjoern A. Zeeb 	u32 undershoot, overshoot;
1203bfcc09ddSBjoern A. Zeeb 	u8 alpha;
1204bfcc09ddSBjoern A. Zeeb 
1205bfcc09ddSBjoern A. Zeeb 	if (!IWL_MVM_FTM_INITIATOR_ENABLE_SMOOTH)
1206bfcc09ddSBjoern A. Zeeb 		return;
1207bfcc09ddSBjoern A. Zeeb 
1208bfcc09ddSBjoern A. Zeeb 	WARN_ON(rtt < 0);
1209bfcc09ddSBjoern A. Zeeb 
1210bfcc09ddSBjoern A. Zeeb 	if (res->status != NL80211_PMSR_STATUS_SUCCESS) {
1211bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
1212bfcc09ddSBjoern A. Zeeb 			       ": %pM: ignore failed measurement. Status=%u\n",
1213bfcc09ddSBjoern A. Zeeb 			       res->addr, res->status);
1214bfcc09ddSBjoern A. Zeeb 		return;
1215bfcc09ddSBjoern A. Zeeb 	}
1216bfcc09ddSBjoern A. Zeeb 
12179af1bba4SBjoern A. Zeeb 	list_for_each_entry(iter, &mvm->ftm_initiator.smooth.resp, list) {
12189af1bba4SBjoern A. Zeeb 		if (!memcmp(res->addr, iter->addr, ETH_ALEN)) {
12199af1bba4SBjoern A. Zeeb 			resp = iter;
1220bfcc09ddSBjoern A. Zeeb 			break;
1221bfcc09ddSBjoern A. Zeeb 		}
1222bfcc09ddSBjoern A. Zeeb 	}
1223bfcc09ddSBjoern A. Zeeb 
12249af1bba4SBjoern A. Zeeb 	if (!resp) {
1225bfcc09ddSBjoern A. Zeeb 		resp = kzalloc(sizeof(*resp), GFP_KERNEL);
1226bfcc09ddSBjoern A. Zeeb 		if (!resp)
1227bfcc09ddSBjoern A. Zeeb 			return;
1228bfcc09ddSBjoern A. Zeeb 
1229bfcc09ddSBjoern A. Zeeb 		memcpy(resp->addr, res->addr, ETH_ALEN);
1230bfcc09ddSBjoern A. Zeeb 		list_add_tail(&resp->list, &mvm->ftm_initiator.smooth.resp);
1231bfcc09ddSBjoern A. Zeeb 
1232bfcc09ddSBjoern A. Zeeb 		resp->rtt_avg = rtt;
1233bfcc09ddSBjoern A. Zeeb 
1234bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "new: %pM: rtt_avg=%lld\n",
1235bfcc09ddSBjoern A. Zeeb 			       resp->addr, resp->rtt_avg);
1236bfcc09ddSBjoern A. Zeeb 		goto update_time;
1237bfcc09ddSBjoern A. Zeeb 	}
1238bfcc09ddSBjoern A. Zeeb 
1239bfcc09ddSBjoern A. Zeeb 	if (res->host_time - resp->host_time >
1240bfcc09ddSBjoern A. Zeeb 	    IWL_MVM_FTM_INITIATOR_SMOOTH_AGE_SEC * 1000000000) {
1241bfcc09ddSBjoern A. Zeeb 		resp->rtt_avg = rtt;
1242bfcc09ddSBjoern A. Zeeb 
1243bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm, "expired: %pM: rtt_avg=%lld\n",
1244bfcc09ddSBjoern A. Zeeb 			       resp->addr, resp->rtt_avg);
1245bfcc09ddSBjoern A. Zeeb 		goto update_time;
1246bfcc09ddSBjoern A. Zeeb 	}
1247bfcc09ddSBjoern A. Zeeb 
1248bfcc09ddSBjoern A. Zeeb 	/* Smooth the results based on the tracked RTT average */
1249bfcc09ddSBjoern A. Zeeb 	undershoot = IWL_MVM_FTM_INITIATOR_SMOOTH_UNDERSHOOT;
1250bfcc09ddSBjoern A. Zeeb 	overshoot = IWL_MVM_FTM_INITIATOR_SMOOTH_OVERSHOOT;
1251bfcc09ddSBjoern A. Zeeb 	alpha = IWL_MVM_FTM_INITIATOR_SMOOTH_ALPHA;
1252bfcc09ddSBjoern A. Zeeb 
1253d9836fb4SBjoern A. Zeeb 	rtt_avg = div_s64(alpha * rtt + (100 - alpha) * resp->rtt_avg, 100);
1254bfcc09ddSBjoern A. Zeeb 
1255bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
1256bfcc09ddSBjoern A. Zeeb 		       "%pM: prev rtt_avg=%lld, new rtt_avg=%lld, rtt=%lld\n",
1257bfcc09ddSBjoern A. Zeeb 		       resp->addr, resp->rtt_avg, rtt_avg, rtt);
1258bfcc09ddSBjoern A. Zeeb 
1259bfcc09ddSBjoern A. Zeeb 	/*
1260bfcc09ddSBjoern A. Zeeb 	 * update the responder's average RTT results regardless of
1261bfcc09ddSBjoern A. Zeeb 	 * the under/over shoot logic below
1262bfcc09ddSBjoern A. Zeeb 	 */
1263bfcc09ddSBjoern A. Zeeb 	resp->rtt_avg = rtt_avg;
1264bfcc09ddSBjoern A. Zeeb 
1265bfcc09ddSBjoern A. Zeeb 	/* smooth the results */
1266bfcc09ddSBjoern A. Zeeb 	if (rtt_avg > rtt && (rtt_avg - rtt) > undershoot) {
1267bfcc09ddSBjoern A. Zeeb 		res->ftm.rtt_avg = rtt_avg;
1268bfcc09ddSBjoern A. Zeeb 
1269bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
1270bfcc09ddSBjoern A. Zeeb 			       "undershoot: val=%lld\n",
1271bfcc09ddSBjoern A. Zeeb 			       (rtt_avg - rtt));
1272bfcc09ddSBjoern A. Zeeb 	} else if (rtt_avg < rtt && (rtt - rtt_avg) >
1273bfcc09ddSBjoern A. Zeeb 		   overshoot) {
1274bfcc09ddSBjoern A. Zeeb 		res->ftm.rtt_avg = rtt_avg;
1275bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
1276bfcc09ddSBjoern A. Zeeb 			       "overshoot: val=%lld\n",
1277bfcc09ddSBjoern A. Zeeb 			       (rtt - rtt_avg));
1278bfcc09ddSBjoern A. Zeeb 	}
1279bfcc09ddSBjoern A. Zeeb 
1280bfcc09ddSBjoern A. Zeeb update_time:
1281bfcc09ddSBjoern A. Zeeb 	resp->host_time = res->host_time;
1282bfcc09ddSBjoern A. Zeeb }
1283bfcc09ddSBjoern A. Zeeb 
1284bfcc09ddSBjoern A. Zeeb static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index,
1285bfcc09ddSBjoern A. Zeeb 				     struct cfg80211_pmsr_result *res)
1286bfcc09ddSBjoern A. Zeeb {
1287bfcc09ddSBjoern A. Zeeb 	s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666);
1288bfcc09ddSBjoern A. Zeeb 
1289bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "entry %d\n", index);
1290bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\tstatus: %d\n", res->status);
1291bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\tBSSID: %pM\n", res->addr);
1292bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\thost time: %llu\n", res->host_time);
12939af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\tburst index: %d\n", res->ftm.burst_index);
1294bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes);
1295bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\trssi: %d\n", res->ftm.rssi_avg);
12969af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\trssi spread: %d\n", res->ftm.rssi_spread);
1297bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\trtt: %lld\n", res->ftm.rtt_avg);
1298bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\trtt var: %llu\n", res->ftm.rtt_variance);
1299bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\trtt spread: %llu\n", res->ftm.rtt_spread);
1300bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "\tdistance: %lld\n", rtt_avg);
1301bfcc09ddSBjoern A. Zeeb }
1302bfcc09ddSBjoern A. Zeeb 
1303bfcc09ddSBjoern A. Zeeb static void
1304bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm,
1305bfcc09ddSBjoern A. Zeeb 			   struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap)
1306bfcc09ddSBjoern A. Zeeb {
1307bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_ftm_pasn_entry *entry;
1308bfcc09ddSBjoern A. Zeeb 
1309bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1310bfcc09ddSBjoern A. Zeeb 
1311bfcc09ddSBjoern A. Zeeb 	list_for_each_entry(entry, &mvm->ftm_initiator.pasn_list, list) {
1312bfcc09ddSBjoern A. Zeeb 		if (memcmp(fw_ap->bssid, entry->addr, sizeof(entry->addr)))
1313bfcc09ddSBjoern A. Zeeb 			continue;
1314bfcc09ddSBjoern A. Zeeb 
1315bfcc09ddSBjoern A. Zeeb 		memcpy(entry->rx_pn, fw_ap->rx_pn, sizeof(entry->rx_pn));
1316bfcc09ddSBjoern A. Zeeb 		memcpy(entry->tx_pn, fw_ap->tx_pn, sizeof(entry->tx_pn));
1317bfcc09ddSBjoern A. Zeeb 		return;
1318bfcc09ddSBjoern A. Zeeb 	}
1319bfcc09ddSBjoern A. Zeeb }
1320bfcc09ddSBjoern A. Zeeb 
1321bfcc09ddSBjoern A. Zeeb static u8 iwl_mvm_ftm_get_range_resp_ver(struct iwl_mvm *mvm)
1322bfcc09ddSBjoern A. Zeeb {
1323bfcc09ddSBjoern A. Zeeb 	if (!fw_has_api(&mvm->fw->ucode_capa,
1324bfcc09ddSBjoern A. Zeeb 			IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ))
1325bfcc09ddSBjoern A. Zeeb 		return 5;
1326bfcc09ddSBjoern A. Zeeb 
1327bfcc09ddSBjoern A. Zeeb 	/* Starting from version 8, the FW advertises the version */
1328bfcc09ddSBjoern A. Zeeb 	if (mvm->cmd_ver.range_resp >= 8)
1329bfcc09ddSBjoern A. Zeeb 		return mvm->cmd_ver.range_resp;
1330bfcc09ddSBjoern A. Zeeb 	else if (fw_has_api(&mvm->fw->ucode_capa,
1331bfcc09ddSBjoern A. Zeeb 			    IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
1332bfcc09ddSBjoern A. Zeeb 		return 7;
1333bfcc09ddSBjoern A. Zeeb 
1334bfcc09ddSBjoern A. Zeeb 	/* The first version of the new range request API */
1335bfcc09ddSBjoern A. Zeeb 	return 6;
1336bfcc09ddSBjoern A. Zeeb }
1337bfcc09ddSBjoern A. Zeeb 
1338bfcc09ddSBjoern A. Zeeb static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len)
1339bfcc09ddSBjoern A. Zeeb {
1340bfcc09ddSBjoern A. Zeeb 	switch (ver) {
1341bfcc09ddSBjoern A. Zeeb 	case 9:
1342bfcc09ddSBjoern A. Zeeb 	case 8:
1343bfcc09ddSBjoern A. Zeeb 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8);
1344bfcc09ddSBjoern A. Zeeb 	case 7:
1345bfcc09ddSBjoern A. Zeeb 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7);
1346bfcc09ddSBjoern A. Zeeb 	case 6:
1347bfcc09ddSBjoern A. Zeeb 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v6);
1348bfcc09ddSBjoern A. Zeeb 	case 5:
1349bfcc09ddSBjoern A. Zeeb 		return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v5);
1350bfcc09ddSBjoern A. Zeeb 	default:
1351bfcc09ddSBjoern A. Zeeb 		WARN_ONCE(1, "FTM: unsupported range response version %u", ver);
1352bfcc09ddSBjoern A. Zeeb 		return false;
1353bfcc09ddSBjoern A. Zeeb 	}
1354bfcc09ddSBjoern A. Zeeb }
1355bfcc09ddSBjoern A. Zeeb 
1356bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1357bfcc09ddSBjoern A. Zeeb {
1358bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
1359bfcc09ddSBjoern A. Zeeb 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
1360bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data;
1361bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data;
1362bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data;
1363bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data;
1364bfcc09ddSBjoern A. Zeeb 	int i;
1365bfcc09ddSBjoern A. Zeeb 	bool new_api = fw_has_api(&mvm->fw->ucode_capa,
1366bfcc09ddSBjoern A. Zeeb 				  IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ);
1367bfcc09ddSBjoern A. Zeeb 	u8 num_of_aps, last_in_batch;
1368bfcc09ddSBjoern A. Zeeb 	u8 notif_ver = iwl_mvm_ftm_get_range_resp_ver(mvm);
1369bfcc09ddSBjoern A. Zeeb 
1370bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1371bfcc09ddSBjoern A. Zeeb 
1372bfcc09ddSBjoern A. Zeeb 	if (!mvm->ftm_initiator.req) {
1373bfcc09ddSBjoern A. Zeeb 		return;
1374bfcc09ddSBjoern A. Zeeb 	}
1375bfcc09ddSBjoern A. Zeeb 
1376bfcc09ddSBjoern A. Zeeb 	if (unlikely(!iwl_mvm_ftm_resp_size_validation(notif_ver, pkt_len)))
1377bfcc09ddSBjoern A. Zeeb 		return;
1378bfcc09ddSBjoern A. Zeeb 
1379bfcc09ddSBjoern A. Zeeb 	if (new_api) {
1380bfcc09ddSBjoern A. Zeeb 		if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v8->request_id,
1381bfcc09ddSBjoern A. Zeeb 						 fw_resp_v8->num_of_aps))
1382bfcc09ddSBjoern A. Zeeb 			return;
1383bfcc09ddSBjoern A. Zeeb 
1384bfcc09ddSBjoern A. Zeeb 		num_of_aps = fw_resp_v8->num_of_aps;
1385bfcc09ddSBjoern A. Zeeb 		last_in_batch = fw_resp_v8->last_report;
1386bfcc09ddSBjoern A. Zeeb 	} else {
1387bfcc09ddSBjoern A. Zeeb 		if (iwl_mvm_ftm_range_resp_valid(mvm, fw_resp_v5->request_id,
1388bfcc09ddSBjoern A. Zeeb 						 fw_resp_v5->num_of_aps))
1389bfcc09ddSBjoern A. Zeeb 			return;
1390bfcc09ddSBjoern A. Zeeb 
1391bfcc09ddSBjoern A. Zeeb 		num_of_aps = fw_resp_v5->num_of_aps;
1392bfcc09ddSBjoern A. Zeeb 		last_in_batch = fw_resp_v5->last_in_batch;
1393bfcc09ddSBjoern A. Zeeb 	}
1394bfcc09ddSBjoern A. Zeeb 
1395bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "Range response received\n");
13969af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n",
1397bfcc09ddSBjoern A. Zeeb 		       mvm->ftm_initiator.req->cookie, num_of_aps);
1398bfcc09ddSBjoern A. Zeeb 
1399bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) {
1400bfcc09ddSBjoern A. Zeeb 		struct cfg80211_pmsr_result result = {};
1401bfcc09ddSBjoern A. Zeeb 		struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap;
1402bfcc09ddSBjoern A. Zeeb 		int peer_idx;
1403bfcc09ddSBjoern A. Zeeb 
1404bfcc09ddSBjoern A. Zeeb 		if (new_api) {
1405bfcc09ddSBjoern A. Zeeb 			if (notif_ver >= 8) {
1406bfcc09ddSBjoern A. Zeeb 				fw_ap = &fw_resp_v8->ap[i];
1407bfcc09ddSBjoern A. Zeeb 				iwl_mvm_ftm_pasn_update_pn(mvm, fw_ap);
1408bfcc09ddSBjoern A. Zeeb 			} else if (notif_ver == 7) {
1409bfcc09ddSBjoern A. Zeeb 				fw_ap = (void *)&fw_resp_v7->ap[i];
1410bfcc09ddSBjoern A. Zeeb 			} else {
1411bfcc09ddSBjoern A. Zeeb 				fw_ap = (void *)&fw_resp_v6->ap[i];
1412bfcc09ddSBjoern A. Zeeb 			}
1413bfcc09ddSBjoern A. Zeeb 
1414bfcc09ddSBjoern A. Zeeb 			result.final = fw_ap->last_burst;
1415bfcc09ddSBjoern A. Zeeb 			result.ap_tsf = le32_to_cpu(fw_ap->start_tsf);
1416bfcc09ddSBjoern A. Zeeb 			result.ap_tsf_valid = 1;
1417bfcc09ddSBjoern A. Zeeb 		} else {
1418bfcc09ddSBjoern A. Zeeb 			/* the first part is the same for old and new APIs */
1419bfcc09ddSBjoern A. Zeeb 			fw_ap = (void *)&fw_resp_v5->ap[i];
1420bfcc09ddSBjoern A. Zeeb 			/*
1421bfcc09ddSBjoern A. Zeeb 			 * FIXME: the firmware needs to report this, we don't
1422bfcc09ddSBjoern A. Zeeb 			 * even know the number of bursts the responder picked
1423bfcc09ddSBjoern A. Zeeb 			 * (if we asked it to)
1424bfcc09ddSBjoern A. Zeeb 			 */
1425bfcc09ddSBjoern A. Zeeb 			result.final = 0;
1426bfcc09ddSBjoern A. Zeeb 		}
1427bfcc09ddSBjoern A. Zeeb 
1428bfcc09ddSBjoern A. Zeeb 		peer_idx = iwl_mvm_ftm_find_peer(mvm->ftm_initiator.req,
1429bfcc09ddSBjoern A. Zeeb 						 fw_ap->bssid);
1430bfcc09ddSBjoern A. Zeeb 		if (peer_idx < 0) {
1431bfcc09ddSBjoern A. Zeeb 			IWL_WARN(mvm,
1432bfcc09ddSBjoern A. Zeeb 				 "Unknown address (%pM, target #%d) in FTM response\n",
1433bfcc09ddSBjoern A. Zeeb 				 fw_ap->bssid, i);
1434bfcc09ddSBjoern A. Zeeb 			continue;
1435bfcc09ddSBjoern A. Zeeb 		}
1436bfcc09ddSBjoern A. Zeeb 
1437bfcc09ddSBjoern A. Zeeb 		switch (fw_ap->measure_status) {
1438bfcc09ddSBjoern A. Zeeb 		case IWL_TOF_ENTRY_SUCCESS:
1439bfcc09ddSBjoern A. Zeeb 			result.status = NL80211_PMSR_STATUS_SUCCESS;
1440bfcc09ddSBjoern A. Zeeb 			break;
1441bfcc09ddSBjoern A. Zeeb 		case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT:
1442bfcc09ddSBjoern A. Zeeb 			result.status = NL80211_PMSR_STATUS_TIMEOUT;
1443bfcc09ddSBjoern A. Zeeb 			break;
1444bfcc09ddSBjoern A. Zeeb 		case IWL_TOF_ENTRY_NO_RESPONSE:
1445bfcc09ddSBjoern A. Zeeb 			result.status = NL80211_PMSR_STATUS_FAILURE;
1446bfcc09ddSBjoern A. Zeeb 			result.ftm.failure_reason =
1447bfcc09ddSBjoern A. Zeeb 				NL80211_PMSR_FTM_FAILURE_NO_RESPONSE;
1448bfcc09ddSBjoern A. Zeeb 			break;
1449bfcc09ddSBjoern A. Zeeb 		case IWL_TOF_ENTRY_REQUEST_REJECTED:
1450bfcc09ddSBjoern A. Zeeb 			result.status = NL80211_PMSR_STATUS_FAILURE;
1451bfcc09ddSBjoern A. Zeeb 			result.ftm.failure_reason =
1452bfcc09ddSBjoern A. Zeeb 				NL80211_PMSR_FTM_FAILURE_PEER_BUSY;
1453bfcc09ddSBjoern A. Zeeb 			result.ftm.busy_retry_time = fw_ap->refusal_period;
1454bfcc09ddSBjoern A. Zeeb 			break;
1455bfcc09ddSBjoern A. Zeeb 		default:
1456bfcc09ddSBjoern A. Zeeb 			result.status = NL80211_PMSR_STATUS_FAILURE;
1457bfcc09ddSBjoern A. Zeeb 			result.ftm.failure_reason =
1458bfcc09ddSBjoern A. Zeeb 				NL80211_PMSR_FTM_FAILURE_UNSPECIFIED;
1459bfcc09ddSBjoern A. Zeeb 			break;
1460bfcc09ddSBjoern A. Zeeb 		}
1461bfcc09ddSBjoern A. Zeeb 		memcpy(result.addr, fw_ap->bssid, ETH_ALEN);
1462bfcc09ddSBjoern A. Zeeb 		result.host_time = iwl_mvm_ftm_get_host_time(mvm,
1463bfcc09ddSBjoern A. Zeeb 							     fw_ap->timestamp);
1464bfcc09ddSBjoern A. Zeeb 		result.type = NL80211_PMSR_TYPE_FTM;
1465bfcc09ddSBjoern A. Zeeb 		result.ftm.burst_index = mvm->ftm_initiator.responses[peer_idx];
1466bfcc09ddSBjoern A. Zeeb 		mvm->ftm_initiator.responses[peer_idx]++;
1467bfcc09ddSBjoern A. Zeeb 		result.ftm.rssi_avg = fw_ap->rssi;
1468bfcc09ddSBjoern A. Zeeb 		result.ftm.rssi_avg_valid = 1;
1469bfcc09ddSBjoern A. Zeeb 		result.ftm.rssi_spread = fw_ap->rssi_spread;
1470bfcc09ddSBjoern A. Zeeb 		result.ftm.rssi_spread_valid = 1;
1471bfcc09ddSBjoern A. Zeeb 		result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt);
1472bfcc09ddSBjoern A. Zeeb 		result.ftm.rtt_avg_valid = 1;
1473bfcc09ddSBjoern A. Zeeb 		result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance);
1474bfcc09ddSBjoern A. Zeeb 		result.ftm.rtt_variance_valid = 1;
1475bfcc09ddSBjoern A. Zeeb 		result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread);
1476bfcc09ddSBjoern A. Zeeb 		result.ftm.rtt_spread_valid = 1;
1477bfcc09ddSBjoern A. Zeeb 
1478bfcc09ddSBjoern A. Zeeb 		iwl_mvm_ftm_get_lci_civic(mvm, &result);
1479bfcc09ddSBjoern A. Zeeb 
1480bfcc09ddSBjoern A. Zeeb 		iwl_mvm_ftm_rtt_smoothing(mvm, &result);
1481bfcc09ddSBjoern A. Zeeb 
1482bfcc09ddSBjoern A. Zeeb 		cfg80211_pmsr_report(mvm->ftm_initiator.req_wdev,
1483bfcc09ddSBjoern A. Zeeb 				     mvm->ftm_initiator.req,
1484bfcc09ddSBjoern A. Zeeb 				     &result, GFP_KERNEL);
1485bfcc09ddSBjoern A. Zeeb 
1486bfcc09ddSBjoern A. Zeeb 		if (fw_has_api(&mvm->fw->ucode_capa,
1487bfcc09ddSBjoern A. Zeeb 			       IWL_UCODE_TLV_API_FTM_RTT_ACCURACY))
14889af1bba4SBjoern A. Zeeb 			IWL_DEBUG_INFO(mvm, "RTT confidence: %u\n",
1489bfcc09ddSBjoern A. Zeeb 				       fw_ap->rttConfidence);
1490bfcc09ddSBjoern A. Zeeb 
1491bfcc09ddSBjoern A. Zeeb 		iwl_mvm_debug_range_resp(mvm, i, &result);
1492bfcc09ddSBjoern A. Zeeb 	}
1493bfcc09ddSBjoern A. Zeeb 
1494bfcc09ddSBjoern A. Zeeb 	if (last_in_batch) {
1495bfcc09ddSBjoern A. Zeeb 		cfg80211_pmsr_complete(mvm->ftm_initiator.req_wdev,
1496bfcc09ddSBjoern A. Zeeb 				       mvm->ftm_initiator.req,
1497bfcc09ddSBjoern A. Zeeb 				       GFP_KERNEL);
1498bfcc09ddSBjoern A. Zeeb 		iwl_mvm_ftm_reset(mvm);
1499bfcc09ddSBjoern A. Zeeb 	}
1500bfcc09ddSBjoern A. Zeeb }
1501bfcc09ddSBjoern A. Zeeb 
1502bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_lc_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
1503bfcc09ddSBjoern A. Zeeb {
1504bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
1505bfcc09ddSBjoern A. Zeeb 	const struct ieee80211_mgmt *mgmt = (void *)pkt->data;
1506bfcc09ddSBjoern A. Zeeb 	size_t len = iwl_rx_packet_payload_len(pkt);
1507bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_loc_entry *entry;
1508bfcc09ddSBjoern A. Zeeb 	const u8 *ies, *lci, *civic, *msr_ie;
1509bfcc09ddSBjoern A. Zeeb 	size_t ies_len, lci_len = 0, civic_len = 0;
1510bfcc09ddSBjoern A. Zeeb 	size_t baselen = IEEE80211_MIN_ACTION_SIZE +
1511bfcc09ddSBjoern A. Zeeb 			 sizeof(mgmt->u.action.u.ftm);
1512bfcc09ddSBjoern A. Zeeb 	static const u8 rprt_type_lci = IEEE80211_SPCT_MSR_RPRT_TYPE_LCI;
1513bfcc09ddSBjoern A. Zeeb 	static const u8 rprt_type_civic = IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC;
1514bfcc09ddSBjoern A. Zeeb 
1515bfcc09ddSBjoern A. Zeeb 	if (len <= baselen)
1516bfcc09ddSBjoern A. Zeeb 		return;
1517bfcc09ddSBjoern A. Zeeb 
1518bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1519bfcc09ddSBjoern A. Zeeb 
1520bfcc09ddSBjoern A. Zeeb 	ies = mgmt->u.action.u.ftm.variable;
1521bfcc09ddSBjoern A. Zeeb 	ies_len = len - baselen;
1522bfcc09ddSBjoern A. Zeeb 
1523bfcc09ddSBjoern A. Zeeb 	msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
1524bfcc09ddSBjoern A. Zeeb 					&rprt_type_lci, 1, 4);
1525bfcc09ddSBjoern A. Zeeb 	if (msr_ie) {
1526bfcc09ddSBjoern A. Zeeb 		lci = msr_ie + 2;
1527bfcc09ddSBjoern A. Zeeb 		lci_len = msr_ie[1];
1528bfcc09ddSBjoern A. Zeeb 	}
1529bfcc09ddSBjoern A. Zeeb 
1530bfcc09ddSBjoern A. Zeeb 	msr_ie = cfg80211_find_ie_match(WLAN_EID_MEASURE_REPORT, ies, ies_len,
1531bfcc09ddSBjoern A. Zeeb 					&rprt_type_civic, 1, 4);
1532bfcc09ddSBjoern A. Zeeb 	if (msr_ie) {
1533bfcc09ddSBjoern A. Zeeb 		civic = msr_ie + 2;
1534bfcc09ddSBjoern A. Zeeb 		civic_len = msr_ie[1];
1535bfcc09ddSBjoern A. Zeeb 	}
1536bfcc09ddSBjoern A. Zeeb 
1537bfcc09ddSBjoern A. Zeeb 	entry = kmalloc(sizeof(*entry) + lci_len + civic_len, GFP_KERNEL);
1538bfcc09ddSBjoern A. Zeeb 	if (!entry)
1539bfcc09ddSBjoern A. Zeeb 		return;
1540bfcc09ddSBjoern A. Zeeb 
1541bfcc09ddSBjoern A. Zeeb 	memcpy(entry->addr, mgmt->bssid, ETH_ALEN);
1542bfcc09ddSBjoern A. Zeeb 
1543bfcc09ddSBjoern A. Zeeb 	entry->lci_len = lci_len;
1544bfcc09ddSBjoern A. Zeeb 	if (lci_len)
1545bfcc09ddSBjoern A. Zeeb 		memcpy(entry->buf, lci, lci_len);
1546bfcc09ddSBjoern A. Zeeb 
1547bfcc09ddSBjoern A. Zeeb 	entry->civic_len = civic_len;
1548bfcc09ddSBjoern A. Zeeb 	if (civic_len)
1549bfcc09ddSBjoern A. Zeeb 		memcpy(entry->buf + lci_len, civic, civic_len);
1550bfcc09ddSBjoern A. Zeeb 
1551bfcc09ddSBjoern A. Zeeb 	list_add_tail(&entry->list, &mvm->ftm_initiator.loc_list);
1552bfcc09ddSBjoern A. Zeeb }
1553