18e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 28e99ea8dSJohannes Berg /* 38e99ea8dSJohannes Berg * Copyright (C) 2015-2017 Intel Deutschland GmbH 4be82ecd3SAvraham Stern * Copyright (C) 2018-2020 Intel Corporation 58e99ea8dSJohannes Berg */ 6b73f9a4aSJohannes Berg #include <net/cfg80211.h> 7b73f9a4aSJohannes Berg #include <linux/etherdevice.h> 8b73f9a4aSJohannes Berg #include "mvm.h" 9b73f9a4aSJohannes Berg #include "constants.h" 10b73f9a4aSJohannes Berg 11be82ecd3SAvraham Stern struct iwl_mvm_pasn_sta { 12be82ecd3SAvraham Stern struct list_head list; 13be82ecd3SAvraham Stern struct iwl_mvm_int_sta int_sta; 14be82ecd3SAvraham Stern u8 addr[ETH_ALEN]; 15be82ecd3SAvraham Stern }; 16be82ecd3SAvraham Stern 173830a01cSAvraham Stern struct iwl_mvm_pasn_hltk_data { 183830a01cSAvraham Stern u8 *addr; 193830a01cSAvraham Stern u8 cipher; 203830a01cSAvraham Stern u8 *hltk; 213830a01cSAvraham Stern }; 223830a01cSAvraham Stern 23b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef, 24b1a6db13SAvraham Stern u8 *bw, u8 *ctrl_ch_position) 25b1a6db13SAvraham Stern { 26b1a6db13SAvraham Stern switch (chandef->width) { 27b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20_NOHT: 28b1a6db13SAvraham Stern *bw = IWL_TOF_BW_20_LEGACY; 29b1a6db13SAvraham Stern break; 30b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20: 31b1a6db13SAvraham Stern *bw = IWL_TOF_BW_20_HT; 32b1a6db13SAvraham Stern break; 33b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_40: 34b1a6db13SAvraham Stern *bw = IWL_TOF_BW_40; 35b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 36b1a6db13SAvraham Stern break; 37b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_80: 38b1a6db13SAvraham Stern *bw = IWL_TOF_BW_80; 39b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 40b1a6db13SAvraham Stern break; 41b1a6db13SAvraham Stern default: 42b1a6db13SAvraham Stern return -ENOTSUPP; 43b1a6db13SAvraham Stern } 44b1a6db13SAvraham Stern 45b1a6db13SAvraham Stern return 0; 46b1a6db13SAvraham Stern } 47b1a6db13SAvraham Stern 48b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef, 49b1a6db13SAvraham Stern u8 *format_bw, 50b1a6db13SAvraham Stern u8 *ctrl_ch_position) 51b1a6db13SAvraham Stern { 52b1a6db13SAvraham Stern switch (chandef->width) { 53b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20_NOHT: 54b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; 55b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 56b1a6db13SAvraham Stern break; 57b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20: 58b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 59b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 60b1a6db13SAvraham Stern break; 61b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_40: 62b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 63b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; 64b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 65b1a6db13SAvraham Stern break; 66b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_80: 67b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; 68b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; 69b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 70b1a6db13SAvraham Stern break; 71b1a6db13SAvraham Stern default: 72b1a6db13SAvraham Stern return -ENOTSUPP; 73b1a6db13SAvraham Stern } 74b1a6db13SAvraham Stern 75b1a6db13SAvraham Stern return 0; 76b1a6db13SAvraham Stern } 77b1a6db13SAvraham Stern 78*20578872SAvraham Stern static void 79*20578872SAvraham Stern iwl_mvm_ftm_responder_set_ndp(struct iwl_mvm *mvm, 80*20578872SAvraham Stern struct iwl_tof_responder_config_cmd_v8 *cmd) 81*20578872SAvraham Stern { 82*20578872SAvraham Stern /* Up to 2 R2I STS are allowed on the responder */ 83*20578872SAvraham Stern u32 r2i_max_sts = IWL_MVM_FTM_R2I_MAX_STS < 2 ? 84*20578872SAvraham Stern IWL_MVM_FTM_R2I_MAX_STS : 1; 85*20578872SAvraham Stern 86*20578872SAvraham Stern cmd->r2i_ndp_params = IWL_MVM_FTM_R2I_MAX_REP | 87*20578872SAvraham Stern (r2i_max_sts << IWL_RESPONDER_STS_POS) | 88*20578872SAvraham Stern (IWL_MVM_FTM_R2I_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS); 89*20578872SAvraham Stern cmd->i2r_ndp_params = IWL_MVM_FTM_I2R_MAX_REP | 90*20578872SAvraham Stern (IWL_MVM_FTM_I2R_MAX_STS << IWL_RESPONDER_STS_POS) | 91*20578872SAvraham Stern (IWL_MVM_FTM_I2R_MAX_TOTAL_LTF << IWL_RESPONDER_TOTAL_LTF_POS); 92*20578872SAvraham Stern cmd->cmd_valid_fields |= 93*20578872SAvraham Stern cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_NDP_PARAMS); 94*20578872SAvraham Stern } 95*20578872SAvraham Stern 96b73f9a4aSJohannes Berg static int 97b73f9a4aSJohannes Berg iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, 98b73f9a4aSJohannes Berg struct ieee80211_vif *vif, 99b73f9a4aSJohannes Berg struct cfg80211_chan_def *chandef) 100b73f9a4aSJohannes Berg { 101b73f9a4aSJohannes Berg struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 102b1a6db13SAvraham Stern /* 103*20578872SAvraham Stern * The command structure is the same for versions 6, 7 and 8 (only the 104b1a6db13SAvraham Stern * field interpretation is different), so the same struct can be use 105b1a6db13SAvraham Stern * for all cases. 106b1a6db13SAvraham Stern */ 107*20578872SAvraham Stern struct iwl_tof_responder_config_cmd_v8 cmd = { 108b73f9a4aSJohannes Berg .channel_num = chandef->chan->hw_value, 109b73f9a4aSJohannes Berg .cmd_valid_fields = 110b73f9a4aSJohannes Berg cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO | 111b73f9a4aSJohannes Berg IWL_TOF_RESPONDER_CMD_VALID_BSSID | 112b73f9a4aSJohannes Berg IWL_TOF_RESPONDER_CMD_VALID_STA_ID), 113b73f9a4aSJohannes Berg .sta_id = mvmvif->bcast_sta.sta_id, 114b73f9a4aSJohannes Berg }; 1154af11950SMordechay Goodstein u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, 1163830a01cSAvraham Stern TOF_RESPONDER_CONFIG_CMD, 6); 117b1a6db13SAvraham Stern int err; 118b73f9a4aSJohannes Berg 119b73f9a4aSJohannes Berg lockdep_assert_held(&mvm->mutex); 120b73f9a4aSJohannes Berg 121*20578872SAvraham Stern if (cmd_ver == 8) 122*20578872SAvraham Stern iwl_mvm_ftm_responder_set_ndp(mvm, &cmd); 123*20578872SAvraham Stern 124*20578872SAvraham Stern if (cmd_ver >= 7) 125b1a6db13SAvraham Stern err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw, 126b1a6db13SAvraham Stern &cmd.ctrl_ch_position); 127b1a6db13SAvraham Stern else 128b1a6db13SAvraham Stern err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw, 129b1a6db13SAvraham Stern &cmd.ctrl_ch_position); 130b1a6db13SAvraham Stern 131b1a6db13SAvraham Stern if (err) { 132b1a6db13SAvraham Stern IWL_ERR(mvm, "Failed to set responder bandwidth\n"); 133b1a6db13SAvraham Stern return err; 134b73f9a4aSJohannes Berg } 135b73f9a4aSJohannes Berg 136b73f9a4aSJohannes Berg memcpy(cmd.bssid, vif->addr, ETH_ALEN); 137b73f9a4aSJohannes Berg 138b73f9a4aSJohannes Berg return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD, 139b73f9a4aSJohannes Berg LOCATION_GROUP, 0), 140b73f9a4aSJohannes Berg 0, sizeof(cmd), &cmd); 141b73f9a4aSJohannes Berg } 142b73f9a4aSJohannes Berg 143b73f9a4aSJohannes Berg static int 1443830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_v2(struct iwl_mvm *mvm, 145b73f9a4aSJohannes Berg struct ieee80211_vif *vif, 146b73f9a4aSJohannes Berg struct ieee80211_ftm_responder_params *params) 147b73f9a4aSJohannes Berg { 1483830a01cSAvraham Stern struct iwl_tof_responder_dyn_config_cmd_v2 cmd = { 149b73f9a4aSJohannes Berg .lci_len = cpu_to_le32(params->lci_len + 2), 150b73f9a4aSJohannes Berg .civic_len = cpu_to_le32(params->civicloc_len + 2), 151b73f9a4aSJohannes Berg }; 152b73f9a4aSJohannes Berg u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0}; 153b73f9a4aSJohannes Berg struct iwl_host_cmd hcmd = { 154b73f9a4aSJohannes Berg .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD, 155b73f9a4aSJohannes Berg LOCATION_GROUP, 0), 156b73f9a4aSJohannes Berg .data[0] = &cmd, 157b73f9a4aSJohannes Berg .len[0] = sizeof(cmd), 158b73f9a4aSJohannes Berg .data[1] = &data, 159b73f9a4aSJohannes Berg /* .len[1] set later */ 160b73f9a4aSJohannes Berg /* may not be able to DMA from stack */ 161b73f9a4aSJohannes Berg .dataflags[1] = IWL_HCMD_DFL_DUP, 162b73f9a4aSJohannes Berg }; 163b73f9a4aSJohannes Berg u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4); 164b73f9a4aSJohannes Berg u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4); 165b73f9a4aSJohannes Berg u8 *pos = data; 166b73f9a4aSJohannes Berg 167b73f9a4aSJohannes Berg lockdep_assert_held(&mvm->mutex); 168b73f9a4aSJohannes Berg 169b73f9a4aSJohannes Berg if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) { 170b73f9a4aSJohannes Berg IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n", 171b73f9a4aSJohannes Berg params->lci_len, params->civicloc_len); 172b73f9a4aSJohannes Berg return -ENOBUFS; 173b73f9a4aSJohannes Berg } 174b73f9a4aSJohannes Berg 175b73f9a4aSJohannes Berg pos[0] = WLAN_EID_MEASURE_REPORT; 176b73f9a4aSJohannes Berg pos[1] = params->lci_len; 177b73f9a4aSJohannes Berg memcpy(pos + 2, params->lci, params->lci_len); 178b73f9a4aSJohannes Berg 179b73f9a4aSJohannes Berg pos += aligned_lci_len; 180b73f9a4aSJohannes Berg pos[0] = WLAN_EID_MEASURE_REPORT; 181b73f9a4aSJohannes Berg pos[1] = params->civicloc_len; 182b73f9a4aSJohannes Berg memcpy(pos + 2, params->civicloc, params->civicloc_len); 183b73f9a4aSJohannes Berg 184b73f9a4aSJohannes Berg hcmd.len[1] = aligned_lci_len + aligned_civicloc_len; 185b73f9a4aSJohannes Berg 186b73f9a4aSJohannes Berg return iwl_mvm_send_cmd(mvm, &hcmd); 187b73f9a4aSJohannes Berg } 188b73f9a4aSJohannes Berg 1893830a01cSAvraham Stern static int 1903830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_v3(struct iwl_mvm *mvm, 1913830a01cSAvraham Stern struct ieee80211_vif *vif, 1923830a01cSAvraham Stern struct ieee80211_ftm_responder_params *params, 1933830a01cSAvraham Stern struct iwl_mvm_pasn_hltk_data *hltk_data) 1943830a01cSAvraham Stern { 1953830a01cSAvraham Stern struct iwl_tof_responder_dyn_config_cmd cmd; 1963830a01cSAvraham Stern struct iwl_host_cmd hcmd = { 1973830a01cSAvraham Stern .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD, 1983830a01cSAvraham Stern LOCATION_GROUP, 0), 1993830a01cSAvraham Stern .data[0] = &cmd, 2003830a01cSAvraham Stern .len[0] = sizeof(cmd), 2013830a01cSAvraham Stern /* may not be able to DMA from stack */ 2023830a01cSAvraham Stern .dataflags[0] = IWL_HCMD_DFL_DUP, 2033830a01cSAvraham Stern }; 2043830a01cSAvraham Stern 2053830a01cSAvraham Stern lockdep_assert_held(&mvm->mutex); 2063830a01cSAvraham Stern 2073830a01cSAvraham Stern cmd.valid_flags = 0; 2083830a01cSAvraham Stern 2093830a01cSAvraham Stern if (params) { 2103830a01cSAvraham Stern if (params->lci_len + 2 > sizeof(cmd.lci_buf) || 2113830a01cSAvraham Stern params->civicloc_len + 2 > sizeof(cmd.civic_buf)) { 2123830a01cSAvraham Stern IWL_ERR(mvm, 2133830a01cSAvraham Stern "LCI/civic data too big (lci=%zd, civic=%zd)\n", 2143830a01cSAvraham Stern params->lci_len, params->civicloc_len); 2153830a01cSAvraham Stern return -ENOBUFS; 2163830a01cSAvraham Stern } 2173830a01cSAvraham Stern 2183830a01cSAvraham Stern cmd.lci_buf[0] = WLAN_EID_MEASURE_REPORT; 2193830a01cSAvraham Stern cmd.lci_buf[1] = params->lci_len; 2203830a01cSAvraham Stern memcpy(cmd.lci_buf + 2, params->lci, params->lci_len); 2213830a01cSAvraham Stern cmd.lci_len = params->lci_len + 2; 2223830a01cSAvraham Stern 2233830a01cSAvraham Stern cmd.civic_buf[0] = WLAN_EID_MEASURE_REPORT; 2243830a01cSAvraham Stern cmd.civic_buf[1] = params->civicloc_len; 2253830a01cSAvraham Stern memcpy(cmd.civic_buf + 2, params->civicloc, 2263830a01cSAvraham Stern params->civicloc_len); 2273830a01cSAvraham Stern cmd.civic_len = params->civicloc_len + 2; 2283830a01cSAvraham Stern 2293830a01cSAvraham Stern cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_LCI | 2303830a01cSAvraham Stern IWL_RESPONDER_DYN_CFG_VALID_CIVIC; 2313830a01cSAvraham Stern } 2323830a01cSAvraham Stern 2333830a01cSAvraham Stern if (hltk_data) { 2343830a01cSAvraham Stern if (hltk_data->cipher > IWL_LOCATION_CIPHER_GCMP_256) { 2353830a01cSAvraham Stern IWL_ERR(mvm, "invalid cipher: %u\n", 2363830a01cSAvraham Stern hltk_data->cipher); 2373830a01cSAvraham Stern return -EINVAL; 2383830a01cSAvraham Stern } 2393830a01cSAvraham Stern 2403830a01cSAvraham Stern cmd.cipher = hltk_data->cipher; 2413830a01cSAvraham Stern memcpy(cmd.addr, hltk_data->addr, sizeof(cmd.addr)); 2423830a01cSAvraham Stern memcpy(cmd.hltk_buf, hltk_data->hltk, sizeof(cmd.hltk_buf)); 2433830a01cSAvraham Stern cmd.valid_flags |= IWL_RESPONDER_DYN_CFG_VALID_PASN_STA; 2443830a01cSAvraham Stern } 2453830a01cSAvraham Stern 2463830a01cSAvraham Stern return iwl_mvm_send_cmd(mvm, &hcmd); 2473830a01cSAvraham Stern } 2483830a01cSAvraham Stern 2493830a01cSAvraham Stern static int 2503830a01cSAvraham Stern iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm, 2513830a01cSAvraham Stern struct ieee80211_vif *vif, 2523830a01cSAvraham Stern struct ieee80211_ftm_responder_params *params) 2533830a01cSAvraham Stern { 2543830a01cSAvraham Stern int ret; 2553830a01cSAvraham Stern u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, 2563830a01cSAvraham Stern TOF_RESPONDER_DYN_CONFIG_CMD, 2); 2573830a01cSAvraham Stern 2583830a01cSAvraham Stern switch (cmd_ver) { 2593830a01cSAvraham Stern case 2: 2603830a01cSAvraham Stern ret = iwl_mvm_ftm_responder_dyn_cfg_v2(mvm, vif, 2613830a01cSAvraham Stern params); 2623830a01cSAvraham Stern break; 2633830a01cSAvraham Stern case 3: 2643830a01cSAvraham Stern ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, 2653830a01cSAvraham Stern params, NULL); 2663830a01cSAvraham Stern break; 2673830a01cSAvraham Stern default: 2683830a01cSAvraham Stern IWL_ERR(mvm, "Unsupported DYN_CONFIG_CMD version %u\n", 2693830a01cSAvraham Stern cmd_ver); 2703830a01cSAvraham Stern ret = -ENOTSUPP; 2713830a01cSAvraham Stern } 2723830a01cSAvraham Stern 2733830a01cSAvraham Stern return ret; 2743830a01cSAvraham Stern } 2753830a01cSAvraham Stern 276890d814bSAvraham Stern static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm, 277890d814bSAvraham Stern struct ieee80211_vif *vif, 278890d814bSAvraham Stern struct iwl_mvm_pasn_sta *sta) 279890d814bSAvraham Stern { 280890d814bSAvraham Stern list_del(&sta->list); 281890d814bSAvraham Stern iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id); 282890d814bSAvraham Stern iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta); 283890d814bSAvraham Stern kfree(sta); 284890d814bSAvraham Stern } 285890d814bSAvraham Stern 286be82ecd3SAvraham Stern int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, 287be82ecd3SAvraham Stern struct ieee80211_vif *vif, 288be82ecd3SAvraham Stern u8 *addr, u32 cipher, u8 *tk, u32 tk_len, 289be82ecd3SAvraham Stern u8 *hltk, u32 hltk_len) 290be82ecd3SAvraham Stern { 291be82ecd3SAvraham Stern int ret; 29268ad2474SAvraham Stern struct iwl_mvm_pasn_sta *sta = NULL; 293890d814bSAvraham Stern struct iwl_mvm_pasn_hltk_data hltk_data = { 294890d814bSAvraham Stern .addr = addr, 295890d814bSAvraham Stern .hltk = hltk, 296890d814bSAvraham Stern }; 297890d814bSAvraham Stern u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, 298890d814bSAvraham Stern TOF_RESPONDER_DYN_CONFIG_CMD, 2); 299be82ecd3SAvraham Stern 300be82ecd3SAvraham Stern lockdep_assert_held(&mvm->mutex); 301be82ecd3SAvraham Stern 302890d814bSAvraham Stern if (cmd_ver < 3) { 303890d814bSAvraham Stern IWL_ERR(mvm, "Adding PASN station not supported by FW\n"); 304890d814bSAvraham Stern return -ENOTSUPP; 305890d814bSAvraham Stern } 306890d814bSAvraham Stern 307890d814bSAvraham Stern hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher); 308890d814bSAvraham Stern if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) { 309890d814bSAvraham Stern IWL_ERR(mvm, "invalid cipher: %u\n", cipher); 310890d814bSAvraham Stern return -EINVAL; 311890d814bSAvraham Stern } 312890d814bSAvraham Stern 31368ad2474SAvraham Stern if (tk && tk_len) { 31468ad2474SAvraham Stern sta = kzalloc(sizeof(*sta), GFP_KERNEL); 315be82ecd3SAvraham Stern if (!sta) 316be82ecd3SAvraham Stern return -ENOBUFS; 317be82ecd3SAvraham Stern 31868ad2474SAvraham Stern ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr, 31968ad2474SAvraham Stern cipher, tk, tk_len); 320be82ecd3SAvraham Stern if (ret) { 321be82ecd3SAvraham Stern kfree(sta); 322be82ecd3SAvraham Stern return ret; 323be82ecd3SAvraham Stern } 324be82ecd3SAvraham Stern 325be82ecd3SAvraham Stern memcpy(sta->addr, addr, ETH_ALEN); 326be82ecd3SAvraham Stern list_add_tail(&sta->list, &mvm->resp_pasn_list); 327bebc14dbSAvraham Stern } 328bebc14dbSAvraham Stern 329bebc14dbSAvraham Stern ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, &hltk_data); 330bebc14dbSAvraham Stern if (ret && sta) 331bebc14dbSAvraham Stern iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); 332bebc14dbSAvraham Stern 333bebc14dbSAvraham Stern return ret; 334be82ecd3SAvraham Stern } 335be82ecd3SAvraham Stern 336be82ecd3SAvraham Stern int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm, 337be82ecd3SAvraham Stern struct ieee80211_vif *vif, u8 *addr) 338be82ecd3SAvraham Stern { 339be82ecd3SAvraham Stern struct iwl_mvm_pasn_sta *sta, *prev; 340be82ecd3SAvraham Stern 341be82ecd3SAvraham Stern lockdep_assert_held(&mvm->mutex); 342be82ecd3SAvraham Stern 343be82ecd3SAvraham Stern list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) { 344be82ecd3SAvraham Stern if (!memcmp(sta->addr, addr, ETH_ALEN)) { 345be82ecd3SAvraham Stern iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); 346be82ecd3SAvraham Stern return 0; 347be82ecd3SAvraham Stern } 348be82ecd3SAvraham Stern } 349be82ecd3SAvraham Stern 350be82ecd3SAvraham Stern IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr); 351be82ecd3SAvraham Stern return -EINVAL; 352be82ecd3SAvraham Stern } 353be82ecd3SAvraham Stern 354b73f9a4aSJohannes Berg int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 355b73f9a4aSJohannes Berg { 356b73f9a4aSJohannes Berg struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 357b73f9a4aSJohannes Berg struct ieee80211_ftm_responder_params *params; 358b73f9a4aSJohannes Berg struct ieee80211_chanctx_conf ctx, *pctx; 359b73f9a4aSJohannes Berg u16 *phy_ctxt_id; 360b73f9a4aSJohannes Berg struct iwl_mvm_phy_ctxt *phy_ctxt; 361b73f9a4aSJohannes Berg int ret; 362b73f9a4aSJohannes Berg 363b73f9a4aSJohannes Berg params = vif->bss_conf.ftmr_params; 364b73f9a4aSJohannes Berg 365b73f9a4aSJohannes Berg lockdep_assert_held(&mvm->mutex); 366b73f9a4aSJohannes Berg 367b73f9a4aSJohannes Berg if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder)) 368b73f9a4aSJohannes Berg return -EINVAL; 369b73f9a4aSJohannes Berg 370b73f9a4aSJohannes Berg if (vif->p2p || vif->type != NL80211_IFTYPE_AP || 371b73f9a4aSJohannes Berg !mvmvif->ap_ibss_active) { 372b73f9a4aSJohannes Berg IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); 373b73f9a4aSJohannes Berg return -EIO; 374b73f9a4aSJohannes Berg } 375b73f9a4aSJohannes Berg 376b73f9a4aSJohannes Berg rcu_read_lock(); 377b73f9a4aSJohannes Berg pctx = rcu_dereference(vif->chanctx_conf); 378b73f9a4aSJohannes Berg /* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care 379b73f9a4aSJohannes Berg * about changes in the ctx after releasing the lock because the driver 380b73f9a4aSJohannes Berg * is still protected by the mutex. */ 381b73f9a4aSJohannes Berg ctx = *pctx; 382b73f9a4aSJohannes Berg phy_ctxt_id = (u16 *)pctx->drv_priv; 383b73f9a4aSJohannes Berg rcu_read_unlock(); 384b73f9a4aSJohannes Berg 385b73f9a4aSJohannes Berg phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; 386b73f9a4aSJohannes Berg ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def, 387b73f9a4aSJohannes Berg ctx.rx_chains_static, 388b73f9a4aSJohannes Berg ctx.rx_chains_dynamic); 389b73f9a4aSJohannes Berg if (ret) 390b73f9a4aSJohannes Berg return ret; 391b73f9a4aSJohannes Berg 392b73f9a4aSJohannes Berg ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def); 393b73f9a4aSJohannes Berg if (ret) 394b73f9a4aSJohannes Berg return ret; 395b73f9a4aSJohannes Berg 396b73f9a4aSJohannes Berg if (params) 397b73f9a4aSJohannes Berg ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params); 398b73f9a4aSJohannes Berg 399b73f9a4aSJohannes Berg return ret; 400b73f9a4aSJohannes Berg } 401b73f9a4aSJohannes Berg 402be82ecd3SAvraham Stern void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm, 403be82ecd3SAvraham Stern struct ieee80211_vif *vif) 404be82ecd3SAvraham Stern { 405be82ecd3SAvraham Stern struct iwl_mvm_pasn_sta *sta, *prev; 406be82ecd3SAvraham Stern 407be82ecd3SAvraham Stern lockdep_assert_held(&mvm->mutex); 408be82ecd3SAvraham Stern 409be82ecd3SAvraham Stern list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) 410be82ecd3SAvraham Stern iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); 411be82ecd3SAvraham Stern } 412be82ecd3SAvraham Stern 413b73f9a4aSJohannes Berg void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm, 414b73f9a4aSJohannes Berg struct ieee80211_vif *vif) 415b73f9a4aSJohannes Berg { 416b73f9a4aSJohannes Berg if (!vif->bss_conf.ftm_responder) 417b73f9a4aSJohannes Berg return; 418b73f9a4aSJohannes Berg 419be82ecd3SAvraham Stern iwl_mvm_ftm_responder_clear(mvm, vif); 420b73f9a4aSJohannes Berg iwl_mvm_ftm_start_responder(mvm, vif); 421b73f9a4aSJohannes Berg } 422b73f9a4aSJohannes Berg 423b73f9a4aSJohannes Berg void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm, 424b73f9a4aSJohannes Berg struct iwl_rx_cmd_buffer *rxb) 425b73f9a4aSJohannes Berg { 426b73f9a4aSJohannes Berg struct iwl_rx_packet *pkt = rxb_addr(rxb); 427b73f9a4aSJohannes Berg struct iwl_ftm_responder_stats *resp = (void *)pkt->data; 428b73f9a4aSJohannes Berg struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats; 429b73f9a4aSJohannes Berg u32 flags = le32_to_cpu(resp->flags); 430b73f9a4aSJohannes Berg 431b73f9a4aSJohannes Berg if (resp->success_ftm == resp->ftm_per_burst) 432b73f9a4aSJohannes Berg stats->success_num++; 433b73f9a4aSJohannes Berg else if (resp->success_ftm >= 2) 434b73f9a4aSJohannes Berg stats->partial_num++; 435b73f9a4aSJohannes Berg else 436b73f9a4aSJohannes Berg stats->failed_num++; 437b73f9a4aSJohannes Berg 438b73f9a4aSJohannes Berg if ((flags & FTM_RESP_STAT_ASAP_REQ) && 439b73f9a4aSJohannes Berg (flags & FTM_RESP_STAT_ASAP_RESP)) 440b73f9a4aSJohannes Berg stats->asap_num++; 441b73f9a4aSJohannes Berg 442b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_NON_ASAP_RESP) 443b73f9a4aSJohannes Berg stats->non_asap_num++; 444b73f9a4aSJohannes Berg 445b73f9a4aSJohannes Berg stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC; 446b73f9a4aSJohannes Berg 447b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN) 448b73f9a4aSJohannes Berg stats->unknown_triggers_num++; 449b73f9a4aSJohannes Berg 450b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_DUP) 451b73f9a4aSJohannes Berg stats->reschedule_requests_num++; 452b73f9a4aSJohannes Berg 453b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN) 454b73f9a4aSJohannes Berg stats->out_of_window_triggers_num++; 455b73f9a4aSJohannes Berg } 456