1 /****************************************************************************** 2 * 3 * This file is provided under a dual BSD/GPLv2 license. When using or 4 * redistributing this file, you may do so under either license. 5 * 6 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH 9 * Copyright (C) 2018 - 2019 Intel Corporation 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of version 2 of the GNU General Public License as 13 * published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * The full GNU General Public License is included in this distribution 21 * in the file called COPYING. 22 * 23 * Contact Information: 24 * Intel Linux Wireless <linuxwifi@intel.com> 25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 26 * 27 * BSD LICENSE 28 * 29 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH 30 * Copyright (C) 2018 - 2019 Intel Corporation 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 37 * * Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * * Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in 41 * the documentation and/or other materials provided with the 42 * distribution. 43 * * Neither the name Intel Corporation nor the names of its 44 * contributors may be used to endorse or promote products derived 45 * from this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 * 59 *****************************************************************************/ 60 #include <net/cfg80211.h> 61 #include <linux/etherdevice.h> 62 #include "mvm.h" 63 #include "constants.h" 64 65 static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef, 66 u8 *bw, u8 *ctrl_ch_position) 67 { 68 switch (chandef->width) { 69 case NL80211_CHAN_WIDTH_20_NOHT: 70 *bw = IWL_TOF_BW_20_LEGACY; 71 break; 72 case NL80211_CHAN_WIDTH_20: 73 *bw = IWL_TOF_BW_20_HT; 74 break; 75 case NL80211_CHAN_WIDTH_40: 76 *bw = IWL_TOF_BW_40; 77 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 78 break; 79 case NL80211_CHAN_WIDTH_80: 80 *bw = IWL_TOF_BW_80; 81 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 82 break; 83 default: 84 return -ENOTSUPP; 85 } 86 87 return 0; 88 } 89 90 static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef, 91 u8 *format_bw, 92 u8 *ctrl_ch_position) 93 { 94 switch (chandef->width) { 95 case NL80211_CHAN_WIDTH_20_NOHT: 96 *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; 97 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 98 break; 99 case NL80211_CHAN_WIDTH_20: 100 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 101 *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 102 break; 103 case NL80211_CHAN_WIDTH_40: 104 *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 105 *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; 106 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 107 break; 108 case NL80211_CHAN_WIDTH_80: 109 *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; 110 *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; 111 *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 112 break; 113 default: 114 return -ENOTSUPP; 115 } 116 117 return 0; 118 } 119 120 static int 121 iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, 122 struct ieee80211_vif *vif, 123 struct cfg80211_chan_def *chandef) 124 { 125 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 126 /* 127 * The command structure is the same for versions 6 and 7, (only the 128 * field interpretation is different), so the same struct can be use 129 * for all cases. 130 */ 131 struct iwl_tof_responder_config_cmd cmd = { 132 .channel_num = chandef->chan->hw_value, 133 .cmd_valid_fields = 134 cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO | 135 IWL_TOF_RESPONDER_CMD_VALID_BSSID | 136 IWL_TOF_RESPONDER_CMD_VALID_STA_ID), 137 .sta_id = mvmvif->bcast_sta.sta_id, 138 }; 139 u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, 140 TOF_RESPONDER_CONFIG_CMD); 141 int err; 142 143 lockdep_assert_held(&mvm->mutex); 144 145 if (cmd_ver == 7) 146 err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw, 147 &cmd.ctrl_ch_position); 148 else 149 err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw, 150 &cmd.ctrl_ch_position); 151 152 if (err) { 153 IWL_ERR(mvm, "Failed to set responder bandwidth\n"); 154 return err; 155 } 156 157 memcpy(cmd.bssid, vif->addr, ETH_ALEN); 158 159 return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD, 160 LOCATION_GROUP, 0), 161 0, sizeof(cmd), &cmd); 162 } 163 164 static int 165 iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm, 166 struct ieee80211_vif *vif, 167 struct ieee80211_ftm_responder_params *params) 168 { 169 struct iwl_tof_responder_dyn_config_cmd cmd = { 170 .lci_len = cpu_to_le32(params->lci_len + 2), 171 .civic_len = cpu_to_le32(params->civicloc_len + 2), 172 }; 173 u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0}; 174 struct iwl_host_cmd hcmd = { 175 .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD, 176 LOCATION_GROUP, 0), 177 .data[0] = &cmd, 178 .len[0] = sizeof(cmd), 179 .data[1] = &data, 180 /* .len[1] set later */ 181 /* may not be able to DMA from stack */ 182 .dataflags[1] = IWL_HCMD_DFL_DUP, 183 }; 184 u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4); 185 u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4); 186 u8 *pos = data; 187 188 lockdep_assert_held(&mvm->mutex); 189 190 if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) { 191 IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n", 192 params->lci_len, params->civicloc_len); 193 return -ENOBUFS; 194 } 195 196 pos[0] = WLAN_EID_MEASURE_REPORT; 197 pos[1] = params->lci_len; 198 memcpy(pos + 2, params->lci, params->lci_len); 199 200 pos += aligned_lci_len; 201 pos[0] = WLAN_EID_MEASURE_REPORT; 202 pos[1] = params->civicloc_len; 203 memcpy(pos + 2, params->civicloc, params->civicloc_len); 204 205 hcmd.len[1] = aligned_lci_len + aligned_civicloc_len; 206 207 return iwl_mvm_send_cmd(mvm, &hcmd); 208 } 209 210 int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 211 { 212 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 213 struct ieee80211_ftm_responder_params *params; 214 struct ieee80211_chanctx_conf ctx, *pctx; 215 u16 *phy_ctxt_id; 216 struct iwl_mvm_phy_ctxt *phy_ctxt; 217 int ret; 218 219 params = vif->bss_conf.ftmr_params; 220 221 lockdep_assert_held(&mvm->mutex); 222 223 if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder)) 224 return -EINVAL; 225 226 if (vif->p2p || vif->type != NL80211_IFTYPE_AP || 227 !mvmvif->ap_ibss_active) { 228 IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); 229 return -EIO; 230 } 231 232 rcu_read_lock(); 233 pctx = rcu_dereference(vif->chanctx_conf); 234 /* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care 235 * about changes in the ctx after releasing the lock because the driver 236 * is still protected by the mutex. */ 237 ctx = *pctx; 238 phy_ctxt_id = (u16 *)pctx->drv_priv; 239 rcu_read_unlock(); 240 241 phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; 242 ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def, 243 ctx.rx_chains_static, 244 ctx.rx_chains_dynamic); 245 if (ret) 246 return ret; 247 248 ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def); 249 if (ret) 250 return ret; 251 252 if (params) 253 ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params); 254 255 return ret; 256 } 257 258 void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm, 259 struct ieee80211_vif *vif) 260 { 261 if (!vif->bss_conf.ftm_responder) 262 return; 263 264 iwl_mvm_ftm_start_responder(mvm, vif); 265 } 266 267 void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm, 268 struct iwl_rx_cmd_buffer *rxb) 269 { 270 struct iwl_rx_packet *pkt = rxb_addr(rxb); 271 struct iwl_ftm_responder_stats *resp = (void *)pkt->data; 272 struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats; 273 u32 flags = le32_to_cpu(resp->flags); 274 275 if (resp->success_ftm == resp->ftm_per_burst) 276 stats->success_num++; 277 else if (resp->success_ftm >= 2) 278 stats->partial_num++; 279 else 280 stats->failed_num++; 281 282 if ((flags & FTM_RESP_STAT_ASAP_REQ) && 283 (flags & FTM_RESP_STAT_ASAP_RESP)) 284 stats->asap_num++; 285 286 if (flags & FTM_RESP_STAT_NON_ASAP_RESP) 287 stats->non_asap_num++; 288 289 stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC; 290 291 if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN) 292 stats->unknown_triggers_num++; 293 294 if (flags & FTM_RESP_STAT_DUP) 295 stats->reschedule_requests_num++; 296 297 if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN) 298 stats->out_of_window_triggers_num++; 299 } 300