@@ -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);
@@ -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;
@@ -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 */
@@ -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,