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) 2012 - 2014 Intel Corporation. All rights reserved. 9 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 10 * Copyright(c) 2017 Intel Deutschland GmbH 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of version 2 of the GNU General Public License as 14 * published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 24 * USA 25 * 26 * The full GNU General Public License is included in this distribution 27 * in the file called COPYING. 28 * 29 * Contact Information: 30 * Intel Linux Wireless <linuxwifi@intel.com> 31 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 32 * 33 * BSD LICENSE 34 * 35 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 36 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 37 * All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 43 * * Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * * Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in 47 * the documentation and/or other materials provided with the 48 * distribution. 49 * * Neither the name Intel Corporation nor the names of its 50 * contributors may be used to endorse or promote products derived 51 * from this software without specific prior written permission. 52 * 53 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 54 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 55 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 56 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 57 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 58 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 59 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 60 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 61 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 62 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 63 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 64 * 65 *****************************************************************************/ 66 67 #include <net/mac80211.h> 68 #include "fw-api.h" 69 #include "mvm.h" 70 71 /* Maps the driver specific channel width definition to the fw values */ 72 u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) 73 { 74 switch (chandef->width) { 75 case NL80211_CHAN_WIDTH_20_NOHT: 76 case NL80211_CHAN_WIDTH_20: 77 return PHY_VHT_CHANNEL_MODE20; 78 case NL80211_CHAN_WIDTH_40: 79 return PHY_VHT_CHANNEL_MODE40; 80 case NL80211_CHAN_WIDTH_80: 81 return PHY_VHT_CHANNEL_MODE80; 82 case NL80211_CHAN_WIDTH_160: 83 return PHY_VHT_CHANNEL_MODE160; 84 default: 85 WARN(1, "Invalid channel width=%u", chandef->width); 86 return PHY_VHT_CHANNEL_MODE20; 87 } 88 } 89 90 /* 91 * Maps the driver specific control channel position (relative to the center 92 * freq) definitions to the the fw values 93 */ 94 u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) 95 { 96 switch (chandef->chan->center_freq - chandef->center_freq1) { 97 case -70: 98 return PHY_VHT_CTRL_POS_4_BELOW; 99 case -50: 100 return PHY_VHT_CTRL_POS_3_BELOW; 101 case -30: 102 return PHY_VHT_CTRL_POS_2_BELOW; 103 case -10: 104 return PHY_VHT_CTRL_POS_1_BELOW; 105 case 10: 106 return PHY_VHT_CTRL_POS_1_ABOVE; 107 case 30: 108 return PHY_VHT_CTRL_POS_2_ABOVE; 109 case 50: 110 return PHY_VHT_CTRL_POS_3_ABOVE; 111 case 70: 112 return PHY_VHT_CTRL_POS_4_ABOVE; 113 default: 114 WARN(1, "Invalid channel definition"); 115 case 0: 116 /* 117 * The FW is expected to check the control channel position only 118 * when in HT/VHT and the channel width is not 20MHz. Return 119 * this value as the default one. 120 */ 121 return PHY_VHT_CTRL_POS_1_BELOW; 122 } 123 } 124 125 /* 126 * Construct the generic fields of the PHY context command 127 */ 128 static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt, 129 struct iwl_phy_context_cmd *cmd, 130 u32 action, u32 apply_time) 131 { 132 memset(cmd, 0, sizeof(struct iwl_phy_context_cmd)); 133 134 cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id, 135 ctxt->color)); 136 cmd->action = cpu_to_le32(action); 137 cmd->apply_time = cpu_to_le32(apply_time); 138 } 139 140 /* 141 * Add the phy configuration to the PHY context command 142 */ 143 static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, 144 struct iwl_phy_context_cmd *cmd, 145 struct cfg80211_chan_def *chandef, 146 u8 chains_static, u8 chains_dynamic) 147 { 148 u8 active_cnt, idle_cnt; 149 150 /* Set the channel info data */ 151 cmd->ci.band = (chandef->chan->band == NL80211_BAND_2GHZ ? 152 PHY_BAND_24 : PHY_BAND_5); 153 154 cmd->ci.channel = chandef->chan->hw_value; 155 cmd->ci.width = iwl_mvm_get_channel_width(chandef); 156 cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); 157 158 /* Set rx the chains */ 159 idle_cnt = chains_static; 160 active_cnt = chains_dynamic; 161 162 /* In scenarios where we only ever use a single-stream rates, 163 * i.e. legacy 11b/g/a associations, single-stream APs or even 164 * static SMPS, enable both chains to get diversity, improving 165 * the case where we're far enough from the AP that attenuation 166 * between the two antennas is sufficiently different to impact 167 * performance. 168 */ 169 if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) { 170 idle_cnt = 2; 171 active_cnt = 2; 172 } 173 174 cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) << 175 PHY_RX_CHAIN_VALID_POS); 176 cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); 177 cmd->rxchain_info |= cpu_to_le32(active_cnt << 178 PHY_RX_CHAIN_MIMO_CNT_POS); 179 #ifdef CONFIG_IWLWIFI_DEBUGFS 180 if (unlikely(mvm->dbgfs_rx_phyinfo)) 181 cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo); 182 #endif 183 184 cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); 185 } 186 187 /* 188 * Send a command to apply the current phy configuration. The command is send 189 * only if something in the configuration changed: in case that this is the 190 * first time that the phy configuration is applied or in case that the phy 191 * configuration changed from the previous apply. 192 */ 193 static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, 194 struct iwl_mvm_phy_ctxt *ctxt, 195 struct cfg80211_chan_def *chandef, 196 u8 chains_static, u8 chains_dynamic, 197 u32 action, u32 apply_time) 198 { 199 struct iwl_phy_context_cmd cmd; 200 int ret; 201 202 /* Set the command header fields */ 203 iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time); 204 205 /* Set the command data */ 206 iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef, 207 chains_static, chains_dynamic); 208 209 ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, 210 sizeof(struct iwl_phy_context_cmd), 211 &cmd); 212 if (ret) 213 IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret); 214 return ret; 215 } 216 217 /* 218 * Send a command to add a PHY context based on the current HW configuration. 219 */ 220 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, 221 struct cfg80211_chan_def *chandef, 222 u8 chains_static, u8 chains_dynamic) 223 { 224 WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && 225 ctxt->ref); 226 lockdep_assert_held(&mvm->mutex); 227 228 ctxt->channel = chandef->chan; 229 230 return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, 231 chains_static, chains_dynamic, 232 FW_CTXT_ACTION_ADD, 0); 233 } 234 235 /* 236 * Update the number of references to the given PHY context. This is valid only 237 * in case the PHY context was already created, i.e., its reference count > 0. 238 */ 239 void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) 240 { 241 lockdep_assert_held(&mvm->mutex); 242 ctxt->ref++; 243 } 244 245 /* 246 * Send a command to modify the PHY context based on the current HW 247 * configuration. Note that the function does not check that the configuration 248 * changed. 249 */ 250 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, 251 struct cfg80211_chan_def *chandef, 252 u8 chains_static, u8 chains_dynamic) 253 { 254 enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY; 255 256 lockdep_assert_held(&mvm->mutex); 257 258 if (fw_has_capa(&mvm->fw->ucode_capa, 259 IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) && 260 ctxt->channel->band != chandef->chan->band) { 261 int ret; 262 263 /* ... remove it here ...*/ 264 ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, 265 chains_static, chains_dynamic, 266 FW_CTXT_ACTION_REMOVE, 0); 267 if (ret) 268 return ret; 269 270 /* ... and proceed to add it again */ 271 action = FW_CTXT_ACTION_ADD; 272 } 273 274 ctxt->channel = chandef->chan; 275 ctxt->width = chandef->width; 276 return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, 277 chains_static, chains_dynamic, 278 action, 0); 279 } 280 281 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) 282 { 283 lockdep_assert_held(&mvm->mutex); 284 285 if (WARN_ON_ONCE(!ctxt)) 286 return; 287 288 ctxt->ref--; 289 } 290 291 static void iwl_mvm_binding_iterator(void *_data, u8 *mac, 292 struct ieee80211_vif *vif) 293 { 294 unsigned long *data = _data; 295 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 296 297 if (!mvmvif->phy_ctxt) 298 return; 299 300 if (vif->type == NL80211_IFTYPE_STATION || 301 vif->type == NL80211_IFTYPE_AP) 302 __set_bit(mvmvif->phy_ctxt->id, data); 303 } 304 305 int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm) 306 { 307 unsigned long phy_ctxt_counter = 0; 308 309 ieee80211_iterate_active_interfaces_atomic(mvm->hw, 310 IEEE80211_IFACE_ITER_NORMAL, 311 iwl_mvm_binding_iterator, 312 &phy_ctxt_counter); 313 314 return hweight8(phy_ctxt_counter); 315 } 316