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