[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