From 6c95151e2e776462de6b6fd9e577865e8b32203e Mon Sep 17 00:00:00 2001 From: Muna Sinada Date: Thu, 23 Oct 2025 17:19:25 -0700 Subject: [PATCH] wifi: ath12k: Add EHT MCS/NSS rates to Peer Assoc Add EHT MCS/NSS rate functionality to peer association. As part of ath12k_peer_assoc_h_eht() add the calculation of EHT MCS/NSS using intersection of link_sta and phy capability. ath12k_mac_max_eht_mcs_nss() function is utilized when comparing the max NSS of link STA and phy capability since in split phy case, phy supports max NSS of 2 for 5G band. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1 Co-developed-by: Vishal Kumar Signed-off-by: Vishal Kumar Co-developed-by: Aloka Dixit Signed-off-by: Aloka Dixit Signed-off-by: Muna Sinada Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20251024001928.257356-4-muna.sinada@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 110 ++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 0efdf87153e2f..f1b5755bb85e1 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -545,6 +545,18 @@ ath12k_mac_max_eht_nss(const u16 eht_mcs_mask[NL80211_EHT_NSS_MAX]) return 1; } +static u32 +ath12k_mac_max_eht_mcs_nss(const u8 *eht_mcs, int eht_mcs_set_size) +{ + int i; + u8 nss = 0; + + for (i = 0; i < eht_mcs_set_size; i++) + nss = max(nss, u8_get_bits(eht_mcs[i], IEEE80211_EHT_MCS_NSS_RX)); + + return nss; +} + static u8 ath12k_parse_mpdudensity(u8 mpdudensity) { /* From IEEE Std 802.11-2020 defined values for "Minimum MPDU Start Spacing": @@ -3016,6 +3028,18 @@ static enum wmi_phy_mode ath12k_mac_get_phymode_eht(struct ath12k *ar, return MODE_UNKNOWN; } +static bool +ath12k_peer_assoc_h_eht_masked(const u16 eht_mcs_mask[NL80211_EHT_NSS_MAX]) +{ + int nss; + + for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++) + if (eht_mcs_mask[nss]) + return false; + + return true; +} + static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ath12k_link_sta *arsta, @@ -3027,6 +3051,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, const u8 *ht_mcs_mask; const u16 *vht_mcs_mask; const u16 *he_mcs_mask; + const u16 *eht_mcs_mask; enum wmi_phy_mode phymode = MODE_UNKNOWN; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -3041,6 +3066,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs; vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs; he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs; + eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs; link_sta = ath12k_mac_get_link_sta(arsta); if (!link_sta) { @@ -3051,7 +3077,8 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar, switch (band) { case NL80211_BAND_2GHZ: - if (link_sta->eht_cap.has_eht) { + if (link_sta->eht_cap.has_eht && + !ath12k_peer_assoc_h_eht_masked(eht_mcs_mask)) { if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11BE_EHT40_2G; else @@ -3197,16 +3224,21 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, { struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + const struct ieee80211_eht_mcs_nss_supp *own_eht_mcs_nss_supp; const struct ieee80211_eht_mcs_nss_supp_20mhz_only *bw_20; + const struct ieee80211_sta_eht_cap *eht_cap, *own_eht_cap; + const struct ieee80211_sband_iftype_data *iftd; const struct ieee80211_eht_mcs_nss_supp_bw *bw; - const struct ieee80211_sta_eht_cap *eht_cap; const struct ieee80211_sta_he_cap *he_cap; struct ieee80211_link_sta *link_sta; struct ieee80211_bss_conf *link_conf; struct cfg80211_chan_def def; + bool user_rate_valid = true; enum nl80211_band band; + int eht_nss, nss_idx; u32 *rx_mcs, *tx_mcs; u16 *eht_mcs_mask; + u8 max_nss = 0; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -3234,6 +3266,16 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, band = def.chan->band; eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs; + iftd = ieee80211_get_sband_iftype_data(&ar->mac.sbands[band], vif->type); + if (!iftd) { + ath12k_warn(ar->ab, + "unable to access iftype_data in struct ieee80211_supported_band\n"); + return; + } + + own_eht_cap = &iftd->eht_cap; + own_eht_mcs_nss_supp = &own_eht_cap->eht_mcs_nss_supp; + arg->eht_flag = true; if ((eht_cap->eht_cap_elem.phy_cap_info[5] & @@ -3250,6 +3292,28 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, rx_mcs = arg->peer_eht_rx_mcs_set; tx_mcs = arg->peer_eht_tx_mcs_set; + eht_nss = ath12k_mac_max_eht_mcs_nss((void *)own_eht_mcs_nss_supp, + sizeof(*own_eht_mcs_nss_supp)); + if (eht_nss > link_sta->rx_nss) { + user_rate_valid = false; + for (nss_idx = (link_sta->rx_nss - 1); nss_idx >= 0; nss_idx--) { + if (eht_mcs_mask[nss_idx]) { + user_rate_valid = true; + break; + } + } + } + + if (!user_rate_valid) { + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "Setting eht range MCS value to peer supported nss %d for peer %pM\n", + link_sta->rx_nss, arsta->addr); + eht_mcs_mask[link_sta->rx_nss - 1] = eht_mcs_mask[eht_nss - 1]; + } + + bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz; + bw = &eht_cap->eht_mcs_nss_supp.bw._80; + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_320: bw = &eht_cap->eht_mcs_nss_supp.bw._320; @@ -3274,11 +3338,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, arg->peer_eht_mcs_count++; fallthrough; default: - if ((he_cap->he_cap_elem.phy_cap_info[0] & - (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0) { + if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz; ath12k_mac_set_eht_mcs(bw_20->rx_tx_mcs7_max_nss, @@ -3305,6 +3366,41 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, arg->punct_bitmap = ~arvif->punct_bitmap; arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15; + + if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + if (bw_20->rx_tx_mcs13_max_nss) + max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs13_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + if (bw_20->rx_tx_mcs11_max_nss) + max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs11_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + if (bw_20->rx_tx_mcs9_max_nss) + max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + if (bw_20->rx_tx_mcs7_max_nss) + max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs7_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + } else { + if (bw->rx_tx_mcs13_max_nss) + max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs13_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + if (bw->rx_tx_mcs11_max_nss) + max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs11_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + if (bw->rx_tx_mcs9_max_nss) + max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_RX)); + } + + max_nss = min(max_nss, (uint8_t)eht_nss); + + arg->peer_nss = min(link_sta->rx_nss, max_nss); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "mac eht peer %pM nss %d mcs cnt %d ru_punct_bitmap 0x%x\n", + arsta->addr, arg->peer_nss, arg->peer_eht_mcs_count, + arg->punct_bitmap); } static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, -- 2.47.3