[PATCH 7/7] AP: Avoid disabling HT/VHT/HE/EHT during CSA
Benjamin Berg
benjamin at sipsolutions.net
Thu Jul 31 06:57:03 PDT 2025
From: Ilan Peer <ilan.peer at intel.com>
During CSA an AP should not disable its HT/VHT/HE/EHT capabilities
as it is not allowed by the IEEE80211 specification.
- Modify the CSA flow to preserve the current HT/VHT/HE/EHT
capabilities allowing only to add additional ones.
- Update the DFS CSA flow to also preserve the HT/VHT/HE/EHT
capabilities.
Modify one of the hwsim tests that verified that VHT capabilities
are disabled during CSA, to verify that they are kept.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
Reviewed-by: Otcheretianski, Andrei <andrei.otcheretianski at intel.com>
Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
hostapd/ctrl_iface.c | 14 ++++++++--
src/ap/ap_config.h | 21 +++++---------
src/ap/ctrl_iface_ap.c | 20 +++++++++++++
src/ap/dfs.c | 9 ++++++
src/ap/drv_callbacks.c | 57 +++++++++-----------------------------
src/ap/hostapd.c | 25 ++++++-----------
tests/hwsim/test_ap_vht.py | 8 +++---
7 files changed, 73 insertions(+), 81 deletions(-)
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index f3b31c79de..f77f457430 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2483,6 +2483,18 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
dfs_range += hostapd_is_dfs_overlap(
iface, bandwidth, settings.freq_params.center_freq2);
+ /* If VHT/HE/EHT was enabled, we must also set the bandwidth and center
+ * frequency
+ */
+ if ((settings.freq_params.vht_enabled ||
+ settings.freq_params.he_enabled ||
+ settings.freq_params.eht_enabled) &&
+ (!settings.freq_params.bandwidth ||
+ !settings.freq_params.center_freq1)) {
+ settings.freq_params.bandwidth = 20;
+ settings.freq_params.center_freq1 = settings.freq_params.freq;
+ }
+
if (dfs_range) {
ret = ieee80211_freq_to_chan(settings.freq_params.freq, &chan);
if (ret == NUM_HOSTAPD_MODES) {
@@ -2517,8 +2529,6 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
}
for (i = 0; i < iface->num_bss; i++) {
-
- /* Save CHAN_SWITCH VHT, HE, and EHT config */
hostapd_chan_switch_config(iface->bss[i],
&settings.freq_params);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index af0d785be2..20aeaaccb3 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1183,15 +1183,13 @@ struct hostapd_config {
bool require_he;
#endif /* CONFIG_IEEE80211AX */
- /* VHT enable/disable config from CHAN_SWITCH */
-#define CH_SWITCH_VHT_ENABLED BIT(0)
-#define CH_SWITCH_VHT_DISABLED BIT(1)
- unsigned int ch_switch_vht_config;
-
- /* HE enable/disable config from CHAN_SWITCH */
-#define CH_SWITCH_HE_ENABLED BIT(0)
-#define CH_SWITCH_HE_DISABLED BIT(1)
- unsigned int ch_switch_he_config;
+ /* Save the expected HT/VHT/HE/EHT configuration so it could be used
+ * once CSA is done
+ */
+ unsigned int ch_switch_ht_config:1;
+ unsigned int ch_switch_vht_config:1;
+ unsigned int ch_switch_he_config:1;
+ unsigned int ch_switch_eht_config:1;
int rssi_reject_assoc_rssi;
int rssi_reject_assoc_timeout;
@@ -1220,11 +1218,6 @@ struct hostapd_config {
u8 eht_bw320_offset;
#endif /* CONFIG_IEEE80211BE */
- /* EHT enable/disable config from CHAN_SWITCH */
-#define CH_SWITCH_EHT_ENABLED BIT(0)
-#define CH_SWITCH_EHT_DISABLED BIT(1)
- unsigned int ch_switch_eht_config;
-
enum mbssid {
MBSSID_DISABLED = 0,
MBSSID_ENABLED = 1,
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index dcea7a0eb1..eb44e45015 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1456,6 +1456,26 @@ int hostapd_parse_csa_settings(struct hostapd_iface *iface,
return ret;
}
+ if (!settings->freq_params.ht_enabled && iface->conf->ieee80211n) {
+ wpa_printf(MSG_WARNING, "CSA: Cannot disable HT. Ignore");
+ settings->freq_params.ht_enabled = iface->conf->ieee80211n;
+ }
+
+ if (!settings->freq_params.vht_enabled && iface->conf->ieee80211ac) {
+ wpa_printf(MSG_WARNING, "CSA: Cannot disable VHT. Ignore");
+ settings->freq_params.vht_enabled = iface->conf->ieee80211ac;
+ }
+
+ if (!settings->freq_params.he_enabled && iface->conf->ieee80211ax) {
+ wpa_printf(MSG_WARNING, "CSA: Cannot disable HE. Ignore");
+ settings->freq_params.he_enabled = iface->conf->ieee80211ax;
+ }
+
+ if (!settings->freq_params.eht_enabled && iface->conf->ieee80211be) {
+ wpa_printf(MSG_WARNING, "CSA: Cannot disable EHT. Ignore");
+ settings->freq_params.eht_enabled = iface->conf->ieee80211be;
+ }
+
return 0;
}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 0cac194b26..3463740290 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1028,7 +1028,16 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
return err;
}
+ /* preserve the HT/VHT/HE/EHT configuration */
+ csa_settings.freq_params.ht_enabled = iface->conf->ieee80211n;
+ csa_settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
+ csa_settings.freq_params.he_enabled = iface->conf->ieee80211ax;
+ csa_settings.freq_params.eht_enabled = iface->conf->ieee80211be;
+
for (i = 0; i < iface->num_bss; i++) {
+ hostapd_chan_switch_config(iface->bss[i],
+ &csa_settings.freq_params);
+
err = hostapd_switch_channel(iface->bss[i], &csa_settings);
if (err)
num_err++;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index aebd52c0c7..0d9cf43398 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1245,8 +1245,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
HOSTAPD_LEVEL_INFO,
"driver %s channel switch: iface->freq=%d, freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d, puncturing_bitmap=0x%x",
finished ? "had" : "starting",
- hapd->iface->freq,
- freq, ht, hapd->iconf->ch_switch_vht_config,
+ hapd->iface->freq, freq,
+ hapd->iconf->ch_switch_ht_config,
+ hapd->iconf->ch_switch_vht_config,
hapd->iconf->ch_switch_he_config,
hapd->iconf->ch_switch_eht_config, offset,
width, channel_width_to_string(width), cf1, cf2,
@@ -1321,49 +1322,17 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
}
hapd->iconf->channel = channel;
- hapd->iconf->ieee80211n = ht;
- if (!ht)
- hapd->iconf->ieee80211ac = 0;
- if (hapd->iconf->ch_switch_vht_config) {
- /* CHAN_SWITCH VHT config */
- if (hapd->iconf->ch_switch_vht_config &
- CH_SWITCH_VHT_ENABLED)
- hapd->iconf->ieee80211ac = 1;
- else if (hapd->iconf->ch_switch_vht_config &
- CH_SWITCH_VHT_DISABLED)
- hapd->iconf->ieee80211ac = 0;
- }
- if (hapd->iconf->ch_switch_he_config) {
- /* CHAN_SWITCH HE config */
- if (hapd->iconf->ch_switch_he_config &
- CH_SWITCH_HE_ENABLED) {
- hapd->iconf->ieee80211ax = 1;
- if (hapd->iface->freq > 4000 &&
- hapd->iface->freq < 5895)
- hapd->iconf->ieee80211ac = 1;
- }
- else if (hapd->iconf->ch_switch_he_config &
- CH_SWITCH_HE_DISABLED)
- hapd->iconf->ieee80211ax = 0;
- }
-#ifdef CONFIG_IEEE80211BE
- if (hapd->iconf->ch_switch_eht_config) {
- /* CHAN_SWITCH EHT config */
- if (hapd->iconf->ch_switch_eht_config &
- CH_SWITCH_EHT_ENABLED) {
- hapd->iconf->ieee80211be = 1;
- hapd->iconf->ieee80211ax = 1;
- if (!is_6ghz_freq(hapd->iface->freq) &&
- hapd->iface->freq > 4000)
- hapd->iconf->ieee80211ac = 1;
- } else if (hapd->iconf->ch_switch_eht_config &
- CH_SWITCH_EHT_DISABLED)
- hapd->iconf->ieee80211be = 0;
+ hapd->iconf->ieee80211n = hapd->iconf->ch_switch_ht_config;
+ hapd->iconf->ieee80211ac = hapd->iconf->ch_switch_vht_config;
+ hapd->iconf->ieee80211ax = hapd->iconf->ch_switch_he_config;
+ hapd->iconf->ieee80211be = hapd->iconf->ch_switch_eht_config;
+
+ if (finished) {
+ hapd->iconf->ch_switch_ht_config = 0;
+ hapd->iconf->ch_switch_vht_config = 0;
+ hapd->iconf->ch_switch_he_config = 0;
+ hapd->iconf->ch_switch_eht_config = 0;
}
-#endif /* CONFIG_IEEE80211BE */
- hapd->iconf->ch_switch_vht_config = 0;
- hapd->iconf->ch_switch_he_config = 0;
- hapd->iconf->ch_switch_eht_config = 0;
if (width == CHAN_WIDTH_40 || width == CHAN_WIDTH_80 ||
width == CHAN_WIDTH_80P80 || width == CHAN_WIDTH_160 ||
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 65df420a06..8be1490b87 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -4646,27 +4646,18 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
void hostapd_chan_switch_config(struct hostapd_data *hapd,
struct hostapd_freq_params *freq_params)
{
- if (freq_params->eht_enabled)
- hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_ENABLED;
- else
- hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_DISABLED;
-
- if (freq_params->he_enabled)
- hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED;
- else
- hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_DISABLED;
-
- if (freq_params->vht_enabled)
- hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED;
- else
- hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED;
+ hapd->iconf->ch_switch_ht_config = freq_params->ht_enabled;
+ hapd->iconf->ch_switch_vht_config = freq_params->vht_enabled;
+ hapd->iconf->ch_switch_he_config = freq_params->he_enabled;
+ hapd->iconf->ch_switch_eht_config = freq_params->eht_enabled;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "CHAN_SWITCH EHT config 0x%x HE config 0x%x VHT config 0x%x",
- hapd->iconf->ch_switch_eht_config,
+ "CHAN_SWITCH: config: ht=%u, vht=%u, he=%u, eht=%u",
+ hapd->iconf->ch_switch_ht_config,
+ hapd->iconf->ch_switch_vht_config,
hapd->iconf->ch_switch_he_config,
- hapd->iconf->ch_switch_vht_config);
+ hapd->iconf->ch_switch_eht_config);
}
diff --git a/tests/hwsim/test_ap_vht.py b/tests/hwsim/test_ap_vht.py
index 3149d51177..16f846c311 100644
--- a/tests/hwsim/test_ap_vht.py
+++ b/tests/hwsim/test_ap_vht.py
@@ -948,8 +948,8 @@ def test_ap_vht_csa_vht20(dev, apdev):
finally:
clear_regdom(hapd, dev, 2)
-def test_ap_vht_csa_vht40_disable(dev, apdev):
- """VHT CSA with VHT40 getting disabled"""
+def test_ap_vht_csa_vht40_unchanged(dev, apdev):
+ """VHT CSA with VHT not specified. Verify the VHT was not disabled"""
csa_supported(dev[0])
try:
hapd = None
@@ -993,8 +993,8 @@ def test_ap_vht_csa_vht40_disable(dev, apdev):
dev[1].connect("vht", key_mgmt="NONE", scan_freq="5200")
hwsim_utils.test_connectivity(dev[1], hapd)
- if dev[1].get_status_field("ieee80211ac") == '1':
- raise Exception("VHT not disabled as part of channel switch")
+ if dev[1].get_status_field("ieee80211ac") != '1':
+ raise Exception("VHT unexpectedly disabled as part of channel switch")
finally:
clear_regdom(hapd, dev, count=2)
--
2.50.1
More information about the Hostap
mailing list