1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2d1e879ecSMiri Korenblit /* 3*36b79cb0SIlan Peer * Copyright (C) 2024-2025 Intel Corporation 4d1e879ecSMiri Korenblit */ 5d1e879ecSMiri Korenblit 6d1e879ecSMiri Korenblit #include <net/mac80211.h> 7d1e879ecSMiri Korenblit 8d1e879ecSMiri Korenblit #include "tlc.h" 9d1e879ecSMiri Korenblit #include "hcmd.h" 10d1e879ecSMiri Korenblit #include "sta.h" 11d1e879ecSMiri Korenblit 12d1e879ecSMiri Korenblit #include "fw/api/rs.h" 13d1e879ecSMiri Korenblit #include "fw/api/context.h" 14d1e879ecSMiri Korenblit #include "fw/api/dhc.h" 15d1e879ecSMiri Korenblit 16d1e879ecSMiri Korenblit static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta) 17d1e879ecSMiri Korenblit { 18d1e879ecSMiri Korenblit switch (link_sta->bandwidth) { 19d1e879ecSMiri Korenblit case IEEE80211_STA_RX_BW_320: 20d1e879ecSMiri Korenblit return IWL_TLC_MNG_CH_WIDTH_320MHZ; 21d1e879ecSMiri Korenblit case IEEE80211_STA_RX_BW_160: 22d1e879ecSMiri Korenblit return IWL_TLC_MNG_CH_WIDTH_160MHZ; 23d1e879ecSMiri Korenblit case IEEE80211_STA_RX_BW_80: 24d1e879ecSMiri Korenblit return IWL_TLC_MNG_CH_WIDTH_80MHZ; 25d1e879ecSMiri Korenblit case IEEE80211_STA_RX_BW_40: 26d1e879ecSMiri Korenblit return IWL_TLC_MNG_CH_WIDTH_40MHZ; 27d1e879ecSMiri Korenblit case IEEE80211_STA_RX_BW_20: 28d1e879ecSMiri Korenblit default: 29d1e879ecSMiri Korenblit return IWL_TLC_MNG_CH_WIDTH_20MHZ; 30d1e879ecSMiri Korenblit } 31d1e879ecSMiri Korenblit } 32d1e879ecSMiri Korenblit 33d1e879ecSMiri Korenblit static __le16 34d1e879ecSMiri Korenblit iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, 35d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 36d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta, 37d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *own_he_cap, 38d1e879ecSMiri Korenblit const struct ieee80211_sta_eht_cap *own_eht_cap) 39d1e879ecSMiri Korenblit { 40d1e879ecSMiri Korenblit struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; 41d1e879ecSMiri Korenblit struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; 42d1e879ecSMiri Korenblit struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; 43d1e879ecSMiri Korenblit bool has_vht = vht_cap->vht_supported; 44d1e879ecSMiri Korenblit u16 flags = 0; 45d1e879ecSMiri Korenblit 46d1e879ecSMiri Korenblit /* STBC flags */ 47d1e879ecSMiri Korenblit if (mld->cfg->ht_params->stbc && 48d1e879ecSMiri Korenblit (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) { 49d1e879ecSMiri Korenblit if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & 50d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) 51d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 52d1e879ecSMiri Korenblit else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) 53d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 54d1e879ecSMiri Korenblit else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) 55d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; 56d1e879ecSMiri Korenblit } 57d1e879ecSMiri Korenblit 58d1e879ecSMiri Korenblit /* LDPC */ 59d1e879ecSMiri Korenblit if (mld->cfg->ht_params->ldpc && 60d1e879ecSMiri Korenblit ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || 61d1e879ecSMiri Korenblit (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) 62d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 63d1e879ecSMiri Korenblit 64d1e879ecSMiri Korenblit if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] & 65d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) 66d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 67d1e879ecSMiri Korenblit 68d1e879ecSMiri Korenblit if (own_he_cap && 69d1e879ecSMiri Korenblit !(own_he_cap->he_cap_elem.phy_cap_info[1] & 70d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) 71d1e879ecSMiri Korenblit flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; 72d1e879ecSMiri Korenblit 73d1e879ecSMiri Korenblit /* DCM */ 74d1e879ecSMiri Korenblit if (he_cap->has_he && 75d1e879ecSMiri Korenblit (he_cap->he_cap_elem.phy_cap_info[3] & 76d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && 77d1e879ecSMiri Korenblit own_he_cap && 78d1e879ecSMiri Korenblit own_he_cap->he_cap_elem.phy_cap_info[3] & 79d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) 80d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; 81d1e879ecSMiri Korenblit 82d1e879ecSMiri Korenblit /* Extra EHT LTF */ 83d1e879ecSMiri Korenblit if (own_eht_cap && 84d1e879ecSMiri Korenblit own_eht_cap->eht_cap_elem.phy_cap_info[5] & 85d1e879ecSMiri Korenblit IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF && 86d1e879ecSMiri Korenblit link_sta->eht_cap.has_eht && 87d1e879ecSMiri Korenblit link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & 88d1e879ecSMiri Korenblit IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) { 89d1e879ecSMiri Korenblit flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK; 90d1e879ecSMiri Korenblit } 91d1e879ecSMiri Korenblit 92d1e879ecSMiri Korenblit return cpu_to_le16(flags); 93d1e879ecSMiri Korenblit } 94d1e879ecSMiri Korenblit 95d1e879ecSMiri Korenblit static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld) 96d1e879ecSMiri Korenblit { 97d1e879ecSMiri Korenblit u8 chains = iwl_mld_get_valid_tx_ant(mld); 98d1e879ecSMiri Korenblit u8 fw_chains = 0; 99d1e879ecSMiri Korenblit 100d1e879ecSMiri Korenblit if (chains & ANT_A) 101d1e879ecSMiri Korenblit fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK; 102d1e879ecSMiri Korenblit if (chains & ANT_B) 103d1e879ecSMiri Korenblit fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; 104d1e879ecSMiri Korenblit 105d1e879ecSMiri Korenblit return fw_chains; 106d1e879ecSMiri Korenblit } 107d1e879ecSMiri Korenblit 108d1e879ecSMiri Korenblit static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta) 109d1e879ecSMiri Korenblit { 110d1e879ecSMiri Korenblit struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; 111d1e879ecSMiri Korenblit struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; 112d1e879ecSMiri Korenblit struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; 113d1e879ecSMiri Korenblit u8 sgi_chwidths = 0; 114d1e879ecSMiri Korenblit 115d1e879ecSMiri Korenblit /* If the association supports HE, HT/VHT rates will never be used for 116d1e879ecSMiri Korenblit * Tx and therefor there's no need to set the 117d1e879ecSMiri Korenblit * sgi-per-channel-width-support bits 118d1e879ecSMiri Korenblit */ 119d1e879ecSMiri Korenblit if (he_cap->has_he) 120d1e879ecSMiri Korenblit return 0; 121d1e879ecSMiri Korenblit 122d1e879ecSMiri Korenblit if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) 123d1e879ecSMiri Korenblit sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); 124d1e879ecSMiri Korenblit if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) 125d1e879ecSMiri Korenblit sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); 126d1e879ecSMiri Korenblit if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) 127d1e879ecSMiri Korenblit sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); 128d1e879ecSMiri Korenblit if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) 129d1e879ecSMiri Korenblit sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); 130d1e879ecSMiri Korenblit 131d1e879ecSMiri Korenblit return sgi_chwidths; 132d1e879ecSMiri Korenblit } 133d1e879ecSMiri Korenblit 134d1e879ecSMiri Korenblit static int 135d1e879ecSMiri Korenblit iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap, 136d1e879ecSMiri Korenblit int nss) 137d1e879ecSMiri Korenblit { 138d1e879ecSMiri Korenblit u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & 139d1e879ecSMiri Korenblit (0x3 << (2 * (nss - 1))); 140d1e879ecSMiri Korenblit rx_mcs >>= (2 * (nss - 1)); 141d1e879ecSMiri Korenblit 142d1e879ecSMiri Korenblit switch (rx_mcs) { 143d1e879ecSMiri Korenblit case IEEE80211_VHT_MCS_SUPPORT_0_7: 144d1e879ecSMiri Korenblit return IWL_TLC_MNG_HT_RATE_MCS7; 145d1e879ecSMiri Korenblit case IEEE80211_VHT_MCS_SUPPORT_0_8: 146d1e879ecSMiri Korenblit return IWL_TLC_MNG_HT_RATE_MCS8; 147d1e879ecSMiri Korenblit case IEEE80211_VHT_MCS_SUPPORT_0_9: 148d1e879ecSMiri Korenblit return IWL_TLC_MNG_HT_RATE_MCS9; 149d1e879ecSMiri Korenblit default: 150d1e879ecSMiri Korenblit WARN_ON_ONCE(1); 151d1e879ecSMiri Korenblit break; 152d1e879ecSMiri Korenblit } 153d1e879ecSMiri Korenblit 154d1e879ecSMiri Korenblit return 0; 155d1e879ecSMiri Korenblit } 156d1e879ecSMiri Korenblit 157d1e879ecSMiri Korenblit static void 158d1e879ecSMiri Korenblit iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta, 159d1e879ecSMiri Korenblit const struct ieee80211_sta_vht_cap *vht_cap, 160d1e879ecSMiri Korenblit struct iwl_tlc_config_cmd_v4 *cmd) 161d1e879ecSMiri Korenblit { 162d1e879ecSMiri Korenblit u16 supp; 163d1e879ecSMiri Korenblit int i, highest_mcs; 164d1e879ecSMiri Korenblit u8 max_nss = link_sta->rx_nss; 165d1e879ecSMiri Korenblit struct ieee80211_vht_cap ieee_vht_cap = { 166d1e879ecSMiri Korenblit .vht_cap_info = cpu_to_le32(vht_cap->cap), 167d1e879ecSMiri Korenblit .supp_mcs = vht_cap->vht_mcs, 168d1e879ecSMiri Korenblit }; 169d1e879ecSMiri Korenblit 170d1e879ecSMiri Korenblit /* the station support only a single receive chain */ 171d1e879ecSMiri Korenblit if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) 172d1e879ecSMiri Korenblit max_nss = 1; 173d1e879ecSMiri Korenblit 174d1e879ecSMiri Korenblit for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { 175d1e879ecSMiri Korenblit int nss = i + 1; 176d1e879ecSMiri Korenblit 177d1e879ecSMiri Korenblit highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss); 178d1e879ecSMiri Korenblit if (!highest_mcs) 179d1e879ecSMiri Korenblit continue; 180d1e879ecSMiri Korenblit 181d1e879ecSMiri Korenblit supp = BIT(highest_mcs + 1) - 1; 182d1e879ecSMiri Korenblit if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) 183d1e879ecSMiri Korenblit supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); 184d1e879ecSMiri Korenblit 185d1e879ecSMiri Korenblit cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp); 186d1e879ecSMiri Korenblit /* Check if VHT extended NSS indicates that the bandwidth/NSS 187d1e879ecSMiri Korenblit * configuration is supported - only for MCS 0 since we already 188d1e879ecSMiri Korenblit * decoded the MCS bits anyway ourselves. 189d1e879ecSMiri Korenblit */ 190d1e879ecSMiri Korenblit if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 && 191d1e879ecSMiri Korenblit ieee80211_get_vht_max_nss(&ieee_vht_cap, 192d1e879ecSMiri Korenblit IEEE80211_VHT_CHANWIDTH_160MHZ, 193d1e879ecSMiri Korenblit 0, true, nss) >= nss) 194d1e879ecSMiri Korenblit cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = 195d1e879ecSMiri Korenblit cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80]; 196d1e879ecSMiri Korenblit } 197d1e879ecSMiri Korenblit } 198d1e879ecSMiri Korenblit 199d1e879ecSMiri Korenblit static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs) 200d1e879ecSMiri Korenblit { 201d1e879ecSMiri Korenblit switch (mcs) { 202d1e879ecSMiri Korenblit case IEEE80211_HE_MCS_SUPPORT_0_7: 203d1e879ecSMiri Korenblit return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1; 204d1e879ecSMiri Korenblit case IEEE80211_HE_MCS_SUPPORT_0_9: 205d1e879ecSMiri Korenblit return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1; 206d1e879ecSMiri Korenblit case IEEE80211_HE_MCS_SUPPORT_0_11: 207d1e879ecSMiri Korenblit return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1; 208d1e879ecSMiri Korenblit case IEEE80211_HE_MCS_NOT_SUPPORTED: 209d1e879ecSMiri Korenblit return 0; 210d1e879ecSMiri Korenblit } 211d1e879ecSMiri Korenblit 212d1e879ecSMiri Korenblit WARN(1, "invalid HE MCS %d\n", mcs); 213d1e879ecSMiri Korenblit return 0; 214d1e879ecSMiri Korenblit } 215d1e879ecSMiri Korenblit 216d1e879ecSMiri Korenblit static void 217d1e879ecSMiri Korenblit iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta, 218d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *own_he_cap, 219d1e879ecSMiri Korenblit struct iwl_tlc_config_cmd_v4 *cmd) 220d1e879ecSMiri Korenblit { 221d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; 222d1e879ecSMiri Korenblit u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); 223d1e879ecSMiri Korenblit u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); 224d1e879ecSMiri Korenblit u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80); 225d1e879ecSMiri Korenblit u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160); 226d1e879ecSMiri Korenblit int i; 227d1e879ecSMiri Korenblit u8 nss = link_sta->rx_nss; 228d1e879ecSMiri Korenblit 229d1e879ecSMiri Korenblit /* the station support only a single receive chain */ 230d1e879ecSMiri Korenblit if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) 231d1e879ecSMiri Korenblit nss = 1; 232d1e879ecSMiri Korenblit 233d1e879ecSMiri Korenblit for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { 234d1e879ecSMiri Korenblit u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3; 235d1e879ecSMiri Korenblit u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3; 236d1e879ecSMiri Korenblit u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3; 237d1e879ecSMiri Korenblit u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3; 238d1e879ecSMiri Korenblit 239d1e879ecSMiri Korenblit /* If one side doesn't support - mark both as not supporting */ 240d1e879ecSMiri Korenblit if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED || 241d1e879ecSMiri Korenblit _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) { 242d1e879ecSMiri Korenblit _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; 243d1e879ecSMiri Korenblit _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; 244d1e879ecSMiri Korenblit } 245d1e879ecSMiri Korenblit if (_mcs_80 > _tx_mcs_80) 246d1e879ecSMiri Korenblit _mcs_80 = _tx_mcs_80; 247d1e879ecSMiri Korenblit cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = 248d1e879ecSMiri Korenblit cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80)); 249d1e879ecSMiri Korenblit 250d1e879ecSMiri Korenblit /* If one side doesn't support - mark both as not supporting */ 251d1e879ecSMiri Korenblit if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED || 252d1e879ecSMiri Korenblit _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) { 253d1e879ecSMiri Korenblit _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; 254d1e879ecSMiri Korenblit _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; 255d1e879ecSMiri Korenblit } 256d1e879ecSMiri Korenblit if (_mcs_160 > _tx_mcs_160) 257d1e879ecSMiri Korenblit _mcs_160 = _tx_mcs_160; 258d1e879ecSMiri Korenblit cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = 259d1e879ecSMiri Korenblit cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160)); 260d1e879ecSMiri Korenblit } 261d1e879ecSMiri Korenblit } 262d1e879ecSMiri Korenblit 263d1e879ecSMiri Korenblit static void iwl_mld_set_eht_mcs(__le16 ht_rates[][3], 264d1e879ecSMiri Korenblit enum IWL_TLC_MCS_PER_BW bw, 265d1e879ecSMiri Korenblit u8 max_nss, u16 mcs_msk) 266d1e879ecSMiri Korenblit { 267d1e879ecSMiri Korenblit if (max_nss >= 2) 268d1e879ecSMiri Korenblit ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk); 269d1e879ecSMiri Korenblit 270d1e879ecSMiri Korenblit if (max_nss >= 1) 271d1e879ecSMiri Korenblit ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk); 272d1e879ecSMiri Korenblit } 273d1e879ecSMiri Korenblit 274d1e879ecSMiri Korenblit static const 275d1e879ecSMiri Korenblit struct ieee80211_eht_mcs_nss_supp_bw * 276d1e879ecSMiri Korenblit iwl_mld_get_eht_mcs_of_bw(enum IWL_TLC_MCS_PER_BW bw, 277d1e879ecSMiri Korenblit const struct ieee80211_eht_mcs_nss_supp *eht_mcs) 278d1e879ecSMiri Korenblit { 279d1e879ecSMiri Korenblit switch (bw) { 280d1e879ecSMiri Korenblit case IWL_TLC_MCS_PER_BW_80: 281d1e879ecSMiri Korenblit return &eht_mcs->bw._80; 282d1e879ecSMiri Korenblit case IWL_TLC_MCS_PER_BW_160: 283d1e879ecSMiri Korenblit return &eht_mcs->bw._160; 284d1e879ecSMiri Korenblit case IWL_TLC_MCS_PER_BW_320: 285d1e879ecSMiri Korenblit return &eht_mcs->bw._320; 286d1e879ecSMiri Korenblit default: 287d1e879ecSMiri Korenblit return NULL; 288d1e879ecSMiri Korenblit } 289d1e879ecSMiri Korenblit } 290d1e879ecSMiri Korenblit 291d1e879ecSMiri Korenblit static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss) 292d1e879ecSMiri Korenblit { 293d1e879ecSMiri Korenblit u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX); 294d1e879ecSMiri Korenblit u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX); 295d1e879ecSMiri Korenblit /* the max nss that can be used, 296d1e879ecSMiri Korenblit * is the min with our tx capa and the peer rx capa. 297d1e879ecSMiri Korenblit */ 298d1e879ecSMiri Korenblit return min(tx, rx); 299d1e879ecSMiri Korenblit } 300d1e879ecSMiri Korenblit 301d1e879ecSMiri Korenblit #define MAX_NSS_MCS(mcs_num, rx, tx) \ 302d1e879ecSMiri Korenblit iwl_mld_get_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \ 303d1e879ecSMiri Korenblit (tx)->rx_tx_mcs ##mcs_num## _max_nss) 304d1e879ecSMiri Korenblit 305d1e879ecSMiri Korenblit static void 306d1e879ecSMiri Korenblit iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, 307d1e879ecSMiri Korenblit const struct ieee80211_link_sta *link_sta, 308d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *own_he_cap, 309d1e879ecSMiri Korenblit const struct ieee80211_sta_eht_cap *own_eht_cap, 310d1e879ecSMiri Korenblit struct iwl_tlc_config_cmd_v4 *cmd) 311d1e879ecSMiri Korenblit { 312d1e879ecSMiri Korenblit /* peer RX mcs capa */ 313d1e879ecSMiri Korenblit const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = 314d1e879ecSMiri Korenblit &link_sta->eht_cap.eht_mcs_nss_supp; 315d1e879ecSMiri Korenblit /* our TX mcs capa */ 316d1e879ecSMiri Korenblit const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs = 317d1e879ecSMiri Korenblit &own_eht_cap->eht_mcs_nss_supp; 318d1e879ecSMiri Korenblit 319d1e879ecSMiri Korenblit enum IWL_TLC_MCS_PER_BW bw; 320d1e879ecSMiri Korenblit struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20; 321d1e879ecSMiri Korenblit struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20; 322d1e879ecSMiri Korenblit 323d1e879ecSMiri Korenblit /* peer is 20 MHz only */ 324d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_AP && 325d1e879ecSMiri Korenblit !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & 326d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { 327d1e879ecSMiri Korenblit mcs_rx_20 = eht_rx_mcs->only_20mhz; 328d1e879ecSMiri Korenblit } else { 329d1e879ecSMiri Korenblit mcs_rx_20.rx_tx_mcs7_max_nss = 330d1e879ecSMiri Korenblit eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; 331d1e879ecSMiri Korenblit mcs_rx_20.rx_tx_mcs9_max_nss = 332d1e879ecSMiri Korenblit eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; 333d1e879ecSMiri Korenblit mcs_rx_20.rx_tx_mcs11_max_nss = 334d1e879ecSMiri Korenblit eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss; 335d1e879ecSMiri Korenblit mcs_rx_20.rx_tx_mcs13_max_nss = 336d1e879ecSMiri Korenblit eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss; 337d1e879ecSMiri Korenblit } 338d1e879ecSMiri Korenblit 339d1e879ecSMiri Korenblit /* NIC is capable of 20 MHz only */ 340d1e879ecSMiri Korenblit if (!(own_he_cap->he_cap_elem.phy_cap_info[0] & 341d1e879ecSMiri Korenblit IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { 342d1e879ecSMiri Korenblit mcs_tx_20 = eht_tx_mcs->only_20mhz; 343d1e879ecSMiri Korenblit } else { 344d1e879ecSMiri Korenblit mcs_tx_20.rx_tx_mcs7_max_nss = 345d1e879ecSMiri Korenblit eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; 346d1e879ecSMiri Korenblit mcs_tx_20.rx_tx_mcs9_max_nss = 347d1e879ecSMiri Korenblit eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; 348d1e879ecSMiri Korenblit mcs_tx_20.rx_tx_mcs11_max_nss = 349d1e879ecSMiri Korenblit eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss; 350d1e879ecSMiri Korenblit mcs_tx_20.rx_tx_mcs13_max_nss = 351d1e879ecSMiri Korenblit eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss; 352d1e879ecSMiri Korenblit } 353d1e879ecSMiri Korenblit 354d1e879ecSMiri Korenblit /* rates for 20/40/80 MHz */ 355d1e879ecSMiri Korenblit bw = IWL_TLC_MCS_PER_BW_80; 356d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 357d1e879ecSMiri Korenblit MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), 358d1e879ecSMiri Korenblit GENMASK(7, 0)); 359d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 360d1e879ecSMiri Korenblit MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), 361d1e879ecSMiri Korenblit GENMASK(9, 8)); 362d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 363d1e879ecSMiri Korenblit MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), 364d1e879ecSMiri Korenblit GENMASK(11, 10)); 365d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 366d1e879ecSMiri Korenblit MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), 367d1e879ecSMiri Korenblit GENMASK(13, 12)); 368d1e879ecSMiri Korenblit 369d1e879ecSMiri Korenblit /* rates for 160/320 MHz */ 370d1e879ecSMiri Korenblit for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) { 371d1e879ecSMiri Korenblit const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx = 372d1e879ecSMiri Korenblit iwl_mld_get_eht_mcs_of_bw(bw, eht_rx_mcs); 373d1e879ecSMiri Korenblit const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx = 374d1e879ecSMiri Korenblit iwl_mld_get_eht_mcs_of_bw(bw, eht_tx_mcs); 375d1e879ecSMiri Korenblit 376d1e879ecSMiri Korenblit /* got unsupported index for bw */ 377d1e879ecSMiri Korenblit if (!mcs_rx || !mcs_tx) 378d1e879ecSMiri Korenblit continue; 379d1e879ecSMiri Korenblit 380d1e879ecSMiri Korenblit /* break out if we don't support the bandwidth */ 381d1e879ecSMiri Korenblit if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ)) 382d1e879ecSMiri Korenblit break; 383d1e879ecSMiri Korenblit 384d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 385d1e879ecSMiri Korenblit MAX_NSS_MCS(9, mcs_rx, mcs_tx), 386d1e879ecSMiri Korenblit GENMASK(9, 0)); 387d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 388d1e879ecSMiri Korenblit MAX_NSS_MCS(11, mcs_rx, mcs_tx), 389d1e879ecSMiri Korenblit GENMASK(11, 10)); 390d1e879ecSMiri Korenblit iwl_mld_set_eht_mcs(cmd->ht_rates, bw, 391d1e879ecSMiri Korenblit MAX_NSS_MCS(13, mcs_rx, mcs_tx), 392d1e879ecSMiri Korenblit GENMASK(13, 12)); 393d1e879ecSMiri Korenblit } 394d1e879ecSMiri Korenblit 395d1e879ecSMiri Korenblit /* the station support only a single receive chain */ 396d1e879ecSMiri Korenblit if (link_sta->smps_mode == IEEE80211_SMPS_STATIC || 397d1e879ecSMiri Korenblit link_sta->rx_nss < 2) 398d1e879ecSMiri Korenblit memset(cmd->ht_rates[IWL_TLC_NSS_2], 0, 399d1e879ecSMiri Korenblit sizeof(cmd->ht_rates[IWL_TLC_NSS_2])); 400d1e879ecSMiri Korenblit } 401d1e879ecSMiri Korenblit 402d1e879ecSMiri Korenblit static void 403d1e879ecSMiri Korenblit iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif, 404d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta, 405d1e879ecSMiri Korenblit struct ieee80211_supported_band *sband, 406d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *own_he_cap, 407d1e879ecSMiri Korenblit const struct ieee80211_sta_eht_cap *own_eht_cap, 408d1e879ecSMiri Korenblit struct iwl_tlc_config_cmd_v4 *cmd) 409d1e879ecSMiri Korenblit { 410d1e879ecSMiri Korenblit int i; 411d1e879ecSMiri Korenblit u16 non_ht_rates = 0; 412d1e879ecSMiri Korenblit unsigned long rates_bitmap; 413d1e879ecSMiri Korenblit const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; 414d1e879ecSMiri Korenblit const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; 415d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; 416d1e879ecSMiri Korenblit 417d1e879ecSMiri Korenblit /* non HT rates */ 418d1e879ecSMiri Korenblit rates_bitmap = link_sta->supp_rates[sband->band]; 419d1e879ecSMiri Korenblit for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG) 420d1e879ecSMiri Korenblit non_ht_rates |= BIT(sband->bitrates[i].hw_value); 421d1e879ecSMiri Korenblit 422d1e879ecSMiri Korenblit cmd->non_ht_rates = cpu_to_le16(non_ht_rates); 423d1e879ecSMiri Korenblit cmd->mode = IWL_TLC_MNG_MODE_NON_HT; 424d1e879ecSMiri Korenblit 425d1e879ecSMiri Korenblit if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) { 426d1e879ecSMiri Korenblit cmd->mode = IWL_TLC_MNG_MODE_EHT; 427d1e879ecSMiri Korenblit iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, 428d1e879ecSMiri Korenblit own_eht_cap, cmd); 429d1e879ecSMiri Korenblit } else if (he_cap->has_he && own_he_cap) { 430d1e879ecSMiri Korenblit cmd->mode = IWL_TLC_MNG_MODE_HE; 431d1e879ecSMiri Korenblit iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd); 432d1e879ecSMiri Korenblit } else if (vht_cap->vht_supported) { 433d1e879ecSMiri Korenblit cmd->mode = IWL_TLC_MNG_MODE_VHT; 434d1e879ecSMiri Korenblit iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd); 435d1e879ecSMiri Korenblit } else if (ht_cap->ht_supported) { 436d1e879ecSMiri Korenblit cmd->mode = IWL_TLC_MNG_MODE_HT; 437d1e879ecSMiri Korenblit cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] = 438d1e879ecSMiri Korenblit cpu_to_le16(ht_cap->mcs.rx_mask[0]); 439d1e879ecSMiri Korenblit 440d1e879ecSMiri Korenblit /* the station support only a single receive chain */ 441d1e879ecSMiri Korenblit if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) 442d1e879ecSMiri Korenblit cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = 443d1e879ecSMiri Korenblit 0; 444d1e879ecSMiri Korenblit else 445d1e879ecSMiri Korenblit cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = 446d1e879ecSMiri Korenblit cpu_to_le16(ht_cap->mcs.rx_mask[1]); 447d1e879ecSMiri Korenblit } 448d1e879ecSMiri Korenblit } 449d1e879ecSMiri Korenblit 450d1e879ecSMiri Korenblit static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, 451d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 452d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta, 453d1e879ecSMiri Korenblit enum nl80211_band band) 454d1e879ecSMiri Korenblit { 455d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); 456d1e879ecSMiri Korenblit struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band]; 457d1e879ecSMiri Korenblit const struct ieee80211_sta_he_cap *own_he_cap = 458d1e879ecSMiri Korenblit ieee80211_get_he_iftype_cap_vif(sband, vif); 459d1e879ecSMiri Korenblit const struct ieee80211_sta_eht_cap *own_eht_cap = 460d1e879ecSMiri Korenblit ieee80211_get_eht_iftype_cap_vif(sband, vif); 461d1e879ecSMiri Korenblit struct iwl_tlc_config_cmd_v4 cmd = { 462d1e879ecSMiri Korenblit /* For AP mode, use 20 MHz until the STA is authorized */ 463d1e879ecSMiri Korenblit .max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ? 464d1e879ecSMiri Korenblit iwl_mld_fw_bw_from_sta_bw(link_sta) : 465d1e879ecSMiri Korenblit IWL_TLC_MNG_CH_WIDTH_20MHZ, 466d1e879ecSMiri Korenblit .flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta, 467d1e879ecSMiri Korenblit own_he_cap, own_eht_cap), 468d1e879ecSMiri Korenblit .chains = iwl_mld_get_fw_chains(mld), 469d1e879ecSMiri Korenblit .sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta), 470*36b79cb0SIlan Peer .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len), 471d1e879ecSMiri Korenblit }; 472d1e879ecSMiri Korenblit int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); 473d1e879ecSMiri Korenblit int ret; 474d1e879ecSMiri Korenblit 475d1e879ecSMiri Korenblit if (fw_sta_id < 0) 476d1e879ecSMiri Korenblit return; 477d1e879ecSMiri Korenblit 478d1e879ecSMiri Korenblit cmd.sta_id = fw_sta_id; 479d1e879ecSMiri Korenblit 480d1e879ecSMiri Korenblit iwl_mld_fill_supp_rates(mld, vif, link_sta, sband, 481d1e879ecSMiri Korenblit own_he_cap, own_eht_cap, 482d1e879ecSMiri Korenblit &cmd); 483d1e879ecSMiri Korenblit 484d1e879ecSMiri Korenblit IWL_DEBUG_RATE(mld, 485d1e879ecSMiri Korenblit "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n", 486d1e879ecSMiri Korenblit cmd.sta_id, cmd.max_ch_width, cmd.mode); 487d1e879ecSMiri Korenblit 488d1e879ecSMiri Korenblit /* Send async since this can be called within a RCU-read section */ 489d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(DATA_PATH_GROUP, 490d1e879ecSMiri Korenblit TLC_MNG_CONFIG_CMD), 491d1e879ecSMiri Korenblit CMD_ASYNC, &cmd); 492d1e879ecSMiri Korenblit if (ret) 493d1e879ecSMiri Korenblit IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret); 494d1e879ecSMiri Korenblit } 495d1e879ecSMiri Korenblit 496d1e879ecSMiri Korenblit int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data) 497d1e879ecSMiri Korenblit { 498d1e879ecSMiri Korenblit struct { 499d1e879ecSMiri Korenblit struct iwl_dhc_cmd dhc; 500d1e879ecSMiri Korenblit struct iwl_dhc_tlc_cmd tlc; 501d1e879ecSMiri Korenblit } __packed cmd = { 502d1e879ecSMiri Korenblit .tlc.sta_id = sta_id, 503d1e879ecSMiri Korenblit .tlc.type = cpu_to_le32(type), 504d1e879ecSMiri Korenblit .tlc.data[0] = cpu_to_le32(data), 505d1e879ecSMiri Korenblit .dhc.length = cpu_to_le32(sizeof(cmd.tlc) >> 2), 506d1e879ecSMiri Korenblit .dhc.index_and_mask = 507d1e879ecSMiri Korenblit cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | 508d1e879ecSMiri Korenblit DHC_INTEGRATION_TLC_DEBUG_CONFIG), 509d1e879ecSMiri Korenblit }; 510d1e879ecSMiri Korenblit int ret; 511d1e879ecSMiri Korenblit 512d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd_with_flags_pdu(mld, 513d1e879ecSMiri Korenblit WIDE_ID(IWL_ALWAYS_LONG_GROUP, 514d1e879ecSMiri Korenblit DEBUG_HOST_COMMAND), 515d1e879ecSMiri Korenblit CMD_ASYNC, &cmd); 516d1e879ecSMiri Korenblit IWL_DEBUG_RATE(mld, "sta_id %d, type: 0x%X, value: 0x%X, ret%d\n", 517d1e879ecSMiri Korenblit sta_id, type, data, ret); 518d1e879ecSMiri Korenblit return ret; 519d1e879ecSMiri Korenblit } 520d1e879ecSMiri Korenblit 521d1e879ecSMiri Korenblit void iwl_mld_config_tlc_link(struct iwl_mld *mld, 522d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 523d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf, 524d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta) 525d1e879ecSMiri Korenblit { 526*36b79cb0SIlan Peer struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); 527d1e879ecSMiri Korenblit enum nl80211_band band; 528d1e879ecSMiri Korenblit 529d1e879ecSMiri Korenblit if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) 530d1e879ecSMiri Korenblit return; 531d1e879ecSMiri Korenblit 532*36b79cb0SIlan Peer /* Before we have information about a station, configure the A-MSDU RC 533*36b79cb0SIlan Peer * limit such that iwlmd and mac80211 would not be allowed to build 534*36b79cb0SIlan Peer * A-MSDUs. 535*36b79cb0SIlan Peer */ 536*36b79cb0SIlan Peer if (mld_sta->sta_state < IEEE80211_STA_ASSOC) { 537*36b79cb0SIlan Peer link_sta->agg.max_rc_amsdu_len = 1; 538*36b79cb0SIlan Peer ieee80211_sta_recalc_aggregates(link_sta->sta); 539*36b79cb0SIlan Peer } 540*36b79cb0SIlan Peer 541d1e879ecSMiri Korenblit band = link_conf->chanreq.oper.chan->band; 542d1e879ecSMiri Korenblit iwl_mld_send_tlc_cmd(mld, vif, link_sta, band); 543d1e879ecSMiri Korenblit } 544d1e879ecSMiri Korenblit 545d1e879ecSMiri Korenblit void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, 546d1e879ecSMiri Korenblit struct ieee80211_sta *sta) 547d1e879ecSMiri Korenblit { 548d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link; 549d1e879ecSMiri Korenblit int link_id; 550d1e879ecSMiri Korenblit 551d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 552d1e879ecSMiri Korenblit 553d1e879ecSMiri Korenblit for_each_vif_active_link(vif, link, link_id) { 554d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta = 555d1e879ecSMiri Korenblit link_sta_dereference_check(sta, link_id); 556d1e879ecSMiri Korenblit 557d1e879ecSMiri Korenblit if (!link || !link_sta) 558d1e879ecSMiri Korenblit continue; 559d1e879ecSMiri Korenblit 560d1e879ecSMiri Korenblit iwl_mld_config_tlc_link(mld, vif, link, link_sta); 561d1e879ecSMiri Korenblit } 562d1e879ecSMiri Korenblit } 563d1e879ecSMiri Korenblit 564d1e879ecSMiri Korenblit static u16 565d1e879ecSMiri Korenblit iwl_mld_get_amsdu_size_of_tid(struct iwl_mld *mld, 566d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta, 567d1e879ecSMiri Korenblit unsigned int tid) 568d1e879ecSMiri Korenblit { 569d1e879ecSMiri Korenblit struct ieee80211_sta *sta = link_sta->sta; 570d1e879ecSMiri Korenblit struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif; 571d1e879ecSMiri Korenblit const u8 tid_to_mac80211_ac[] = { 572d1e879ecSMiri Korenblit IEEE80211_AC_BE, 573d1e879ecSMiri Korenblit IEEE80211_AC_BK, 574d1e879ecSMiri Korenblit IEEE80211_AC_BK, 575d1e879ecSMiri Korenblit IEEE80211_AC_BE, 576d1e879ecSMiri Korenblit IEEE80211_AC_VI, 577d1e879ecSMiri Korenblit IEEE80211_AC_VI, 578d1e879ecSMiri Korenblit IEEE80211_AC_VO, 579d1e879ecSMiri Korenblit IEEE80211_AC_VO, 580d1e879ecSMiri Korenblit }; 581d1e879ecSMiri Korenblit unsigned int result = link_sta->agg.max_rc_amsdu_len; 582d1e879ecSMiri Korenblit u8 ac, txf, lmac; 583d1e879ecSMiri Korenblit 584d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 585d1e879ecSMiri Korenblit 586d1e879ecSMiri Korenblit /* Don't send an AMSDU that will be longer than the TXF. 587d1e879ecSMiri Korenblit * Add a security margin of 256 for the TX command + headers. 588d1e879ecSMiri Korenblit * We also want to have the start of the next packet inside the 589d1e879ecSMiri Korenblit * fifo to be able to send bursts. 590d1e879ecSMiri Korenblit */ 591d1e879ecSMiri Korenblit 592d1e879ecSMiri Korenblit if (WARN_ON(tid >= ARRAY_SIZE(tid_to_mac80211_ac))) 593d1e879ecSMiri Korenblit return 0; 594d1e879ecSMiri Korenblit 595d1e879ecSMiri Korenblit ac = tid_to_mac80211_ac[tid]; 596d1e879ecSMiri Korenblit 597d1e879ecSMiri Korenblit /* For HE redirect to trigger based fifos */ 598d1e879ecSMiri Korenblit if (link_sta->he_cap.has_he) 599d1e879ecSMiri Korenblit ac += 4; 600d1e879ecSMiri Korenblit 601d1e879ecSMiri Korenblit txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(ac); 602d1e879ecSMiri Korenblit 603d1e879ecSMiri Korenblit /* Only one link: take the lmac according to the band */ 604d1e879ecSMiri Korenblit if (hweight16(sta->valid_links) <= 1) { 605d1e879ecSMiri Korenblit enum nl80211_band band; 606d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link = 607d1e879ecSMiri Korenblit wiphy_dereference(mld->wiphy, 608d1e879ecSMiri Korenblit vif->link_conf[link_sta->link_id]); 609d1e879ecSMiri Korenblit 610d1e879ecSMiri Korenblit if (WARN_ON(!link || !link->chanreq.oper.chan)) 611d1e879ecSMiri Korenblit band = NL80211_BAND_2GHZ; 612d1e879ecSMiri Korenblit else 613d1e879ecSMiri Korenblit band = link->chanreq.oper.chan->band; 614d1e879ecSMiri Korenblit lmac = iwl_mld_get_lmac_id(mld, band); 615d1e879ecSMiri Korenblit 616d1e879ecSMiri Korenblit /* More than one link but with 2 lmacs: take the minimum */ 617d1e879ecSMiri Korenblit } else if (fw_has_capa(&mld->fw->ucode_capa, 618d1e879ecSMiri Korenblit IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) { 619d1e879ecSMiri Korenblit lmac = IWL_LMAC_5G_INDEX; 620d1e879ecSMiri Korenblit result = min_t(unsigned int, result, 621d1e879ecSMiri Korenblit mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); 622d1e879ecSMiri Korenblit lmac = IWL_LMAC_24G_INDEX; 623d1e879ecSMiri Korenblit /* More than one link but only one lmac */ 624d1e879ecSMiri Korenblit } else { 625d1e879ecSMiri Korenblit lmac = IWL_LMAC_24G_INDEX; 626d1e879ecSMiri Korenblit } 627d1e879ecSMiri Korenblit 628d1e879ecSMiri Korenblit return min_t(unsigned int, result, 629d1e879ecSMiri Korenblit mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); 630d1e879ecSMiri Korenblit } 631d1e879ecSMiri Korenblit 632d1e879ecSMiri Korenblit void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, 633d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 634d1e879ecSMiri Korenblit { 635d1e879ecSMiri Korenblit struct iwl_tlc_update_notif *notif = (void *)pkt->data; 636d1e879ecSMiri Korenblit struct ieee80211_link_sta *link_sta; 637d1e879ecSMiri Korenblit u32 flags = le32_to_cpu(notif->flags); 638d1e879ecSMiri Korenblit u32 enabled; 639d1e879ecSMiri Korenblit u16 size; 640d1e879ecSMiri Korenblit 641d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, notif->sta_id >= mld->fw->ucode_capa.num_stations, 642d1e879ecSMiri Korenblit "Invalid sta id (%d) in TLC notification\n", 643d1e879ecSMiri Korenblit notif->sta_id)) 644d1e879ecSMiri Korenblit return; 645d1e879ecSMiri Korenblit 646d1e879ecSMiri Korenblit link_sta = wiphy_dereference(mld->wiphy, 647d1e879ecSMiri Korenblit mld->fw_id_to_link_sta[notif->sta_id]); 648d1e879ecSMiri Korenblit 649d1e879ecSMiri Korenblit if (WARN(IS_ERR_OR_NULL(link_sta), 650d1e879ecSMiri Korenblit "link_sta of sta id (%d) doesn't exist\n", notif->sta_id)) 651d1e879ecSMiri Korenblit return; 652d1e879ecSMiri Korenblit 653d1e879ecSMiri Korenblit if (flags & IWL_TLC_NOTIF_FLAG_RATE) { 654d1e879ecSMiri Korenblit struct iwl_mld_link_sta *mld_link_sta = 655d1e879ecSMiri Korenblit iwl_mld_link_sta_from_mac80211(link_sta); 656d1e879ecSMiri Korenblit char pretty_rate[100]; 657d1e879ecSMiri Korenblit 658d1e879ecSMiri Korenblit if (WARN_ON(!mld_link_sta)) 659d1e879ecSMiri Korenblit return; 660d1e879ecSMiri Korenblit 661d1e879ecSMiri Korenblit mld_link_sta->last_rate_n_flags = le32_to_cpu(notif->rate); 662d1e879ecSMiri Korenblit 663d1e879ecSMiri Korenblit rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), 664d1e879ecSMiri Korenblit mld_link_sta->last_rate_n_flags); 665d1e879ecSMiri Korenblit IWL_DEBUG_RATE(mld, "TLC notif: new rate = %s\n", pretty_rate); 666d1e879ecSMiri Korenblit } 667d1e879ecSMiri Korenblit 668d1e879ecSMiri Korenblit /* We are done processing the notif */ 669d1e879ecSMiri Korenblit if (!(flags & IWL_TLC_NOTIF_FLAG_AMSDU)) 670d1e879ecSMiri Korenblit return; 671d1e879ecSMiri Korenblit 672d1e879ecSMiri Korenblit enabled = le32_to_cpu(notif->amsdu_enabled); 673d1e879ecSMiri Korenblit size = le32_to_cpu(notif->amsdu_size); 674d1e879ecSMiri Korenblit 675d1e879ecSMiri Korenblit if (size < 2000) { 676d1e879ecSMiri Korenblit size = 0; 677d1e879ecSMiri Korenblit enabled = 0; 678d1e879ecSMiri Korenblit } 679d1e879ecSMiri Korenblit 680d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, size > link_sta->agg.max_amsdu_len, 681d1e879ecSMiri Korenblit "Invalid AMSDU len in TLC notif: %d (Max AMSDU len: %d)\n", 682d1e879ecSMiri Korenblit size, link_sta->agg.max_amsdu_len)) 683d1e879ecSMiri Korenblit return; 684d1e879ecSMiri Korenblit 685d1e879ecSMiri Korenblit link_sta->agg.max_rc_amsdu_len = size; 686d1e879ecSMiri Korenblit 687d1e879ecSMiri Korenblit for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { 688d1e879ecSMiri Korenblit if (enabled & BIT(i)) 689d1e879ecSMiri Korenblit link_sta->agg.max_tid_amsdu_len[i] = 690d1e879ecSMiri Korenblit iwl_mld_get_amsdu_size_of_tid(mld, link_sta, i); 691d1e879ecSMiri Korenblit else 692*36b79cb0SIlan Peer link_sta->agg.max_tid_amsdu_len[i] = 1; 693d1e879ecSMiri Korenblit } 694d1e879ecSMiri Korenblit 695d1e879ecSMiri Korenblit ieee80211_sta_recalc_aggregates(link_sta->sta); 696d1e879ecSMiri Korenblit 697d1e879ecSMiri Korenblit IWL_DEBUG_RATE(mld, 698d1e879ecSMiri Korenblit "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", 699d1e879ecSMiri Korenblit le32_to_cpu(notif->amsdu_size), size, enabled); 700d1e879ecSMiri Korenblit } 701