From: Neo Jou <neojou@xxxxxxxxx> This patch is to extend the existing hw_mode=any to cover the not-offloaded-ACS case. We DONOT duplicate for the mode HOSTAPD_MODE_IEEE80211ANY in wpa_driver_nl80211_postprocess_modes(), so hostapd_select_hw_mode() cannot find suitable hw_mode and iface->current_mode is NULL. Thus, ACS code combine channels from multiple hw_mode lists, to determine which channel needs to be surveyed. Thus, there are some patches in src/ap/acs.c to handle the case that iface->current_mode is NULL, and to combine channels. Also in hostapd_is_usable_chans() called by hostapd_check_chans(), it updates the hw_mode, based on the frequency channel ACS picked. By this way, "hw_mode=any" can be used for not-offloaded-ACS case. Signed-off-by: Neo Jou <neojou@xxxxxxxxx> --- src/ap/acs.c | 327 +++++++++++++++++++++++++++++++++++++------------ src/ap/drv_callbacks.c | 23 +++- src/ap/hostapd.h | 1 + src/ap/hw_features.c | 50 +++++++- 4 files changed, 311 insertions(+), 90 deletions(-) diff --git a/src/ap/acs.c b/src/ap/acs.c index 232afa8..2ffa2ff 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -261,13 +261,15 @@ static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) } -void acs_cleanup(struct hostapd_iface *iface) +static inline +void acs_cleanup_by_mode(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) { int i; struct hostapd_channel_data *chan; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) acs_clean_chan_surveys(chan); @@ -276,12 +278,31 @@ void acs_cleanup(struct hostapd_iface *iface) chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; chan->min_nf = 0; } +} + +void acs_cleanup(struct hostapd_iface *iface) +{ + if (iface->current_mode == NULL) { + int i; + struct hostapd_hw_modes *mode; + + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + + if (iface->hw_modes_has_11g && + (mode->mode == HOSTAPD_MODE_IEEE80211B)) + continue; + + acs_cleanup_by_mode(iface, mode); + } + } else { + acs_cleanup_by_mode(iface, iface->current_mode); + } iface->chans_surveyed = 0; iface->acs_num_completed_scans = 0; } - static void acs_fail(struct hostapd_iface *iface) { wpa_printf(MSG_ERROR, "ACS: Failed to start"); @@ -453,19 +474,45 @@ static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan) } -static int acs_surveys_are_sufficient(struct hostapd_iface *iface) +static inline +int acs_surveys_are_sufficient_by_mode(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) { int i; struct hostapd_channel_data *chan; int valid = 0; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && acs_survey_list_is_sufficient(chan)) valid++; } + return valid; +} + +static int acs_surveys_are_sufficient(struct hostapd_iface *iface) +{ + int valid = 0; + + if (iface->current_mode == NULL) { + int i; + struct hostapd_hw_modes *mode; + + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + + if (iface->hw_modes_has_11g && + (mode->mode == HOSTAPD_MODE_IEEE80211B)) + continue; + + valid = acs_surveys_are_sufficient_by_mode(iface, mode); + } + } else { + valid = acs_surveys_are_sufficient_by_mode(iface, iface->current_mode); + } + /* We need at least survey data for one channel */ return !!valid; } @@ -489,14 +536,15 @@ static int is_in_chanlist(struct hostapd_iface *iface, } -static void acs_survey_all_chans_intereference_factor( - struct hostapd_iface *iface) +static void acs_survey_all_chans_intereference_factor_by_mode( + struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) { int i; struct hostapd_channel_data *chan; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; if (!acs_usable_chan(chan)) continue; @@ -514,15 +562,40 @@ static void acs_survey_all_chans_intereference_factor( } } +static void acs_survey_all_chans_intereference_factor( + struct hostapd_iface *iface) +{ + if (iface->current_mode == NULL) { + int i; + struct hostapd_hw_modes *mode; -static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, - int freq) + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + + if (iface->hw_modes_has_11g && + (mode->mode == HOSTAPD_MODE_IEEE80211B)) + continue; + + acs_survey_all_chans_intereference_factor_by_mode( + iface, mode); + + } + } else + acs_survey_all_chans_intereference_factor_by_mode( + iface, iface->current_mode); +} + + +static inline struct hostapd_channel_data * +acs_find_chan_by_mode(struct hostapd_iface *iface, + int freq, + struct hostapd_hw_modes *mode) { struct hostapd_channel_data *chan; int i; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; @@ -534,6 +607,30 @@ static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, return NULL; } +static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, + int freq) +{ + struct hostapd_channel_data *chan = NULL; + + if (iface->current_mode == NULL) { + int i; + struct hostapd_hw_modes *mode; + + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + + if (iface->hw_modes_has_11g && + (mode->mode == HOSTAPD_MODE_IEEE80211B)) + continue; + + chan = acs_find_chan_by_mode(iface, freq, mode); + } + } else { + chan = acs_find_chan_by_mode(iface, freq, iface->current_mode); + } + + return chan; +} static int is_24ghz_mode(enum hostapd_hw_mode mode) { @@ -565,58 +662,25 @@ static int is_common_24ghz_chan(int chan) #define ACS_24GHZ_PREFER_1_6_11 0.8 #endif /* ACS_24GHZ_PREFER_1_6_11 */ -/* - * At this point it's assumed chan->interface_factor has been computed. - * This function should be reusable regardless of interference computation - * option (survey, BSS, spectral, ...). chan->interference factor must be - * summable (i.e., must be always greater than zero). - */ -static struct hostapd_channel_data * -acs_find_ideal_chan(struct hostapd_iface *iface) +static void +acs_find_ideal_chan_by_mode(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode, + int n_chans, + u32 bw, + struct hostapd_channel_data **rand_chan, + struct hostapd_channel_data **ideal_chan, + long double *ideal_factor) { - struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL, - *rand_chan = NULL; - long double factor, ideal_factor = 0; + struct hostapd_channel_data *chan, *adj_chan = NULL; + long double factor; int i, j; - int n_chans = 1; - u32 bw; unsigned int k; - /* TODO: HT40- support */ - - if (iface->conf->ieee80211n && - iface->conf->secondary_channel == -1) { - wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); - return NULL; - } - - if (iface->conf->ieee80211n && - iface->conf->secondary_channel) - n_chans = 2; - - if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { - switch (hostapd_get_oper_chwidth(iface->conf)) { - case CHANWIDTH_80MHZ: - n_chans = 4; - break; - case CHANWIDTH_160MHZ: - n_chans = 8; - break; - } - } - - bw = num_chan_to_bw(n_chans); - - /* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */ - - wpa_printf(MSG_DEBUG, - "ACS: Survey analysis for selected bandwidth %d MHz", bw); - - for (i = 0; i < iface->current_mode->num_channels; i++) { + for (i = 0; i < mode->num_channels; i++) { double total_weight; struct acs_bias *bias, tmp_bias; - chan = &iface->current_mode->channels[i]; + chan = &mode->channels[i]; /* Since in the current ACS implementation the first channel is * always a primary channel, skip channels not available as @@ -637,7 +701,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) /* HT40 on 5 GHz has a limited set of primary channels as per * 11n Annex J */ - if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + if (mode->mode == HOSTAPD_MODE_IEEE80211A && iface->conf->ieee80211n && iface->conf->secondary_channel && !acs_usable_ht40_chan(chan)) { @@ -646,7 +710,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) continue; } - if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + if (mode->mode == HOSTAPD_MODE_IEEE80211A && (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) { if (hostapd_get_oper_chwidth(iface->conf) == CHANWIDTH_80MHZ && @@ -698,7 +762,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent * channel interference factor. */ - if (is_24ghz_mode(iface->current_mode->mode)) { + if (is_24ghz_mode(mode->mode)) { for (j = 0; j < n_chans; j++) { adj_chan = acs_find_chan(iface, chan->freq + (j * 20) - 5); @@ -744,7 +808,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) break; bias = NULL; } - } else if (is_24ghz_mode(iface->current_mode->mode) && + } else if (is_24ghz_mode(mode->mode) && is_common_24ghz_chan(chan->chan)) { tmp_bias.channel = chan->chan; tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11; @@ -763,14 +827,78 @@ acs_find_ideal_chan(struct hostapd_iface *iface) } if (acs_usable_chan(chan) && - (!ideal_chan || factor < ideal_factor)) { - ideal_factor = factor; - ideal_chan = chan; + (!*ideal_chan || factor < *ideal_factor)) { + *ideal_factor = factor; + *ideal_chan = chan; } /* This channel would at least be usable */ - if (!rand_chan) - rand_chan = chan; + if (!(*rand_chan)) + *rand_chan = chan; + } +} + + + +/* + * At this point it's assumed chan->interface_factor has been computed. + * This function should be reusable regardless of interference computation + * option (survey, BSS, spectral, ...). chan->interference factor must be + * summable (i.e., must be always greater than zero). + */ +static struct hostapd_channel_data * +acs_find_ideal_chan(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *ideal_chan = NULL, + *rand_chan = NULL; + long double ideal_factor = 0; + int i; + int n_chans = 1; + u32 bw; + struct hostapd_hw_modes *mode; + + /* TODO: HT40- support */ + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel == -1) { + wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); + return NULL; + } + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { + switch (hostapd_get_oper_chwidth(iface->conf)) { + case CHANWIDTH_80MHZ: + n_chans = 4; + break; + case CHANWIDTH_160MHZ: + n_chans = 8; + break; + } + } + + bw = num_chan_to_bw(n_chans); + + /* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */ + + wpa_printf(MSG_DEBUG, + "ACS: Survey analysis for selected bandwidth %d MHz", bw); + + if (iface->current_mode == NULL) { + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + + if (iface->hw_modes_has_11g && + (mode->mode == HOSTAPD_MODE_IEEE80211B)) + continue; + + acs_find_ideal_chan_by_mode(iface, mode, n_chans, bw, &rand_chan, &ideal_chan, &ideal_factor); + } + } else { + acs_find_ideal_chan_by_mode(iface, iface->current_mode, n_chans, bw, &rand_chan, &ideal_chan, &ideal_factor); } if (ideal_chan) { @@ -917,30 +1045,68 @@ fail: acs_fail(iface); } +static int *acs_request_scan_add_freq_by_mode(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode, + int *freq) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (!is_in_chanlist(iface, chan)) + continue; + + *freq++ = chan->freq; + } + + return freq; +} + static int acs_request_scan(struct hostapd_iface *iface) { struct wpa_driver_scan_params params; - struct hostapd_channel_data *chan; int i, *freq; + int num_channels; + struct hostapd_hw_modes *mode; os_memset(¶ms, 0, sizeof(params)); - params.freqs = os_calloc(iface->current_mode->num_channels + 1, - sizeof(params.freqs[0])); + if (iface->current_mode == NULL) { + num_channels = 0; + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + num_channels += mode->num_channels; + } + } else { + num_channels = iface->current_mode->num_channels; + } + + params.freqs = os_calloc(num_channels + 1, + sizeof(params.freqs[0])); if (params.freqs == NULL) return -1; freq = params.freqs; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - continue; - if (!is_in_chanlist(iface, chan)) - continue; + if (iface->current_mode == NULL) { + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; - *freq++ = chan->freq; + if (iface->hw_modes_has_11g && + (mode->mode == HOSTAPD_MODE_IEEE80211B)) + continue; + + freq = acs_request_scan_add_freq_by_mode(iface, mode, freq); + } + } else { + freq = acs_request_scan_add_freq_by_mode(iface, iface->current_mode, freq); } + *freq = 0; if (params.freqs == freq) { @@ -978,7 +1144,8 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) return HOSTAPD_CHAN_ACS; } - if (!iface->current_mode) + if ((!iface->current_mode) && + (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)) return HOSTAPD_CHAN_INVALID; acs_cleanup(iface); diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 8a4b0ef..4b3e4ea 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -1429,13 +1429,26 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, static struct hostapd_channel_data * hostapd_get_mode_channel( struct hostapd_iface *iface, unsigned int freq) { - int i; + int i, j; struct hostapd_channel_data *chan; + struct hostapd_hw_modes *mode; - for (i = 0; i < iface->current_mode->num_channels; i++) { - chan = &iface->current_mode->channels[i]; - if ((unsigned int) chan->freq == freq) - return chan; + if (iface->current_mode == NULL) { + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + + for (j = 0; j < mode->num_channels; j++) { + chan = &mode->channels[j]; + if ((unsigned int) chan->freq == freq) + return chan; + } + } + } else { + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if ((unsigned int) chan->freq == freq) + return chan; + } } return NULL; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 2358d16..2b4d656 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -480,6 +480,7 @@ struct hostapd_iface { struct hostapd_hw_modes *hw_features; int num_hw_features; + Boolean hw_modes_has_11g; struct hostapd_hw_modes *current_mode; /* Rates that are currently used (i.e., filtered copy of * current_mode->channels */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index ba10752..7ce1f74 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -859,8 +859,37 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface) int secondary_freq; struct hostapd_channel_data *pri_chan; - if (!iface->current_mode) - return 0; + if (!iface->current_mode) { + if (iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) { + int i; + enum hostapd_hw_mode target_mode; + + if (iface->freq < 4000) + if (iface->hw_modes_has_11g) + target_mode = HOSTAPD_MODE_IEEE80211G; + else + target_mode = HOSTAPD_MODE_IEEE80211B; + else if (iface->freq > 50000) + target_mode = HOSTAPD_MODE_IEEE80211AD; + else + target_mode = HOSTAPD_MODE_IEEE80211A; + + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = &iface->hw_features[i]; + if (mode->mode == target_mode) { + iface->current_mode = mode; + break; + } + } + + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211ANY) { + wpa_printf(MSG_ERROR, "ACS: cannot decide mode"); + return 0; + } + } else + return 0; + } + pri_chan = hw_get_channel_freq(iface->current_mode->mode, iface->freq, NULL, iface->hw_features, @@ -1031,10 +1060,21 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) } } + /* set if there is 11g already */ + iface->hw_modes_has_11g = FALSE; + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = &iface->hw_features[i]; + + if (mode->mode == HOSTAPD_MODE_IEEE80211G) { + iface->hw_modes_has_11g = TRUE; + break; + } + } + if (iface->current_mode == NULL) { - if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || - !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) - { + if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || + !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) && + (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)) { wpa_printf(MSG_ERROR, "Hardware does not support configured mode"); hostapd_logger(iface->bss[0], NULL, -- 2.7.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap