xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/ftm-responder.c (revision a4128aad8503277614f2d214011ef60a19447b83)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2015-2017 Intel Deutschland GmbH
4*a4128aadSBjoern A. Zeeb  * Copyright (C) 2018-2024 Intel Corporation
5bfcc09ddSBjoern A. Zeeb  */
6bfcc09ddSBjoern A. Zeeb #include <net/cfg80211.h>
7bfcc09ddSBjoern A. Zeeb #include <linux/etherdevice.h>
8bfcc09ddSBjoern A. Zeeb #include "mvm.h"
9bfcc09ddSBjoern A. Zeeb #include "constants.h"
10bfcc09ddSBjoern A. Zeeb 
11bfcc09ddSBjoern A. Zeeb struct iwl_mvm_pasn_sta {
12bfcc09ddSBjoern A. Zeeb 	struct list_head list;
13bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_int_sta int_sta;
14bfcc09ddSBjoern A. Zeeb 	u8 addr[ETH_ALEN];
15*a4128aadSBjoern A. Zeeb 
16*a4128aadSBjoern A. Zeeb 	/* must be last as it followed by buffer holding the key */
17*a4128aadSBjoern A. Zeeb 	struct ieee80211_key_conf keyconf;
18bfcc09ddSBjoern A. Zeeb };
19bfcc09ddSBjoern A. Zeeb 
20bfcc09ddSBjoern A. Zeeb struct iwl_mvm_pasn_hltk_data {
21bfcc09ddSBjoern A. Zeeb 	u8 *addr;
22bfcc09ddSBjoern A. Zeeb 	u8 cipher;
23bfcc09ddSBjoern A. Zeeb 	u8 *hltk;
24bfcc09ddSBjoern A. Zeeb };
25bfcc09ddSBjoern A. Zeeb 
26bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
27bfcc09ddSBjoern A. Zeeb 					   u8 *bw, u8 *ctrl_ch_position)
28bfcc09ddSBjoern A. Zeeb {
29bfcc09ddSBjoern A. Zeeb 	switch (chandef->width) {
30bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20_NOHT:
31bfcc09ddSBjoern A. Zeeb 		*bw = IWL_TOF_BW_20_LEGACY;
32bfcc09ddSBjoern A. Zeeb 		break;
33bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20:
34bfcc09ddSBjoern A. Zeeb 		*bw = IWL_TOF_BW_20_HT;
35bfcc09ddSBjoern A. Zeeb 		break;
36bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_40:
37bfcc09ddSBjoern A. Zeeb 		*bw = IWL_TOF_BW_40;
38bfcc09ddSBjoern A. Zeeb 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
39bfcc09ddSBjoern A. Zeeb 		break;
40bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80:
41bfcc09ddSBjoern A. Zeeb 		*bw = IWL_TOF_BW_80;
42bfcc09ddSBjoern A. Zeeb 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
43bfcc09ddSBjoern A. Zeeb 		break;
44bfcc09ddSBjoern A. Zeeb 	default:
45*a4128aadSBjoern A. Zeeb 		return -EOPNOTSUPP;
46bfcc09ddSBjoern A. Zeeb 	}
47bfcc09ddSBjoern A. Zeeb 
48bfcc09ddSBjoern A. Zeeb 	return 0;
49bfcc09ddSBjoern A. Zeeb }
50bfcc09ddSBjoern A. Zeeb 
51bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
52bfcc09ddSBjoern A. Zeeb 					   u8 *format_bw, u8 *ctrl_ch_position,
53bfcc09ddSBjoern A. Zeeb 					   u8 cmd_ver)
54bfcc09ddSBjoern A. Zeeb {
55bfcc09ddSBjoern A. Zeeb 	switch (chandef->width) {
56bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20_NOHT:
57bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
58bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
59bfcc09ddSBjoern A. Zeeb 		break;
60bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20:
61bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
62bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
63bfcc09ddSBjoern A. Zeeb 		break;
64bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_40:
65bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
66bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
67bfcc09ddSBjoern A. Zeeb 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
68bfcc09ddSBjoern A. Zeeb 		break;
69bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80:
70bfcc09ddSBjoern A. Zeeb 		*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
71bfcc09ddSBjoern A. Zeeb 		*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
72bfcc09ddSBjoern A. Zeeb 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
73bfcc09ddSBjoern A. Zeeb 		break;
74bfcc09ddSBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_160:
75bfcc09ddSBjoern A. Zeeb 		if (cmd_ver >= 9) {
76bfcc09ddSBjoern A. Zeeb 			*format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
77bfcc09ddSBjoern A. Zeeb 			*format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
78bfcc09ddSBjoern A. Zeeb 			*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
79bfcc09ddSBjoern A. Zeeb 			break;
80bfcc09ddSBjoern A. Zeeb 		}
81bfcc09ddSBjoern A. Zeeb 		fallthrough;
82bfcc09ddSBjoern A. Zeeb 	default:
83*a4128aadSBjoern A. Zeeb 		return -EOPNOTSUPP;
84bfcc09ddSBjoern A. Zeeb 	}
85bfcc09ddSBjoern A. Zeeb 
86bfcc09ddSBjoern A. Zeeb 	return 0;
87bfcc09ddSBjoern A. Zeeb }
88bfcc09ddSBjoern A. Zeeb 
89bfcc09ddSBjoern A. Zeeb static void
90bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_responder_set_ndp(struct iwl_mvm *mvm,
91*a4128aadSBjoern A. Zeeb 			      struct iwl_tof_responder_config_cmd *cmd)
92bfcc09ddSBjoern A. Zeeb {
93bfcc09ddSBjoern A. Zeeb 	/* Up to 2 R2I STS are allowed on the responder */
94bfcc09ddSBjoern A. Zeeb 	u32 r2i_max_sts = IWL_MVM_FTM_R2I_MAX_STS < 2 ?
95bfcc09ddSBjoern A. Zeeb 		IWL_MVM_FTM_R2I_MAX_STS : 1;
96bfcc09ddSBjoern A. Zeeb 
97bfcc09ddSBjoern A. Zeeb 	cmd->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP |
98bfcc09ddSBjoern A. Zeeb 		(r2i_max_sts << IWL_RESPONDER_STS_POS) |
99bfcc09ddSBjoern A. Zeeb 		(IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS);
100bfcc09ddSBjoern A. Zeeb 	cmd->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP |
101bfcc09ddSBjoern A. Zeeb 		(IWL_MVM_FTM_I2R_MAX_STS << IWL_RESPONDER_STS_POS) |
102bfcc09ddSBjoern A. Zeeb 		(IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS);
103bfcc09ddSBjoern A. Zeeb 	cmd->cmd_valid_fields |=
104bfcc09ddSBjoern A. Zeeb 		cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_NDP_PARAMS);
105bfcc09ddSBjoern A. Zeeb }
106bfcc09ddSBjoern A. Zeeb 
107bfcc09ddSBjoern A. Zeeb static int
108bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
109bfcc09ddSBjoern A. Zeeb 			  struct ieee80211_vif *vif,
1109af1bba4SBjoern A. Zeeb 			  struct cfg80211_chan_def *chandef,
1119af1bba4SBjoern A. Zeeb 			  struct ieee80211_bss_conf *link_conf)
112bfcc09ddSBjoern A. Zeeb {
113d9836fb4SBjoern A. Zeeb 	u32 cmd_id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_CONFIG_CMD);
114bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
115bfcc09ddSBjoern A. Zeeb 	/*
116bfcc09ddSBjoern A. Zeeb 	 * The command structure is the same for versions 6, 7 and 8 (only the
117bfcc09ddSBjoern A. Zeeb 	 * field interpretation is different), so the same struct can be use
118bfcc09ddSBjoern A. Zeeb 	 * for all cases.
119bfcc09ddSBjoern A. Zeeb 	 */
120*a4128aadSBjoern A. Zeeb 	struct iwl_tof_responder_config_cmd cmd = {
121bfcc09ddSBjoern A. Zeeb 		.channel_num = chandef->chan->hw_value,
122bfcc09ddSBjoern A. Zeeb 		.cmd_valid_fields =
123bfcc09ddSBjoern A. Zeeb 			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
124bfcc09ddSBjoern A. Zeeb 				    IWL_TOF_RESPONDER_CMD_VALID_BSSID |
125bfcc09ddSBjoern A. Zeeb 				    IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
1269af1bba4SBjoern A. Zeeb 		.sta_id = mvmvif->link[link_conf->link_id]->bcast_sta.sta_id,
127bfcc09ddSBjoern A. Zeeb 	};
128d9836fb4SBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 6);
129bfcc09ddSBjoern A. Zeeb 	int err;
130bfcc09ddSBjoern A. Zeeb 	int cmd_size;
131bfcc09ddSBjoern A. Zeeb 
132bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
133bfcc09ddSBjoern A. Zeeb 
134*a4128aadSBjoern A. Zeeb 	if (cmd_ver == 10) {
135*a4128aadSBjoern A. Zeeb 		cmd.band =
136*a4128aadSBjoern A. Zeeb 			iwl_mvm_phy_band_from_nl80211(chandef->chan->band);
137*a4128aadSBjoern A. Zeeb 	}
138*a4128aadSBjoern A. Zeeb 
139bfcc09ddSBjoern A. Zeeb 	/* Use a default of bss_color=1 for now */
140*a4128aadSBjoern A. Zeeb 	if (cmd_ver >= 9) {
141bfcc09ddSBjoern A. Zeeb 		cmd.cmd_valid_fields |=
142bfcc09ddSBjoern A. Zeeb 			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_BSS_COLOR |
143bfcc09ddSBjoern A. Zeeb 				    IWL_TOF_RESPONDER_CMD_VALID_MIN_MAX_TIME_BETWEEN_MSR);
144bfcc09ddSBjoern A. Zeeb 		cmd.bss_color = 1;
145bfcc09ddSBjoern A. Zeeb 		cmd.min_time_between_msr =
146bfcc09ddSBjoern A. Zeeb 			cpu_to_le16(IWL_MVM_FTM_NON_TB_MIN_TIME_BETWEEN_MSR);
147bfcc09ddSBjoern A. Zeeb 		cmd.max_time_between_msr =
148bfcc09ddSBjoern A. Zeeb 			cpu_to_le16(IWL_MVM_FTM_NON_TB_MAX_TIME_BETWEEN_MSR);
149bfcc09ddSBjoern A. Zeeb 		cmd_size = sizeof(struct iwl_tof_responder_config_cmd_v9);
150bfcc09ddSBjoern A. Zeeb 	} else {
151bfcc09ddSBjoern A. Zeeb 		/* All versions up to version 8 have the same size */
152bfcc09ddSBjoern A. Zeeb 		cmd_size = sizeof(struct iwl_tof_responder_config_cmd_v8);
153bfcc09ddSBjoern A. Zeeb 	}
154bfcc09ddSBjoern A. Zeeb 
155bfcc09ddSBjoern A. Zeeb 	if (cmd_ver >= 8)
156*a4128aadSBjoern A. Zeeb 		iwl_mvm_ftm_responder_set_ndp(mvm, (void *)&cmd);
157bfcc09ddSBjoern A. Zeeb 
158bfcc09ddSBjoern A. Zeeb 	if (cmd_ver >= 7)
159bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
160bfcc09ddSBjoern A. Zeeb 						      &cmd.ctrl_ch_position,
161bfcc09ddSBjoern A. Zeeb 						      cmd_ver);
162bfcc09ddSBjoern A. Zeeb 	else
163bfcc09ddSBjoern A. Zeeb 		err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
164bfcc09ddSBjoern A. Zeeb 						      &cmd.ctrl_ch_position);
165bfcc09ddSBjoern A. Zeeb 
166bfcc09ddSBjoern A. Zeeb 	if (err) {
167bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to set responder bandwidth\n");
168bfcc09ddSBjoern A. Zeeb 		return err;
169bfcc09ddSBjoern A. Zeeb 	}
170bfcc09ddSBjoern A. Zeeb 
171bfcc09ddSBjoern A. Zeeb 	memcpy(cmd.bssid, vif->addr, ETH_ALEN);
172bfcc09ddSBjoern A. Zeeb 
173d9836fb4SBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd);
174bfcc09ddSBjoern A. Zeeb }
175bfcc09ddSBjoern A. Zeeb 
176bfcc09ddSBjoern A. Zeeb static int
177bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm,
178bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
179bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_ftm_responder_params *params)
180bfcc09ddSBjoern A. Zeeb {
181bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_responder_dyn_config_cmd_v2 cmd = {
182bfcc09ddSBjoern A. Zeeb 		.lci_len = cpu_to_le32(params->lci_len + 2),
183bfcc09ddSBjoern A. Zeeb 		.civic_len = cpu_to_le32(params->civicloc_len + 2),
184bfcc09ddSBjoern A. Zeeb 	};
185bfcc09ddSBjoern A. Zeeb 	u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0};
186bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
187d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
188bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
189bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
190bfcc09ddSBjoern A. Zeeb 		.data[1] = &data,
191bfcc09ddSBjoern A. Zeeb 		/* .len[1] set later */
192bfcc09ddSBjoern A. Zeeb 		/* may not be able to DMA from stack */
193bfcc09ddSBjoern A. Zeeb 		.dataflags[1] = IWL_HCMD_DFL_DUP,
194bfcc09ddSBjoern A. Zeeb 	};
195bfcc09ddSBjoern A. Zeeb 	u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
196bfcc09ddSBjoern A. Zeeb 	u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
197bfcc09ddSBjoern A. Zeeb 	u8 *pos = data;
198bfcc09ddSBjoern A. Zeeb 
199bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
200bfcc09ddSBjoern A. Zeeb 
201bfcc09ddSBjoern A. Zeeb 	if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
202bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
203bfcc09ddSBjoern A. Zeeb 			params->lci_len, params->civicloc_len);
204bfcc09ddSBjoern A. Zeeb 		return -ENOBUFS;
205bfcc09ddSBjoern A. Zeeb 	}
206bfcc09ddSBjoern A. Zeeb 
207bfcc09ddSBjoern A. Zeeb 	pos[0] = WLAN_EID_MEASURE_REPORT;
208bfcc09ddSBjoern A. Zeeb 	pos[1] = params->lci_len;
209bfcc09ddSBjoern A. Zeeb 	memcpy(pos + 2, params->lci, params->lci_len);
210bfcc09ddSBjoern A. Zeeb 
211bfcc09ddSBjoern A. Zeeb 	pos += aligned_lci_len;
212bfcc09ddSBjoern A. Zeeb 	pos[0] = WLAN_EID_MEASURE_REPORT;
213bfcc09ddSBjoern A. Zeeb 	pos[1] = params->civicloc_len;
214bfcc09ddSBjoern A. Zeeb 	memcpy(pos + 2, params->civicloc, params->civicloc_len);
215bfcc09ddSBjoern A. Zeeb 
216bfcc09ddSBjoern A. Zeeb 	hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
217bfcc09ddSBjoern A. Zeeb 
218bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_send_cmd(mvm, &hcmd);
219bfcc09ddSBjoern A. Zeeb }
220bfcc09ddSBjoern A. Zeeb 
221bfcc09ddSBjoern A. Zeeb static int
222bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm,
223bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif,
224bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_ftm_responder_params *params,
225bfcc09ddSBjoern A. Zeeb 				 struct iwl_mvm_pasn_hltk_data *hltk_data)
226bfcc09ddSBjoern A. Zeeb {
227bfcc09ddSBjoern A. Zeeb 	struct iwl_tof_responder_dyn_config_cmd cmd;
228bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd hcmd = {
229d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
230bfcc09ddSBjoern A. Zeeb 		.data[0] = &cmd,
231bfcc09ddSBjoern A. Zeeb 		.len[0] = sizeof(cmd),
232bfcc09ddSBjoern A. Zeeb 		/* may not be able to DMA from stack */
233bfcc09ddSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_DUP,
234bfcc09ddSBjoern A. Zeeb 	};
235bfcc09ddSBjoern A. Zeeb 
236bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
237bfcc09ddSBjoern A. Zeeb 
238bfcc09ddSBjoern A. Zeeb 	cmd.valid_flags = 0;
239bfcc09ddSBjoern A. Zeeb 
240bfcc09ddSBjoern A. Zeeb 	if (params) {
241bfcc09ddSBjoern A. Zeeb 		if (params->lci_len + 2 > sizeof(cmd.lci_buf) ||
242bfcc09ddSBjoern A. Zeeb 		    params->civicloc_len + 2 > sizeof(cmd.civic_buf)) {
243bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm,
244bfcc09ddSBjoern A. Zeeb 				"LCI/civic data too big (lci=%zd, civic=%zd)\n",
245bfcc09ddSBjoern A. Zeeb 				params->lci_len, params->civicloc_len);
246bfcc09ddSBjoern A. Zeeb 			return -ENOBUFS;
247bfcc09ddSBjoern A. Zeeb 		}
248bfcc09ddSBjoern A. Zeeb 
249bfcc09ddSBjoern A. Zeeb 		cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT;
250bfcc09ddSBjoern A. Zeeb 		cmd.lci_buf[1] = params->lci_len;
251bfcc09ddSBjoern A. Zeeb 		memcpy(cmd.lci_buf + 2, params->lci, params->lci_len);
252bfcc09ddSBjoern A. Zeeb 		cmd.lci_len = params->lci_len + 2;
253bfcc09ddSBjoern A. Zeeb 
254bfcc09ddSBjoern A. Zeeb 		cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT;
255bfcc09ddSBjoern A. Zeeb 		cmd.civic_buf[1] = params->civicloc_len;
256bfcc09ddSBjoern A. Zeeb 		memcpy(cmd.civic_buf + 2, params->civicloc,
257bfcc09ddSBjoern A. Zeeb 		       params->civicloc_len);
258bfcc09ddSBjoern A. Zeeb 		cmd.civic_len = params->civicloc_len + 2;
259bfcc09ddSBjoern A. Zeeb 
260bfcc09ddSBjoern A. Zeeb 		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI |
261bfcc09ddSBjoern A. Zeeb 			IWL_RESPONDER_DYN_CFG_VALID_CIVIC;
262bfcc09ddSBjoern A. Zeeb 	}
263bfcc09ddSBjoern A. Zeeb 
264bfcc09ddSBjoern A. Zeeb 	if (hltk_data) {
265bfcc09ddSBjoern A. Zeeb 		if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) {
266bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "invalid cipher: %u\n",
267bfcc09ddSBjoern A. Zeeb 				hltk_data->cipher);
268bfcc09ddSBjoern A. Zeeb 			return -EINVAL;
269bfcc09ddSBjoern A. Zeeb 		}
270bfcc09ddSBjoern A. Zeeb 
271bfcc09ddSBjoern A. Zeeb 		cmd.cipher = hltk_data->cipher;
272bfcc09ddSBjoern A. Zeeb 		memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr));
273bfcc09ddSBjoern A. Zeeb 		memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf));
274bfcc09ddSBjoern A. Zeeb 		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA;
275bfcc09ddSBjoern A. Zeeb 	}
276bfcc09ddSBjoern A. Zeeb 
277bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_send_cmd(mvm, &hcmd);
278bfcc09ddSBjoern A. Zeeb }
279bfcc09ddSBjoern A. Zeeb 
280bfcc09ddSBjoern A. Zeeb static int
281bfcc09ddSBjoern A. Zeeb iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
282bfcc09ddSBjoern A. Zeeb 				  struct ieee80211_vif *vif,
283bfcc09ddSBjoern A. Zeeb 				  struct ieee80211_ftm_responder_params *params)
284bfcc09ddSBjoern A. Zeeb {
285bfcc09ddSBjoern A. Zeeb 	int ret;
286d9836fb4SBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
287d9836fb4SBjoern A. Zeeb 					   WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
288d9836fb4SBjoern A. Zeeb 					   2);
289bfcc09ddSBjoern A. Zeeb 
290bfcc09ddSBjoern A. Zeeb 	switch (cmd_ver) {
291bfcc09ddSBjoern A. Zeeb 	case 2:
292bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif,
293bfcc09ddSBjoern A. Zeeb 						       params);
294bfcc09ddSBjoern A. Zeeb 		break;
295bfcc09ddSBjoern A. Zeeb 	case 3:
296bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif,
297bfcc09ddSBjoern A. Zeeb 						       params, NULL);
298bfcc09ddSBjoern A. Zeeb 		break;
299bfcc09ddSBjoern A. Zeeb 	default:
300bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n",
301bfcc09ddSBjoern A. Zeeb 			cmd_ver);
302*a4128aadSBjoern A. Zeeb 		ret = -EOPNOTSUPP;
303bfcc09ddSBjoern A. Zeeb 	}
304bfcc09ddSBjoern A. Zeeb 
305bfcc09ddSBjoern A. Zeeb 	return ret;
306bfcc09ddSBjoern A. Zeeb }
307bfcc09ddSBjoern A. Zeeb 
308bfcc09ddSBjoern A. Zeeb static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
309bfcc09ddSBjoern A. Zeeb 				      struct ieee80211_vif *vif,
310bfcc09ddSBjoern A. Zeeb 				      struct iwl_mvm_pasn_sta *sta)
311bfcc09ddSBjoern A. Zeeb {
312bfcc09ddSBjoern A. Zeeb 	list_del(&sta->list);
313*a4128aadSBjoern A. Zeeb 
314*a4128aadSBjoern A. Zeeb 	if (sta->keyconf.keylen)
315*a4128aadSBjoern A. Zeeb 		iwl_mvm_sec_key_del_pasn(mvm, vif, BIT(sta->int_sta.sta_id),
316*a4128aadSBjoern A. Zeeb 					 &sta->keyconf);
317*a4128aadSBjoern A. Zeeb 
318*a4128aadSBjoern A. Zeeb 	if (iwl_mvm_has_mld_api(mvm->fw))
319*a4128aadSBjoern A. Zeeb 		iwl_mvm_mld_rm_sta_id(mvm, sta->int_sta.sta_id);
320*a4128aadSBjoern A. Zeeb 	else
321bfcc09ddSBjoern A. Zeeb 		iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
322*a4128aadSBjoern A. Zeeb 
323bfcc09ddSBjoern A. Zeeb 	iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
324bfcc09ddSBjoern A. Zeeb 	kfree(sta);
325bfcc09ddSBjoern A. Zeeb }
326bfcc09ddSBjoern A. Zeeb 
3277db7bfe1SBjoern A. Zeeb #if defined(__linux__)
328bfcc09ddSBjoern A. Zeeb int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
329bfcc09ddSBjoern A. Zeeb 				      struct ieee80211_vif *vif,
330bfcc09ddSBjoern A. Zeeb 				      u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
331bfcc09ddSBjoern A. Zeeb 				      u8 *hltk, u32 hltk_len)
332bfcc09ddSBjoern A. Zeeb {
333bfcc09ddSBjoern A. Zeeb 	int ret;
334bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_pasn_sta *sta = NULL;
335bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_pasn_hltk_data hltk_data = {
336bfcc09ddSBjoern A. Zeeb 		.addr = addr,
337bfcc09ddSBjoern A. Zeeb 		.hltk = hltk,
338bfcc09ddSBjoern A. Zeeb 	};
3399af1bba4SBjoern A. Zeeb 	struct iwl_mvm_pasn_hltk_data *hltk_data_ptr = NULL;
3409af1bba4SBjoern A. Zeeb 
341d9836fb4SBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
342d9836fb4SBjoern A. Zeeb 					   WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD),
343d9836fb4SBjoern A. Zeeb 					   2);
344bfcc09ddSBjoern A. Zeeb 
345bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
346bfcc09ddSBjoern A. Zeeb 
347bfcc09ddSBjoern A. Zeeb 	if (cmd_ver < 3) {
348bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
349*a4128aadSBjoern A. Zeeb 		return -EOPNOTSUPP;
350bfcc09ddSBjoern A. Zeeb 	}
351bfcc09ddSBjoern A. Zeeb 
3529af1bba4SBjoern A. Zeeb 	if ((!hltk || !hltk_len) && (!tk || !tk_len)) {
3539af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "TK and HLTK not set\n");
3549af1bba4SBjoern A. Zeeb 		return -EINVAL;
3559af1bba4SBjoern A. Zeeb 	}
3569af1bba4SBjoern A. Zeeb 
3579af1bba4SBjoern A. Zeeb 	if (hltk && hltk_len) {
358*a4128aadSBjoern A. Zeeb 		if (!fw_has_capa(&mvm->fw->ucode_capa,
359*a4128aadSBjoern A. Zeeb 				 IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) {
360*a4128aadSBjoern A. Zeeb 			IWL_ERR(mvm, "No support for secure LTF measurement\n");
361*a4128aadSBjoern A. Zeeb 			return -EINVAL;
362*a4128aadSBjoern A. Zeeb 		}
363*a4128aadSBjoern A. Zeeb 
364bfcc09ddSBjoern A. Zeeb 		hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
365bfcc09ddSBjoern A. Zeeb 		if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
366bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
367bfcc09ddSBjoern A. Zeeb 			return -EINVAL;
368bfcc09ddSBjoern A. Zeeb 		}
369bfcc09ddSBjoern A. Zeeb 
3709af1bba4SBjoern A. Zeeb 		hltk_data_ptr = &hltk_data;
3719af1bba4SBjoern A. Zeeb 	}
3729af1bba4SBjoern A. Zeeb 
373bfcc09ddSBjoern A. Zeeb 	if (tk && tk_len) {
374*a4128aadSBjoern A. Zeeb 		sta = kzalloc(sizeof(*sta) + tk_len, GFP_KERNEL);
375bfcc09ddSBjoern A. Zeeb 		if (!sta)
376bfcc09ddSBjoern A. Zeeb 			return -ENOBUFS;
377bfcc09ddSBjoern A. Zeeb 
378bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
379*a4128aadSBjoern A. Zeeb 					   cipher, tk, tk_len, &sta->keyconf);
380bfcc09ddSBjoern A. Zeeb 		if (ret) {
381bfcc09ddSBjoern A. Zeeb 			kfree(sta);
382bfcc09ddSBjoern A. Zeeb 			return ret;
383bfcc09ddSBjoern A. Zeeb 		}
384bfcc09ddSBjoern A. Zeeb 
385bfcc09ddSBjoern A. Zeeb 		memcpy(sta->addr, addr, ETH_ALEN);
386bfcc09ddSBjoern A. Zeeb 		list_add_tail(&sta->list, &mvm->resp_pasn_list);
387bfcc09ddSBjoern A. Zeeb 	}
388bfcc09ddSBjoern A. Zeeb 
3899af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, hltk_data_ptr);
390bfcc09ddSBjoern A. Zeeb 	if (ret && sta)
391bfcc09ddSBjoern A. Zeeb 		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
392bfcc09ddSBjoern A. Zeeb 
393bfcc09ddSBjoern A. Zeeb 	return ret;
394bfcc09ddSBjoern A. Zeeb }
395bfcc09ddSBjoern A. Zeeb 
396bfcc09ddSBjoern A. Zeeb int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
397bfcc09ddSBjoern A. Zeeb 				     struct ieee80211_vif *vif, u8 *addr)
398bfcc09ddSBjoern A. Zeeb {
399bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_pasn_sta *sta, *prev;
400bfcc09ddSBjoern A. Zeeb 
401bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
402bfcc09ddSBjoern A. Zeeb 
403bfcc09ddSBjoern A. Zeeb 	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
404bfcc09ddSBjoern A. Zeeb 		if (!memcmp(sta->addr, addr, ETH_ALEN)) {
405bfcc09ddSBjoern A. Zeeb 			iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
406bfcc09ddSBjoern A. Zeeb 			return 0;
407bfcc09ddSBjoern A. Zeeb 		}
408bfcc09ddSBjoern A. Zeeb 	}
409bfcc09ddSBjoern A. Zeeb 
410bfcc09ddSBjoern A. Zeeb 	IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
411bfcc09ddSBjoern A. Zeeb 	return -EINVAL;
412bfcc09ddSBjoern A. Zeeb }
4137db7bfe1SBjoern A. Zeeb #endif
414bfcc09ddSBjoern A. Zeeb 
4159af1bba4SBjoern A. Zeeb int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
4169af1bba4SBjoern A. Zeeb 				struct ieee80211_bss_conf *bss_conf)
417bfcc09ddSBjoern A. Zeeb {
418bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
419bfcc09ddSBjoern A. Zeeb 	struct ieee80211_ftm_responder_params *params;
420bfcc09ddSBjoern A. Zeeb 	struct ieee80211_chanctx_conf ctx, *pctx;
421bfcc09ddSBjoern A. Zeeb 	u16 *phy_ctxt_id;
422bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_phy_ctxt *phy_ctxt;
423bfcc09ddSBjoern A. Zeeb 	int ret;
424bfcc09ddSBjoern A. Zeeb 
4259af1bba4SBjoern A. Zeeb 	params = bss_conf->ftmr_params;
426bfcc09ddSBjoern A. Zeeb 
427bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
428bfcc09ddSBjoern A. Zeeb 
4299af1bba4SBjoern A. Zeeb 	if (WARN_ON_ONCE(!bss_conf->ftm_responder))
430bfcc09ddSBjoern A. Zeeb 		return -EINVAL;
431bfcc09ddSBjoern A. Zeeb 
432bfcc09ddSBjoern A. Zeeb 	if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
433bfcc09ddSBjoern A. Zeeb 	    !mvmvif->ap_ibss_active) {
434bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
435bfcc09ddSBjoern A. Zeeb 		return -EIO;
436bfcc09ddSBjoern A. Zeeb 	}
437bfcc09ddSBjoern A. Zeeb 
438bfcc09ddSBjoern A. Zeeb 	rcu_read_lock();
4399af1bba4SBjoern A. Zeeb 	pctx = rcu_dereference(bss_conf->chanctx_conf);
440bfcc09ddSBjoern A. Zeeb 	/* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care
441bfcc09ddSBjoern A. Zeeb 	 * about changes in the ctx after releasing the lock because the driver
442bfcc09ddSBjoern A. Zeeb 	 * is still protected by the mutex. */
443bfcc09ddSBjoern A. Zeeb 	ctx = *pctx;
444bfcc09ddSBjoern A. Zeeb 	phy_ctxt_id  = (u16 *)pctx->drv_priv;
445bfcc09ddSBjoern A. Zeeb 	rcu_read_unlock();
446bfcc09ddSBjoern A. Zeeb 
447bfcc09ddSBjoern A. Zeeb 	phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
448*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def, &ctx.ap,
449bfcc09ddSBjoern A. Zeeb 				       ctx.rx_chains_static,
450bfcc09ddSBjoern A. Zeeb 				       ctx.rx_chains_dynamic);
451bfcc09ddSBjoern A. Zeeb 	if (ret)
452bfcc09ddSBjoern A. Zeeb 		return ret;
453bfcc09ddSBjoern A. Zeeb 
4549af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def, bss_conf);
455bfcc09ddSBjoern A. Zeeb 	if (ret)
456bfcc09ddSBjoern A. Zeeb 		return ret;
457bfcc09ddSBjoern A. Zeeb 
458bfcc09ddSBjoern A. Zeeb 	if (params)
459bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
460bfcc09ddSBjoern A. Zeeb 
461bfcc09ddSBjoern A. Zeeb 	return ret;
462bfcc09ddSBjoern A. Zeeb }
463bfcc09ddSBjoern A. Zeeb 
464bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
465bfcc09ddSBjoern A. Zeeb 				 struct ieee80211_vif *vif)
466bfcc09ddSBjoern A. Zeeb {
467bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_pasn_sta *sta, *prev;
468bfcc09ddSBjoern A. Zeeb 
469bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
470bfcc09ddSBjoern A. Zeeb 
471bfcc09ddSBjoern A. Zeeb 	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
472bfcc09ddSBjoern A. Zeeb 		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
473bfcc09ddSBjoern A. Zeeb }
474bfcc09ddSBjoern A. Zeeb 
475bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
4769af1bba4SBjoern A. Zeeb 				   struct ieee80211_vif *vif,
4779af1bba4SBjoern A. Zeeb 				   struct ieee80211_bss_conf *bss_conf)
478bfcc09ddSBjoern A. Zeeb {
4799af1bba4SBjoern A. Zeeb 	if (!bss_conf->ftm_responder)
480bfcc09ddSBjoern A. Zeeb 		return;
481bfcc09ddSBjoern A. Zeeb 
482bfcc09ddSBjoern A. Zeeb 	iwl_mvm_ftm_responder_clear(mvm, vif);
4839af1bba4SBjoern A. Zeeb 	iwl_mvm_ftm_start_responder(mvm, vif, bss_conf);
484bfcc09ddSBjoern A. Zeeb }
485bfcc09ddSBjoern A. Zeeb 
486bfcc09ddSBjoern A. Zeeb void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
487bfcc09ddSBjoern A. Zeeb 				 struct iwl_rx_cmd_buffer *rxb)
488bfcc09ddSBjoern A. Zeeb {
489bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
490bfcc09ddSBjoern A. Zeeb 	struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
491bfcc09ddSBjoern A. Zeeb 	struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
492bfcc09ddSBjoern A. Zeeb 	u32 flags = le32_to_cpu(resp->flags);
493bfcc09ddSBjoern A. Zeeb 
494bfcc09ddSBjoern A. Zeeb 	if (resp->success_ftm == resp->ftm_per_burst)
495bfcc09ddSBjoern A. Zeeb 		stats->success_num++;
496bfcc09ddSBjoern A. Zeeb 	else if (resp->success_ftm >= 2)
497bfcc09ddSBjoern A. Zeeb 		stats->partial_num++;
498bfcc09ddSBjoern A. Zeeb 	else
499bfcc09ddSBjoern A. Zeeb 		stats->failed_num++;
500bfcc09ddSBjoern A. Zeeb 
501bfcc09ddSBjoern A. Zeeb 	if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
502bfcc09ddSBjoern A. Zeeb 	    (flags & FTM_RESP_STAT_ASAP_RESP))
503bfcc09ddSBjoern A. Zeeb 		stats->asap_num++;
504bfcc09ddSBjoern A. Zeeb 
505bfcc09ddSBjoern A. Zeeb 	if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
506bfcc09ddSBjoern A. Zeeb 		stats->non_asap_num++;
507bfcc09ddSBjoern A. Zeeb 
508bfcc09ddSBjoern A. Zeeb 	stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
509bfcc09ddSBjoern A. Zeeb 
510bfcc09ddSBjoern A. Zeeb 	if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
511bfcc09ddSBjoern A. Zeeb 		stats->unknown_triggers_num++;
512bfcc09ddSBjoern A. Zeeb 
513bfcc09ddSBjoern A. Zeeb 	if (flags & FTM_RESP_STAT_DUP)
514bfcc09ddSBjoern A. Zeeb 		stats->reschedule_requests_num++;
515bfcc09ddSBjoern A. Zeeb 
516bfcc09ddSBjoern A. Zeeb 	if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
517bfcc09ddSBjoern A. Zeeb 		stats->out_of_window_triggers_num++;
518bfcc09ddSBjoern A. Zeeb }
519