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