Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright (C) 2017 Intel Deutschland GmbH
0004  * Copyright (C) 2018-2022 Intel Corporation
0005  */
0006 #include "rs.h"
0007 #include "fw-api.h"
0008 #include "sta.h"
0009 #include "iwl-op-mode.h"
0010 #include "mvm.h"
0011 
0012 static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta)
0013 {
0014     switch (sta->deflink.bandwidth) {
0015     case IEEE80211_STA_RX_BW_160:
0016         return IWL_TLC_MNG_CH_WIDTH_160MHZ;
0017     case IEEE80211_STA_RX_BW_80:
0018         return IWL_TLC_MNG_CH_WIDTH_80MHZ;
0019     case IEEE80211_STA_RX_BW_40:
0020         return IWL_TLC_MNG_CH_WIDTH_40MHZ;
0021     case IEEE80211_STA_RX_BW_20:
0022     default:
0023         return IWL_TLC_MNG_CH_WIDTH_20MHZ;
0024     }
0025 }
0026 
0027 static u8 rs_fw_set_active_chains(u8 chains)
0028 {
0029     u8 fw_chains = 0;
0030 
0031     if (chains & ANT_A)
0032         fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK;
0033     if (chains & ANT_B)
0034         fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
0035 
0036     return fw_chains;
0037 }
0038 
0039 static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)
0040 {
0041     struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
0042     struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
0043     struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
0044     u8 supp = 0;
0045 
0046     if (he_cap->has_he)
0047         return 0;
0048 
0049     if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
0050         supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ);
0051     if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
0052         supp |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ);
0053     if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
0054         supp |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ);
0055     if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
0056         supp |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ);
0057 
0058     return supp;
0059 }
0060 
0061 static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm,
0062                   struct ieee80211_sta *sta,
0063                   struct ieee80211_supported_band *sband)
0064 {
0065     struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
0066     struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
0067     struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
0068     bool vht_ena = vht_cap->vht_supported;
0069     u16 flags = 0;
0070 
0071     /* get STBC flags */
0072     if (mvm->cfg->ht_params->stbc &&
0073         (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) {
0074         if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] &
0075                       IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
0076             flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
0077         else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
0078             flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
0079         else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)
0080             flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
0081     }
0082 
0083     if (mvm->cfg->ht_params->ldpc &&
0084         ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) ||
0085          (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
0086         flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
0087 
0088     /* consider LDPC support in case of HE */
0089     if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] &
0090         IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
0091         flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
0092 
0093     if (sband->iftype_data && sband->iftype_data->he_cap.has_he &&
0094         !(sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &
0095          IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
0096         flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
0097 
0098     if (he_cap->has_he &&
0099         (he_cap->he_cap_elem.phy_cap_info[3] &
0100          IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK &&
0101          sband->iftype_data &&
0102          sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[3] &
0103          IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK))
0104         flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK;
0105 
0106     return flags;
0107 }
0108 
0109 static
0110 int rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap,
0111                    int nss)
0112 {
0113     u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
0114         (0x3 << (2 * (nss - 1)));
0115     rx_mcs >>= (2 * (nss - 1));
0116 
0117     switch (rx_mcs) {
0118     case IEEE80211_VHT_MCS_SUPPORT_0_7:
0119         return IWL_TLC_MNG_HT_RATE_MCS7;
0120     case IEEE80211_VHT_MCS_SUPPORT_0_8:
0121         return IWL_TLC_MNG_HT_RATE_MCS8;
0122     case IEEE80211_VHT_MCS_SUPPORT_0_9:
0123         return IWL_TLC_MNG_HT_RATE_MCS9;
0124     default:
0125         WARN_ON_ONCE(1);
0126         break;
0127     }
0128 
0129     return 0;
0130 }
0131 
0132 static void
0133 rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta,
0134                 const struct ieee80211_sta_vht_cap *vht_cap,
0135                 struct iwl_tlc_config_cmd_v4 *cmd)
0136 {
0137     u16 supp;
0138     int i, highest_mcs;
0139     u8 max_nss = sta->deflink.rx_nss;
0140     struct ieee80211_vht_cap ieee_vht_cap = {
0141         .vht_cap_info = cpu_to_le32(vht_cap->cap),
0142         .supp_mcs = vht_cap->vht_mcs,
0143     };
0144 
0145     /* the station support only a single receive chain */
0146     if (sta->smps_mode == IEEE80211_SMPS_STATIC)
0147         max_nss = 1;
0148 
0149     for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) {
0150         int nss = i + 1;
0151 
0152         highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, nss);
0153         if (!highest_mcs)
0154             continue;
0155 
0156         supp = BIT(highest_mcs + 1) - 1;
0157         if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20)
0158             supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
0159 
0160         cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp);
0161         /*
0162          * Check if VHT extended NSS indicates that the bandwidth/NSS
0163          * configuration is supported - only for MCS 0 since we already
0164          * decoded the MCS bits anyway ourselves.
0165          */
0166         if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160 &&
0167             ieee80211_get_vht_max_nss(&ieee_vht_cap,
0168                           IEEE80211_VHT_CHANWIDTH_160MHZ,
0169                           0, true, nss) >= nss)
0170             cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
0171                 cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80];
0172     }
0173 }
0174 
0175 static u16 rs_fw_he_ieee80211_mcs_to_rs_mcs(u16 mcs)
0176 {
0177     switch (mcs) {
0178     case IEEE80211_HE_MCS_SUPPORT_0_7:
0179         return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1;
0180     case IEEE80211_HE_MCS_SUPPORT_0_9:
0181         return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1;
0182     case IEEE80211_HE_MCS_SUPPORT_0_11:
0183         return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1;
0184     case IEEE80211_HE_MCS_NOT_SUPPORTED:
0185         return 0;
0186     }
0187 
0188     WARN(1, "invalid HE MCS %d\n", mcs);
0189     return 0;
0190 }
0191 
0192 static void
0193 rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta,
0194                struct ieee80211_supported_band *sband,
0195                struct iwl_tlc_config_cmd_v4 *cmd)
0196 {
0197     const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
0198     u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
0199     u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
0200     u16 tx_mcs_80 =
0201         le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80);
0202     u16 tx_mcs_160 =
0203         le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160);
0204     int i;
0205     u8 nss = sta->deflink.rx_nss;
0206 
0207     /* the station support only a single receive chain */
0208     if (sta->smps_mode == IEEE80211_SMPS_STATIC)
0209         nss = 1;
0210 
0211     for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) {
0212         u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3;
0213         u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3;
0214         u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3;
0215         u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3;
0216 
0217         /* If one side doesn't support - mark both as not supporting */
0218         if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
0219             _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
0220             _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
0221             _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED;
0222         }
0223         if (_mcs_80 > _tx_mcs_80)
0224             _mcs_80 = _tx_mcs_80;
0225         cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] =
0226             cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_80));
0227 
0228         /* If one side doesn't support - mark both as not supporting */
0229         if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED ||
0230             _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) {
0231             _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
0232             _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED;
0233         }
0234         if (_mcs_160 > _tx_mcs_160)
0235             _mcs_160 = _tx_mcs_160;
0236         cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] =
0237             cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_160));
0238     }
0239 }
0240 
0241 static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
0242                  struct ieee80211_supported_band *sband,
0243                  struct iwl_tlc_config_cmd_v4 *cmd)
0244 {
0245     int i;
0246     u16 supp = 0;
0247     unsigned long tmp; /* must be unsigned long for for_each_set_bit */
0248     const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
0249     const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
0250     const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
0251 
0252     /* non HT rates */
0253     tmp = sta->deflink.supp_rates[sband->band];
0254     for_each_set_bit(i, &tmp, BITS_PER_LONG)
0255         supp |= BIT(sband->bitrates[i].hw_value);
0256 
0257     cmd->non_ht_rates = cpu_to_le16(supp);
0258     cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
0259 
0260     /* HT/VHT rates */
0261     if (he_cap->has_he) {
0262         cmd->mode = IWL_TLC_MNG_MODE_HE;
0263         rs_fw_he_set_enabled_rates(sta, sband, cmd);
0264     } else if (vht_cap->vht_supported) {
0265         cmd->mode = IWL_TLC_MNG_MODE_VHT;
0266         rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd);
0267     } else if (ht_cap->ht_supported) {
0268         cmd->mode = IWL_TLC_MNG_MODE_HT;
0269         cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] =
0270             cpu_to_le16(ht_cap->mcs.rx_mask[0]);
0271 
0272         /* the station support only a single receive chain */
0273         if (sta->smps_mode == IEEE80211_SMPS_STATIC)
0274             cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
0275                 0;
0276         else
0277             cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] =
0278                 cpu_to_le16(ht_cap->mcs.rx_mask[1]);
0279     }
0280 }
0281 
0282 void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm,
0283                   struct iwl_rx_cmd_buffer *rxb)
0284 {
0285     struct iwl_rx_packet *pkt = rxb_addr(rxb);
0286     struct iwl_tlc_update_notif *notif;
0287     struct ieee80211_sta *sta;
0288     struct iwl_mvm_sta *mvmsta;
0289     struct iwl_lq_sta_rs_fw *lq_sta;
0290     u32 flags;
0291 
0292     rcu_read_lock();
0293 
0294     notif = (void *)pkt->data;
0295     sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]);
0296     if (IS_ERR_OR_NULL(sta)) {
0297         /* can happen in remove station flow where mvm removed internally
0298          * the station before removing from FW
0299          */
0300         IWL_DEBUG_RATE(mvm,
0301                    "Invalid mvm RCU pointer for sta id (%d) in TLC notification\n",
0302                    notif->sta_id);
0303         goto out;
0304     }
0305 
0306     mvmsta = iwl_mvm_sta_from_mac80211(sta);
0307 
0308     if (!mvmsta) {
0309         IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n",
0310             notif->sta_id);
0311         goto out;
0312     }
0313 
0314     flags = le32_to_cpu(notif->flags);
0315 
0316     lq_sta = &mvmsta->lq_sta.rs_fw;
0317 
0318     if (flags & IWL_TLC_NOTIF_FLAG_RATE) {
0319         char pretty_rate[100];
0320 
0321         if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP,
0322                         TLC_MNG_UPDATE_NOTIF, 0) < 3) {
0323             rs_pretty_print_rate_v1(pretty_rate,
0324                         sizeof(pretty_rate),
0325                         le32_to_cpu(notif->rate));
0326             IWL_DEBUG_RATE(mvm,
0327                        "Got rate in old format. Rate: %s. Converting.\n",
0328                        pretty_rate);
0329             lq_sta->last_rate_n_flags =
0330                 iwl_new_rate_from_v1(le32_to_cpu(notif->rate));
0331         } else {
0332             lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate);
0333         }
0334         rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate),
0335                      lq_sta->last_rate_n_flags);
0336         IWL_DEBUG_RATE(mvm, "new rate: %s\n", pretty_rate);
0337     }
0338 
0339     if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvmsta->orig_amsdu_len) {
0340         u16 size = le32_to_cpu(notif->amsdu_size);
0341         int i;
0342 
0343         if (sta->max_amsdu_len < size) {
0344             /*
0345              * In debug sta->max_amsdu_len < size
0346              * so also check with orig_amsdu_len which holds the
0347              * original data before debugfs changed the value
0348              */
0349             WARN_ON(mvmsta->orig_amsdu_len < size);
0350             goto out;
0351         }
0352 
0353         mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled);
0354         mvmsta->max_amsdu_len = size;
0355         sta->max_rc_amsdu_len = mvmsta->max_amsdu_len;
0356 
0357         for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
0358             if (mvmsta->amsdu_enabled & BIT(i))
0359                 sta->max_tid_amsdu_len[i] =
0360                     iwl_mvm_max_amsdu_size(mvm, sta, i);
0361             else
0362                 /*
0363                  * Not so elegant, but this will effectively
0364                  * prevent AMSDU on this TID
0365                  */
0366                 sta->max_tid_amsdu_len[i] = 1;
0367         }
0368 
0369         IWL_DEBUG_RATE(mvm,
0370                    "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n",
0371                    le32_to_cpu(notif->amsdu_size), size,
0372                    mvmsta->amsdu_enabled);
0373     }
0374 out:
0375     rcu_read_unlock();
0376 }
0377 
0378 u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta)
0379 {
0380     struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
0381     const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap;
0382     const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap;
0383 
0384     if (mvmsta->vif->bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) {
0385         switch (le16_get_bits(sta->deflink.he_6ghz_capa.capa,
0386                       IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) {
0387         case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
0388             return IEEE80211_MAX_MPDU_LEN_VHT_11454;
0389         case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
0390             return IEEE80211_MAX_MPDU_LEN_VHT_7991;
0391         default:
0392             return IEEE80211_MAX_MPDU_LEN_VHT_3895;
0393         }
0394     } else
0395     if (vht_cap->vht_supported) {
0396         switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
0397         case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
0398             return IEEE80211_MAX_MPDU_LEN_VHT_11454;
0399         case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
0400             return IEEE80211_MAX_MPDU_LEN_VHT_7991;
0401         default:
0402             return IEEE80211_MAX_MPDU_LEN_VHT_3895;
0403         }
0404     } else if (ht_cap->ht_supported) {
0405         if (ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU)
0406             /*
0407              * agg is offloaded so we need to assume that agg
0408              * are enabled and max mpdu in ampdu is 4095
0409              * (spec 802.11-2016 9.3.2.1)
0410              */
0411             return IEEE80211_MAX_MPDU_LEN_HT_BA;
0412         else
0413             return IEEE80211_MAX_MPDU_LEN_HT_3839;
0414     }
0415 
0416     /* in legacy mode no amsdu is enabled so return zero */
0417     return 0;
0418 }
0419 
0420 void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
0421              enum nl80211_band band, bool update)
0422 {
0423     struct ieee80211_hw *hw = mvm->hw;
0424     struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
0425     struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
0426     u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD);
0427     struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
0428     u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta);
0429     struct iwl_tlc_config_cmd_v4 cfg_cmd = {
0430         .sta_id = mvmsta->sta_id,
0431         .max_ch_width = update ?
0432             rs_fw_bw_from_sta_bw(sta) : RATE_MCS_CHAN_WIDTH_20,
0433         .flags = cpu_to_le16(rs_fw_get_config_flags(mvm, sta, sband)),
0434         .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)),
0435         .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta),
0436         .max_mpdu_len = iwl_mvm_is_csum_supported(mvm) ?
0437                 cpu_to_le16(max_amsdu_len) : 0,
0438     };
0439     int ret;
0440     int cmd_ver;
0441 
0442     memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
0443 
0444 #ifdef CONFIG_IWLWIFI_DEBUGFS
0445     iwl_mvm_reset_frame_stats(mvm);
0446 #endif
0447     rs_fw_set_supp_rates(sta, sband, &cfg_cmd);
0448 
0449     /*
0450      * since TLC offload works with one mode we can assume
0451      * that only vht/ht is used and also set it as station max amsdu
0452      */
0453     sta->max_amsdu_len = max_amsdu_len;
0454 
0455     cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw,
0456                     WIDE_ID(DATA_PATH_GROUP,
0457                         TLC_MNG_CONFIG_CMD),
0458                     0);
0459     IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n",
0460                cfg_cmd.sta_id, cfg_cmd.max_ch_width, cfg_cmd.mode);
0461     IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, chains=0x%X, ch_wid_supp=%d, flags=0x%X\n",
0462                cfg_cmd.chains, cfg_cmd.sgi_ch_width_supp, cfg_cmd.flags);
0463     IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, mpdu_len=%d, no_ht_rate=0x%X, tx_op=%d\n",
0464                cfg_cmd.max_mpdu_len, cfg_cmd.non_ht_rates, cfg_cmd.max_tx_op);
0465     IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][0]=0x%X, ht_rate[1][0]=0x%X\n",
0466                cfg_cmd.ht_rates[0][0], cfg_cmd.ht_rates[1][0]);
0467     IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][1]=0x%X, ht_rate[1][1]=0x%X\n",
0468                cfg_cmd.ht_rates[0][1], cfg_cmd.ht_rates[1][1]);
0469     IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][2]=0x%X, ht_rate[1][2]=0x%X\n",
0470                cfg_cmd.ht_rates[0][2], cfg_cmd.ht_rates[1][2]);
0471     if (cmd_ver == 4) {
0472         ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC,
0473                        sizeof(cfg_cmd), &cfg_cmd);
0474     } else if (cmd_ver < 4) {
0475         struct iwl_tlc_config_cmd_v3 cfg_cmd_v3 = {
0476             .sta_id = cfg_cmd.sta_id,
0477             .max_ch_width = cfg_cmd.max_ch_width,
0478             .mode = cfg_cmd.mode,
0479             .chains = cfg_cmd.chains,
0480             .amsdu = !!cfg_cmd.max_mpdu_len,
0481             .flags = cfg_cmd.flags,
0482             .non_ht_rates = cfg_cmd.non_ht_rates,
0483             .ht_rates[0][0] = cfg_cmd.ht_rates[0][0],
0484             .ht_rates[0][1] = cfg_cmd.ht_rates[0][1],
0485             .ht_rates[1][0] = cfg_cmd.ht_rates[1][0],
0486             .ht_rates[1][1] = cfg_cmd.ht_rates[1][1],
0487             .sgi_ch_width_supp = cfg_cmd.sgi_ch_width_supp,
0488             .max_mpdu_len = cfg_cmd.max_mpdu_len,
0489         };
0490 
0491         u16 cmd_size = sizeof(cfg_cmd_v3);
0492 
0493         /* In old versions of the API the struct is 4 bytes smaller */
0494         if (iwl_fw_lookup_cmd_ver(mvm->fw,
0495                       WIDE_ID(DATA_PATH_GROUP,
0496                           TLC_MNG_CONFIG_CMD), 0) < 3)
0497             cmd_size -= 4;
0498 
0499         ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, cmd_size,
0500                        &cfg_cmd_v3);
0501     } else {
0502         ret = -EINVAL;
0503     }
0504 
0505     if (ret)
0506         IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret);
0507 }
0508 
0509 int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
0510             bool enable)
0511 {
0512     /* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */
0513     IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n");
0514     return 0;
0515 }
0516 
0517 void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta)
0518 {
0519     struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
0520 
0521     IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
0522 
0523     lq_sta->pers.drv = mvm;
0524     lq_sta->pers.sta_id = mvmsta->sta_id;
0525     lq_sta->pers.chains = 0;
0526     memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal));
0527     lq_sta->pers.last_rssi = S8_MIN;
0528     lq_sta->last_rate_n_flags = 0;
0529 
0530 #ifdef CONFIG_MAC80211_DEBUGFS
0531     lq_sta->pers.dbg_fixed_rate = 0;
0532 #endif
0533 }