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 9*b1a6db13SAvraham Stern * Copyright (C) 2018 - 2019 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 30*b1a6db13SAvraham Stern * Copyright (C) 2018 - 2019 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 65*b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef, 66*b1a6db13SAvraham Stern u8 *bw, u8 *ctrl_ch_position) 67*b1a6db13SAvraham Stern { 68*b1a6db13SAvraham Stern switch (chandef->width) { 69*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20_NOHT: 70*b1a6db13SAvraham Stern *bw = IWL_TOF_BW_20_LEGACY; 71*b1a6db13SAvraham Stern break; 72*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20: 73*b1a6db13SAvraham Stern *bw = IWL_TOF_BW_20_HT; 74*b1a6db13SAvraham Stern break; 75*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_40: 76*b1a6db13SAvraham Stern *bw = IWL_TOF_BW_40; 77*b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 78*b1a6db13SAvraham Stern break; 79*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_80: 80*b1a6db13SAvraham Stern *bw = IWL_TOF_BW_80; 81*b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 82*b1a6db13SAvraham Stern break; 83*b1a6db13SAvraham Stern default: 84*b1a6db13SAvraham Stern return -ENOTSUPP; 85*b1a6db13SAvraham Stern } 86*b1a6db13SAvraham Stern 87*b1a6db13SAvraham Stern return 0; 88*b1a6db13SAvraham Stern } 89*b1a6db13SAvraham Stern 90*b1a6db13SAvraham Stern static int iwl_mvm_ftm_responder_set_bw_v2(struct cfg80211_chan_def *chandef, 91*b1a6db13SAvraham Stern u8 *format_bw, 92*b1a6db13SAvraham Stern u8 *ctrl_ch_position) 93*b1a6db13SAvraham Stern { 94*b1a6db13SAvraham Stern switch (chandef->width) { 95*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20_NOHT: 96*b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; 97*b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 98*b1a6db13SAvraham Stern break; 99*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_20: 100*b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 101*b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; 102*b1a6db13SAvraham Stern break; 103*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_40: 104*b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_HT; 105*b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; 106*b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 107*b1a6db13SAvraham Stern break; 108*b1a6db13SAvraham Stern case NL80211_CHAN_WIDTH_80: 109*b1a6db13SAvraham Stern *format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; 110*b1a6db13SAvraham Stern *format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; 111*b1a6db13SAvraham Stern *ctrl_ch_position = iwl_mvm_get_ctrl_pos(chandef); 112*b1a6db13SAvraham Stern break; 113*b1a6db13SAvraham Stern default: 114*b1a6db13SAvraham Stern return -ENOTSUPP; 115*b1a6db13SAvraham Stern } 116*b1a6db13SAvraham Stern 117*b1a6db13SAvraham Stern return 0; 118*b1a6db13SAvraham Stern } 119*b1a6db13SAvraham Stern 120b73f9a4aSJohannes Berg static int 121b73f9a4aSJohannes Berg iwl_mvm_ftm_responder_cmd(struct iwl_mvm *mvm, 122b73f9a4aSJohannes Berg struct ieee80211_vif *vif, 123b73f9a4aSJohannes Berg struct cfg80211_chan_def *chandef) 124b73f9a4aSJohannes Berg { 125b73f9a4aSJohannes Berg struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 126*b1a6db13SAvraham Stern /* 127*b1a6db13SAvraham Stern * The command structure is the same for versions 6 and 7, (only the 128*b1a6db13SAvraham Stern * field interpretation is different), so the same struct can be use 129*b1a6db13SAvraham Stern * for all cases. 130*b1a6db13SAvraham Stern */ 131b73f9a4aSJohannes Berg struct iwl_tof_responder_config_cmd cmd = { 132b73f9a4aSJohannes Berg .channel_num = chandef->chan->hw_value, 133b73f9a4aSJohannes Berg .cmd_valid_fields = 134b73f9a4aSJohannes Berg cpu_to_le32(IWL_TOF_RESPONDER_CMD_VALID_CHAN_INFO | 135b73f9a4aSJohannes Berg IWL_TOF_RESPONDER_CMD_VALID_BSSID | 136b73f9a4aSJohannes Berg IWL_TOF_RESPONDER_CMD_VALID_STA_ID), 137b73f9a4aSJohannes Berg .sta_id = mvmvif->bcast_sta.sta_id, 138b73f9a4aSJohannes Berg }; 139*b1a6db13SAvraham Stern u8 cmd_ver = iwl_mvm_lookup_cmd_ver(mvm->fw, LOCATION_GROUP, 140*b1a6db13SAvraham Stern TOF_RESPONDER_CONFIG_CMD); 141*b1a6db13SAvraham Stern int err; 142b73f9a4aSJohannes Berg 143b73f9a4aSJohannes Berg lockdep_assert_held(&mvm->mutex); 144b73f9a4aSJohannes Berg 145*b1a6db13SAvraham Stern if (cmd_ver == 7) 146*b1a6db13SAvraham Stern err = iwl_mvm_ftm_responder_set_bw_v2(chandef, &cmd.format_bw, 147*b1a6db13SAvraham Stern &cmd.ctrl_ch_position); 148*b1a6db13SAvraham Stern else 149*b1a6db13SAvraham Stern err = iwl_mvm_ftm_responder_set_bw_v1(chandef, &cmd.format_bw, 150*b1a6db13SAvraham Stern &cmd.ctrl_ch_position); 151*b1a6db13SAvraham Stern 152*b1a6db13SAvraham Stern if (err) { 153*b1a6db13SAvraham Stern IWL_ERR(mvm, "Failed to set responder bandwidth\n"); 154*b1a6db13SAvraham Stern return err; 155b73f9a4aSJohannes Berg } 156b73f9a4aSJohannes Berg 157b73f9a4aSJohannes Berg memcpy(cmd.bssid, vif->addr, ETH_ALEN); 158b73f9a4aSJohannes Berg 159b73f9a4aSJohannes Berg return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_RESPONDER_CONFIG_CMD, 160b73f9a4aSJohannes Berg LOCATION_GROUP, 0), 161b73f9a4aSJohannes Berg 0, sizeof(cmd), &cmd); 162b73f9a4aSJohannes Berg } 163b73f9a4aSJohannes Berg 164b73f9a4aSJohannes Berg static int 165b73f9a4aSJohannes Berg iwl_mvm_ftm_responder_dyn_cfg_cmd(struct iwl_mvm *mvm, 166b73f9a4aSJohannes Berg struct ieee80211_vif *vif, 167b73f9a4aSJohannes Berg struct ieee80211_ftm_responder_params *params) 168b73f9a4aSJohannes Berg { 169b73f9a4aSJohannes Berg struct iwl_tof_responder_dyn_config_cmd cmd = { 170b73f9a4aSJohannes Berg .lci_len = cpu_to_le32(params->lci_len + 2), 171b73f9a4aSJohannes Berg .civic_len = cpu_to_le32(params->civicloc_len + 2), 172b73f9a4aSJohannes Berg }; 173b73f9a4aSJohannes Berg u8 data[IWL_LCI_CIVIC_IE_MAX_SIZE] = {0}; 174b73f9a4aSJohannes Berg struct iwl_host_cmd hcmd = { 175b73f9a4aSJohannes Berg .id = iwl_cmd_id(TOF_RESPONDER_DYN_CONFIG_CMD, 176b73f9a4aSJohannes Berg LOCATION_GROUP, 0), 177b73f9a4aSJohannes Berg .data[0] = &cmd, 178b73f9a4aSJohannes Berg .len[0] = sizeof(cmd), 179b73f9a4aSJohannes Berg .data[1] = &data, 180b73f9a4aSJohannes Berg /* .len[1] set later */ 181b73f9a4aSJohannes Berg /* may not be able to DMA from stack */ 182b73f9a4aSJohannes Berg .dataflags[1] = IWL_HCMD_DFL_DUP, 183b73f9a4aSJohannes Berg }; 184b73f9a4aSJohannes Berg u32 aligned_lci_len = ALIGN(params->lci_len + 2, 4); 185b73f9a4aSJohannes Berg u32 aligned_civicloc_len = ALIGN(params->civicloc_len + 2, 4); 186b73f9a4aSJohannes Berg u8 *pos = data; 187b73f9a4aSJohannes Berg 188b73f9a4aSJohannes Berg lockdep_assert_held(&mvm->mutex); 189b73f9a4aSJohannes Berg 190b73f9a4aSJohannes Berg if (aligned_lci_len + aligned_civicloc_len > sizeof(data)) { 191b73f9a4aSJohannes Berg IWL_ERR(mvm, "LCI/civicloc data too big (%zd + %zd)\n", 192b73f9a4aSJohannes Berg params->lci_len, params->civicloc_len); 193b73f9a4aSJohannes Berg return -ENOBUFS; 194b73f9a4aSJohannes Berg } 195b73f9a4aSJohannes Berg 196b73f9a4aSJohannes Berg pos[0] = WLAN_EID_MEASURE_REPORT; 197b73f9a4aSJohannes Berg pos[1] = params->lci_len; 198b73f9a4aSJohannes Berg memcpy(pos + 2, params->lci, params->lci_len); 199b73f9a4aSJohannes Berg 200b73f9a4aSJohannes Berg pos += aligned_lci_len; 201b73f9a4aSJohannes Berg pos[0] = WLAN_EID_MEASURE_REPORT; 202b73f9a4aSJohannes Berg pos[1] = params->civicloc_len; 203b73f9a4aSJohannes Berg memcpy(pos + 2, params->civicloc, params->civicloc_len); 204b73f9a4aSJohannes Berg 205b73f9a4aSJohannes Berg hcmd.len[1] = aligned_lci_len + aligned_civicloc_len; 206b73f9a4aSJohannes Berg 207b73f9a4aSJohannes Berg return iwl_mvm_send_cmd(mvm, &hcmd); 208b73f9a4aSJohannes Berg } 209b73f9a4aSJohannes Berg 210b73f9a4aSJohannes Berg int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif) 211b73f9a4aSJohannes Berg { 212b73f9a4aSJohannes Berg struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 213b73f9a4aSJohannes Berg struct ieee80211_ftm_responder_params *params; 214b73f9a4aSJohannes Berg struct ieee80211_chanctx_conf ctx, *pctx; 215b73f9a4aSJohannes Berg u16 *phy_ctxt_id; 216b73f9a4aSJohannes Berg struct iwl_mvm_phy_ctxt *phy_ctxt; 217b73f9a4aSJohannes Berg int ret; 218b73f9a4aSJohannes Berg 219b73f9a4aSJohannes Berg params = vif->bss_conf.ftmr_params; 220b73f9a4aSJohannes Berg 221b73f9a4aSJohannes Berg lockdep_assert_held(&mvm->mutex); 222b73f9a4aSJohannes Berg 223b73f9a4aSJohannes Berg if (WARN_ON_ONCE(!vif->bss_conf.ftm_responder)) 224b73f9a4aSJohannes Berg return -EINVAL; 225b73f9a4aSJohannes Berg 226b73f9a4aSJohannes Berg if (vif->p2p || vif->type != NL80211_IFTYPE_AP || 227b73f9a4aSJohannes Berg !mvmvif->ap_ibss_active) { 228b73f9a4aSJohannes Berg IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); 229b73f9a4aSJohannes Berg return -EIO; 230b73f9a4aSJohannes Berg } 231b73f9a4aSJohannes Berg 232b73f9a4aSJohannes Berg rcu_read_lock(); 233b73f9a4aSJohannes Berg pctx = rcu_dereference(vif->chanctx_conf); 234b73f9a4aSJohannes Berg /* Copy the ctx to unlock the rcu and send the phy ctxt. We don't care 235b73f9a4aSJohannes Berg * about changes in the ctx after releasing the lock because the driver 236b73f9a4aSJohannes Berg * is still protected by the mutex. */ 237b73f9a4aSJohannes Berg ctx = *pctx; 238b73f9a4aSJohannes Berg phy_ctxt_id = (u16 *)pctx->drv_priv; 239b73f9a4aSJohannes Berg rcu_read_unlock(); 240b73f9a4aSJohannes Berg 241b73f9a4aSJohannes Berg phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; 242b73f9a4aSJohannes Berg ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx.def, 243b73f9a4aSJohannes Berg ctx.rx_chains_static, 244b73f9a4aSJohannes Berg ctx.rx_chains_dynamic); 245b73f9a4aSJohannes Berg if (ret) 246b73f9a4aSJohannes Berg return ret; 247b73f9a4aSJohannes Berg 248b73f9a4aSJohannes Berg ret = iwl_mvm_ftm_responder_cmd(mvm, vif, &ctx.def); 249b73f9a4aSJohannes Berg if (ret) 250b73f9a4aSJohannes Berg return ret; 251b73f9a4aSJohannes Berg 252b73f9a4aSJohannes Berg if (params) 253b73f9a4aSJohannes Berg ret = iwl_mvm_ftm_responder_dyn_cfg_cmd(mvm, vif, params); 254b73f9a4aSJohannes Berg 255b73f9a4aSJohannes Berg return ret; 256b73f9a4aSJohannes Berg } 257b73f9a4aSJohannes Berg 258b73f9a4aSJohannes Berg void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm, 259b73f9a4aSJohannes Berg struct ieee80211_vif *vif) 260b73f9a4aSJohannes Berg { 261b73f9a4aSJohannes Berg if (!vif->bss_conf.ftm_responder) 262b73f9a4aSJohannes Berg return; 263b73f9a4aSJohannes Berg 264b73f9a4aSJohannes Berg iwl_mvm_ftm_start_responder(mvm, vif); 265b73f9a4aSJohannes Berg } 266b73f9a4aSJohannes Berg 267b73f9a4aSJohannes Berg void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm, 268b73f9a4aSJohannes Berg struct iwl_rx_cmd_buffer *rxb) 269b73f9a4aSJohannes Berg { 270b73f9a4aSJohannes Berg struct iwl_rx_packet *pkt = rxb_addr(rxb); 271b73f9a4aSJohannes Berg struct iwl_ftm_responder_stats *resp = (void *)pkt->data; 272b73f9a4aSJohannes Berg struct cfg80211_ftm_responder_stats *stats = &mvm->ftm_resp_stats; 273b73f9a4aSJohannes Berg u32 flags = le32_to_cpu(resp->flags); 274b73f9a4aSJohannes Berg 275b73f9a4aSJohannes Berg if (resp->success_ftm == resp->ftm_per_burst) 276b73f9a4aSJohannes Berg stats->success_num++; 277b73f9a4aSJohannes Berg else if (resp->success_ftm >= 2) 278b73f9a4aSJohannes Berg stats->partial_num++; 279b73f9a4aSJohannes Berg else 280b73f9a4aSJohannes Berg stats->failed_num++; 281b73f9a4aSJohannes Berg 282b73f9a4aSJohannes Berg if ((flags & FTM_RESP_STAT_ASAP_REQ) && 283b73f9a4aSJohannes Berg (flags & FTM_RESP_STAT_ASAP_RESP)) 284b73f9a4aSJohannes Berg stats->asap_num++; 285b73f9a4aSJohannes Berg 286b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_NON_ASAP_RESP) 287b73f9a4aSJohannes Berg stats->non_asap_num++; 288b73f9a4aSJohannes Berg 289b73f9a4aSJohannes Berg stats->total_duration_ms += le32_to_cpu(resp->duration) / USEC_PER_MSEC; 290b73f9a4aSJohannes Berg 291b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_TRIGGER_UNKNOWN) 292b73f9a4aSJohannes Berg stats->unknown_triggers_num++; 293b73f9a4aSJohannes Berg 294b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_DUP) 295b73f9a4aSJohannes Berg stats->reschedule_requests_num++; 296b73f9a4aSJohannes Berg 297b73f9a4aSJohannes Berg if (flags & FTM_RESP_STAT_NON_ASAP_OUT_WIN) 298b73f9a4aSJohannes Berg stats->out_of_window_triggers_num++; 299b73f9a4aSJohannes Berg } 300