diff --git a/hostapd/config_file.c b/hostapd/config_file.c index e3cad7c9f..3466f2851 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1856,6 +1856,48 @@ static struct wpabuf * hostapd_parse_bin(const char *buf) #endif /* CONFIG_WPS_NFC */ +#ifdef CONFIG_ACS +static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf, + char *pos) +{ + struct acs_bias *bias = NULL, *tmp; + unsigned int num = 0; + char *end; + + while (*pos) { + tmp = os_realloc_array(bias, num + 1, sizeof(*bias)); + if (!tmp) + goto fail; + bias = tmp; + + bias[num].channel = atoi(pos); + if (bias[num].channel <= 0) + goto fail; + pos = os_strchr(pos, ':'); + if (!pos) + goto fail; + pos++; + bias[num].bias = strtod(pos, &end); + if (end == pos || bias[num].bias < 0.0) + goto fail; + pos = end; + if (*pos != ' ' && *pos != '\0') + goto fail; + num++; + } + + os_free(conf->acs_chan_bias); + conf->acs_chan_bias = bias; + conf->num_acs_chan_bias = num; + + return 0; +fail: + os_free(bias); + return -1; +} +#endif /* CONFIG_ACS */ + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, char *buf, char *pos, int line) @@ -2508,6 +2550,12 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } conf->acs_num_scans = val; + } else if (os_strcmp(buf, "acs_chan_bias") == 0) { + if (hostapd_config_parse_acs_chan_bias(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid acs_chan_bias", + line); + return -1; + } #endif /* CONFIG_ACS */ } else if (os_strcmp(buf, "dtim_period") == 0) { bss->dtim_period = atoi(pos); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 1e5695984..dd05c2ffd 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -154,8 +154,19 @@ channel=1 # interference that may help choosing a better channel. This can also help fine # tune the ACS scan time in case a driver has different scan dwell times. # +# acs_chan_bias is a space-separated list of : pairs. It can be +# used to increase (or decrease) the likelihood of a specific channel to be +# selected by the ACS algorithm. The total interference factor for each channel +# gets multiplied by the specified bias value before finding the channel with +# the lowest value. In other words, values between 0.0 and 1.0 can be used to +# make a channel more likely to be picked while values larger than 1.0 make the +# specified channel less likely to be picked. This can be used, e.g., to prefer +# the commonly used 2.4 GHz band channels 1, 6, and 11 (which is the default +# behavior on 2.4 GHz band if no acs_chan_bias parameter is specified). +# # Defaults: #acs_num_scans=5 +#acs_chan_bias=1:0.8 6:0.8 11:0.8 # Channel list restriction. This option allows hostapd to select one of the # provided channels when a channel should be automatically selected. diff --git a/src/ap/acs.c b/src/ap/acs.c index 78fd949af..15a474164 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -517,6 +517,19 @@ static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, } +static int is_24ghz_mode(enum hostapd_hw_mode mode) +{ + return mode == HOSTAPD_MODE_IEEE80211B || + mode == HOSTAPD_MODE_IEEE80211G; +} + + +static int is_common_24ghz_chan(int chan) +{ + return chan == 1 || chan == 6 || chan == 11; +} + + #ifndef ACS_ADJ_WEIGHT #define ACS_ADJ_WEIGHT 0.85 #endif /* ACS_ADJ_WEIGHT */ @@ -525,6 +538,15 @@ static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, #define ACS_NEXT_ADJ_WEIGHT 0.55 #endif /* ACS_NEXT_ADJ_WEIGHT */ +#ifndef ACS_24GHZ_PREFER_1_6_11 +/* + * Select commonly used channels 1, 6, 11 by default even if a neighboring + * channel has a smaller interference factor as long as it is not better by more + * than this multiplier. + */ +#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 @@ -539,6 +561,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) long double factor, ideal_factor = 0; int i, j; int n_chans = 1; + unsigned int k; /* TODO: HT40- support */ @@ -566,6 +589,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) for (i = 0; i < iface->current_mode->num_channels; i++) { double total_weight; + struct acs_bias *bias, tmp_bias; chan = &iface->current_mode->channels[i]; @@ -619,8 +643,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent * channel interference factor. */ - if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B || - iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) { + if (is_24ghz_mode(iface->current_mode->mode)) { for (j = 0; j < n_chans; j++) { adj_chan = acs_find_chan(iface, chan->freq + (j * 20) - 5); @@ -658,8 +681,31 @@ acs_find_ideal_chan(struct hostapd_iface *iface) factor /= total_weight; - wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg", - chan->chan, factor); + bias = NULL; + if (iface->conf->acs_chan_bias) { + for (k = 0; k < iface->conf->num_acs_chan_bias; k++) { + bias = &iface->conf->acs_chan_bias[k]; + if (bias->channel == chan->chan) + break; + bias = NULL; + } + } else if (is_24ghz_mode(iface->current_mode->mode) && + is_common_24ghz_chan(chan->chan)) { + tmp_bias.channel = chan->chan; + tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11; + bias = &tmp_bias; + } + + if (bias) { + factor *= bias->bias; + wpa_printf(MSG_DEBUG, + "ACS: * channel %d: total interference = %Lg (%f bias)", + chan->chan, factor, bias->bias); + } else { + wpa_printf(MSG_DEBUG, + "ACS: * channel %d: total interference = %Lg", + chan->chan, factor); + } if (acs_usable_chan(chan) && (!ideal_chan || factor < ideal_factor)) { diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 1c0ed7aa9..c1861d4f7 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -574,6 +574,9 @@ void hostapd_config_free(struct hostapd_config *conf) os_free(conf->basic_rates); os_free(conf->chanlist); os_free(conf->driver_params); +#ifdef CONFIG_ACS + os_free(conf->acs_chan_bias); +#endif /* CONFIG_ACS */ os_free(conf); } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index e5215c529..0f33ac9a0 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -639,6 +639,11 @@ struct hostapd_config { #ifdef CONFIG_ACS unsigned int acs_num_scans; + struct acs_bias { + int channel; + double bias; + } *acs_chan_bias; + unsigned int num_acs_chan_bias; #endif /* CONFIG_ACS */ };