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