Add the UHR capabilities element to both probe request and association request frames, if advertised by the driver. Introduce IEEE80211_CONN_MODE_UHR flag to disable the UHR connection if AP doesn't send UHR operation or the operating channel is not UHR supported. Signed-off-by: Karthikeyan Kathirvel --- include/net/mac80211.h | 14 ++++++++++++++ net/mac80211/ieee80211_i.h | 4 ++++ net/mac80211/main.c | 12 +++++++++++- net/mac80211/mlme.c | 37 +++++++++++++++++++++++++++++++++++- net/mac80211/parse.c | 4 ++++ net/mac80211/util.c | 39 +++++++++++++++++++++++++++++++++++++- 6 files changed, 107 insertions(+), 3 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c2e49542626c..7f1d2953a074 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7236,6 +7236,20 @@ ieee80211_get_eht_iftype_cap_vif(const struct ieee80211_supported_band *sband, return ieee80211_get_eht_iftype_cap(sband, ieee80211_vif_type_p2p(vif)); } +/** + * ieee80211_get_uhr_iftype_cap_vif - return UHR capabilities for sband/vif + * @sband: the sband to search for the iftype on + * @vif: the vif to get the iftype from + * + * Return: pointer to the struct ieee80211_sta_uhr_cap, or %NULL is none found + */ +static inline const struct ieee80211_sta_uhr_cap * +ieee80211_get_uhr_iftype_cap_vif(const struct ieee80211_supported_band *sband, + struct ieee80211_vif *vif) +{ + return ieee80211_get_uhr_iftype_cap(sband, ieee80211_vif_type_p2p(vif)); +} + /** * ieee80211_update_mu_groups - set the VHT MU-MIMO groud data * diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 04f1df209cec..3a61710b1532 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -394,6 +394,7 @@ enum ieee80211_conn_mode { IEEE80211_CONN_MODE_VHT, IEEE80211_CONN_MODE_HE, IEEE80211_CONN_MODE_EHT, + IEEE80211_CONN_MODE_UHR, }; #define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_EHT @@ -2691,6 +2692,9 @@ int ieee80211_put_eht_cap(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata, const struct ieee80211_supported_band *sband, const struct ieee80211_conn_settings *conn); +int ieee80211_put_uhr_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_supported_band *sband); int ieee80211_put_reg_conn(struct sk_buff *skb, enum ieee80211_channel_flags flags); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b05e313c7f17..ffe957eb0c8e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1123,7 +1123,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) int result, i; enum nl80211_band band; int channels, max_bitrates; - bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g; + bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g, supp_uhr; struct cfg80211_chan_def dflt_chandef = {}; if (ieee80211_hw_check(hw, QUEUE_CONTROL) && @@ -1237,6 +1237,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_he = false; supp_eht = false; supp_s1g = false; + supp_uhr = false; for (band = 0; band < NUM_NL80211_BANDS; band++) { const struct ieee80211_sband_iftype_data *iftd; struct ieee80211_supported_band *sband; @@ -1293,6 +1294,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_he = supp_he || iftd->he_cap.has_he; supp_eht = supp_eht || iftd->eht_cap.has_eht; + supp_uhr = supp_uhr || iftd->uhr_cap.has_uhr; if (band == NL80211_BAND_2GHZ) he_40_mhz_cap = @@ -1325,6 +1327,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (WARN_ON(supp_eht && !supp_he)) return -EINVAL; + /* UHR requires EHT support */ + if (WARN_ON(supp_uhr && !supp_eht)) + return -EINVAL; + if (!sband->ht_cap.ht_supported) continue; @@ -1435,6 +1441,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) 3 + sizeof(struct ieee80211_eht_cap_elem) + sizeof(struct ieee80211_eht_mcs_nss_supp) + IEEE80211_EHT_PPE_THRES_MAX_LEN; + + if (supp_uhr) + local->scan_ies_len += + 3 + sizeof(struct ieee80211_uhr_cap_elem); } if (!local->ops->hw_scan) { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 73649d57d0c3..9b730aa5fc3a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -162,6 +162,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, const struct ieee80211_vht_operation *vht_oper = elems->vht_operation; const struct ieee80211_he_operation *he_oper = elems->he_operation; const struct ieee80211_eht_operation *eht_oper = elems->eht_operation; + const struct ieee80211_uhr_operation *uhr_oper = elems->uhr_operation; struct ieee80211_supported_band *sband = sdata->local->hw.wiphy->bands[channel->band]; struct cfg80211_chan_def vht_chandef; @@ -340,7 +341,12 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata, *chandef = eht_chandef; } - return IEEE80211_CONN_MODE_EHT; + /* stick to EHT if STA or the AP don't have UHR */ + if (conn->mode < IEEE80211_CONN_MODE_UHR || + !uhr_oper) + return IEEE80211_CONN_MODE_EHT; + + return IEEE80211_CONN_MODE_UHR; } static bool @@ -1091,6 +1097,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, IEEE80211_CONN_BW_LIMIT_160); break; case IEEE80211_CONN_MODE_EHT: + case IEEE80211_CONN_MODE_UHR: conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, conn->bw_limit, IEEE80211_CONN_BW_LIMIT_320); @@ -1152,6 +1159,15 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata, IEEE80211_CONN_BW_LIMIT_160); } + if (conn->mode >= IEEE80211_CONN_MODE_UHR && + !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper, + IEEE80211_CHAN_NO_UHR)) { + conn->mode = IEEE80211_CONN_MODE_EHT; + conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, + conn->bw_limit, + IEEE80211_CONN_BW_LIMIT_160); + } + if (chanreq->oper.width != ap_chandef->width || ap_mode != conn->mode) link_id_info(sdata, link_id, "regulatory prevented using AP config, downgraded\n"); @@ -1887,6 +1903,9 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata, if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY); + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR) + ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_UHR_CAPABILITY); + if (link_id == assoc_data->assoc_link_id) ieee80211_assoc_add_ml_elem(sdata, skb, orig_capab, ext_capa, present_elems, assoc_data); @@ -1898,6 +1917,9 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata, ieee80211_put_eht_cap(skb, sdata, sband, &assoc_data->link[link_id].conn); + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR) + ieee80211_put_uhr_cap(skb, sdata, sband); + if (sband->band == NL80211_BAND_S1GHZ) { ieee80211_add_aid_request_ie(sdata, skb); ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb); @@ -2132,6 +2154,8 @@ ieee80211_link_common_elems_size(struct ieee80211_sub_if_data *sdata, sizeof(struct ieee80211_eht_mcs_nss_supp) + IEEE80211_EHT_PPE_THRES_MAX_LEN; + size += 2 + 1 + sizeof(struct ieee80211_uhr_cap_elem); + return size; } @@ -5808,6 +5832,7 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, const struct ieee80211_sta_he_cap *he_cap; const struct ieee80211_sta_eht_cap *eht_cap; struct ieee80211_sta_vht_cap vht_cap; + const struct ieee80211_sta_uhr_cap *uhr_cap; if (sband->band == NL80211_BAND_S1GHZ) { conn->mode = IEEE80211_CONN_MODE_S1G; @@ -5995,6 +6020,16 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, mlme_link_id_dbg(sdata, link_id, "no EHT 320 MHz cap in 6 GHz, limiting to 160 MHz\n"); + uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif); + if (!uhr_cap) { + mlme_link_id_dbg(sdata, link_id, + "no UHR support, limiting to EHT\n"); + goto out; + } + + /* we have UHR */ + conn->mode = IEEE80211_CONN_MODE_UHR; + out: mlme_link_id_dbg(sdata, link_id, "determined local STA to be %s, BW limited to %d MHz\n", diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index ed8411aa8b32..43fe1483c32b 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -190,12 +190,16 @@ ieee80211_parse_extension_element(u32 *crc, } break; case WLAN_EID_EXT_UHR_CAPABILITY: + if (params->mode < IEEE80211_CONN_MODE_UHR) + break; if (ieee80211_uhr_capa_size_ok(data, len)) { elems->uhr_cap = data; elems->uhr_cap_len = len; } break; case WLAN_EID_EXT_UHR_OPERATION: + if (params->mode < IEEE80211_CONN_MODE_UHR) + break; if (ieee80211_uhr_oper_size_ok(data, len, params->is_beacon)) elems->uhr_operation = data; calc_crc = true; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0c46009a3d63..dc8706305ebe 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -47,7 +47,7 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) EXPORT_SYMBOL(wiphy_to_ieee80211_hw); const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = { - .mode = IEEE80211_CONN_MODE_EHT, + .mode = IEEE80211_CONN_MODE_UHR, .bw_limit = IEEE80211_CONN_BW_LIMIT_320, }; @@ -1365,6 +1365,15 @@ static int ieee80211_put_preq_ies_band(struct sk_buff *skb, return err; } + if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), + IEEE80211_CHAN_NO_HE | + IEEE80211_CHAN_NO_EHT | + IEEE80211_CHAN_NO_UHR)) { + err = ieee80211_put_uhr_cap(skb, sdata, sband); + if (err) + return err; + } + err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF); if (err) return err; @@ -4475,6 +4484,33 @@ int ieee80211_put_eht_cap(struct sk_buff *skb, return 0; } +int ieee80211_put_uhr_cap(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_supported_band *sband) +{ + const struct ieee80211_sta_uhr_cap *uhr_cap = + ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif); + struct ieee80211_uhr_cap_elem_fixed fixed; + u8 ie_len; + + /* Make sure we have place for the IE */ + if (!uhr_cap) + return 0; + + fixed = uhr_cap->uhr_cap_elem; + + ie_len = 2 + 1 + sizeof(uhr_cap->uhr_cap_elem); + if (skb_tailroom(skb) < ie_len) + return -ENOBUFS; + + skb_put_u8(skb, WLAN_EID_EXTENSION); + skb_put_u8(skb, ie_len - 2); + skb_put_u8(skb, WLAN_EID_EXT_UHR_CAPABILITY); + skb_put_data(skb, &fixed, sizeof(fixed)); + + return 0; +} + const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode) { static const char * const modes[] = { @@ -4484,6 +4520,7 @@ const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode) [IEEE80211_CONN_MODE_VHT] = "VHT", [IEEE80211_CONN_MODE_HE] = "HE", [IEEE80211_CONN_MODE_EHT] = "EHT", + [IEEE80211_CONN_MODE_UHR] = "UHR", }; if (WARN_ON(mode >= ARRAY_SIZE(modes))) -- 2.34.1