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