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