[PATCH v3 7/8] ap: allow configuring UHR DBE
Johannes Berg
johannes at sipsolutions.net
Fri Apr 24 00:11:59 PDT 2026
From: Johannes Berg <johannes.berg at intel.com>
Allow configuring, and push down to the driver's frequency
configuration, UHR DBE (dynamic bandwidth extension).
Make up the UHR DBE capabilities on the fly assuming that
the EHT capabilities cover the MCS set etc. already. The
position of the DBE capabilities is after PHY, as per
802.11bn D1.4.
Signed-off-by: Johannes Berg <johannes.berg at intel.com>
---
hostapd/config_file.c | 5 ++
hostapd/hostapd.conf | 11 ++++
src/ap/ap_config.c | 5 ++
src/ap/ap_config.h | 2 +
src/ap/beacon.c | 4 ++
src/ap/ieee802_11_uhr.c | 30 +++++++++--
src/common/hw_features_common.c | 93 ++++++++++++++++++++++++++++++++-
src/common/hw_features_common.h | 7 +++
src/common/ieee802_11_defs.h | 10 +++-
9 files changed, 162 insertions(+), 5 deletions(-)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 3cb200ea358b..da943e3191ee 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4997,6 +4997,11 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->ieee80211bn = atoi(pos);
} else if (os_strcmp(buf, "require_uhr") == 0) {
conf->require_uhr = atoi(pos);
+ } else if (os_strcmp(buf, "dbe_bandwidth") == 0) {
+ conf->dbe_bandwidth = atoi(pos);
+ } else if (os_strcmp(buf, "dbe_punct_bitmap") == 0) {
+ if (get_u16(pos, line, &conf->dbe_punct_bitmap))
+ return 1;
#endif /* CONFIG_IEEE80211BN */
} else if (os_strcmp(buf, "i2r_lmr_policy") == 0) {
conf->i2r_lmr_policy = atoi(pos);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 9eca437ec565..d9c6268dfdb9 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1145,6 +1145,17 @@ wmm_ac_vo_acm=0
# Require stations to support UHR PHY (reject association if they do not)
#require_uhr=0
+# UHR DBE (dynamic bandwidth extension) bandwidth
+# Indicates the bandwidth (as encoded in the spec, 802.11bn D1.4 Table 9-bb2
+# "Encoding of the DBE Bandwidth field") for UHR DBE, 0 is reserved in the
+# spec and here means DBE is not used.
+# Must be wider than the EHT bandwidth (from eht_oper_chwidth or operating
+# class).
+#dbe_bandwidth=0
+
+# UHR DBE puncturing bitmap
+#dbe_punct_bitmap=0
+
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 36a4dad65626..6dbf30bbb7b2 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -316,6 +316,11 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->disable_mcs15_rx = true;
+#ifdef CONFIG_IEEE80211BN
+ conf->dbe_bandwidth = 0;
+ conf->dbe_punct_bitmap = 0;
+#endif
+
return conf;
}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 57ee0917bbef..7709f3ae82cb 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1249,6 +1249,8 @@ struct hostapd_config {
int ieee80211bn;
#ifdef CONFIG_IEEE80211BN
bool require_uhr;
+ u8 dbe_bandwidth;
+ u16 dbe_punct_bitmap;
#endif
/* EHT enable/disable config from CHAN_SWITCH */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index cedb4bd9d537..9ee7de6c118f 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2859,6 +2859,10 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
.he.cap = cmode ? &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
.eht.cap = cmode ? &cmode->eht_capab[IEEE80211_MODE_AP] : NULL,
.eht.punct_bitmap = hostapd_get_punct_bitmap(hapd),
+ .uhr.enabled = iconf->ieee80211bn,
+ .uhr.dbe_bandwidth = iconf->dbe_bandwidth,
+ .uhr.cap = cmode ? &cmode->uhr_capab[IEEE80211_MODE_AP] : NULL,
+ .uhr.dbe_punct_bitmap = iconf->dbe_punct_bitmap,
};
if (cmode && hostapd_set_freq_params(&freq, &info) == 0) {
diff --git a/src/ap/ieee802_11_uhr.c b/src/ap/ieee802_11_uhr.c
index 2476b3412b75..9e3dcdc6da25 100644
--- a/src/ap/ieee802_11_uhr.c
+++ b/src/ap/ieee802_11_uhr.c
@@ -109,25 +109,49 @@ u8 * hostapd_eid_uhr_capab(struct hostapd_data *hapd, u8 *eid,
u8 * hostapd_eid_uhr_operation(struct hostapd_data *hapd, u8 *eid, bool beacon)
{
struct ieee80211_uhr_operation *oper;
- u8 *pos = eid;
+ u8 *pos = eid, *len, *start = eid;
if (!hapd->iface->current_mode)
return eid;
*pos++ = WLAN_EID_EXTENSION;
- *pos++ = 1 + sizeof(*oper);
+ len = pos++;
*pos++ = WLAN_EID_EXT_UHR_OPERATION;
oper = (void *) pos;
oper->oper_params = 0;
+ if (hapd->iconf->dbe_bandwidth) {
+ u8 dbe_bw = hapd->iconf->dbe_bandwidth <<
+ UHR_OPER_PARAMS_DBE_BW_SHIFT;
+
+ oper->oper_params |= host_to_le16(UHR_OPER_PARAMS_DBE_ENA);
+ oper->oper_params |= host_to_le16(dbe_bw);
+ }
+
/* TODO: Fill in appropriate UHR-MCS max Nss information */
oper->basic_uhr_mcs_nss_set[0] = 0x11;
oper->basic_uhr_mcs_nss_set[1] = 0x00;
oper->basic_uhr_mcs_nss_set[2] = 0x00;
oper->basic_uhr_mcs_nss_set[3] = 0x00;
- return pos + sizeof(*oper);
+ pos += sizeof(*oper);
+
+ if (!beacon && hapd->iconf->dbe_bandwidth) {
+ *pos = hapd->iconf->dbe_bandwidth;
+
+ if (hapd->iconf->dbe_punct_bitmap)
+ *pos |= IEEE80211_UHR_OPER_DBE_DIS_SUBCH_BMAP_PRES;
+
+ pos++;
+ if (hapd->iconf->dbe_punct_bitmap) {
+ WPA_PUT_LE16(pos, hapd->iconf->dbe_punct_bitmap);
+ pos += sizeof(u16);
+ }
+ }
+
+ *len = pos - start - 2;
+ return pos;
}
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index ea5d24300048..1a3b8dcd7757 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -475,6 +475,79 @@ void punct_update_legacy_bw(u16 bitmap, u8 pri, enum oper_chan_width *width,
}
+static int hostapd_set_freq_dbe(struct hostapd_freq_params *data, u8 dbe_bw)
+{
+ int starting_freq, offset, index, bw_mhz, start_new, start_old;
+ bool is_5ghz = is_5ghz_freq(data->freq);
+ bool is_6ghz = is_6ghz_freq(data->freq);
+ unsigned int punct_shift;
+
+ if (is_6ghz)
+ starting_freq = 5955;
+ else if (data->freq < 5745)
+ starting_freq = 5180;
+ else
+ starting_freq = 5745;
+
+ switch (dbe_bw) {
+ case IEEE80211_UHR_OPER_DBE_BW_40_MHZ:
+ case IEEE80211_UHR_OPER_DBE_BW_80_MHZ:
+ case IEEE80211_UHR_OPER_DBE_BW_160_MHZ:
+ if (is_5ghz)
+ break;
+ /* fallthrough */
+ case IEEE80211_UHR_OPER_DBE_BW_320_2_MHZ:
+ case IEEE80211_UHR_OPER_DBE_BW_320_1_MHZ:
+ if (is_6ghz)
+ break;
+ /* fallthrough */
+ default:
+ return -1;
+ }
+
+ switch (dbe_bw) {
+ case IEEE80211_UHR_OPER_DBE_BW_40_MHZ:
+ bw_mhz = 40;
+ break;
+ case IEEE80211_UHR_OPER_DBE_BW_80_MHZ:
+ bw_mhz = 80;
+ break;
+ case IEEE80211_UHR_OPER_DBE_BW_160_MHZ:
+ bw_mhz = 160;
+ break;
+ case IEEE80211_UHR_OPER_DBE_BW_320_2_MHZ:
+ starting_freq += 160;
+ /* fallthrough */
+ case IEEE80211_UHR_OPER_DBE_BW_320_1_MHZ:
+ bw_mhz = 320;
+ break;
+ default:
+ /* already handled above - shut up stupid compilers */
+ return -1;
+ }
+
+ if (data->freq < starting_freq)
+ return -1;
+
+ start_old = data->center_freq1 - data->bandwidth / 2;
+
+ offset = data->freq - starting_freq;
+ index = offset / bw_mhz;
+ start_new = starting_freq - 10 + index * bw_mhz;
+ data->center_freq1 = start_new + bw_mhz / 2;
+ data->bandwidth = bw_mhz;
+
+ if (start_new < start_old)
+ punct_shift = (start_old - start_new) / 20;
+ else
+ punct_shift = 0;
+
+ data->punct_bitmap <<= punct_shift;
+
+ return 0;
+}
+
+
int hostapd_set_freq_params(struct hostapd_freq_params *data,
const struct hostapd_channel_info *info)
{
@@ -495,6 +568,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
const struct he_capabilities *he_cap = info->he.cap;
const struct eht_capabilities *eht_cap = info->eht.cap;
u16 punct_bitmap = info->eht.punct_bitmap;
+ int uhr_enabled = info->uhr.enabled;
enum oper_chan_width oper_chwidth_legacy;
u8 seg0_legacy, seg1_legacy;
@@ -502,6 +576,9 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
he_enabled = 0;
if (!eht_cap || !eht_cap->eht_supported)
eht_enabled = 0;
+ if (!info->uhr.cap || !info->uhr.cap->uhr_supported)
+ uhr_enabled = 0;
+
os_memset(data, 0, sizeof(*data));
data->mode = mode;
data->freq = freq;
@@ -597,7 +674,7 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
data->ht_enabled = 0;
data->vht_enabled = 0;
- return 0;
+ goto handle_uhr;
}
if (data->eht_enabled) switch (oper_chwidth) {
@@ -840,6 +917,20 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data,
break;
}
+handle_uhr:
+ if (uhr_enabled && info->uhr.dbe_bandwidth) {
+ /* check DBE against UHR capabilities? */
+
+ if (hostapd_set_freq_dbe(data, info->uhr.dbe_bandwidth)) {
+ wpa_printf(MSG_ERROR,
+ "invalid DBE bandwidth %d\n",
+ info->uhr.dbe_bandwidth);
+ return -1;
+ }
+
+ data->punct_bitmap = info->uhr.dbe_punct_bitmap;
+ }
+
return 0;
}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 224cb4d52fb3..48b0bc3c1d5c 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -77,6 +77,13 @@ struct hostapd_channel_info {
const struct eht_capabilities *cap;
u16 punct_bitmap;
} eht;
+
+ struct {
+ int enabled;
+ const struct uhr_capabilities *cap;
+ u8 dbe_bandwidth;
+ u16 dbe_punct_bitmap;
+ } uhr;
};
int hostapd_set_freq_params(struct hostapd_freq_params *data,
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 8b32e95e227d..a1121f991b45 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -3318,14 +3318,22 @@ struct ieee80211_uhr_capabilities {
#define UHR_OPER_PARAMS_NPCA_ENA 0x0002
#define UHR_OPER_PARAMS_PEDCA_ENA 0x0004
#define UHR_OPER_PARAMS_DBE_ENA 0x0008
+#define UHR_OPER_PARAMS_DBE_BW_SHIFT 4
/* UHR Operation element format */
struct ieee80211_uhr_operation {
le16 oper_params; /* UHR Operation Parameters: UHR_OPER_* bits */
u8 basic_uhr_mcs_nss_set[4];
- /* FIXME: DPS, P-EDCA, DBE */
+ /* FIXME: DPS, P-EDCA */
} STRUCT_PACKED;
+#define IEEE80211_UHR_OPER_DBE_BW_40_MHZ 1
+#define IEEE80211_UHR_OPER_DBE_BW_80_MHZ 2
+#define IEEE80211_UHR_OPER_DBE_BW_160_MHZ 3
+#define IEEE80211_UHR_OPER_DBE_BW_320_1_MHZ 4
+#define IEEE80211_UHR_OPER_DBE_BW_320_2_MHZ 5
+#define IEEE80211_UHR_OPER_DBE_DIS_SUBCH_BMAP_PRES 0x8
+
/* Max size in Draft P802.11bn D1.3 with DPS, NPCA, P-EDCA, DBE */
#define IEEE80211_UHR_OPER_MAX_SIZE \
(sizeof(struct ieee80211_uhr_operation) + 4 + 6 + 3 + 3)
--
2.53.0
More information about the Hostap
mailing list