xref: /linux/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28e99ea8dSJohannes Berg /*
38e99ea8dSJohannes Berg  * Copyright (C) 2015-2017 Intel Deutschland GmbH
4*f1c9ba44SAvraham Stern  * Copyright (C) 2018-2024 Intel Corporation
58e99ea8dSJohannes Berg  */
6b73f9a4aSJohannes Berg #include <net/cfg80211.h>
7b73f9a4aSJohannes Berg #include <linux/etherdevice.h>
8b73f9a4aSJohannes Berg #include "mvm.h"
9b73f9a4aSJohannes Berg #include "constants.h"
10b73f9a4aSJohannes Berg 
11be82ecd3SAvraham Stern struct iwl_mvm_pasn_sta {
12be82ecd3SAvraham Stern 	struct list_head list;
13be82ecd3SAvraham Stern 	struct iwl_mvm_int_sta int_sta;
14be82ecd3SAvraham Stern 	u8 addr[ETH_ALEN];
15619a900fSIlan Peer 
16619a900fSIlan Peer 	/* must be last as it followed by buffer holding the key */
17619a900fSIlan Peer 	struct ieee80211_key_conf keyconf;
18be82ecd3SAvraham Stern };
19be82ecd3SAvraham Stern 
203830a01cSAvraham Stern struct iwl_mvm_pasn_hltk_data {
213830a01cSAvraham Stern 	u8 *addr;
223830a01cSAvraham Stern 	u8 cipher;
233830a01cSAvraham Stern 	u8 *hltk;
243830a01cSAvraham Stern };
253830a01cSAvraham Stern 
iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def * chandef,u8 * bw,u8 * ctrl_ch_position)26b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
27b1a6db13SAvraham Stern 					   u8 *bw, u8 *ctrl_ch_position)
28b1a6db13SAvraham Stern {
29b1a6db13SAvraham Stern 	switch (chandef->width) {
30b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20_NOHT:
31b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_20_LEGACY;
32b1a6db13SAvraham Stern 		break;
33b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20:
34b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_20_HT;
35b1a6db13SAvraham Stern 		break;
36b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_40:
37b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_40;
38b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
39b1a6db13SAvraham Stern 		break;
40b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_80:
41b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_80;
42b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
43b1a6db13SAvraham Stern 		break;
44b1a6db13SAvraham Stern 	default:
4596850912SAndrei Otcheretianski 		return -EOPNOTSUPP;
46b1a6db13SAvraham Stern 	}
47b1a6db13SAvraham Stern 
48b1a6db13SAvraham Stern 	return 0;
49b1a6db13SAvraham Stern }
50b1a6db13SAvraham Stern 
iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def * chandef,u8 * format_bw,u8 * ctrl_ch_position,u8 cmd_ver)51b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
528a2c1516SAvraham Stern 					   u8 *format_bw, u8 *ctrl_ch_position,
538a2c1516SAvraham Stern 					   u8 cmd_ver)
54b1a6db13SAvraham Stern {
55b1a6db13SAvraham Stern 	switch (chandef->width) {
56b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20_NOHT:
57b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
58b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
59b1a6db13SAvraham Stern 		break;
60b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20:
61b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
62b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
63b1a6db13SAvraham Stern 		break;
64b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_40:
65b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
66b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
67b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
68b1a6db13SAvraham Stern 		break;
69b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_80:
70b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
71b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
72b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
73b1a6db13SAvraham Stern 		break;
748a2c1516SAvraham Stern 	case NL80211_CHAN_WIDTH_160:
758a2c1516SAvraham Stern 		if (cmd_ver >= 9) {
768a2c1516SAvraham Stern 			*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
778a2c1516SAvraham Stern 			*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
788a2c1516SAvraham Stern 			*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
798a2c1516SAvraham Stern 			break;
808a2c1516SAvraham Stern 		}
818a2c1516SAvraham Stern 		fallthrough;
82b1a6db13SAvraham Stern 	default:
8396850912SAndrei Otcheretianski 		return -EOPNOTSUPP;
84b1a6db13SAvraham Stern 	}
85b1a6db13SAvraham Stern 
86b1a6db13SAvraham Stern 	return 0;
87b1a6db13SAvraham Stern }
88b1a6db13SAvraham Stern 
8920578872SAvraham Stern static void
iwl_mvm_ftm_responder_set_ndp(struct iwl_mvm * mvm,struct iwl_tof_responder_config_cmd * cmd)9020578872SAvraham Stern iwl_mvm_ftm_responder_set_ndp(struct iwl_mvm *mvm,
91*f1c9ba44SAvraham Stern 			      struct iwl_tof_responder_config_cmd *cmd)
9220578872SAvraham Stern {
9320578872SAvraham Stern 	/* Up to 2 R2I STS are allowed on the responder */
9420578872SAvraham Stern 	u32 r2i_max_sts = IWL_MVM_FTM_R2I_MAX_STS < 2 ?
9520578872SAvraham Stern 		IWL_MVM_FTM_R2I_MAX_STS : 1;
9620578872SAvraham Stern 
9720578872SAvraham Stern 	cmd->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |
9820578872SAvraham Stern 		(r2i_max_sts << IWL_RESPONDER_STS_POS) |
9920578872SAvraham Stern 		(IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS);
10020578872SAvraham Stern 	cmd->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |
10120578872SAvraham Stern 		(IWL_MVM_FTM_I2R_MAX_STS << IWL_RESPONDER_STS_POS) |
10220578872SAvraham Stern 		(IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS);
10320578872SAvraham Stern 	cmd->cmd_valid_fields |=
10420578872SAvraham Stern 		cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_NDP_PARAMS);
10520578872SAvraham Stern }
10620578872SAvraham Stern 
107b73f9a4aSJohannes Berg static int
iwl_mvm_ftm_responder_cmd(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct cfg80211_chan_def * chandef,struct ieee80211_bss_conf * link_conf)108b73f9a4aSJohannes Berg iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
109b73f9a4aSJohannes Berg 			  struct ieee80211_vif *vif,
110fd940de7SAvraham Stern 			  struct cfg80211_chan_def *chandef,
111fd940de7SAvraham Stern 			  struct ieee80211_bss_conf *link_conf)
112b73f9a4aSJohannes Berg {
113971cbe50SJohannes Berg 	u32 cmd_id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_CONFIG_CMD);
114b73f9a4aSJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
115b1a6db13SAvraham Stern 	/*
11620578872SAvraham Stern 	 * The command structure is the same for versions 6, 7 and 8 (only the
117b1a6db13SAvraham Stern 	 * field interpretation is different), so the same struct can be use
118b1a6db13SAvraham Stern 	 * for all cases.
119b1a6db13SAvraham Stern 	 */
120*f1c9ba44SAvraham Stern 	struct iwl_tof_responder_config_cmd cmd = {
121b73f9a4aSJohannes Berg 		.channel_num = chandef->chan->hw_value,
122b73f9a4aSJohannes Berg 		.cmd_valid_fields =
123b73f9a4aSJohannes Berg 			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
124b73f9a4aSJohannes Berg 				    IWL_TOF_RESPONDER_CMD_VALID_BSSID |
125b73f9a4aSJohannes Berg 				    IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
126fd940de7SAvraham Stern 		.sta_id = mvmvif->link[link_conf->link_id]->bcast_sta.sta_id,
127b73f9a4aSJohannes Berg 	};
128971cbe50SJohannes Berg 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 6);
129b1a6db13SAvraham Stern 	int err;
130bd34ff38SAvraham Stern 	int cmd_size;
131b73f9a4aSJohannes Berg 
132b73f9a4aSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
133b73f9a4aSJohannes Berg 
134*f1c9ba44SAvraham Stern 	if (cmd_ver == 10) {
135*f1c9ba44SAvraham Stern 		cmd.band =
136*f1c9ba44SAvraham Stern 			iwl_mvm_phy_band_from_nl80211(chandef->chan->band);
137*f1c9ba44SAvraham Stern 	}
138*f1c9ba44SAvraham Stern 
139bd34ff38SAvraham Stern 	/* Use a default of bss_color=1 for now */
140*f1c9ba44SAvraham Stern 	if (cmd_ver >= 9) {
141bd34ff38SAvraham Stern 		cmd.cmd_valid_fields |=
142bd34ff38SAvraham Stern 			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_BSS_COLOR |
143bd34ff38SAvraham Stern 				    IWL_TOF_RESPONDER_CMD_VALID_MIN_MAX_TIME_BETWEEN_MSR);
144bd34ff38SAvraham Stern 		cmd.bss_color = 1;
145bd34ff38SAvraham Stern 		cmd.min_time_between_msr =
146bd34ff38SAvraham Stern 			cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
147bd34ff38SAvraham Stern 		cmd.max_time_between_msr =
148bd34ff38SAvraham Stern 			cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
149bd34ff38SAvraham Stern 		cmd_size = sizeof(struct iwl_tof_responder_config_cmd_v9);
150bd34ff38SAvraham Stern 	} else {
151bd34ff38SAvraham Stern 		/* All versions up to version 8 have the same size */
152bd34ff38SAvraham Stern 		cmd_size = sizeof(struct iwl_tof_responder_config_cmd_v8);
153bd34ff38SAvraham Stern 	}
154bd34ff38SAvraham Stern 
155bd34ff38SAvraham Stern 	if (cmd_ver >= 8)
156*f1c9ba44SAvraham Stern 		iwl_mvm_ftm_responder_set_ndp(mvm, (void *)&cmd);
15720578872SAvraham Stern 
15820578872SAvraham Stern 	if (cmd_ver >= 7)
159b1a6db13SAvraham Stern 		err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
1608a2c1516SAvraham Stern 						      &cmd.ctrl_ch_position,
1618a2c1516SAvraham Stern 						      cmd_ver);
162b1a6db13SAvraham Stern 	else
163b1a6db13SAvraham Stern 		err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
164b1a6db13SAvraham Stern 						      &cmd.ctrl_ch_position);
165b1a6db13SAvraham Stern 
166b1a6db13SAvraham Stern 	if (err) {
167b1a6db13SAvraham Stern 		IWL_ERR(mvm, "Failed to set responder bandwidth\n");
168b1a6db13SAvraham Stern 		return err;
169b73f9a4aSJohannes Berg 	}
170b73f9a4aSJohannes Berg 
171b73f9a4aSJohannes Berg 	memcpy(cmd.bssid, vif->addr, ETH_ALEN);
172b73f9a4aSJohannes Berg 
173971cbe50SJohannes Berg 	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd);
174b73f9a4aSJohannes Berg }
175b73f9a4aSJohannes Berg 
176b73f9a4aSJohannes Berg static int
iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_ftm_responder_params * params)1773830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm,
178b73f9a4aSJohannes Berg 				 struct ieee80211_vif *vif,
179b73f9a4aSJohannes Berg 				 struct ieee80211_ftm_responder_params *params)
180b73f9a4aSJohannes Berg {
1813830a01cSAvraham Stern 	struct iwl_tof_responder_dyn_config_cmd_v2 cmd = {
182b73f9a4aSJohannes Berg 		.lci_len = cpu_to_le32(params->lci_len + 2),
183b73f9a4aSJohannes Berg 		.civic_len = cpu_to_le32(params->civicloc_len + 2),
184b73f9a4aSJohannes Berg 	};
185b73f9a4aSJohannes Berg 	u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0};
186b73f9a4aSJohannes Berg 	struct iwl_host_cmd hcmd = {
187f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
188b73f9a4aSJohannes Berg 		.data[0] = &cmd,
189b73f9a4aSJohannes Berg 		.len[0] = sizeof(cmd),
190b73f9a4aSJohannes Berg 		.data[1] = &data,
191b73f9a4aSJohannes Berg 		/* .len[1] set later */
192b73f9a4aSJohannes Berg 		/* may not be able to DMA from stack */
193b73f9a4aSJohannes Berg 		.dataflags[1] = IWL_HCMD_DFL_DUP,
194b73f9a4aSJohannes Berg 	};
195b73f9a4aSJohannes Berg 	u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
196b73f9a4aSJohannes Berg 	u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
197b73f9a4aSJohannes Berg 	u8 *pos = data;
198b73f9a4aSJohannes Berg 
199b73f9a4aSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
200b73f9a4aSJohannes Berg 
201b73f9a4aSJohannes Berg 	if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
202b73f9a4aSJohannes Berg 		IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
203b73f9a4aSJohannes Berg 			params->lci_len, params->civicloc_len);
204b73f9a4aSJohannes Berg 		return -ENOBUFS;
205b73f9a4aSJohannes Berg 	}
206b73f9a4aSJohannes Berg 
207b73f9a4aSJohannes Berg 	pos[0] = WLAN_EID_MEASURE_REPORT;
208b73f9a4aSJohannes Berg 	pos[1] = params->lci_len;
209b73f9a4aSJohannes Berg 	memcpy(pos + 2, params->lci, params->lci_len);
210b73f9a4aSJohannes Berg 
211b73f9a4aSJohannes Berg 	pos += aligned_lci_len;
212b73f9a4aSJohannes Berg 	pos[0] = WLAN_EID_MEASURE_REPORT;
213b73f9a4aSJohannes Berg 	pos[1] = params->civicloc_len;
214b73f9a4aSJohannes Berg 	memcpy(pos + 2, params->civicloc, params->civicloc_len);
215b73f9a4aSJohannes Berg 
216b73f9a4aSJohannes Berg 	hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
217b73f9a4aSJohannes Berg 
218b73f9a4aSJohannes Berg 	return iwl_mvm_send_cmd(mvm, &hcmd);
219b73f9a4aSJohannes Berg }
220b73f9a4aSJohannes Berg 
2213830a01cSAvraham Stern static int
iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_ftm_responder_params * params,struct iwl_mvm_pasn_hltk_data * hltk_data)2223830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm,
2233830a01cSAvraham Stern 				 struct ieee80211_vif *vif,
2243830a01cSAvraham Stern 				 struct ieee80211_ftm_responder_params *params,
2253830a01cSAvraham Stern 				 struct iwl_mvm_pasn_hltk_data *hltk_data)
2263830a01cSAvraham Stern {
2273830a01cSAvraham Stern 	struct iwl_tof_responder_dyn_config_cmd cmd;
2283830a01cSAvraham Stern 	struct iwl_host_cmd hcmd = {
229f0c86427SJohannes Berg 		.id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
2303830a01cSAvraham Stern 		.data[0] = &cmd,
2313830a01cSAvraham Stern 		.len[0] = sizeof(cmd),
2323830a01cSAvraham Stern 		/* may not be able to DMA from stack */
2333830a01cSAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
2343830a01cSAvraham Stern 	};
2353830a01cSAvraham Stern 
2363830a01cSAvraham Stern 	lockdep_assert_held(&mvm->mutex);
2373830a01cSAvraham Stern 
2383830a01cSAvraham Stern 	cmd.valid_flags = 0;
2393830a01cSAvraham Stern 
2403830a01cSAvraham Stern 	if (params) {
2413830a01cSAvraham Stern 		if (params->lci_len + 2 > sizeof(cmd.lci_buf) ||
2423830a01cSAvraham Stern 		    params->civicloc_len + 2 > sizeof(cmd.civic_buf)) {
2433830a01cSAvraham Stern 			IWL_ERR(mvm,
2443830a01cSAvraham Stern 				"LCI/civic data too big (lci=%zd, civic=%zd)\n",
2453830a01cSAvraham Stern 				params->lci_len, params->civicloc_len);
2463830a01cSAvraham Stern 			return -ENOBUFS;
2473830a01cSAvraham Stern 		}
2483830a01cSAvraham Stern 
2493830a01cSAvraham Stern 		cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT;
2503830a01cSAvraham Stern 		cmd.lci_buf[1] = params->lci_len;
2513830a01cSAvraham Stern 		memcpy(cmd.lci_buf + 2, params->lci, params->lci_len);
2523830a01cSAvraham Stern 		cmd.lci_len = params->lci_len + 2;
2533830a01cSAvraham Stern 
2543830a01cSAvraham Stern 		cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT;
2553830a01cSAvraham Stern 		cmd.civic_buf[1] = params->civicloc_len;
2563830a01cSAvraham Stern 		memcpy(cmd.civic_buf + 2, params->civicloc,
2573830a01cSAvraham Stern 		       params->civicloc_len);
2583830a01cSAvraham Stern 		cmd.civic_len = params->civicloc_len + 2;
2593830a01cSAvraham Stern 
2603830a01cSAvraham Stern 		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI |
2613830a01cSAvraham Stern 			IWL_RESPONDER_DYN_CFG_VALID_CIVIC;
2623830a01cSAvraham Stern 	}
2633830a01cSAvraham Stern 
2643830a01cSAvraham Stern 	if (hltk_data) {
2653830a01cSAvraham Stern 		if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) {
2663830a01cSAvraham Stern 			IWL_ERR(mvm, "invalid cipher: %u\n",
2673830a01cSAvraham Stern 				hltk_data->cipher);
2683830a01cSAvraham Stern 			return -EINVAL;
2693830a01cSAvraham Stern 		}
2703830a01cSAvraham Stern 
2713830a01cSAvraham Stern 		cmd.cipher = hltk_data->cipher;
2723830a01cSAvraham Stern 		memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr));
2733830a01cSAvraham Stern 		memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf));
2743830a01cSAvraham Stern 		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA;
2753830a01cSAvraham Stern 	}
2763830a01cSAvraham Stern 
2773830a01cSAvraham Stern 	return iwl_mvm_send_cmd(mvm, &hcmd);
2783830a01cSAvraham Stern }
2793830a01cSAvraham Stern 
2803830a01cSAvraham Stern static int
iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_ftm_responder_params * params)2813830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
2823830a01cSAvraham Stern 				  struct ieee80211_vif *vif,
2833830a01cSAvraham Stern 				  struct ieee80211_ftm_responder_params *params)
2843830a01cSAvraham Stern {
2853830a01cSAvraham Stern 	int ret;
286971cbe50SJohannes Berg 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
287971cbe50SJohannes Berg 					   WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
288971cbe50SJohannes Berg 					   2);
2893830a01cSAvraham Stern 
2903830a01cSAvraham Stern 	switch (cmd_ver) {
2913830a01cSAvraham Stern 	case 2:
2923830a01cSAvraham Stern 		ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif,
2933830a01cSAvraham Stern 						       params);
2943830a01cSAvraham Stern 		break;
2953830a01cSAvraham Stern 	case 3:
2963830a01cSAvraham Stern 		ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif,
2973830a01cSAvraham Stern 						       params, NULL);
2983830a01cSAvraham Stern 		break;
2993830a01cSAvraham Stern 	default:
3003830a01cSAvraham Stern 		IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n",
3013830a01cSAvraham Stern 			cmd_ver);
30296850912SAndrei Otcheretianski 		ret = -EOPNOTSUPP;
3033830a01cSAvraham Stern 	}
3043830a01cSAvraham Stern 
3053830a01cSAvraham Stern 	return ret;
3063830a01cSAvraham Stern }
3073830a01cSAvraham Stern 
iwl_mvm_resp_del_pasn_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_mvm_pasn_sta * sta)308890d814bSAvraham Stern static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
309890d814bSAvraham Stern 				      struct ieee80211_vif *vif,
310890d814bSAvraham Stern 				      struct iwl_mvm_pasn_sta *sta)
311890d814bSAvraham Stern {
312890d814bSAvraham Stern 	list_del(&sta->list);
313ff268761SAvraham Stern 
314619a900fSIlan Peer 	if (sta->keyconf.keylen)
315619a900fSIlan Peer 		iwl_mvm_sec_key_del_pasn(mvm, vif, BIT(sta->int_sta.sta_id),
316619a900fSIlan Peer 					 &sta->keyconf);
317619a900fSIlan Peer 
318ff268761SAvraham Stern 	if (iwl_mvm_has_mld_api(mvm->fw))
319ff268761SAvraham Stern 		iwl_mvm_mld_rm_sta_id(mvm, sta->int_sta.sta_id);
320ff268761SAvraham Stern 	else
321890d814bSAvraham Stern 		iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
322ff268761SAvraham Stern 
323890d814bSAvraham Stern 	iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
324890d814bSAvraham Stern 	kfree(sta);
325890d814bSAvraham Stern }
326890d814bSAvraham Stern 
iwl_mvm_ftm_respoder_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)327be82ecd3SAvraham Stern int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
328be82ecd3SAvraham Stern 				      struct ieee80211_vif *vif,
329be82ecd3SAvraham Stern 				      u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
330be82ecd3SAvraham Stern 				      u8 *hltk, u32 hltk_len)
331be82ecd3SAvraham Stern {
332be82ecd3SAvraham Stern 	int ret;
33368ad2474SAvraham Stern 	struct iwl_mvm_pasn_sta *sta = NULL;
334890d814bSAvraham Stern 	struct iwl_mvm_pasn_hltk_data hltk_data = {
335890d814bSAvraham Stern 		.addr = addr,
336890d814bSAvraham Stern 		.hltk = hltk,
337890d814bSAvraham Stern 	};
33826c680b7SAvraham Stern 	struct iwl_mvm_pasn_hltk_data *hltk_data_ptr = NULL;
33926c680b7SAvraham Stern 
340971cbe50SJohannes Berg 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
341971cbe50SJohannes Berg 					   WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
342971cbe50SJohannes Berg 					   2);
343be82ecd3SAvraham Stern 
344be82ecd3SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
345be82ecd3SAvraham Stern 
346890d814bSAvraham Stern 	if (cmd_ver < 3) {
347890d814bSAvraham Stern 		IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
34896850912SAndrei Otcheretianski 		return -EOPNOTSUPP;
349890d814bSAvraham Stern 	}
350890d814bSAvraham Stern 
35126c680b7SAvraham Stern 	if ((!hltk || !hltk_len) && (!tk || !tk_len)) {
35226c680b7SAvraham Stern 		IWL_ERR(mvm, "TK and HLTK not set\n");
35326c680b7SAvraham Stern 		return -EINVAL;
35426c680b7SAvraham Stern 	}
35526c680b7SAvraham Stern 
35626c680b7SAvraham Stern 	if (hltk && hltk_len) {
3574d951e26SIlan Peer 		if (!fw_has_capa(&mvm->fw->ucode_capa,
3584d951e26SIlan Peer 				 IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) {
3594d951e26SIlan Peer 			IWL_ERR(mvm, "No support for secure LTF measurement\n");
3604d951e26SIlan Peer 			return -EINVAL;
3614d951e26SIlan Peer 		}
3624d951e26SIlan Peer 
363890d814bSAvraham Stern 		hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
364890d814bSAvraham Stern 		if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
365890d814bSAvraham Stern 			IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
366890d814bSAvraham Stern 			return -EINVAL;
367890d814bSAvraham Stern 		}
368890d814bSAvraham Stern 
36926c680b7SAvraham Stern 		hltk_data_ptr = &hltk_data;
37026c680b7SAvraham Stern 	}
37126c680b7SAvraham Stern 
37268ad2474SAvraham Stern 	if (tk && tk_len) {
373619a900fSIlan Peer 		sta = kzalloc(sizeof(*sta) + tk_len, GFP_KERNEL);
374be82ecd3SAvraham Stern 		if (!sta)
375be82ecd3SAvraham Stern 			return -ENOBUFS;
376be82ecd3SAvraham Stern 
37768ad2474SAvraham Stern 		ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
378619a900fSIlan Peer 					   cipher, tk, tk_len, &sta->keyconf);
379be82ecd3SAvraham Stern 		if (ret) {
380be82ecd3SAvraham Stern 			kfree(sta);
381be82ecd3SAvraham Stern 			return ret;
382be82ecd3SAvraham Stern 		}
383be82ecd3SAvraham Stern 
384be82ecd3SAvraham Stern 		memcpy(sta->addr, addr, ETH_ALEN);
385be82ecd3SAvraham Stern 		list_add_tail(&sta->list, &mvm->resp_pasn_list);
386bebc14dbSAvraham Stern 	}
387bebc14dbSAvraham Stern 
38826c680b7SAvraham Stern 	ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, hltk_data_ptr);
389bebc14dbSAvraham Stern 	if (ret && sta)
390bebc14dbSAvraham Stern 		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
391bebc14dbSAvraham Stern 
392bebc14dbSAvraham Stern 	return ret;
393be82ecd3SAvraham Stern }
394be82ecd3SAvraham Stern 
iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm * mvm,struct ieee80211_vif * vif,u8 * addr)395be82ecd3SAvraham Stern int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
396be82ecd3SAvraham Stern 				     struct ieee80211_vif *vif, u8 *addr)
397be82ecd3SAvraham Stern {
398be82ecd3SAvraham Stern 	struct iwl_mvm_pasn_sta *sta, *prev;
399be82ecd3SAvraham Stern 
400be82ecd3SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
401be82ecd3SAvraham Stern 
402be82ecd3SAvraham Stern 	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
403be82ecd3SAvraham Stern 		if (!memcmp(sta->addr, addr, ETH_ALEN)) {
404be82ecd3SAvraham Stern 			iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
405be82ecd3SAvraham Stern 			return 0;
406be82ecd3SAvraham Stern 		}
407be82ecd3SAvraham Stern 	}
408be82ecd3SAvraham Stern 
409be82ecd3SAvraham Stern 	IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
410be82ecd3SAvraham Stern 	return -EINVAL;
411be82ecd3SAvraham Stern }
412be82ecd3SAvraham Stern 
iwl_mvm_ftm_start_responder(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * bss_conf)413fd940de7SAvraham Stern int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
414fd940de7SAvraham Stern 				struct ieee80211_bss_conf *bss_conf)
415b73f9a4aSJohannes Berg {
416b73f9a4aSJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
417b73f9a4aSJohannes Berg 	struct ieee80211_ftm_responder_params *params;
418b73f9a4aSJohannes Berg 	struct ieee80211_chanctx_conf ctx, *pctx;
419b73f9a4aSJohannes Berg 	u16 *phy_ctxt_id;
420b73f9a4aSJohannes Berg 	struct iwl_mvm_phy_ctxt *phy_ctxt;
421b73f9a4aSJohannes Berg 	int ret;
422b73f9a4aSJohannes Berg 
423fd940de7SAvraham Stern 	params = bss_conf->ftmr_params;
424b73f9a4aSJohannes Berg 
425b73f9a4aSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
426b73f9a4aSJohannes Berg 
427fd940de7SAvraham Stern 	if (WARN_ON_ONCE(!bss_conf->ftm_responder))
428b73f9a4aSJohannes Berg 		return -EINVAL;
429b73f9a4aSJohannes Berg 
430b73f9a4aSJohannes Berg 	if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
431b73f9a4aSJohannes Berg 	    !mvmvif->ap_ibss_active) {
432b73f9a4aSJohannes Berg 		IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
433b73f9a4aSJohannes Berg 		return -EIO;
434b73f9a4aSJohannes Berg 	}
435b73f9a4aSJohannes Berg 
436b73f9a4aSJohannes Berg 	rcu_read_lock();
437fd940de7SAvraham Stern 	pctx = rcu_dereference(bss_conf->chanctx_conf);
438b73f9a4aSJohannes Berg 	/* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care
439b73f9a4aSJohannes Berg 	 * about changes in the ctx after releasing the lock because the driver
440b73f9a4aSJohannes Berg 	 * is still protected by the mutex. */
441b73f9a4aSJohannes Berg 	ctx = *pctx;
442b73f9a4aSJohannes Berg 	phy_ctxt_id  = (u16 *)pctx->drv_priv;
443b73f9a4aSJohannes Berg 	rcu_read_unlock();
444b73f9a4aSJohannes Berg 
445b73f9a4aSJohannes Berg 	phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
44632a5690eSJohannes Berg 	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def, &ctx.ap,
447b73f9a4aSJohannes Berg 				       ctx.rx_chains_static,
448b73f9a4aSJohannes Berg 				       ctx.rx_chains_dynamic);
449b73f9a4aSJohannes Berg 	if (ret)
450b73f9a4aSJohannes Berg 		return ret;
451b73f9a4aSJohannes Berg 
452fd940de7SAvraham Stern 	ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def, bss_conf);
453b73f9a4aSJohannes Berg 	if (ret)
454b73f9a4aSJohannes Berg 		return ret;
455b73f9a4aSJohannes Berg 
456b73f9a4aSJohannes Berg 	if (params)
457b73f9a4aSJohannes Berg 		ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
458b73f9a4aSJohannes Berg 
459b73f9a4aSJohannes Berg 	return ret;
460b73f9a4aSJohannes Berg }
461b73f9a4aSJohannes Berg 
iwl_mvm_ftm_responder_clear(struct iwl_mvm * mvm,struct ieee80211_vif * vif)462be82ecd3SAvraham Stern void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
463be82ecd3SAvraham Stern 				 struct ieee80211_vif *vif)
464be82ecd3SAvraham Stern {
465be82ecd3SAvraham Stern 	struct iwl_mvm_pasn_sta *sta, *prev;
466be82ecd3SAvraham Stern 
467be82ecd3SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
468be82ecd3SAvraham Stern 
469be82ecd3SAvraham Stern 	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
470be82ecd3SAvraham Stern 		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
471be82ecd3SAvraham Stern }
472be82ecd3SAvraham Stern 
iwl_mvm_ftm_restart_responder(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_bss_conf * bss_conf)473b73f9a4aSJohannes Berg void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
474fd940de7SAvraham Stern 				   struct ieee80211_vif *vif,
475fd940de7SAvraham Stern 				   struct ieee80211_bss_conf *bss_conf)
476b73f9a4aSJohannes Berg {
477fd940de7SAvraham Stern 	if (!bss_conf->ftm_responder)
478b73f9a4aSJohannes Berg 		return;
479b73f9a4aSJohannes Berg 
480be82ecd3SAvraham Stern 	iwl_mvm_ftm_responder_clear(mvm, vif);
481fd940de7SAvraham Stern 	iwl_mvm_ftm_start_responder(mvm, vif, bss_conf);
482b73f9a4aSJohannes Berg }
483b73f9a4aSJohannes Berg 
iwl_mvm_ftm_responder_stats(struct iwl_mvm * mvm,struct iwl_rx_cmd_buffer * rxb)484b73f9a4aSJohannes Berg void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
485b73f9a4aSJohannes Berg 				 struct iwl_rx_cmd_buffer *rxb)
486b73f9a4aSJohannes Berg {
487b73f9a4aSJohannes Berg 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
488b73f9a4aSJohannes Berg 	struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
489b73f9a4aSJohannes Berg 	struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
490b73f9a4aSJohannes Berg 	u32 flags = le32_to_cpu(resp->flags);
491b73f9a4aSJohannes Berg 
492b73f9a4aSJohannes Berg 	if (resp->success_ftm == resp->ftm_per_burst)
493b73f9a4aSJohannes Berg 		stats->success_num++;
494b73f9a4aSJohannes Berg 	else if (resp->success_ftm >= 2)
495b73f9a4aSJohannes Berg 		stats->partial_num++;
496b73f9a4aSJohannes Berg 	else
497b73f9a4aSJohannes Berg 		stats->failed_num++;
498b73f9a4aSJohannes Berg 
499b73f9a4aSJohannes Berg 	if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
500b73f9a4aSJohannes Berg 	    (flags & FTM_RESP_STAT_ASAP_RESP))
501b73f9a4aSJohannes Berg 		stats->asap_num++;
502b73f9a4aSJohannes Berg 
503b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
504b73f9a4aSJohannes Berg 		stats->non_asap_num++;
505b73f9a4aSJohannes Berg 
506b73f9a4aSJohannes Berg 	stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
507b73f9a4aSJohannes Berg 
508b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
509b73f9a4aSJohannes Berg 		stats->unknown_triggers_num++;
510b73f9a4aSJohannes Berg 
511b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_DUP)
512b73f9a4aSJohannes Berg 		stats->reschedule_requests_num++;
513b73f9a4aSJohannes Berg 
514b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
515b73f9a4aSJohannes Berg 		stats->out_of_window_triggers_num++;
516b73f9a4aSJohannes Berg }
517