From 68fa00c341e26be69c1faffa58f94867b5f1cfd3 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 6 Feb 2015 17:59:57 +0200 Subject: [PATCH] ACS: Allow specific channels to be preferred The new acs_chan_bias configuration parameter 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). Signed-off-by: Jouni Malinen --- hostapd/config_file.c | 48 ++++++++++++++++++++++++++++++++++++++ hostapd/hostapd.conf | 11 +++++++++ src/ap/acs.c | 54 +++++++++++++++++++++++++++++++++++++++---- src/ap/ap_config.c | 3 +++ src/ap/ap_config.h | 5 ++++ 5 files changed, 117 insertions(+), 4 deletions(-) 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 */ }; -- 2.39.5