xref: /linux/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c (revision 68ad24742f174735d24dc5b60653c80140d249b4)
1b73f9a4aSJohannes Berg /******************************************************************************
2b73f9a4aSJohannes Berg  *
3b73f9a4aSJohannes Berg  * This file is provided under a dual BSD/GPLv2 license.  When using or
4b73f9a4aSJohannes Berg  * redistributing this file, you may do so under either license.
5b73f9a4aSJohannes Berg  *
6b73f9a4aSJohannes Berg  * GPL LICENSE SUMMARY
7b73f9a4aSJohannes Berg  *
8b73f9a4aSJohannes Berg  * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
9be82ecd3SAvraham Stern  * Copyright (C) 2018 - 2020 Intel Corporation
10b73f9a4aSJohannes Berg  *
11b73f9a4aSJohannes Berg  * This program is free software; you can redistribute it and/or modify
12b73f9a4aSJohannes Berg  * it under the terms of version 2 of the GNU General Public License as
13b73f9a4aSJohannes Berg  * published by the Free Software Foundation.
14b73f9a4aSJohannes Berg  *
15b73f9a4aSJohannes Berg  * This program is distributed in the hope that it will be useful, but
16b73f9a4aSJohannes Berg  * WITHOUT ANY WARRANTY; without even the implied warranty of
17b73f9a4aSJohannes Berg  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18b73f9a4aSJohannes Berg  * General Public License for more details.
19b73f9a4aSJohannes Berg  *
20b73f9a4aSJohannes Berg  * The full GNU General Public License is included in this distribution
21b73f9a4aSJohannes Berg  * in the file called COPYING.
22b73f9a4aSJohannes Berg  *
23b73f9a4aSJohannes Berg  * Contact Information:
24b73f9a4aSJohannes Berg  * Intel Linux Wireless <linuxwifi@intel.com>
25b73f9a4aSJohannes Berg  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
26b73f9a4aSJohannes Berg  *
27b73f9a4aSJohannes Berg  * BSD LICENSE
28b73f9a4aSJohannes Berg  *
29b73f9a4aSJohannes Berg  * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
30be82ecd3SAvraham Stern  * Copyright (C) 2018 - 2020 Intel Corporation
31b73f9a4aSJohannes Berg  * All rights reserved.
32b73f9a4aSJohannes Berg  *
33b73f9a4aSJohannes Berg  * Redistribution and use in source and binary forms, with or without
34b73f9a4aSJohannes Berg  * modification, are permitted provided that the following conditions
35b73f9a4aSJohannes Berg  * are met:
36b73f9a4aSJohannes Berg  *
37b73f9a4aSJohannes Berg  *  * Redistributions of source code must retain the above copyright
38b73f9a4aSJohannes Berg  *    notice, this list of conditions and the following disclaimer.
39b73f9a4aSJohannes Berg  *  * Redistributions in binary form must reproduce the above copyright
40b73f9a4aSJohannes Berg  *    notice, this list of conditions and the following disclaimer in
41b73f9a4aSJohannes Berg  *    the documentation and/or other materials provided with the
42b73f9a4aSJohannes Berg  *    distribution.
43b73f9a4aSJohannes Berg  *  * Neither the name Intel Corporation nor the names of its
44b73f9a4aSJohannes Berg  *    contributors may be used to endorse or promote products derived
45b73f9a4aSJohannes Berg  *    from this software without specific prior written permission.
46b73f9a4aSJohannes Berg  *
47b73f9a4aSJohannes Berg  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48b73f9a4aSJohannes Berg  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49b73f9a4aSJohannes Berg  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
50b73f9a4aSJohannes Berg  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
51b73f9a4aSJohannes Berg  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52b73f9a4aSJohannes Berg  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
53b73f9a4aSJohannes Berg  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54b73f9a4aSJohannes Berg  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55b73f9a4aSJohannes Berg  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56b73f9a4aSJohannes Berg  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
57b73f9a4aSJohannes Berg  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58b73f9a4aSJohannes Berg  *
59b73f9a4aSJohannes Berg  *****************************************************************************/
60b73f9a4aSJohannes Berg #include <net/cfg80211.h>
61b73f9a4aSJohannes Berg #include <linux/etherdevice.h>
62b73f9a4aSJohannes Berg #include "mvm.h"
63b73f9a4aSJohannes Berg #include "constants.h"
64b73f9a4aSJohannes Berg 
65be82ecd3SAvraham Stern struct iwl_mvm_pasn_sta {
66be82ecd3SAvraham Stern 	struct list_head list;
67be82ecd3SAvraham Stern 	struct iwl_mvm_int_sta int_sta;
68be82ecd3SAvraham Stern 	u8 addr[ETH_ALEN];
69be82ecd3SAvraham Stern };
70be82ecd3SAvraham Stern 
713830a01cSAvraham Stern struct iwl_mvm_pasn_hltk_data {
723830a01cSAvraham Stern 	u8 *addr;
733830a01cSAvraham Stern 	u8 cipher;
743830a01cSAvraham Stern 	u8 *hltk;
753830a01cSAvraham Stern };
763830a01cSAvraham Stern 
77b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
78b1a6db13SAvraham Stern 					   u8 *bw, u8 *ctrl_ch_position)
79b1a6db13SAvraham Stern {
80b1a6db13SAvraham Stern 	switch (chandef->width) {
81b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20_NOHT:
82b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_20_LEGACY;
83b1a6db13SAvraham Stern 		break;
84b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20:
85b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_20_HT;
86b1a6db13SAvraham Stern 		break;
87b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_40:
88b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_40;
89b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
90b1a6db13SAvraham Stern 		break;
91b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_80:
92b1a6db13SAvraham Stern 		*bw = IWL_TOF_BW_80;
93b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
94b1a6db13SAvraham Stern 		break;
95b1a6db13SAvraham Stern 	default:
96b1a6db13SAvraham Stern 		return -ENOTSUPP;
97b1a6db13SAvraham Stern 	}
98b1a6db13SAvraham Stern 
99b1a6db13SAvraham Stern 	return 0;
100b1a6db13SAvraham Stern }
101b1a6db13SAvraham Stern 
102b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
103b1a6db13SAvraham Stern 					   u8 *format_bw,
104b1a6db13SAvraham Stern 					   u8 *ctrl_ch_position)
105b1a6db13SAvraham Stern {
106b1a6db13SAvraham Stern 	switch (chandef->width) {
107b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20_NOHT:
108b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
109b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
110b1a6db13SAvraham Stern 		break;
111b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_20:
112b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
113b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
114b1a6db13SAvraham Stern 		break;
115b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_40:
116b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
117b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
118b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
119b1a6db13SAvraham Stern 		break;
120b1a6db13SAvraham Stern 	case NL80211_CHAN_WIDTH_80:
121b1a6db13SAvraham Stern 		*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
122b1a6db13SAvraham Stern 		*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
123b1a6db13SAvraham Stern 		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
124b1a6db13SAvraham Stern 		break;
125b1a6db13SAvraham Stern 	default:
126b1a6db13SAvraham Stern 		return -ENOTSUPP;
127b1a6db13SAvraham Stern 	}
128b1a6db13SAvraham Stern 
129b1a6db13SAvraham Stern 	return 0;
130b1a6db13SAvraham Stern }
131b1a6db13SAvraham Stern 
132b73f9a4aSJohannes Berg static int
133b73f9a4aSJohannes Berg iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
134b73f9a4aSJohannes Berg 			  struct ieee80211_vif *vif,
135b73f9a4aSJohannes Berg 			  struct cfg80211_chan_def *chandef)
136b73f9a4aSJohannes Berg {
137b73f9a4aSJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
138b1a6db13SAvraham Stern 	/*
139b1a6db13SAvraham Stern 	 * The command structure is the same for versions 6 and 7, (only the
140b1a6db13SAvraham Stern 	 * field interpretation is different), so the same struct can be use
141b1a6db13SAvraham Stern 	 * for all cases.
142b1a6db13SAvraham Stern 	 */
143b73f9a4aSJohannes Berg 	struct iwl_tof_responder_config_cmd cmd = {
144b73f9a4aSJohannes Berg 		.channel_num = chandef->chan->hw_value,
145b73f9a4aSJohannes Berg 		.cmd_valid_fields =
146b73f9a4aSJohannes Berg 			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
147b73f9a4aSJohannes Berg 				    IWL_TOF_RESPONDER_CMD_VALID_BSSID |
148b73f9a4aSJohannes Berg 				    IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
149b73f9a4aSJohannes Berg 		.sta_id = mvmvif->bcast_sta.sta_id,
150b73f9a4aSJohannes Berg 	};
1514af11950SMordechay Goodstein 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
1523830a01cSAvraham Stern 					   TOF_RESPONDER_CONFIG_CMD, 6);
153b1a6db13SAvraham Stern 	int err;
154b73f9a4aSJohannes Berg 
155b73f9a4aSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
156b73f9a4aSJohannes Berg 
157b1a6db13SAvraham Stern 	if (cmd_ver == 7)
158b1a6db13SAvraham Stern 		err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
159b1a6db13SAvraham Stern 						      &cmd.ctrl_ch_position);
160b1a6db13SAvraham Stern 	else
161b1a6db13SAvraham Stern 		err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
162b1a6db13SAvraham Stern 						      &cmd.ctrl_ch_position);
163b1a6db13SAvraham Stern 
164b1a6db13SAvraham Stern 	if (err) {
165b1a6db13SAvraham Stern 		IWL_ERR(mvm, "Failed to set responder bandwidth\n");
166b1a6db13SAvraham Stern 		return err;
167b73f9a4aSJohannes Berg 	}
168b73f9a4aSJohannes Berg 
169b73f9a4aSJohannes Berg 	memcpy(cmd.bssid, vif->addr, ETH_ALEN);
170b73f9a4aSJohannes Berg 
171b73f9a4aSJohannes Berg 	return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD,
172b73f9a4aSJohannes Berg 						    LOCATION_GROUP, 0),
173b73f9a4aSJohannes Berg 				    0, sizeof(cmd), &cmd);
174b73f9a4aSJohannes Berg }
175b73f9a4aSJohannes Berg 
176b73f9a4aSJohannes Berg static int
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 = {
187b73f9a4aSJohannes Berg 		.id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
188b73f9a4aSJohannes Berg 				 LOCATION_GROUP, 0),
189b73f9a4aSJohannes Berg 		.data[0] = &cmd,
190b73f9a4aSJohannes Berg 		.len[0] = sizeof(cmd),
191b73f9a4aSJohannes Berg 		.data[1] = &data,
192b73f9a4aSJohannes Berg 		/* .len[1] set later */
193b73f9a4aSJohannes Berg 		/* may not be able to DMA from stack */
194b73f9a4aSJohannes Berg 		.dataflags[1] = IWL_HCMD_DFL_DUP,
195b73f9a4aSJohannes Berg 	};
196b73f9a4aSJohannes Berg 	u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
197b73f9a4aSJohannes Berg 	u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
198b73f9a4aSJohannes Berg 	u8 *pos = data;
199b73f9a4aSJohannes Berg 
200b73f9a4aSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
201b73f9a4aSJohannes Berg 
202b73f9a4aSJohannes Berg 	if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
203b73f9a4aSJohannes Berg 		IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
204b73f9a4aSJohannes Berg 			params->lci_len, params->civicloc_len);
205b73f9a4aSJohannes Berg 		return -ENOBUFS;
206b73f9a4aSJohannes Berg 	}
207b73f9a4aSJohannes Berg 
208b73f9a4aSJohannes Berg 	pos[0] = WLAN_EID_MEASURE_REPORT;
209b73f9a4aSJohannes Berg 	pos[1] = params->lci_len;
210b73f9a4aSJohannes Berg 	memcpy(pos + 2, params->lci, params->lci_len);
211b73f9a4aSJohannes Berg 
212b73f9a4aSJohannes Berg 	pos += aligned_lci_len;
213b73f9a4aSJohannes Berg 	pos[0] = WLAN_EID_MEASURE_REPORT;
214b73f9a4aSJohannes Berg 	pos[1] = params->civicloc_len;
215b73f9a4aSJohannes Berg 	memcpy(pos + 2, params->civicloc, params->civicloc_len);
216b73f9a4aSJohannes Berg 
217b73f9a4aSJohannes Berg 	hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
218b73f9a4aSJohannes Berg 
219b73f9a4aSJohannes Berg 	return iwl_mvm_send_cmd(mvm, &hcmd);
220b73f9a4aSJohannes Berg }
221b73f9a4aSJohannes Berg 
2223830a01cSAvraham Stern static int
2233830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm,
2243830a01cSAvraham Stern 				 struct ieee80211_vif *vif,
2253830a01cSAvraham Stern 				 struct ieee80211_ftm_responder_params *params,
2263830a01cSAvraham Stern 				 struct iwl_mvm_pasn_hltk_data *hltk_data)
2273830a01cSAvraham Stern {
2283830a01cSAvraham Stern 	struct iwl_tof_responder_dyn_config_cmd cmd;
2293830a01cSAvraham Stern 	struct iwl_host_cmd hcmd = {
2303830a01cSAvraham Stern 		.id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
2313830a01cSAvraham Stern 				 LOCATION_GROUP, 0),
2323830a01cSAvraham Stern 		.data[0] = &cmd,
2333830a01cSAvraham Stern 		.len[0] = sizeof(cmd),
2343830a01cSAvraham Stern 		/* may not be able to DMA from stack */
2353830a01cSAvraham Stern 		.dataflags[0] = IWL_HCMD_DFL_DUP,
2363830a01cSAvraham Stern 	};
2373830a01cSAvraham Stern 
2383830a01cSAvraham Stern 	lockdep_assert_held(&mvm->mutex);
2393830a01cSAvraham Stern 
2403830a01cSAvraham Stern 	cmd.valid_flags = 0;
2413830a01cSAvraham Stern 
2423830a01cSAvraham Stern 	if (params) {
2433830a01cSAvraham Stern 		if (params->lci_len + 2 > sizeof(cmd.lci_buf) ||
2443830a01cSAvraham Stern 		    params->civicloc_len + 2 > sizeof(cmd.civic_buf)) {
2453830a01cSAvraham Stern 			IWL_ERR(mvm,
2463830a01cSAvraham Stern 				"LCI/civic data too big (lci=%zd, civic=%zd)\n",
2473830a01cSAvraham Stern 				params->lci_len, params->civicloc_len);
2483830a01cSAvraham Stern 			return -ENOBUFS;
2493830a01cSAvraham Stern 		}
2503830a01cSAvraham Stern 
2513830a01cSAvraham Stern 		cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT;
2523830a01cSAvraham Stern 		cmd.lci_buf[1] = params->lci_len;
2533830a01cSAvraham Stern 		memcpy(cmd.lci_buf + 2, params->lci, params->lci_len);
2543830a01cSAvraham Stern 		cmd.lci_len = params->lci_len + 2;
2553830a01cSAvraham Stern 
2563830a01cSAvraham Stern 		cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT;
2573830a01cSAvraham Stern 		cmd.civic_buf[1] = params->civicloc_len;
2583830a01cSAvraham Stern 		memcpy(cmd.civic_buf + 2, params->civicloc,
2593830a01cSAvraham Stern 		       params->civicloc_len);
2603830a01cSAvraham Stern 		cmd.civic_len = params->civicloc_len + 2;
2613830a01cSAvraham Stern 
2623830a01cSAvraham Stern 		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI |
2633830a01cSAvraham Stern 			IWL_RESPONDER_DYN_CFG_VALID_CIVIC;
2643830a01cSAvraham Stern 	}
2653830a01cSAvraham Stern 
2663830a01cSAvraham Stern 	if (hltk_data) {
2673830a01cSAvraham Stern 		if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) {
2683830a01cSAvraham Stern 			IWL_ERR(mvm, "invalid cipher: %u\n",
2693830a01cSAvraham Stern 				hltk_data->cipher);
2703830a01cSAvraham Stern 			return -EINVAL;
2713830a01cSAvraham Stern 		}
2723830a01cSAvraham Stern 
2733830a01cSAvraham Stern 		cmd.cipher = hltk_data->cipher;
2743830a01cSAvraham Stern 		memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr));
2753830a01cSAvraham Stern 		memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf));
2763830a01cSAvraham Stern 		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA;
2773830a01cSAvraham Stern 	}
2783830a01cSAvraham Stern 
2793830a01cSAvraham Stern 	return iwl_mvm_send_cmd(mvm, &hcmd);
2803830a01cSAvraham Stern }
2813830a01cSAvraham Stern 
2823830a01cSAvraham Stern static int
2833830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
2843830a01cSAvraham Stern 				  struct ieee80211_vif *vif,
2853830a01cSAvraham Stern 				  struct ieee80211_ftm_responder_params *params)
2863830a01cSAvraham Stern {
2873830a01cSAvraham Stern 	int ret;
2883830a01cSAvraham Stern 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
2893830a01cSAvraham Stern 					   TOF_RESPONDER_DYN_CONFIG_CMD, 2);
2903830a01cSAvraham Stern 
2913830a01cSAvraham Stern 	switch (cmd_ver) {
2923830a01cSAvraham Stern 	case 2:
2933830a01cSAvraham Stern 		ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif,
2943830a01cSAvraham Stern 						       params);
2953830a01cSAvraham Stern 		break;
2963830a01cSAvraham Stern 	case 3:
2973830a01cSAvraham Stern 		ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif,
2983830a01cSAvraham Stern 						       params, NULL);
2993830a01cSAvraham Stern 		break;
3003830a01cSAvraham Stern 	default:
3013830a01cSAvraham Stern 		IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n",
3023830a01cSAvraham Stern 			cmd_ver);
3033830a01cSAvraham Stern 		ret = -ENOTSUPP;
3043830a01cSAvraham Stern 	}
3053830a01cSAvraham Stern 
3063830a01cSAvraham Stern 	return ret;
3073830a01cSAvraham Stern }
3083830a01cSAvraham Stern 
309890d814bSAvraham Stern static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
310890d814bSAvraham Stern 				      struct ieee80211_vif *vif,
311890d814bSAvraham Stern 				      struct iwl_mvm_pasn_sta *sta)
312890d814bSAvraham Stern {
313890d814bSAvraham Stern 	list_del(&sta->list);
314890d814bSAvraham Stern 	iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
315890d814bSAvraham Stern 	iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
316890d814bSAvraham Stern 	kfree(sta);
317890d814bSAvraham Stern }
318890d814bSAvraham Stern 
319be82ecd3SAvraham Stern int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
320be82ecd3SAvraham Stern 				      struct ieee80211_vif *vif,
321be82ecd3SAvraham Stern 				      u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
322be82ecd3SAvraham Stern 				      u8 *hltk, u32 hltk_len)
323be82ecd3SAvraham Stern {
324be82ecd3SAvraham Stern 	int ret;
325*68ad2474SAvraham Stern 	struct iwl_mvm_pasn_sta *sta = NULL;
326890d814bSAvraham Stern 	struct iwl_mvm_pasn_hltk_data hltk_data = {
327890d814bSAvraham Stern 		.addr = addr,
328890d814bSAvraham Stern 		.hltk = hltk,
329890d814bSAvraham Stern 	};
330890d814bSAvraham Stern 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
331890d814bSAvraham Stern 					   TOF_RESPONDER_DYN_CONFIG_CMD, 2);
332be82ecd3SAvraham Stern 
333be82ecd3SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
334be82ecd3SAvraham Stern 
335890d814bSAvraham Stern 	if (cmd_ver < 3) {
336890d814bSAvraham Stern 		IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
337890d814bSAvraham Stern 		return -ENOTSUPP;
338890d814bSAvraham Stern 	}
339890d814bSAvraham Stern 
340890d814bSAvraham Stern 	hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
341890d814bSAvraham Stern 	if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
342890d814bSAvraham Stern 		IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
343890d814bSAvraham Stern 		return -EINVAL;
344890d814bSAvraham Stern 	}
345890d814bSAvraham Stern 
346*68ad2474SAvraham Stern 	if (tk && tk_len) {
347*68ad2474SAvraham Stern 		sta = kzalloc(sizeof(*sta), GFP_KERNEL);
348be82ecd3SAvraham Stern 		if (!sta)
349be82ecd3SAvraham Stern 			return -ENOBUFS;
350be82ecd3SAvraham Stern 
351*68ad2474SAvraham Stern 		ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
352*68ad2474SAvraham Stern 					   cipher, tk, tk_len);
353be82ecd3SAvraham Stern 		if (ret) {
354be82ecd3SAvraham Stern 			kfree(sta);
355be82ecd3SAvraham Stern 			return ret;
356be82ecd3SAvraham Stern 		}
357*68ad2474SAvraham Stern 	}
358be82ecd3SAvraham Stern 
359890d814bSAvraham Stern 	ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, &hltk_data);
360890d814bSAvraham Stern 	if (ret) {
361*68ad2474SAvraham Stern 		if (sta)
362890d814bSAvraham Stern 			iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
363890d814bSAvraham Stern 		return ret;
364890d814bSAvraham Stern 	}
365be82ecd3SAvraham Stern 
366be82ecd3SAvraham Stern 	memcpy(sta->addr, addr, ETH_ALEN);
367be82ecd3SAvraham Stern 	list_add_tail(&sta->list, &mvm->resp_pasn_list);
368be82ecd3SAvraham Stern 	return 0;
369be82ecd3SAvraham Stern }
370be82ecd3SAvraham Stern 
371be82ecd3SAvraham Stern int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
372be82ecd3SAvraham Stern 				     struct ieee80211_vif *vif, u8 *addr)
373be82ecd3SAvraham Stern {
374be82ecd3SAvraham Stern 	struct iwl_mvm_pasn_sta *sta, *prev;
375be82ecd3SAvraham Stern 
376be82ecd3SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
377be82ecd3SAvraham Stern 
378be82ecd3SAvraham Stern 	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
379be82ecd3SAvraham Stern 		if (!memcmp(sta->addr, addr, ETH_ALEN)) {
380be82ecd3SAvraham Stern 			iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
381be82ecd3SAvraham Stern 			return 0;
382be82ecd3SAvraham Stern 		}
383be82ecd3SAvraham Stern 	}
384be82ecd3SAvraham Stern 
385be82ecd3SAvraham Stern 	IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
386be82ecd3SAvraham Stern 	return -EINVAL;
387be82ecd3SAvraham Stern }
388be82ecd3SAvraham Stern 
389b73f9a4aSJohannes Berg int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
390b73f9a4aSJohannes Berg {
391b73f9a4aSJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
392b73f9a4aSJohannes Berg 	struct ieee80211_ftm_responder_params *params;
393b73f9a4aSJohannes Berg 	struct ieee80211_chanctx_conf ctx, *pctx;
394b73f9a4aSJohannes Berg 	u16 *phy_ctxt_id;
395b73f9a4aSJohannes Berg 	struct iwl_mvm_phy_ctxt *phy_ctxt;
396b73f9a4aSJohannes Berg 	int ret;
397b73f9a4aSJohannes Berg 
398b73f9a4aSJohannes Berg 	params = vif->bss_conf.ftmr_params;
399b73f9a4aSJohannes Berg 
400b73f9a4aSJohannes Berg 	lockdep_assert_held(&mvm->mutex);
401b73f9a4aSJohannes Berg 
402b73f9a4aSJohannes Berg 	if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder))
403b73f9a4aSJohannes Berg 		return -EINVAL;
404b73f9a4aSJohannes Berg 
405b73f9a4aSJohannes Berg 	if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
406b73f9a4aSJohannes Berg 	    !mvmvif->ap_ibss_active) {
407b73f9a4aSJohannes Berg 		IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
408b73f9a4aSJohannes Berg 		return -EIO;
409b73f9a4aSJohannes Berg 	}
410b73f9a4aSJohannes Berg 
411b73f9a4aSJohannes Berg 	rcu_read_lock();
412b73f9a4aSJohannes Berg 	pctx = rcu_dereference(vif->chanctx_conf);
413b73f9a4aSJohannes Berg 	/* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care
414b73f9a4aSJohannes Berg 	 * about changes in the ctx after releasing the lock because the driver
415b73f9a4aSJohannes Berg 	 * is still protected by the mutex. */
416b73f9a4aSJohannes Berg 	ctx = *pctx;
417b73f9a4aSJohannes Berg 	phy_ctxt_id  = (u16 *)pctx->drv_priv;
418b73f9a4aSJohannes Berg 	rcu_read_unlock();
419b73f9a4aSJohannes Berg 
420b73f9a4aSJohannes Berg 	phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
421b73f9a4aSJohannes Berg 	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def,
422b73f9a4aSJohannes Berg 				       ctx.rx_chains_static,
423b73f9a4aSJohannes Berg 				       ctx.rx_chains_dynamic);
424b73f9a4aSJohannes Berg 	if (ret)
425b73f9a4aSJohannes Berg 		return ret;
426b73f9a4aSJohannes Berg 
427b73f9a4aSJohannes Berg 	ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def);
428b73f9a4aSJohannes Berg 	if (ret)
429b73f9a4aSJohannes Berg 		return ret;
430b73f9a4aSJohannes Berg 
431b73f9a4aSJohannes Berg 	if (params)
432b73f9a4aSJohannes Berg 		ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
433b73f9a4aSJohannes Berg 
434b73f9a4aSJohannes Berg 	return ret;
435b73f9a4aSJohannes Berg }
436b73f9a4aSJohannes Berg 
437be82ecd3SAvraham Stern void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
438be82ecd3SAvraham Stern 				 struct ieee80211_vif *vif)
439be82ecd3SAvraham Stern {
440be82ecd3SAvraham Stern 	struct iwl_mvm_pasn_sta *sta, *prev;
441be82ecd3SAvraham Stern 
442be82ecd3SAvraham Stern 	lockdep_assert_held(&mvm->mutex);
443be82ecd3SAvraham Stern 
444be82ecd3SAvraham Stern 	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
445be82ecd3SAvraham Stern 		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
446be82ecd3SAvraham Stern }
447be82ecd3SAvraham Stern 
448b73f9a4aSJohannes Berg void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
449b73f9a4aSJohannes Berg 				   struct ieee80211_vif *vif)
450b73f9a4aSJohannes Berg {
451b73f9a4aSJohannes Berg 	if (!vif->bss_conf.ftm_responder)
452b73f9a4aSJohannes Berg 		return;
453b73f9a4aSJohannes Berg 
454be82ecd3SAvraham Stern 	iwl_mvm_ftm_responder_clear(mvm, vif);
455b73f9a4aSJohannes Berg 	iwl_mvm_ftm_start_responder(mvm, vif);
456b73f9a4aSJohannes Berg }
457b73f9a4aSJohannes Berg 
458b73f9a4aSJohannes Berg void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
459b73f9a4aSJohannes Berg 				 struct iwl_rx_cmd_buffer *rxb)
460b73f9a4aSJohannes Berg {
461b73f9a4aSJohannes Berg 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
462b73f9a4aSJohannes Berg 	struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
463b73f9a4aSJohannes Berg 	struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
464b73f9a4aSJohannes Berg 	u32 flags = le32_to_cpu(resp->flags);
465b73f9a4aSJohannes Berg 
466b73f9a4aSJohannes Berg 	if (resp->success_ftm == resp->ftm_per_burst)
467b73f9a4aSJohannes Berg 		stats->success_num++;
468b73f9a4aSJohannes Berg 	else if (resp->success_ftm >= 2)
469b73f9a4aSJohannes Berg 		stats->partial_num++;
470b73f9a4aSJohannes Berg 	else
471b73f9a4aSJohannes Berg 		stats->failed_num++;
472b73f9a4aSJohannes Berg 
473b73f9a4aSJohannes Berg 	if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
474b73f9a4aSJohannes Berg 	    (flags & FTM_RESP_STAT_ASAP_RESP))
475b73f9a4aSJohannes Berg 		stats->asap_num++;
476b73f9a4aSJohannes Berg 
477b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
478b73f9a4aSJohannes Berg 		stats->non_asap_num++;
479b73f9a4aSJohannes Berg 
480b73f9a4aSJohannes Berg 	stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
481b73f9a4aSJohannes Berg 
482b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
483b73f9a4aSJohannes Berg 		stats->unknown_triggers_num++;
484b73f9a4aSJohannes Berg 
485b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_DUP)
486b73f9a4aSJohannes Berg 		stats->reschedule_requests_num++;
487b73f9a4aSJohannes Berg 
488b73f9a4aSJohannes Berg 	if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
489b73f9a4aSJohannes Berg 		stats->out_of_window_triggers_num++;
490b73f9a4aSJohannes Berg }
491