[PATCH v3 1/8] initial UHR support
Raja Mani
raja.mani at oss.qualcomm.com
Mon Apr 27 04:18:55 PDT 2026
On 4/24/2026 12:41 PM, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg at intel.com>
>
> Add initial UHR support, based on a very "superficial"
> reading of D1.3 (it's incomplete and not well-specified
> in quite a few places.)
>
> Signed-off-by: Johannes Berg <johannes.berg at intel.com>
> ---
> hostapd/Android.mk | 6 +
> hostapd/Makefile | 6 +
> hostapd/config_file.c | 8 ++
> hostapd/hostapd.conf | 13 ++
> src/ap/ap_config.h | 6 +
> src/ap/ap_drv_ops.c | 4 +
> src/ap/ap_drv_ops.h | 2 +
> src/ap/beacon.c | 24 ++++
> src/ap/ctrl_iface_ap.c | 16 +++
> src/ap/hostapd.h | 6 +
> src/ap/ieee802_11.c | 53 +++++++-
> src/ap/ieee802_11.h | 13 ++
> src/ap/ieee802_11_uhr.c | 145 ++++++++++++++++++++++
> src/ap/sta_info.c | 10 +-
> src/ap/sta_info.h | 65 +++++-----
> src/common/ieee802_11_common.c | 16 +++
> src/common/ieee802_11_common.h | 4 +
> src/common/ieee802_11_defs.h | 27 ++++
> src/drivers/driver.h | 22 ++++
> src/drivers/driver_common.c | 6 +
> src/drivers/driver_nl80211.c | 14 +++
> src/drivers/driver_nl80211_capa.c | 19 +++
> tests/hwsim/example-hostapd.config | 2 +
> tests/hwsim/example-wpa_supplicant.config | 1 +
> tests/hwsim/test_uhr.py | 145 ++++++++++++++++++++++
> wpa_supplicant/Android.mk | 3 +
> wpa_supplicant/Makefile | 7 ++
> wpa_supplicant/config.c | 1 +
> wpa_supplicant/config_file.c | 1 +
> wpa_supplicant/config_ssid.h | 8 ++
> wpa_supplicant/ctrl_iface.c | 8 +-
> wpa_supplicant/events.c | 13 ++
> wpa_supplicant/sme.c | 1 +
> wpa_supplicant/wpa_cli.c | 1 +
> wpa_supplicant/wpa_supplicant.c | 17 ++-
> wpa_supplicant/wpa_supplicant_i.h | 4 +
> 36 files changed, 656 insertions(+), 41 deletions(-)
> create mode 100644 src/ap/ieee802_11_uhr.c
> create mode 100644 tests/hwsim/test_uhr.py
>
> diff --git a/hostapd/Android.mk b/hostapd/Android.mk
> index bff81cac4a31..492d54e50aed 100644
> --- a/hostapd/Android.mk
> +++ b/hostapd/Android.mk
> @@ -299,6 +299,12 @@ ifdef CONFIG_IEEE80211AC
> L_CFLAGS += -DCONFIG_IEEE80211AC
> endif
>
> +ifdef CONFIG_IEEE80211BN
> +CONFIG_IEEE80211BE=y
> +L_CFLAGS += -DCONFIG_IEEE80211BN
> +OBJS += src/ap/ieee802_11_uhr.c
> +endif
> +
> ifdef CONFIG_IEEE80211BE
> CONFIG_IEEE80211AX=y
> L_CFLAGS += -DCONFIG_IEEE80211BE
> diff --git a/hostapd/Makefile b/hostapd/Makefile
> index b2420e8474be..b12a40cbc058 100644
> --- a/hostapd/Makefile
> +++ b/hostapd/Makefile
> @@ -344,6 +344,12 @@ ifdef CONFIG_IEEE80211AC
> CFLAGS += -DCONFIG_IEEE80211AC
> endif
>
> +ifdef CONFIG_IEEE80211BN
> +CONFIG_IEEE80211BE=y
> +CFLAGS += -DCONFIG_IEEE80211BN
> +OBJS += ../src/ap/ieee802_11_uhr.o
> +endif
> +
> ifdef CONFIG_IEEE80211BE
> CONFIG_IEEE80211AX=y
> CFLAGS += -DCONFIG_IEEE80211BE
[..]
> diff --git a/src/ap/beacon.c b/src/ap/beacon.c
> index a7a1601c2968..31f6178d0b05 100644
> --- a/src/ap/beacon.c
> +++ b/src/ap/beacon.c
> @@ -805,6 +805,13 @@ static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
> }
> #endif /* CONFIG_IEEE80211BE */
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hapd->iconf->ieee80211bn && !hapd->conf->disable_11bn) {
Can the above line replaced with hostapd_is_uhr_enabled(hapd) ?
> + buflen += hostapd_eid_uhr_capab_len(hapd, IEEE80211_MODE_AP);
> + buflen += 3 + IEEE80211_UHR_OPER_MAX_SIZE;
> + }
> +#endif /* CONFIG_IEEE80211BN */
> +
> buflen += hostapd_eid_mbssid_len(hapd_probed, WLAN_FC_STYPE_PROBE_RESP,
> NULL,
> params->known_bss,
> @@ -977,6 +984,13 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
> }
> #endif /* CONFIG_IEEE80211BE */
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd)) {
> + pos = hostapd_eid_uhr_capab(hapd, pos, IEEE80211_MODE_AP);
> + pos = hostapd_eid_uhr_operation(hapd, pos, false);
> + }
> +#endif /* CONFIG_IEEE80211BN */
> +
> #ifdef CONFIG_IEEE80211AC
> if (hapd->conf->vendor_vht)
> pos = hostapd_eid_vendor_vht(hapd, pos);
> @@ -2295,6 +2309,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
> }
> #endif /* CONFIG_IEEE80211BE */
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd))
> + tail_len += 3 + sizeof(struct ieee80211_uhr_operation);
> +#endif /* CONFIG_IEEE80211BN */
> +
> if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
> hapd == hostapd_mbssid_get_tx_bss(hapd))
> tail_len += 5; /* Multiple BSSID Configuration element */
> @@ -2477,6 +2496,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
> }
> #endif /* CONFIG_IEEE80211BE */
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd))
> + tailpos = hostapd_eid_uhr_operation(hapd, tailpos, true);
> +#endif /* CONFIG_IEEE80211BN */
> +
> #ifdef CONFIG_IEEE80211AC
> if (hapd->conf->vendor_vht)
> tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
[..]
> diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
> index de2702eb148e..88308fb0d1af 100644
> --- a/src/ap/ieee802_11.c
> +++ b/src/ap/ieee802_11.c
> @@ -155,6 +155,11 @@ static size_t hostapd_supp_rates(struct hostapd_data *hapd, u8 *buf)
> *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY;
> #endif /* CONFIG_IEEE80211BE */
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hapd->iconf->ieee80211bn && hapd->iconf->require_uhr)
> + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_UHR_PHY;
> +#endif /* CONFIG_IEEE80211BN */
> +
> #ifdef CONFIG_SAE
> if ((hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
> hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
> @@ -5543,6 +5548,24 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
> }
> }
> #endif /* CONFIG_IEEE80211BE */
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd)) {
> + resp = copy_sta_uhr_capab(hapd, sta, IEEE80211_MODE_AP,
> + elems->uhr_capabilities,
> + elems->uhr_capabilities_len);
> + if (resp != WLAN_STATUS_SUCCESS)
> + return resp;
> +
> + if (hapd->iconf->require_uhr && !(sta->flags & WLAN_STA_UHR)) {
> + hostapd_logger(hapd, sta->addr,
> + HOSTAPD_MODULE_IEEE80211,
> + HOSTAPD_LEVEL_INFO,
> + "Station does not support mandatory UHR PHY - reject association");
> + /* FIXME - need assignment from spec */
> + return WLAN_STATUS_DENIED_EHT_NOT_SUPPORTED;
11bn draft 1.3 has defined the status code 157(DENIED_UHR_NOT_SUPPORTED)
in section "9.4.1.9 Status Code field". Can this be replaced with that?
> + }
> + }
> +#endif /* CONFIG_IEEE80211BN */
>
> #ifdef CONFIG_P2P
> if (elems->p2p && ies && ies_len) {
> @@ -6201,6 +6224,13 @@ void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
> }
> }
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd)) {
> + p = hostapd_eid_uhr_capab(hapd, p, IEEE80211_MODE_AP);
> + p = hostapd_eid_uhr_operation(hapd, p, false);
> + }
> +#endif /* CONFIG_IEEE80211BN */
> +
> p = hostapd_eid_ext_capab(hapd, p, false);
> p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
> p = hostapd_eid_wmm(hapd, p);
> @@ -6472,6 +6502,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
> struct ieee80211_vht_capabilities vht_cap;
> struct ieee80211_he_capabilities he_cap;
> struct ieee80211_eht_capabilities eht_cap;
> + struct ieee80211_uhr_capabilities uhr_cap;
> int set = 1;
> const u8 *mld_link_addr = NULL;
> bool mld_link_sta = false, epp_sta = false;
> @@ -6553,6 +6584,11 @@ static int add_associated_sta(struct hostapd_data *hapd,
> hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
> sta->eht_capab_len);
> #endif /* CONFIG_IEEE80211BE */
> +#ifdef CONFIG_IEEE80211BN
> + if (sta->flags & WLAN_STA_UHR)
> + hostapd_get_uhr_capab(hapd, sta->uhr_capab, &uhr_cap,
> + sta->uhr_capab_len);
> +#endif /* CONFIG_IEEE80211BN */
>
> /*
> * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
> @@ -6568,6 +6604,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
> sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
> sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
> sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
> + sta->flags & WLAN_STA_UHR ? &uhr_cap : NULL,
> + sta->flags & WLAN_STA_UHR ? sta->uhr_capab_len : 0,
> sta->he_6ghz_capab,
> sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
> sta->vht_opmode, sta->p2p_ie ? 1 : 0,
> @@ -6629,6 +6667,12 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
> buflen += hostapd_eid_eht_ml_tid_to_link_map_len(hapd);
> }
> #endif /* CONFIG_IEEE80211BE */
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd)) {
> + buflen += hostapd_eid_uhr_capab_len(hapd, IEEE80211_MODE_AP);
> + buflen += 3 + IEEE80211_UHR_OPER_MAX_SIZE;
> + }
> +#endif /* CONFIG_IEEE80211BN */
>
> buf = os_zalloc(buflen);
> if (!buf) {
> @@ -6791,6 +6835,13 @@ rsnxe_done:
> }
> #endif /* CONFIG_IEEE80211BE */
>
> +#ifdef CONFIG_IEEE80211BN
> + if (hostapd_is_uhr_enabled(hapd)) {
> + p = hostapd_eid_uhr_capab(hapd, p, IEEE80211_MODE_AP);
> + p = hostapd_eid_uhr_operation(hapd, p, false);
> + }
> +#endif /* CONFIG_IEEE80211BN */
> +
> #ifdef CONFIG_OWE
> if (((hapd->conf->wpa_key_mgmt | hapd->conf->rsn_override_key_mgmt |
> hapd->conf->rsn_override_key_mgmt_2) & WPA_KEY_MGMT_OWE) &&
> @@ -7340,7 +7391,7 @@ static void handle_assoc(struct hostapd_data *hapd,
> hostapd_logger(hapd, mgmt->sa,
> HOSTAPD_MODULE_IEEE80211,
> HOSTAPD_LEVEL_INFO,
> - "Station tried to associate before authentication (aid=%d flags=0x%x)",
> + "Station tried to associate before authentication (aid=%d flags=0x%llx)",
> sta ? sta->aid : -1,
> sta ? sta->flags : 0);
> send_deauth(hapd, mgmt->sa,
[..]
> diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
> index 620cff5d8d7c..615164b0381d 100644
> --- a/src/drivers/driver_nl80211_capa.c
> +++ b/src/drivers/driver_nl80211_capa.c
> @@ -2189,6 +2189,7 @@ static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
> size_t len;
> struct he_capabilities *he_capab = &mode->he_capab[opmode];
> struct eht_capabilities *eht_capab = &mode->eht_capab[opmode];
> + struct uhr_capabilities *uhr_capab = &mode->uhr_capab[opmode];
>
> switch (opmode) {
> case IEEE80211_MODE_INFRA:
> @@ -2296,6 +2297,24 @@ static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
> nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]),
> len);
> }
> +
> + if (!tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC] ||
> + !tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY])
> + return;
> +
> + uhr_capab->uhr_supported = true;
> +
> + if (tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC] &&
> + nla_len(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]) >= (int)sizeof(uhr_capab->mac))
> + os_memcpy(uhr_capab->mac,
> + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]),
> + sizeof(uhr_capab->mac));
Just to consider minimum possible mac cap size for os_memcpy(),
Can this be modified like this?
if (tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]) {
len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]);
if (len > sizeof(uhr_capab->mac))
len = sizeof(uhr_capab->mac);
os_memcpy(uhr_capab->mac,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC]),
len);
}
and in the below hunk as well?
> +
> + if (tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY] &&
> + nla_len(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY]) >= (int)sizeof(uhr_capab->phy))
> + os_memcpy(uhr_capab->phy,
> + nla_data(tb[NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY]),
> + sizeof(uhr_capab->phy));
> }
>
[..]
More information about the Hostap
mailing list