From 123d16d860fae8b36f50d4dc13bd0cc24ed83fe0 Mon Sep 17 00:00:00 2001 From: Nijun Gong Date: Mon, 10 Jul 2023 21:19:29 +0800 Subject: [PATCH] Update hw_mode when CSA finishes The driver might decide to change the operating band on its own, e.g., when trying to use a single channel in AP + AP case. A CSA event will be notified to hostapd to update the channel/frequency, but hw_mode did not get updated accordingly. This may cause hostapd interface restarting to fail, e.g., with control interface DISABLE / ENABLE commands at configured_fixed_chan_to_freq(), because of the mismatch between conf->channel and conf->hw_mode. Update hw_mode right after channel change to fix this. Signed-off-by: ngong --- src/ap/drv_callbacks.c | 6 ++++++ src/ap/hw_features.c | 30 ++++++++++++++++++++++++++---- src/ap/hw_features.h | 11 +++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 5bc8402db..0516213f4 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -1085,6 +1085,12 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, break; } + /* The operating channel changed when CSA finished, so need to update + * hw_mode for all following operations to cover the cases where the + * driver changed the operating band. */ + if (finished && hostapd_csa_update_hwmode(hapd->iface)) + return; + switch (hapd->iface->current_mode->mode) { case HOSTAPD_MODE_IEEE80211A: if (cf1 == 5935) diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 2fcc437d3..9edbb5ae2 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -1079,14 +1079,14 @@ static bool skip_mode(struct hostapd_iface *iface, } -void hostapd_determine_mode(struct hostapd_iface *iface) +int hostapd_determine_mode(struct hostapd_iface *iface) { int i; enum hostapd_hw_mode target_mode; if (iface->current_mode || iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY) - return; + return 0; if (iface->freq < 4000) target_mode = HOSTAPD_MODE_IEEE80211G; @@ -1109,8 +1109,11 @@ void hostapd_determine_mode(struct hostapd_iface *iface) } } - if (!iface->current_mode) - wpa_printf(MSG_ERROR, "ACS: Cannot decide mode"); + if (!iface->current_mode) { + wpa_printf(MSG_ERROR, "ACS/CSA: Cannot decide mode"); + return -1; + } + return 0; } @@ -1215,6 +1218,25 @@ out: } +/** + * hostapd_csa_update_hwmode - Update hardware mode + * @iface: Pointer to interface data. + * Returns: 0 on success, < 0 on failure + * + * Update hardware mode when the operating channel changed because of CSA. + */ +int hostapd_csa_update_hwmode(struct hostapd_iface *iface) +{ + if (!iface || !iface->conf) + return -1; + + iface->current_mode = NULL; + iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY; + + return hostapd_determine_mode(iface); +} + + /** * hostapd_select_hw_mode - Select the hardware mode * @iface: Pointer to interface data. diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h index 092941f77..c682c6d20 100644 --- a/src/ap/hw_features.h +++ b/src/ap/hw_features.h @@ -15,6 +15,7 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, size_t num_hw_features); int hostapd_get_hw_features(struct hostapd_iface *iface); +int hostapd_csa_update_hwmode(struct hostapd_iface *iface); int hostapd_acs_completed(struct hostapd_iface *iface, int err); int hostapd_select_hw_mode(struct hostapd_iface *iface); const char * hostapd_hw_mode_txt(int mode); @@ -28,7 +29,7 @@ int hostapd_prepare_rates(struct hostapd_iface *iface, void hostapd_stop_setup_timers(struct hostapd_iface *iface); int hostapd_hw_skip_mode(struct hostapd_iface *iface, struct hostapd_hw_modes *mode); -void hostapd_determine_mode(struct hostapd_iface *iface); +int hostapd_determine_mode(struct hostapd_iface *iface); #else /* NEED_AP_MLME */ static inline void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, @@ -41,6 +42,11 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface) return -1; } +static inline int hostapd_csa_update_hwmode(struct hostapd_iface *iface) +{ + return 0; +} + static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err) { return -1; @@ -92,8 +98,9 @@ static inline int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface) return 0; } -static inline void hostapd_determine_mode(struct hostapd_iface *iface) +static inline int hostapd_determine_mode(struct hostapd_iface *iface) { + return 0; } #endif /* NEED_AP_MLME */