@@ -4997,6 +4997,13 @@ 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);
+ if (conf->dbe_bandwidth > 5)
+ return 1;
+ } 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);
@@ -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
@@ -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;
}
@@ -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 */
@@ -2863,6 +2863,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) {
@@ -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;
}
@@ -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;
}
@@ -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,
@@ -3319,14 +3319,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)