ACS: Allow specific channels to be preferred
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 6 Feb 2015 15:59:57 +0000 (17:59 +0200)
committerJouni Malinen <j@w1.fi>
Fri, 6 Feb 2015 15:59:57 +0000 (17:59 +0200)
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>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/acs.c
src/ap/ap_config.c
src/ap/ap_config.h

index e3cad7c..3466f28 100644 (file)
@@ -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);
index 1e56959..dd05c2f 100644 (file)
@@ -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.
index 78fd949..15a4741 100644 (file)
@@ -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)) {
index 1c0ed7a..c1861d4 100644 (file)
@@ -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);
 }
index e5215c5..0f33ac9 100644 (file)
@@ -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 */
 };