18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
98c2ecf20Sopenharmony_ci * Copyright (C) 2018 - 2020 Intel Corporation
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
128c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
138c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
168c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
178c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
188c2ecf20Sopenharmony_ci * General Public License for more details.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
218c2ecf20Sopenharmony_ci * in the file called COPYING.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Contact Information:
248c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com>
258c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * BSD LICENSE
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
308c2ecf20Sopenharmony_ci * Copyright (C) 2018 - 2020 Intel Corporation
318c2ecf20Sopenharmony_ci * All rights reserved.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
348c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
358c2ecf20Sopenharmony_ci * are met:
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
388c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
398c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
408c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
418c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
428c2ecf20Sopenharmony_ci *    distribution.
438c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
448c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
458c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
488c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
498c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
508c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
518c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
528c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
538c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
548c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
558c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
568c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
578c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *****************************************************************************/
608c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
618c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
628c2ecf20Sopenharmony_ci#include "mvm.h"
638c2ecf20Sopenharmony_ci#include "constants.h"
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct iwl_mvm_pasn_sta {
668c2ecf20Sopenharmony_ci	struct list_head list;
678c2ecf20Sopenharmony_ci	struct iwl_mvm_int_sta int_sta;
688c2ecf20Sopenharmony_ci	u8 addr[ETH_ALEN];
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct iwl_mvm_pasn_hltk_data {
728c2ecf20Sopenharmony_ci	u8 *addr;
738c2ecf20Sopenharmony_ci	u8 cipher;
748c2ecf20Sopenharmony_ci	u8 *hltk;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
788c2ecf20Sopenharmony_ci					   u8 *bw, u8 *ctrl_ch_position)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	switch (chandef->width) {
818c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
828c2ecf20Sopenharmony_ci		*bw = IWL_TOF_BW_20_LEGACY;
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
858c2ecf20Sopenharmony_ci		*bw = IWL_TOF_BW_20_HT;
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
888c2ecf20Sopenharmony_ci		*bw = IWL_TOF_BW_40;
898c2ecf20Sopenharmony_ci		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
928c2ecf20Sopenharmony_ci		*bw = IWL_TOF_BW_80;
938c2ecf20Sopenharmony_ci		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
948c2ecf20Sopenharmony_ci		break;
958c2ecf20Sopenharmony_ci	default:
968c2ecf20Sopenharmony_ci		return -ENOTSUPP;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef,
1038c2ecf20Sopenharmony_ci					   u8 *format_bw,
1048c2ecf20Sopenharmony_ci					   u8 *ctrl_ch_position)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	switch (chandef->width) {
1078c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
1088c2ecf20Sopenharmony_ci		*format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY;
1098c2ecf20Sopenharmony_ci		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
1128c2ecf20Sopenharmony_ci		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
1138c2ecf20Sopenharmony_ci		*format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
1148c2ecf20Sopenharmony_ci		break;
1158c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
1168c2ecf20Sopenharmony_ci		*format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
1178c2ecf20Sopenharmony_ci		*format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
1188c2ecf20Sopenharmony_ci		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
1198c2ecf20Sopenharmony_ci		break;
1208c2ecf20Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
1218c2ecf20Sopenharmony_ci		*format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
1228c2ecf20Sopenharmony_ci		*format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
1238c2ecf20Sopenharmony_ci		*ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef);
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	default:
1268c2ecf20Sopenharmony_ci		return -ENOTSUPP;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int
1338c2ecf20Sopenharmony_ciiwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm,
1348c2ecf20Sopenharmony_ci			  struct ieee80211_vif *vif,
1358c2ecf20Sopenharmony_ci			  struct cfg80211_chan_def *chandef)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1388c2ecf20Sopenharmony_ci	/*
1398c2ecf20Sopenharmony_ci	 * The command structure is the same for versions 6 and 7, (only the
1408c2ecf20Sopenharmony_ci	 * field interpretation is different), so the same struct can be use
1418c2ecf20Sopenharmony_ci	 * for all cases.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	struct iwl_tof_responder_config_cmd cmd = {
1448c2ecf20Sopenharmony_ci		.channel_num = chandef->chan->hw_value,
1458c2ecf20Sopenharmony_ci		.cmd_valid_fields =
1468c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO |
1478c2ecf20Sopenharmony_ci				    IWL_TOF_RESPONDER_CMD_VALID_BSSID |
1488c2ecf20Sopenharmony_ci				    IWL_TOF_RESPONDER_CMD_VALID_STA_ID),
1498c2ecf20Sopenharmony_ci		.sta_id = mvmvif->bcast_sta.sta_id,
1508c2ecf20Sopenharmony_ci	};
1518c2ecf20Sopenharmony_ci	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
1528c2ecf20Sopenharmony_ci					   TOF_RESPONDER_CONFIG_CMD, 6);
1538c2ecf20Sopenharmony_ci	int err;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (cmd_ver == 7)
1588c2ecf20Sopenharmony_ci		err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw,
1598c2ecf20Sopenharmony_ci						      &cmd.ctrl_ch_position);
1608c2ecf20Sopenharmony_ci	else
1618c2ecf20Sopenharmony_ci		err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw,
1628c2ecf20Sopenharmony_ci						      &cmd.ctrl_ch_position);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (err) {
1658c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Failed to set responder bandwidth\n");
1668c2ecf20Sopenharmony_ci		return err;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	memcpy(cmd.bssid, vif->addr, ETH_ALEN);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD,
1728c2ecf20Sopenharmony_ci						    LOCATION_GROUP, 0),
1738c2ecf20Sopenharmony_ci				    0, sizeof(cmd), &cmd);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int
1778c2ecf20Sopenharmony_ciiwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm,
1788c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
1798c2ecf20Sopenharmony_ci				 struct ieee80211_ftm_responder_params *params)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct iwl_tof_responder_dyn_config_cmd_v2 cmd = {
1828c2ecf20Sopenharmony_ci		.lci_len = cpu_to_le32(params->lci_len + 2),
1838c2ecf20Sopenharmony_ci		.civic_len = cpu_to_le32(params->civicloc_len + 2),
1848c2ecf20Sopenharmony_ci	};
1858c2ecf20Sopenharmony_ci	u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0};
1868c2ecf20Sopenharmony_ci	struct iwl_host_cmd hcmd = {
1878c2ecf20Sopenharmony_ci		.id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
1888c2ecf20Sopenharmony_ci				 LOCATION_GROUP, 0),
1898c2ecf20Sopenharmony_ci		.data[0] = &cmd,
1908c2ecf20Sopenharmony_ci		.len[0] = sizeof(cmd),
1918c2ecf20Sopenharmony_ci		.data[1] = &data,
1928c2ecf20Sopenharmony_ci		/* .len[1] set later */
1938c2ecf20Sopenharmony_ci		/* may not be able to DMA from stack */
1948c2ecf20Sopenharmony_ci		.dataflags[1] = IWL_HCMD_DFL_DUP,
1958c2ecf20Sopenharmony_ci	};
1968c2ecf20Sopenharmony_ci	u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4);
1978c2ecf20Sopenharmony_ci	u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4);
1988c2ecf20Sopenharmony_ci	u8 *pos = data;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) {
2038c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n",
2048c2ecf20Sopenharmony_ci			params->lci_len, params->civicloc_len);
2058c2ecf20Sopenharmony_ci		return -ENOBUFS;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	pos[0] = WLAN_EID_MEASURE_REPORT;
2098c2ecf20Sopenharmony_ci	pos[1] = params->lci_len;
2108c2ecf20Sopenharmony_ci	memcpy(pos + 2, params->lci, params->lci_len);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	pos += aligned_lci_len;
2138c2ecf20Sopenharmony_ci	pos[0] = WLAN_EID_MEASURE_REPORT;
2148c2ecf20Sopenharmony_ci	pos[1] = params->civicloc_len;
2158c2ecf20Sopenharmony_ci	memcpy(pos + 2, params->civicloc, params->civicloc_len);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	hcmd.len[1] = aligned_lci_len + aligned_civicloc_len;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd(mvm, &hcmd);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int
2238c2ecf20Sopenharmony_ciiwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm,
2248c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
2258c2ecf20Sopenharmony_ci				 struct ieee80211_ftm_responder_params *params,
2268c2ecf20Sopenharmony_ci				 struct iwl_mvm_pasn_hltk_data *hltk_data)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct iwl_tof_responder_dyn_config_cmd cmd;
2298c2ecf20Sopenharmony_ci	struct iwl_host_cmd hcmd = {
2308c2ecf20Sopenharmony_ci		.id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD,
2318c2ecf20Sopenharmony_ci				 LOCATION_GROUP, 0),
2328c2ecf20Sopenharmony_ci		.data[0] = &cmd,
2338c2ecf20Sopenharmony_ci		.len[0] = sizeof(cmd),
2348c2ecf20Sopenharmony_ci		/* may not be able to DMA from stack */
2358c2ecf20Sopenharmony_ci		.dataflags[0] = IWL_HCMD_DFL_DUP,
2368c2ecf20Sopenharmony_ci	};
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	cmd.valid_flags = 0;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (params) {
2438c2ecf20Sopenharmony_ci		if (params->lci_len + 2 > sizeof(cmd.lci_buf) ||
2448c2ecf20Sopenharmony_ci		    params->civicloc_len + 2 > sizeof(cmd.civic_buf)) {
2458c2ecf20Sopenharmony_ci			IWL_ERR(mvm,
2468c2ecf20Sopenharmony_ci				"LCI/civic data too big (lci=%zd, civic=%zd)\n",
2478c2ecf20Sopenharmony_ci				params->lci_len, params->civicloc_len);
2488c2ecf20Sopenharmony_ci			return -ENOBUFS;
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT;
2528c2ecf20Sopenharmony_ci		cmd.lci_buf[1] = params->lci_len;
2538c2ecf20Sopenharmony_ci		memcpy(cmd.lci_buf + 2, params->lci, params->lci_len);
2548c2ecf20Sopenharmony_ci		cmd.lci_len = params->lci_len + 2;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT;
2578c2ecf20Sopenharmony_ci		cmd.civic_buf[1] = params->civicloc_len;
2588c2ecf20Sopenharmony_ci		memcpy(cmd.civic_buf + 2, params->civicloc,
2598c2ecf20Sopenharmony_ci		       params->civicloc_len);
2608c2ecf20Sopenharmony_ci		cmd.civic_len = params->civicloc_len + 2;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI |
2638c2ecf20Sopenharmony_ci			IWL_RESPONDER_DYN_CFG_VALID_CIVIC;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (hltk_data) {
2678c2ecf20Sopenharmony_ci		if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) {
2688c2ecf20Sopenharmony_ci			IWL_ERR(mvm, "invalid cipher: %u\n",
2698c2ecf20Sopenharmony_ci				hltk_data->cipher);
2708c2ecf20Sopenharmony_ci			return -EINVAL;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		cmd.cipher = hltk_data->cipher;
2748c2ecf20Sopenharmony_ci		memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr));
2758c2ecf20Sopenharmony_ci		memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf));
2768c2ecf20Sopenharmony_ci		cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd(mvm, &hcmd);
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int
2838c2ecf20Sopenharmony_ciiwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm,
2848c2ecf20Sopenharmony_ci				  struct ieee80211_vif *vif,
2858c2ecf20Sopenharmony_ci				  struct ieee80211_ftm_responder_params *params)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	int ret;
2888c2ecf20Sopenharmony_ci	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
2898c2ecf20Sopenharmony_ci					   TOF_RESPONDER_DYN_CONFIG_CMD, 2);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	switch (cmd_ver) {
2928c2ecf20Sopenharmony_ci	case 2:
2938c2ecf20Sopenharmony_ci		ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif,
2948c2ecf20Sopenharmony_ci						       params);
2958c2ecf20Sopenharmony_ci		break;
2968c2ecf20Sopenharmony_ci	case 3:
2978c2ecf20Sopenharmony_ci		ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif,
2988c2ecf20Sopenharmony_ci						       params, NULL);
2998c2ecf20Sopenharmony_ci		break;
3008c2ecf20Sopenharmony_ci	default:
3018c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n",
3028c2ecf20Sopenharmony_ci			cmd_ver);
3038c2ecf20Sopenharmony_ci		ret = -ENOTSUPP;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return ret;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
3108c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif,
3118c2ecf20Sopenharmony_ci				      struct iwl_mvm_pasn_sta *sta)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	list_del(&sta->list);
3148c2ecf20Sopenharmony_ci	iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
3158c2ecf20Sopenharmony_ci	iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
3168c2ecf20Sopenharmony_ci	kfree(sta);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ciint iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
3208c2ecf20Sopenharmony_ci				      struct ieee80211_vif *vif,
3218c2ecf20Sopenharmony_ci				      u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
3228c2ecf20Sopenharmony_ci				      u8 *hltk, u32 hltk_len)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	int ret;
3258c2ecf20Sopenharmony_ci	struct iwl_mvm_pasn_sta *sta = NULL;
3268c2ecf20Sopenharmony_ci	struct iwl_mvm_pasn_hltk_data hltk_data = {
3278c2ecf20Sopenharmony_ci		.addr = addr,
3288c2ecf20Sopenharmony_ci		.hltk = hltk,
3298c2ecf20Sopenharmony_ci	};
3308c2ecf20Sopenharmony_ci	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP,
3318c2ecf20Sopenharmony_ci					   TOF_RESPONDER_DYN_CONFIG_CMD, 2);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (cmd_ver < 3) {
3368c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Adding PASN station not supported by FW\n");
3378c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher);
3418c2ecf20Sopenharmony_ci	if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) {
3428c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "invalid cipher: %u\n", cipher);
3438c2ecf20Sopenharmony_ci		return -EINVAL;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (tk && tk_len) {
3478c2ecf20Sopenharmony_ci		sta = kzalloc(sizeof(*sta), GFP_KERNEL);
3488c2ecf20Sopenharmony_ci		if (!sta)
3498c2ecf20Sopenharmony_ci			return -ENOBUFS;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr,
3528c2ecf20Sopenharmony_ci					   cipher, tk, tk_len);
3538c2ecf20Sopenharmony_ci		if (ret) {
3548c2ecf20Sopenharmony_ci			kfree(sta);
3558c2ecf20Sopenharmony_ci			return ret;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		memcpy(sta->addr, addr, ETH_ALEN);
3598c2ecf20Sopenharmony_ci		list_add_tail(&sta->list, &mvm->resp_pasn_list);
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, &hltk_data);
3638c2ecf20Sopenharmony_ci	if (ret && sta)
3648c2ecf20Sopenharmony_ci		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return ret;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ciint iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
3708c2ecf20Sopenharmony_ci				     struct ieee80211_vif *vif, u8 *addr)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct iwl_mvm_pasn_sta *sta, *prev;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
3778c2ecf20Sopenharmony_ci		if (!memcmp(sta->addr, addr, ETH_ALEN)) {
3788c2ecf20Sopenharmony_ci			iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
3798c2ecf20Sopenharmony_ci			return 0;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
3848c2ecf20Sopenharmony_ci	return -EINVAL;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ciint iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3908c2ecf20Sopenharmony_ci	struct ieee80211_ftm_responder_params *params;
3918c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf ctx, *pctx;
3928c2ecf20Sopenharmony_ci	u16 *phy_ctxt_id;
3938c2ecf20Sopenharmony_ci	struct iwl_mvm_phy_ctxt *phy_ctxt;
3948c2ecf20Sopenharmony_ci	int ret;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	params = vif->bss_conf.ftmr_params;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder))
4018c2ecf20Sopenharmony_ci		return -EINVAL;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (vif->p2p || vif->type != NL80211_IFTYPE_AP ||
4048c2ecf20Sopenharmony_ci	    !mvmvif->ap_ibss_active) {
4058c2ecf20Sopenharmony_ci		IWL_ERR(mvm, "Cannot start responder, not in AP mode\n");
4068c2ecf20Sopenharmony_ci		return -EIO;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	rcu_read_lock();
4108c2ecf20Sopenharmony_ci	pctx = rcu_dereference(vif->chanctx_conf);
4118c2ecf20Sopenharmony_ci	/* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care
4128c2ecf20Sopenharmony_ci	 * about changes in the ctx after releasing the lock because the driver
4138c2ecf20Sopenharmony_ci	 * is still protected by the mutex. */
4148c2ecf20Sopenharmony_ci	ctx = *pctx;
4158c2ecf20Sopenharmony_ci	phy_ctxt_id  = (u16 *)pctx->drv_priv;
4168c2ecf20Sopenharmony_ci	rcu_read_unlock();
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
4198c2ecf20Sopenharmony_ci	ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def,
4208c2ecf20Sopenharmony_ci				       ctx.rx_chains_static,
4218c2ecf20Sopenharmony_ci				       ctx.rx_chains_dynamic);
4228c2ecf20Sopenharmony_ci	if (ret)
4238c2ecf20Sopenharmony_ci		return ret;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def);
4268c2ecf20Sopenharmony_ci	if (ret)
4278c2ecf20Sopenharmony_ci		return ret;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (params)
4308c2ecf20Sopenharmony_ci		ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return ret;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_civoid iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
4368c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct iwl_mvm_pasn_sta *sta, *prev;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
4438c2ecf20Sopenharmony_ci		iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_civoid iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
4478c2ecf20Sopenharmony_ci				   struct ieee80211_vif *vif)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	if (!vif->bss_conf.ftm_responder)
4508c2ecf20Sopenharmony_ci		return;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	iwl_mvm_ftm_responder_clear(mvm, vif);
4538c2ecf20Sopenharmony_ci	iwl_mvm_ftm_start_responder(mvm, vif);
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_civoid iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
4578c2ecf20Sopenharmony_ci				 struct iwl_rx_cmd_buffer *rxb)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
4608c2ecf20Sopenharmony_ci	struct iwl_ftm_responder_stats *resp = (void *)pkt->data;
4618c2ecf20Sopenharmony_ci	struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats;
4628c2ecf20Sopenharmony_ci	u32 flags = le32_to_cpu(resp->flags);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (resp->success_ftm == resp->ftm_per_burst)
4658c2ecf20Sopenharmony_ci		stats->success_num++;
4668c2ecf20Sopenharmony_ci	else if (resp->success_ftm >= 2)
4678c2ecf20Sopenharmony_ci		stats->partial_num++;
4688c2ecf20Sopenharmony_ci	else
4698c2ecf20Sopenharmony_ci		stats->failed_num++;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if ((flags & FTM_RESP_STAT_ASAP_REQ) &&
4728c2ecf20Sopenharmony_ci	    (flags & FTM_RESP_STAT_ASAP_RESP))
4738c2ecf20Sopenharmony_ci		stats->asap_num++;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (flags & FTM_RESP_STAT_NON_ASAP_RESP)
4768c2ecf20Sopenharmony_ci		stats->non_asap_num++;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN)
4818c2ecf20Sopenharmony_ci		stats->unknown_triggers_num++;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (flags & FTM_RESP_STAT_DUP)
4848c2ecf20Sopenharmony_ci		stats->reschedule_requests_num++;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN)
4878c2ecf20Sopenharmony_ci		stats->out_of_window_triggers_num++;
4888c2ecf20Sopenharmony_ci}
489