ACS: Allow specific channels to be preferred
The new acs_chan_bias configuration parameter is a space-separated list of <channel>:<bias> 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 <jouni@qca.qualcomm.com>
This commit is contained in:
parent
6f41a25805
commit
68fa00c341
5 changed files with 117 additions and 4 deletions
|
@ -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);
|
||||
|
|
|
@ -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 <channel>:<bias> 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.
|
||||
|
|
54
src/ap/acs.c
54
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)) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue