diff --git a/hostapd/config_file.c b/hostapd/config_file.c index b84107472..15aaca924 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -4861,6 +4861,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); return 1; } + } else if (os_strcmp(buf, "eht_bw320_offset") == 0) { + conf->eht_bw320_offset = atoi(pos); #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcmp(buf, "eht_oper_puncturing_override") == 0) { if (get_u16(pos, line, &bss->eht_oper_puncturing_override)) diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 747e5400b..1aeeb7353 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1044,6 +1044,15 @@ wmm_ac_vo_acm=0 # 1 = PE field duration is 20 us #eht_default_pe_duration=0 +#eht_bw320_offset: For automatic channel selection (ACS) to indicate a preferred +# 320 MHz channelization in EHT mode. +# If the channel is decided or the bandwidth is not 320 MHz, this option is +# meaningless. +# 0 = auto-detect by hostapd +# 1 = 320 MHz-1 (channel center frequency 31, 95, 159) +# 2 = 320 MHz-2 (channel center frequency 63, 127, 191) +#eht_bw320_offset=0 + # Disabled subchannel bitmap (16 bits) as per IEEE P802.11be/3.0, # Figure 9-1002c (EHT Operation Information field format). Each bit corresponds # to a 20 MHz channel, the lowest bit corresponds to the lowest frequency. A diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index f53aeae94..89fd12d1d 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -299,6 +299,8 @@ struct hostapd_config * hostapd_config_defaults(void) conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL; #endif /* CONFIG_AIRTIME_POLICY */ + hostapd_set_and_check_bw320_offset(conf, 0); + return conf; } @@ -1560,6 +1562,10 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config) "Cannot set ieee80211be without ieee80211ax"); return -1; } + + if (full_config) + hostapd_set_and_check_bw320_offset(conf, + conf->eht_bw320_offset); #endif /* CONFIG_IEEE80211BE */ if (full_config && conf->mbssid && !conf->ieee80211ax) { diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index b5bb2201e..1d3949559 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1199,6 +1199,7 @@ struct hostapd_config { u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */ u8 punct_acs_threshold; u8 eht_default_pe_duration; + u8 eht_bw320_offset; #endif /* CONFIG_IEEE80211BE */ /* EHT enable/disable config from CHAN_SWITCH */ @@ -1299,6 +1300,43 @@ hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf, conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx; } +static inline u8 +hostapd_get_bw320_offset(struct hostapd_config *conf) +{ +#ifdef CONFIG_IEEE80211BE + if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) && + hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ) + return conf->eht_bw320_offset; +#endif /* CONFIG_IEEE80211BE */ + return 0; +} + +static inline void +hostapd_set_and_check_bw320_offset(struct hostapd_config *conf, + u8 bw320_offset) +{ +#ifdef CONFIG_IEEE80211BE + if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) && + op_class_to_ch_width(conf->op_class) == CONF_OPER_CHWIDTH_320MHZ) { + if (conf->channel) { + /* If the channel is set, then calculate bw320_offset + * by center frequency segment 0. + */ + u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf); + + conf->eht_bw320_offset = (seg0 - 31) % 64 ? 2 : 1; + } else { + /* If the channel is not set, bw320_offset indicates + * preferred offset of 320 MHz. + */ + conf->eht_bw320_offset = bw320_offset; + } + } else { + conf->eht_bw320_offset = 0; + } +#endif /* CONFIG_IEEE80211BE */ +} + int hostapd_mac_comp(const void *a, const void *b); struct hostapd_config * hostapd_config_defaults(void); diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 32a5b8ecd..537867180 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -830,6 +830,17 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, if (os_snprintf_error(buflen - len, ret)) return len; len += ret; + + if (is_6ghz_op_class(iface->conf->op_class) && + hostapd_get_oper_chwidth(iface->conf) == + CONF_OPER_CHWIDTH_320MHZ) { + ret = os_snprintf(buf + len, buflen - len, + "eht_bw320_offset=%d\n", + iface->conf->eht_bw320_offset); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } } #endif /* CONFIG_IEEE80211BE */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index ace0ce3d7..210068a94 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -1170,6 +1170,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hostapd_set_oper_chwidth(hapd->iconf, chwidth); hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx); hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx); + /* Auto-detect new bw320_offset */ + hostapd_set_and_check_bw320_offset(hapd->iconf, 0); #ifdef CONFIG_IEEE80211BE hapd->iconf->punct_bitmap = punct_bitmap; #endif /* CONFIG_IEEE80211BE */