rs-fw.c (16826B)
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2/* 3 * Copyright (C) 2017 Intel Deutschland GmbH 4 * Copyright (C) 2018-2022 Intel Corporation 5 */ 6#include "rs.h" 7#include "fw-api.h" 8#include "sta.h" 9#include "iwl-op-mode.h" 10#include "mvm.h" 11 12static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta) 13{ 14 switch (sta->deflink.bandwidth) { 15 case IEEE80211_STA_RX_BW_160: 16 return IWL_TLC_MNG_CH_WIDTH_160MHZ; 17 case IEEE80211_STA_RX_BW_80: 18 return IWL_TLC_MNG_CH_WIDTH_80MHZ; 19 case IEEE80211_STA_RX_BW_40: 20 return IWL_TLC_MNG_CH_WIDTH_40MHZ; 21 case IEEE80211_STA_RX_BW_20: 22 default: 23 return IWL_TLC_MNG_CH_WIDTH_20MHZ; 24 } 25} 26 27static u8 rs_fw_set_active_chains(u8 chains) 28{ 29 u8 fw_chains = 0; 30 31 if (chains & ANT_A) 32 fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK; 33 if (chains & ANT_B) 34 fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; 35 36 return fw_chains; 37} 38 39static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta) 40{ 41 struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; 42 struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; 43 struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; 44 u8 supp = 0; 45 46 if (he_cap->has_he) 47 return 0; 48 49 if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) 50 supp |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); 51 if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) 52 supp |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); 53 if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) 54 supp |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); 55 if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) 56 supp |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); 57 58 return supp; 59} 60 61static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, 62 struct ieee80211_sta *sta, 63 struct ieee80211_supported_band *sband) 64{ 65 struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; 66 struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; 67 struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; 68 bool vht_ena = vht_cap->vht_supported; 69 u16 flags = 0; 70 71 /* get STBC flags */ 72 if (mvm->cfg->ht_params->stbc && 73 (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) { 74 if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & 75 IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) 76 flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 77 else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) 78 flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 79 else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) 80 flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 81 } 82 83 if (mvm->cfg->ht_params->ldpc && 84 ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || 85 (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) 86 flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 87 88 /* consider LDPC support in case of HE */ 89 if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] & 90 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) 91 flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 92 93 if (sband->iftype_data && sband->iftype_data->he_cap.has_he && 94 !(sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[1] & 95 IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) 96 flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 97 98 if (he_cap->has_he && 99 (he_cap->he_cap_elem.phy_cap_info[3] & 100 IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && 101 sband->iftype_data && 102 sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[3] & 103 IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) 104 flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; 105 106 return flags; 107} 108 109static 110int rs_fw_vht_highest_rx_mcs_index(const struct ieee80211_sta_vht_cap *vht_cap, 111 int nss) 112{ 113 u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & 114 (0x3 << (2 * (nss - 1))); 115 rx_mcs >>= (2 * (nss - 1)); 116 117 switch (rx_mcs) { 118 case IEEE80211_VHT_MCS_SUPPORT_0_7: 119 return IWL_TLC_MNG_HT_RATE_MCS7; 120 case IEEE80211_VHT_MCS_SUPPORT_0_8: 121 return IWL_TLC_MNG_HT_RATE_MCS8; 122 case IEEE80211_VHT_MCS_SUPPORT_0_9: 123 return IWL_TLC_MNG_HT_RATE_MCS9; 124 default: 125 WARN_ON_ONCE(1); 126 break; 127 } 128 129 return 0; 130} 131 132static void 133rs_fw_vht_set_enabled_rates(const struct ieee80211_sta *sta, 134 const struct ieee80211_sta_vht_cap *vht_cap, 135 struct iwl_tlc_config_cmd_v4 *cmd) 136{ 137 u16 supp; 138 int i, highest_mcs; 139 u8 max_nss = sta->deflink.rx_nss; 140 struct ieee80211_vht_cap ieee_vht_cap = { 141 .vht_cap_info = cpu_to_le32(vht_cap->cap), 142 .supp_mcs = vht_cap->vht_mcs, 143 }; 144 145 /* the station support only a single receive chain */ 146 if (sta->smps_mode == IEEE80211_SMPS_STATIC) 147 max_nss = 1; 148 149 for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { 150 int nss = i + 1; 151 152 highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, nss); 153 if (!highest_mcs) 154 continue; 155 156 supp = BIT(highest_mcs + 1) - 1; 157 if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) 158 supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); 159 160 cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp); 161 /* 162 * Check if VHT extended NSS indicates that the bandwidth/NSS 163 * configuration is supported - only for MCS 0 since we already 164 * decoded the MCS bits anyway ourselves. 165 */ 166 if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160 && 167 ieee80211_get_vht_max_nss(&ieee_vht_cap, 168 IEEE80211_VHT_CHANWIDTH_160MHZ, 169 0, true, nss) >= nss) 170 cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = 171 cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80]; 172 } 173} 174 175static u16 rs_fw_he_ieee80211_mcs_to_rs_mcs(u16 mcs) 176{ 177 switch (mcs) { 178 case IEEE80211_HE_MCS_SUPPORT_0_7: 179 return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1; 180 case IEEE80211_HE_MCS_SUPPORT_0_9: 181 return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1; 182 case IEEE80211_HE_MCS_SUPPORT_0_11: 183 return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1; 184 case IEEE80211_HE_MCS_NOT_SUPPORTED: 185 return 0; 186 } 187 188 WARN(1, "invalid HE MCS %d\n", mcs); 189 return 0; 190} 191 192static void 193rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta, 194 struct ieee80211_supported_band *sband, 195 struct iwl_tlc_config_cmd_v4 *cmd) 196{ 197 const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; 198 u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); 199 u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); 200 u16 tx_mcs_80 = 201 le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80); 202 u16 tx_mcs_160 = 203 le16_to_cpu(sband->iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160); 204 int i; 205 u8 nss = sta->deflink.rx_nss; 206 207 /* the station support only a single receive chain */ 208 if (sta->smps_mode == IEEE80211_SMPS_STATIC) 209 nss = 1; 210 211 for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { 212 u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3; 213 u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3; 214 u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3; 215 u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3; 216 217 /* If one side doesn't support - mark both as not supporting */ 218 if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED || 219 _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) { 220 _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; 221 _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; 222 } 223 if (_mcs_80 > _tx_mcs_80) 224 _mcs_80 = _tx_mcs_80; 225 cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = 226 cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_80)); 227 228 /* If one side doesn't support - mark both as not supporting */ 229 if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED || 230 _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) { 231 _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; 232 _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; 233 } 234 if (_mcs_160 > _tx_mcs_160) 235 _mcs_160 = _tx_mcs_160; 236 cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = 237 cpu_to_le16(rs_fw_he_ieee80211_mcs_to_rs_mcs(_mcs_160)); 238 } 239} 240 241static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, 242 struct ieee80211_supported_band *sband, 243 struct iwl_tlc_config_cmd_v4 *cmd) 244{ 245 int i; 246 u16 supp = 0; 247 unsigned long tmp; /* must be unsigned long for for_each_set_bit */ 248 const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; 249 const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; 250 const struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; 251 252 /* non HT rates */ 253 tmp = sta->deflink.supp_rates[sband->band]; 254 for_each_set_bit(i, &tmp, BITS_PER_LONG) 255 supp |= BIT(sband->bitrates[i].hw_value); 256 257 cmd->non_ht_rates = cpu_to_le16(supp); 258 cmd->mode = IWL_TLC_MNG_MODE_NON_HT; 259 260 /* HT/VHT rates */ 261 if (he_cap->has_he) { 262 cmd->mode = IWL_TLC_MNG_MODE_HE; 263 rs_fw_he_set_enabled_rates(sta, sband, cmd); 264 } else if (vht_cap->vht_supported) { 265 cmd->mode = IWL_TLC_MNG_MODE_VHT; 266 rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd); 267 } else if (ht_cap->ht_supported) { 268 cmd->mode = IWL_TLC_MNG_MODE_HT; 269 cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] = 270 cpu_to_le16(ht_cap->mcs.rx_mask[0]); 271 272 /* the station support only a single receive chain */ 273 if (sta->smps_mode == IEEE80211_SMPS_STATIC) 274 cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = 275 0; 276 else 277 cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = 278 cpu_to_le16(ht_cap->mcs.rx_mask[1]); 279 } 280} 281 282void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, 283 struct iwl_rx_cmd_buffer *rxb) 284{ 285 struct iwl_rx_packet *pkt = rxb_addr(rxb); 286 struct iwl_tlc_update_notif *notif; 287 struct ieee80211_sta *sta; 288 struct iwl_mvm_sta *mvmsta; 289 struct iwl_lq_sta_rs_fw *lq_sta; 290 u32 flags; 291 292 rcu_read_lock(); 293 294 notif = (void *)pkt->data; 295 sta = rcu_dereference(mvm->fw_id_to_mac_id[notif->sta_id]); 296 if (IS_ERR_OR_NULL(sta)) { 297 /* can happen in remove station flow where mvm removed internally 298 * the station before removing from FW 299 */ 300 IWL_DEBUG_RATE(mvm, 301 "Invalid mvm RCU pointer for sta id (%d) in TLC notification\n", 302 notif->sta_id); 303 goto out; 304 } 305 306 mvmsta = iwl_mvm_sta_from_mac80211(sta); 307 308 if (!mvmsta) { 309 IWL_ERR(mvm, "Invalid sta id (%d) in FW TLC notification\n", 310 notif->sta_id); 311 goto out; 312 } 313 314 flags = le32_to_cpu(notif->flags); 315 316 lq_sta = &mvmsta->lq_sta.rs_fw; 317 318 if (flags & IWL_TLC_NOTIF_FLAG_RATE) { 319 char pretty_rate[100]; 320 321 if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, 322 TLC_MNG_UPDATE_NOTIF, 0) < 3) { 323 rs_pretty_print_rate_v1(pretty_rate, 324 sizeof(pretty_rate), 325 le32_to_cpu(notif->rate)); 326 IWL_DEBUG_RATE(mvm, 327 "Got rate in old format. Rate: %s. Converting.\n", 328 pretty_rate); 329 lq_sta->last_rate_n_flags = 330 iwl_new_rate_from_v1(le32_to_cpu(notif->rate)); 331 } else { 332 lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate); 333 } 334 rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), 335 lq_sta->last_rate_n_flags); 336 IWL_DEBUG_RATE(mvm, "new rate: %s\n", pretty_rate); 337 } 338 339 if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvmsta->orig_amsdu_len) { 340 u16 size = le32_to_cpu(notif->amsdu_size); 341 int i; 342 343 if (sta->max_amsdu_len < size) { 344 /* 345 * In debug sta->max_amsdu_len < size 346 * so also check with orig_amsdu_len which holds the 347 * original data before debugfs changed the value 348 */ 349 WARN_ON(mvmsta->orig_amsdu_len < size); 350 goto out; 351 } 352 353 mvmsta->amsdu_enabled = le32_to_cpu(notif->amsdu_enabled); 354 mvmsta->max_amsdu_len = size; 355 sta->max_rc_amsdu_len = mvmsta->max_amsdu_len; 356 357 for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 358 if (mvmsta->amsdu_enabled & BIT(i)) 359 sta->max_tid_amsdu_len[i] = 360 iwl_mvm_max_amsdu_size(mvm, sta, i); 361 else 362 /* 363 * Not so elegant, but this will effectively 364 * prevent AMSDU on this TID 365 */ 366 sta->max_tid_amsdu_len[i] = 1; 367 } 368 369 IWL_DEBUG_RATE(mvm, 370 "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", 371 le32_to_cpu(notif->amsdu_size), size, 372 mvmsta->amsdu_enabled); 373 } 374out: 375 rcu_read_unlock(); 376} 377 378u16 rs_fw_get_max_amsdu_len(struct ieee80211_sta *sta) 379{ 380 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 381 const struct ieee80211_sta_vht_cap *vht_cap = &sta->deflink.vht_cap; 382 const struct ieee80211_sta_ht_cap *ht_cap = &sta->deflink.ht_cap; 383 384 if (mvmsta->vif->bss_conf.chandef.chan->band == NL80211_BAND_6GHZ) { 385 switch (le16_get_bits(sta->deflink.he_6ghz_capa.capa, 386 IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN)) { 387 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: 388 return IEEE80211_MAX_MPDU_LEN_VHT_11454; 389 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: 390 return IEEE80211_MAX_MPDU_LEN_VHT_7991; 391 default: 392 return IEEE80211_MAX_MPDU_LEN_VHT_3895; 393 } 394 } else 395 if (vht_cap->vht_supported) { 396 switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { 397 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: 398 return IEEE80211_MAX_MPDU_LEN_VHT_11454; 399 case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: 400 return IEEE80211_MAX_MPDU_LEN_VHT_7991; 401 default: 402 return IEEE80211_MAX_MPDU_LEN_VHT_3895; 403 } 404 } else if (ht_cap->ht_supported) { 405 if (ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU) 406 /* 407 * agg is offloaded so we need to assume that agg 408 * are enabled and max mpdu in ampdu is 4095 409 * (spec 802.11-2016 9.3.2.1) 410 */ 411 return IEEE80211_MAX_MPDU_LEN_HT_BA; 412 else 413 return IEEE80211_MAX_MPDU_LEN_HT_3839; 414 } 415 416 /* in legacy mode no amsdu is enabled so return zero */ 417 return 0; 418} 419 420void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 421 enum nl80211_band band, bool update) 422{ 423 struct ieee80211_hw *hw = mvm->hw; 424 struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 425 struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; 426 u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD); 427 struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; 428 u16 max_amsdu_len = rs_fw_get_max_amsdu_len(sta); 429 struct iwl_tlc_config_cmd_v4 cfg_cmd = { 430 .sta_id = mvmsta->sta_id, 431 .max_ch_width = update ? 432 rs_fw_bw_from_sta_bw(sta) : RATE_MCS_CHAN_WIDTH_20, 433 .flags = cpu_to_le16(rs_fw_get_config_flags(mvm, sta, sband)), 434 .chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)), 435 .sgi_ch_width_supp = rs_fw_sgi_cw_support(sta), 436 .max_mpdu_len = iwl_mvm_is_csum_supported(mvm) ? 437 cpu_to_le16(max_amsdu_len) : 0, 438 }; 439 int ret; 440 int cmd_ver; 441 442 memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); 443 444#ifdef CONFIG_IWLWIFI_DEBUGFS 445 iwl_mvm_reset_frame_stats(mvm); 446#endif 447 rs_fw_set_supp_rates(sta, sband, &cfg_cmd); 448 449 /* 450 * since TLC offload works with one mode we can assume 451 * that only vht/ht is used and also set it as station max amsdu 452 */ 453 sta->max_amsdu_len = max_amsdu_len; 454 455 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, 456 WIDE_ID(DATA_PATH_GROUP, 457 TLC_MNG_CONFIG_CMD), 458 0); 459 IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n", 460 cfg_cmd.sta_id, cfg_cmd.max_ch_width, cfg_cmd.mode); 461 IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, chains=0x%X, ch_wid_supp=%d, flags=0x%X\n", 462 cfg_cmd.chains, cfg_cmd.sgi_ch_width_supp, cfg_cmd.flags); 463 IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, mpdu_len=%d, no_ht_rate=0x%X, tx_op=%d\n", 464 cfg_cmd.max_mpdu_len, cfg_cmd.non_ht_rates, cfg_cmd.max_tx_op); 465 IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][0]=0x%X, ht_rate[1][0]=0x%X\n", 466 cfg_cmd.ht_rates[0][0], cfg_cmd.ht_rates[1][0]); 467 IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][1]=0x%X, ht_rate[1][1]=0x%X\n", 468 cfg_cmd.ht_rates[0][1], cfg_cmd.ht_rates[1][1]); 469 IWL_DEBUG_RATE(mvm, "TLC CONFIG CMD, ht_rate[0][2]=0x%X, ht_rate[1][2]=0x%X\n", 470 cfg_cmd.ht_rates[0][2], cfg_cmd.ht_rates[1][2]); 471 if (cmd_ver == 4) { 472 ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, 473 sizeof(cfg_cmd), &cfg_cmd); 474 } else if (cmd_ver < 4) { 475 struct iwl_tlc_config_cmd_v3 cfg_cmd_v3 = { 476 .sta_id = cfg_cmd.sta_id, 477 .max_ch_width = cfg_cmd.max_ch_width, 478 .mode = cfg_cmd.mode, 479 .chains = cfg_cmd.chains, 480 .amsdu = !!cfg_cmd.max_mpdu_len, 481 .flags = cfg_cmd.flags, 482 .non_ht_rates = cfg_cmd.non_ht_rates, 483 .ht_rates[0][0] = cfg_cmd.ht_rates[0][0], 484 .ht_rates[0][1] = cfg_cmd.ht_rates[0][1], 485 .ht_rates[1][0] = cfg_cmd.ht_rates[1][0], 486 .ht_rates[1][1] = cfg_cmd.ht_rates[1][1], 487 .sgi_ch_width_supp = cfg_cmd.sgi_ch_width_supp, 488 .max_mpdu_len = cfg_cmd.max_mpdu_len, 489 }; 490 491 u16 cmd_size = sizeof(cfg_cmd_v3); 492 493 /* In old versions of the API the struct is 4 bytes smaller */ 494 if (iwl_fw_lookup_cmd_ver(mvm->fw, 495 WIDE_ID(DATA_PATH_GROUP, 496 TLC_MNG_CONFIG_CMD), 0) < 3) 497 cmd_size -= 4; 498 499 ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, CMD_ASYNC, cmd_size, 500 &cfg_cmd_v3); 501 } else { 502 ret = -EINVAL; 503 } 504 505 if (ret) 506 IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret); 507} 508 509int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, 510 bool enable) 511{ 512 /* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */ 513 IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n"); 514 return 0; 515} 516 517void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta) 518{ 519 struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw; 520 521 IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); 522 523 lq_sta->pers.drv = mvm; 524 lq_sta->pers.sta_id = mvmsta->sta_id; 525 lq_sta->pers.chains = 0; 526 memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); 527 lq_sta->pers.last_rssi = S8_MIN; 528 lq_sta->last_rate_n_flags = 0; 529 530#ifdef CONFIG_MAC80211_DEBUGFS 531 lq_sta->pers.dbg_fixed_rate = 0; 532#endif 533}