mac80211: consider RX NSS in UHB connection
authorMordechay Goodstein <mordechay.goodstein@intel.com>
Wed, 2 Feb 2022 08:49:35 +0000 (10:49 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 4 Feb 2022 15:23:01 +0000 (16:23 +0100)
In UHB connection we don't have any HT/VHT elemens so in order to
calculate the max RX-NSS we need also to look at HE capa element, this
causes to limit us to max rx nss in UHB to 1.

Also anyway we need to look at HE max rx NSS and not only at HT/VHT
capa to determine the max rx nss over the connection.

Signed-off-by: Mordechay Goodstein <mordechay.goodstein@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20220202104617.3713e0dea5dd.I3b9a15b4c53465c3f86f35459e9dc15ae4ea2abd@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/mlme.c

index 55e2155..291226a 100644 (file)
@@ -4907,13 +4907,20 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
        rcu_read_unlock();
 }
 
-static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
-                                    struct cfg80211_bss *cbss)
+static u8 ieee80211_max_rx_chains(struct ieee80211_sub_if_data *sdata,
+                                 struct cfg80211_bss *cbss)
 {
+       struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const struct element *ht_cap_elem, *vht_cap_elem;
+       const struct cfg80211_bss_ies *ies;
        const struct ieee80211_ht_cap *ht_cap;
        const struct ieee80211_vht_cap *vht_cap;
+       const struct ieee80211_he_cap_elem *he_cap;
+       const struct element *he_cap_elem;
+       u16 mcs_80_map, mcs_160_map;
+       int i, mcs_nss_size;
+       bool support_160;
        u8 chains = 1;
 
        if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
@@ -4948,6 +4955,54 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
                chains = max(chains, nss);
        }
 
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_HE)
+               return chains;
+
+       ies = rcu_dereference(cbss->ies);
+       he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+                                            ies->data, ies->len);
+
+       if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap))
+               return chains;
+
+       /* skip one byte ext_tag_id */
+       he_cap = (void *)(he_cap_elem->data + 1);
+       mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
+
+       /* invalid HE IE */
+       if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap))
+               return chains;
+
+       /* mcs_nss is right after he_cap info */
+       he_mcs_nss_supp = (void *)(he_cap + 1);
+
+       mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
+
+       for (i = 7; i >= 0; i--) {
+               u8 mcs_80 = mcs_80_map >> (2 * i) & 3;
+
+               if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+                       chains = max_t(u8, chains, i + 1);
+                       break;
+               }
+       }
+
+       support_160 = he_cap->phy_cap_info[0] &
+                     IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+       if (!support_160)
+               return chains;
+
+       mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160);
+       for (i = 7; i >= 0; i--) {
+               u8 mcs_160 = mcs_160_map >> (2 * i) & 3;
+
+               if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+                       chains = max_t(u8, chains, i + 1);
+                       break;
+               }
+       }
+
        return chains;
 }
 
@@ -5162,7 +5217,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                                                     s1g_oper,
                                                     &chandef, false);
 
-       sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
+       sdata->needed_rx_chains = min(ieee80211_max_rx_chains(sdata, cbss),
                                      local->rx_chains);
 
        rcu_read_unlock();