ACS: Accept channel if any (rather than all) survey results are valid
Previously, a channel with even a single scan/survey result missing information was skipped in ACS. This may not be desirable in cases when multiple scan iterations are used (which is the case by default in hostapd). Instead, use all channels that provided at least one complete set of results. Calculate the average interference factor as an average of the iterations that did provide complete values. This seems to help with some cases, e.g., when ath9k may not be able to report the noise floor for all channels from the first scan iteration immediately after the driver has been loaded, but then returns it for all other scan iterations. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
65013c9331
commit
0d7eb4344f
1 changed files with 32 additions and 11 deletions
43
src/ap/acs.c
43
src/ap/acs.c
|
@ -242,6 +242,7 @@
|
||||||
|
|
||||||
|
|
||||||
static int acs_request_scan(struct hostapd_iface *iface);
|
static int acs_request_scan(struct hostapd_iface *iface);
|
||||||
|
static int acs_survey_is_sufficient(struct freq_survey *survey);
|
||||||
|
|
||||||
|
|
||||||
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
|
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
|
||||||
|
@ -328,6 +329,7 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface,
|
||||||
struct freq_survey *survey;
|
struct freq_survey *survey;
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
long double int_factor = 0;
|
long double int_factor = 0;
|
||||||
|
unsigned count = 0;
|
||||||
|
|
||||||
if (dl_list_empty(&chan->survey_list))
|
if (dl_list_empty(&chan->survey_list))
|
||||||
return;
|
return;
|
||||||
|
@ -339,18 +341,27 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface,
|
||||||
|
|
||||||
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||||
{
|
{
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (!acs_survey_is_sufficient(survey)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
int_factor = acs_survey_interference_factor(survey,
|
int_factor = acs_survey_interference_factor(survey,
|
||||||
iface->lowest_nf);
|
iface->lowest_nf);
|
||||||
chan->interference_factor += int_factor;
|
chan->interference_factor += int_factor;
|
||||||
wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
|
wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
|
||||||
++i, chan->min_nf, int_factor,
|
i, chan->min_nf, int_factor,
|
||||||
survey->nf, (unsigned long) survey->channel_time,
|
survey->nf, (unsigned long) survey->channel_time,
|
||||||
(unsigned long) survey->channel_time_busy,
|
(unsigned long) survey->channel_time_busy,
|
||||||
(unsigned long) survey->channel_time_rx);
|
(unsigned long) survey->channel_time_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
chan->interference_factor = chan->interference_factor /
|
if (!count)
|
||||||
dl_list_len(&chan->survey_list);
|
return;
|
||||||
|
chan->interference_factor /= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -384,18 +395,19 @@ static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
|
||||||
static int acs_survey_is_sufficient(struct freq_survey *survey)
|
static int acs_survey_is_sufficient(struct freq_survey *survey)
|
||||||
{
|
{
|
||||||
if (!(survey->filled & SURVEY_HAS_NF)) {
|
if (!(survey->filled & SURVEY_HAS_NF)) {
|
||||||
wpa_printf(MSG_ERROR, "ACS: Survey is missing noise floor");
|
wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
|
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
|
||||||
wpa_printf(MSG_ERROR, "ACS: Survey is missing channel time");
|
wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
|
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
|
||||||
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
|
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
|
||||||
wpa_printf(MSG_ERROR, "ACS: Survey is missing RX and busy time (at least one is required)");
|
wpa_printf(MSG_INFO,
|
||||||
|
"ACS: Survey is missing RX and busy time (at least one is required)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,18 +418,27 @@ static int acs_survey_is_sufficient(struct freq_survey *survey)
|
||||||
static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
|
static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
|
||||||
{
|
{
|
||||||
struct freq_survey *survey;
|
struct freq_survey *survey;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||||
{
|
{
|
||||||
if (!acs_survey_is_sufficient(survey)) {
|
if (acs_survey_is_sufficient(survey)) {
|
||||||
wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
|
ret = 1;
|
||||||
chan->chan);
|
break;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
if (ret == -1)
|
||||||
|
ret = 1; /* no survey list entries */
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
wpa_printf(MSG_INFO,
|
||||||
|
"ACS: Channel %d has insufficient survey data",
|
||||||
|
chan->chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue