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