cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}