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 <quic_ngong@quicinc.com>
This commit is contained in:
Nijun Gong 2023-07-10 21:19:29 +08:00 committed by Jouni Malinen
parent 32dcec9529
commit 123d16d860
3 changed files with 41 additions and 6 deletions

View file

@ -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)

View file

@ -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.

View file

@ -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 */