#include "utils/common.h"
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_drv_ops.h"
}
-void acs_fail(struct hostapd_iface *iface)
+static void acs_fail(struct hostapd_iface *iface)
{
wpa_printf(MSG_ERROR, "ACS: Failed to start");
acs_cleanup(iface);
+ hostapd_disable_iface(iface);
}
157, 184, 192 };
unsigned int i;
- for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++)
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
+ if (chan->chan == allowed[i])
+ return 1;
+
+ return 0;
+}
+
+
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+ const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
if (chan->chan == allowed[i])
return 1;
for (i = 0; i < iface->current_mode->num_channels; i++) {
chan = &iface->current_mode->channels[i];
- if (!acs_usable_chan(chan))
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (chan->freq == freq)
static struct hostapd_channel_data *
acs_find_ideal_chan(struct hostapd_iface *iface)
{
- struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL;
+ struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
+ *rand_chan = NULL;
long double factor, ideal_factor = 0;
int i, j;
int n_chans = 1;
for (i = 0; i < iface->current_mode->num_channels; i++) {
chan = &iface->current_mode->channels[i];
- if (!acs_usable_chan(chan))
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
+
/* HT40 on 5 GHz has a limited set of primary channels as per
* 11n Annex J */
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
continue;
}
- factor = chan->interference_factor;
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->conf->ieee80211ac &&
+ iface->conf->vht_oper_chwidth == 1 &&
+ !acs_usable_vht80_chan(chan)) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+ chan->chan);
+ continue;
+ }
+
+ factor = 0;
+ if (acs_usable_chan(chan))
+ factor = chan->interference_factor;
for (j = 1; j < n_chans; j++) {
adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
if (!adj_chan)
break;
- factor += adj_chan->interference_factor;
+ if (acs_usable_chan(adj_chan))
+ factor += adj_chan->interference_factor;
}
if (j != n_chans) {
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) - 5);
- if (adj_chan)
+ if (adj_chan && acs_usable_chan(adj_chan))
factor += adj_chan->interference_factor;
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) - 10);
- if (adj_chan)
+ if (adj_chan && acs_usable_chan(adj_chan))
factor += adj_chan->interference_factor;
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) + 5);
- if (adj_chan)
+ if (adj_chan && acs_usable_chan(adj_chan))
factor += adj_chan->interference_factor;
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) + 10);
- if (adj_chan)
+ if (adj_chan && acs_usable_chan(adj_chan))
factor += adj_chan->interference_factor;
}
}
wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg",
chan->chan, factor);
- if (!ideal_chan || factor < ideal_factor) {
+ if (acs_usable_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 (ideal_chan)
+ if (ideal_chan) {
wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
ideal_chan->chan, ideal_chan->freq, ideal_factor);
+ return ideal_chan;
+ }
- return ideal_chan;
+ return rand_chan;
}
static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
{
+ int offset;
+
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
switch (iface->conf->vht_oper_chwidth) {
case VHT_CHANWIDTH_USE_HT:
- iface->conf->vht_oper_centr_freq_seg0_idx =
- iface->conf->channel + 2;
+ offset = 2 * iface->conf->secondary_channel;
break;
case VHT_CHANWIDTH_80MHZ:
- iface->conf->vht_oper_centr_freq_seg0_idx =
- iface->conf->channel + 6;
+ offset = 6;
break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
- break;
+ return;
}
+
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ iface->conf->channel + offset;
}
err = hostapd_drv_get_survey(iface->bss[0], 0);
if (err) {
wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
- acs_fail(iface);
+ goto fail;
}
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
err = acs_request_scan(iface);
if (err) {
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
- acs_fail(iface);
- return;
+ goto fail;
}
return;
}
acs_study(iface);
+ return;
+fail:
+ hostapd_acs_completed(iface, 1);
+ acs_fail(iface);
}
if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
acs_cleanup(iface);
+ os_free(params.freqs);
return -1;
}
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+ wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+ err = hostapd_drv_do_acs(iface->bss[0]);
+ if (err)
+ return HOSTAPD_CHAN_INVALID;
+ return HOSTAPD_CHAN_ACS;
+ }
+
acs_cleanup(iface);
err = acs_request_scan(iface);
if (err < 0)
return HOSTAPD_CHAN_INVALID;
+ hostapd_set_state(iface, HAPD_IFACE_ACS);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
+
return HOSTAPD_CHAN_ACS;
}