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