Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright (C) 2012-2014, 2018-2022 Intel Corporation
0004  * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
0005  * Copyright (C) 2017 Intel Deutschland GmbH
0006  */
0007 #include <net/mac80211.h>
0008 #include "fw-api.h"
0009 #include "mvm.h"
0010 
0011 /* Maps the driver specific channel width definition to the fw values */
0012 u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
0013 {
0014     switch (chandef->width) {
0015     case NL80211_CHAN_WIDTH_20_NOHT:
0016     case NL80211_CHAN_WIDTH_20:
0017         return PHY_VHT_CHANNEL_MODE20;
0018     case NL80211_CHAN_WIDTH_40:
0019         return PHY_VHT_CHANNEL_MODE40;
0020     case NL80211_CHAN_WIDTH_80:
0021         return PHY_VHT_CHANNEL_MODE80;
0022     case NL80211_CHAN_WIDTH_160:
0023         return PHY_VHT_CHANNEL_MODE160;
0024     default:
0025         WARN(1, "Invalid channel width=%u", chandef->width);
0026         return PHY_VHT_CHANNEL_MODE20;
0027     }
0028 }
0029 
0030 /*
0031  * Maps the driver specific control channel position (relative to the center
0032  * freq) definitions to the the fw values
0033  */
0034 u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
0035 {
0036     switch (chandef->chan->center_freq - chandef->center_freq1) {
0037     case -70:
0038         return PHY_VHT_CTRL_POS_4_BELOW;
0039     case -50:
0040         return PHY_VHT_CTRL_POS_3_BELOW;
0041     case -30:
0042         return PHY_VHT_CTRL_POS_2_BELOW;
0043     case -10:
0044         return PHY_VHT_CTRL_POS_1_BELOW;
0045     case  10:
0046         return PHY_VHT_CTRL_POS_1_ABOVE;
0047     case  30:
0048         return PHY_VHT_CTRL_POS_2_ABOVE;
0049     case  50:
0050         return PHY_VHT_CTRL_POS_3_ABOVE;
0051     case  70:
0052         return PHY_VHT_CTRL_POS_4_ABOVE;
0053     default:
0054         WARN(1, "Invalid channel definition");
0055         fallthrough;
0056     case 0:
0057         /*
0058          * The FW is expected to check the control channel position only
0059          * when in HT/VHT and the channel width is not 20MHz. Return
0060          * this value as the default one.
0061          */
0062         return PHY_VHT_CTRL_POS_1_BELOW;
0063     }
0064 }
0065 
0066 /*
0067  * Construct the generic fields of the PHY context command
0068  */
0069 static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
0070                      struct iwl_phy_context_cmd *cmd,
0071                      u32 action)
0072 {
0073     cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
0074                                 ctxt->color));
0075     cmd->action = cpu_to_le32(action);
0076 }
0077 
0078 static void iwl_mvm_phy_ctxt_set_rxchain(struct iwl_mvm *mvm,
0079                      struct iwl_mvm_phy_ctxt *ctxt,
0080                      __le32 *rxchain_info,
0081                      u8 chains_static,
0082                      u8 chains_dynamic)
0083 {
0084     u8 active_cnt, idle_cnt;
0085 
0086     /* Set rx the chains */
0087     idle_cnt = chains_static;
0088     active_cnt = chains_dynamic;
0089 
0090     /* In scenarios where we only ever use a single-stream rates,
0091      * i.e. legacy 11b/g/a associations, single-stream APs or even
0092      * static SMPS, enable both chains to get diversity, improving
0093      * the case where we're far enough from the AP that attenuation
0094      * between the two antennas is sufficiently different to impact
0095      * performance.
0096      */
0097     if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm, ctxt)) {
0098         idle_cnt = 2;
0099         active_cnt = 2;
0100     }
0101 
0102     /*
0103      * If the firmware requested it, then we know that it supports
0104      * getting zero for the values to indicate "use one, but pick
0105      * which one yourself", which means it can dynamically pick one
0106      * that e.g. has better RSSI.
0107      */
0108     if (mvm->fw_static_smps_request && active_cnt == 1 && idle_cnt == 1) {
0109         idle_cnt = 0;
0110         active_cnt = 0;
0111     }
0112 
0113     *rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) <<
0114                     PHY_RX_CHAIN_VALID_POS);
0115     *rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
0116     *rxchain_info |= cpu_to_le32(active_cnt <<
0117                      PHY_RX_CHAIN_MIMO_CNT_POS);
0118 #ifdef CONFIG_IWLWIFI_DEBUGFS
0119     if (unlikely(mvm->dbgfs_rx_phyinfo))
0120         *rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
0121 #endif
0122 }
0123 
0124 /*
0125  * Add the phy configuration to the PHY context command
0126  */
0127 static void iwl_mvm_phy_ctxt_cmd_data_v1(struct iwl_mvm *mvm,
0128                      struct iwl_mvm_phy_ctxt *ctxt,
0129                      struct iwl_phy_context_cmd_v1 *cmd,
0130                      struct cfg80211_chan_def *chandef,
0131                      u8 chains_static, u8 chains_dynamic)
0132 {
0133     struct iwl_phy_context_cmd_tail *tail =
0134         iwl_mvm_chan_info_cmd_tail(mvm, &cmd->ci);
0135 
0136     /* Set the channel info data */
0137     iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
0138 
0139     iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &tail->rxchain_info,
0140                      chains_static, chains_dynamic);
0141 
0142     tail->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
0143 }
0144 
0145 /*
0146  * Add the phy configuration to the PHY context command
0147  */
0148 static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
0149                       struct iwl_mvm_phy_ctxt *ctxt,
0150                       struct iwl_phy_context_cmd *cmd,
0151                       struct cfg80211_chan_def *chandef,
0152                       u8 chains_static, u8 chains_dynamic)
0153 {
0154     cmd->lmac_id = cpu_to_le32(iwl_mvm_get_lmac_id(mvm->fw,
0155                                chandef->chan->band));
0156 
0157     /* Set the channel info data */
0158     iwl_mvm_set_chan_info_chandef(mvm, &cmd->ci, chandef);
0159 
0160     /* we only support RLC command version 2 */
0161     if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2)
0162         iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd->rxchain_info,
0163                          chains_static, chains_dynamic);
0164 }
0165 
0166 static int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm,
0167                 struct iwl_mvm_phy_ctxt *ctxt,
0168                 u8 chains_static, u8 chains_dynamic)
0169 {
0170     struct iwl_rlc_config_cmd cmd = {
0171         .phy_id = cpu_to_le32(ctxt->id),
0172     };
0173 
0174     if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) < 2)
0175         return 0;
0176 
0177     BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_DRIVER_FORCE !=
0178              PHY_RX_CHAIN_DRIVER_FORCE_MSK);
0179     BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_VALID !=
0180              PHY_RX_CHAIN_VALID_MSK);
0181     BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_FORCE !=
0182              PHY_RX_CHAIN_FORCE_SEL_MSK);
0183     BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_FORCE_MIMO !=
0184              PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK);
0185     BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_COUNT != PHY_RX_CHAIN_CNT_MSK);
0186     BUILD_BUG_ON(IWL_RLC_CHAIN_INFO_MIMO_COUNT !=
0187              PHY_RX_CHAIN_MIMO_CNT_MSK);
0188 
0189     iwl_mvm_phy_ctxt_set_rxchain(mvm, ctxt, &cmd.rlc.rx_chain_info,
0190                      chains_static, chains_dynamic);
0191 
0192     return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(RLC_CONFIG_CMD,
0193                             DATA_PATH_GROUP, 2),
0194                     0, sizeof(cmd), &cmd);
0195 }
0196 
0197 /*
0198  * Send a command to apply the current phy configuration. The command is send
0199  * only if something in the configuration changed: in case that this is the
0200  * first time that the phy configuration is applied or in case that the phy
0201  * configuration changed from the previous apply.
0202  */
0203 static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
0204                   struct iwl_mvm_phy_ctxt *ctxt,
0205                   struct cfg80211_chan_def *chandef,
0206                   u8 chains_static, u8 chains_dynamic,
0207                   u32 action)
0208 {
0209     int ret;
0210     int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1);
0211 
0212     if (ver == 3 || ver == 4) {
0213         struct iwl_phy_context_cmd cmd = {};
0214 
0215         /* Set the command header fields */
0216         iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action);
0217 
0218         /* Set the command data */
0219         iwl_mvm_phy_ctxt_cmd_data(mvm, ctxt, &cmd, chandef,
0220                       chains_static,
0221                       chains_dynamic);
0222 
0223         ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
0224                        0, sizeof(cmd), &cmd);
0225     } else if (ver < 3) {
0226         struct iwl_phy_context_cmd_v1 cmd = {};
0227         u16 len = sizeof(cmd) - iwl_mvm_chan_info_padding(mvm);
0228 
0229         /* Set the command header fields */
0230         iwl_mvm_phy_ctxt_cmd_hdr(ctxt,
0231                      (struct iwl_phy_context_cmd *)&cmd,
0232                      action);
0233 
0234         /* Set the command data */
0235         iwl_mvm_phy_ctxt_cmd_data_v1(mvm, ctxt, &cmd, chandef,
0236                          chains_static,
0237                          chains_dynamic);
0238         ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
0239                        0, len, &cmd);
0240     } else {
0241         IWL_ERR(mvm, "PHY ctxt cmd error ver %d not supported\n", ver);
0242         return -EOPNOTSUPP;
0243     }
0244 
0245 
0246     if (ret) {
0247         IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
0248         return ret;
0249     }
0250 
0251     if (action != FW_CTXT_ACTION_REMOVE)
0252         return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
0253                         chains_dynamic);
0254 
0255     return 0;
0256 }
0257 
0258 /*
0259  * Send a command to add a PHY context based on the current HW configuration.
0260  */
0261 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
0262              struct cfg80211_chan_def *chandef,
0263              u8 chains_static, u8 chains_dynamic)
0264 {
0265     WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
0266         ctxt->ref);
0267     lockdep_assert_held(&mvm->mutex);
0268 
0269     ctxt->channel = chandef->chan;
0270     ctxt->width = chandef->width;
0271     ctxt->center_freq1 = chandef->center_freq1;
0272 
0273     return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
0274                       chains_static, chains_dynamic,
0275                       FW_CTXT_ACTION_ADD);
0276 }
0277 
0278 /*
0279  * Update the number of references to the given PHY context. This is valid only
0280  * in case the PHY context was already created, i.e., its reference count > 0.
0281  */
0282 void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
0283 {
0284     lockdep_assert_held(&mvm->mutex);
0285     ctxt->ref++;
0286 }
0287 
0288 /*
0289  * Send a command to modify the PHY context based on the current HW
0290  * configuration. Note that the function does not check that the configuration
0291  * changed.
0292  */
0293 int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
0294                  struct cfg80211_chan_def *chandef,
0295                  u8 chains_static, u8 chains_dynamic)
0296 {
0297     enum iwl_ctxt_action action = FW_CTXT_ACTION_MODIFY;
0298 
0299     lockdep_assert_held(&mvm->mutex);
0300 
0301     if (iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, RLC_CONFIG_CMD), 0) >= 2 &&
0302         ctxt->channel == chandef->chan &&
0303         ctxt->width == chandef->width &&
0304         ctxt->center_freq1 == chandef->center_freq1)
0305         return iwl_mvm_phy_send_rlc(mvm, ctxt, chains_static,
0306                         chains_dynamic);
0307 
0308     if (fw_has_capa(&mvm->fw->ucode_capa,
0309             IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
0310         ctxt->channel->band != chandef->chan->band) {
0311         int ret;
0312 
0313         /* ... remove it here ...*/
0314         ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
0315                          chains_static, chains_dynamic,
0316                          FW_CTXT_ACTION_REMOVE);
0317         if (ret)
0318             return ret;
0319 
0320         /* ... and proceed to add it again */
0321         action = FW_CTXT_ACTION_ADD;
0322     }
0323 
0324     ctxt->channel = chandef->chan;
0325     ctxt->width = chandef->width;
0326     ctxt->center_freq1 = chandef->center_freq1;
0327 
0328     return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
0329                       chains_static, chains_dynamic,
0330                       action);
0331 }
0332 
0333 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
0334 {
0335     lockdep_assert_held(&mvm->mutex);
0336 
0337     if (WARN_ON_ONCE(!ctxt))
0338         return;
0339 
0340     ctxt->ref--;
0341 
0342     /*
0343      * Move unused phy's to a default channel. When the phy is moved the,
0344      * fw will cleanup immediate quiet bit if it was previously set,
0345      * otherwise we might not be able to reuse this phy.
0346      */
0347     if (ctxt->ref == 0) {
0348         struct ieee80211_channel *chan = NULL;
0349         struct cfg80211_chan_def chandef;
0350         struct ieee80211_supported_band *sband;
0351         enum nl80211_band band;
0352         int channel;
0353 
0354         for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
0355             sband = mvm->hw->wiphy->bands[band];
0356 
0357             if (!sband)
0358                 continue;
0359 
0360             for (channel = 0; channel < sband->n_channels; channel++)
0361                 if (!(sband->channels[channel].flags &
0362                         IEEE80211_CHAN_DISABLED)) {
0363                     chan = &sband->channels[channel];
0364                     break;
0365                 }
0366 
0367             if (chan)
0368                 break;
0369         }
0370 
0371         if (WARN_ON(!chan))
0372             return;
0373 
0374         cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
0375         iwl_mvm_phy_ctxt_changed(mvm, ctxt, &chandef, 1, 1);
0376     }
0377 }
0378 
0379 static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
0380                      struct ieee80211_vif *vif)
0381 {
0382     unsigned long *data = _data;
0383     struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
0384 
0385     if (!mvmvif->phy_ctxt)
0386         return;
0387 
0388     if (vif->type == NL80211_IFTYPE_STATION ||
0389         vif->type == NL80211_IFTYPE_AP)
0390         __set_bit(mvmvif->phy_ctxt->id, data);
0391 }
0392 
0393 int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
0394 {
0395     unsigned long phy_ctxt_counter = 0;
0396 
0397     ieee80211_iterate_active_interfaces_atomic(mvm->hw,
0398                            IEEE80211_IFACE_ITER_NORMAL,
0399                            iwl_mvm_binding_iterator,
0400                            &phy_ctxt_counter);
0401 
0402     return hweight8(phy_ctxt_counter);
0403 }