Changeset View
Changeset View
Standalone View
Standalone View
contrib/wpa/src/ap/acs.c
Context not available. | |||||
} | } | ||||
void acs_cleanup(struct hostapd_iface *iface) | static void acs_cleanup_mode(struct hostapd_hw_modes *mode) | ||||
{ | { | ||||
int i; | int i; | ||||
struct hostapd_channel_data *chan; | struct hostapd_channel_data *chan; | ||||
for (i = 0; i < iface->current_mode->num_channels; i++) { | for (i = 0; i < mode->num_channels; i++) { | ||||
chan = &iface->current_mode->channels[i]; | chan = &mode->channels[i]; | ||||
if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) | if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) | ||||
acs_clean_chan_surveys(chan); | acs_clean_chan_surveys(chan); | ||||
Context not available. | |||||
chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; | chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; | ||||
chan->min_nf = 0; | chan->min_nf = 0; | ||||
} | } | ||||
} | |||||
void acs_cleanup(struct hostapd_iface *iface) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < iface->num_hw_features; i++) | |||||
acs_cleanup_mode(&iface->hw_features[i]); | |||||
iface->chans_surveyed = 0; | iface->chans_surveyed = 0; | ||||
iface->acs_num_completed_scans = 0; | iface->acs_num_completed_scans = 0; | ||||
Context not available. | |||||
} | } | ||||
static int acs_surveys_are_sufficient(struct hostapd_iface *iface) | static int acs_surveys_are_sufficient_mode(struct hostapd_hw_modes *mode) | ||||
{ | { | ||||
int i; | int i; | ||||
struct hostapd_channel_data *chan; | struct hostapd_channel_data *chan; | ||||
int valid = 0; | |||||
for (i = 0; i < iface->current_mode->num_channels; i++) { | for (i = 0; i < mode->num_channels; i++) { | ||||
chan = &iface->current_mode->channels[i]; | chan = &mode->channels[i]; | ||||
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && | if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && | ||||
acs_survey_list_is_sufficient(chan)) | acs_survey_list_is_sufficient(chan)) | ||||
valid++; | return 1; | ||||
} | |||||
return 0; | |||||
} | |||||
static int acs_surveys_are_sufficient(struct hostapd_iface *iface) | |||||
{ | |||||
int i; | |||||
struct hostapd_hw_modes *mode; | |||||
for (i = 0; i < iface->num_hw_features; i++) { | |||||
mode = &iface->hw_features[i]; | |||||
if (!hostapd_hw_skip_mode(iface, mode) && | |||||
acs_surveys_are_sufficient_mode(mode)) | |||||
return 1; | |||||
} | } | ||||
/* We need at least survey data for one channel */ | return 0; | ||||
return !!valid; | |||||
} | } | ||||
Context not available. | |||||
} | } | ||||
static void acs_survey_all_chans_intereference_factor( | static int is_in_freqlist(struct hostapd_iface *iface, | ||||
struct hostapd_iface *iface) | struct hostapd_channel_data *chan) | ||||
{ | |||||
if (!iface->conf->acs_freq_list.num) | |||||
return 1; | |||||
return freq_range_list_includes(&iface->conf->acs_freq_list, | |||||
chan->freq); | |||||
} | |||||
static void acs_survey_mode_interference_factor( | |||||
struct hostapd_iface *iface, struct hostapd_hw_modes *mode) | |||||
{ | { | ||||
int i; | int i; | ||||
struct hostapd_channel_data *chan; | struct hostapd_channel_data *chan; | ||||
for (i = 0; i < iface->current_mode->num_channels; i++) { | for (i = 0; i < mode->num_channels; i++) { | ||||
chan = &iface->current_mode->channels[i]; | chan = &mode->channels[i]; | ||||
if (!acs_usable_chan(chan)) | if (!acs_usable_chan(chan)) | ||||
continue; | continue; | ||||
Context not available. | |||||
if (!is_in_chanlist(iface, chan)) | if (!is_in_chanlist(iface, chan)) | ||||
continue; | continue; | ||||
if (!is_in_freqlist(iface, chan)) | |||||
continue; | |||||
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", | wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", | ||||
chan->chan, chan->freq); | chan->chan, chan->freq); | ||||
Context not available. | |||||
} | } | ||||
static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, | static void acs_survey_all_chans_interference_factor( | ||||
int freq) | struct hostapd_iface *iface) | ||||
{ | |||||
int i; | |||||
struct hostapd_hw_modes *mode; | |||||
for (i = 0; i < iface->num_hw_features; i++) { | |||||
mode = &iface->hw_features[i]; | |||||
if (!hostapd_hw_skip_mode(iface, mode)) | |||||
acs_survey_mode_interference_factor(iface, mode); | |||||
} | |||||
} | |||||
static struct hostapd_channel_data * | |||||
acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq) | |||||
{ | { | ||||
struct hostapd_channel_data *chan; | struct hostapd_channel_data *chan; | ||||
int i; | int i; | ||||
for (i = 0; i < iface->current_mode->num_channels; i++) { | for (i = 0; i < mode->num_channels; i++) { | ||||
chan = &iface->current_mode->channels[i]; | chan = &mode->channels[i]; | ||||
if (chan->flag & HOSTAPD_CHAN_DISABLED) | if (chan->flag & HOSTAPD_CHAN_DISABLED) | ||||
continue; | continue; | ||||
Context not available. | |||||
} | } | ||||
static struct hostapd_channel_data * | |||||
acs_find_chan(struct hostapd_iface *iface, int freq) | |||||
{ | |||||
int i; | |||||
struct hostapd_hw_modes *mode; | |||||
struct hostapd_channel_data *chan; | |||||
for (i = 0; i < iface->num_hw_features; i++) { | |||||
mode = &iface->hw_features[i]; | |||||
if (!hostapd_hw_skip_mode(iface, mode)) { | |||||
chan = acs_find_chan_mode(mode, freq); | |||||
if (chan) | |||||
return chan; | |||||
} | |||||
} | |||||
return NULL; | |||||
} | |||||
static int is_24ghz_mode(enum hostapd_hw_mode mode) | static int is_24ghz_mode(enum hostapd_hw_mode mode) | ||||
{ | { | ||||
return mode == HOSTAPD_MODE_IEEE80211B || | return mode == HOSTAPD_MODE_IEEE80211B || | ||||
Context not available. | |||||
#define ACS_24GHZ_PREFER_1_6_11 0.8 | #define ACS_24GHZ_PREFER_1_6_11 0.8 | ||||
#endif /* ACS_24GHZ_PREFER_1_6_11 */ | #endif /* ACS_24GHZ_PREFER_1_6_11 */ | ||||
/* | static void | ||||
* At this point it's assumed chan->interface_factor has been computed. | acs_find_ideal_chan_mode(struct hostapd_iface *iface, | ||||
* This function should be reusable regardless of interference computation | struct hostapd_hw_modes *mode, | ||||
* option (survey, BSS, spectral, ...). chan->interference factor must be | int n_chans, u32 bw, | ||||
* summable (i.e., must be always greater than zero). | struct hostapd_channel_data **rand_chan, | ||||
*/ | struct hostapd_channel_data **ideal_chan, | ||||
static struct hostapd_channel_data * | long double *ideal_factor) | ||||
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 = NULL; | ||||
*rand_chan = NULL; | long double factor; | ||||
long double factor, ideal_factor = 0; | |||||
int i, j; | int i, j; | ||||
int n_chans = 1; | |||||
u32 bw; | |||||
unsigned int k; | unsigned int k; | ||||
/* TODO: HT40- support */ | for (i = 0; i < mode->num_channels; i++) { | ||||
if (iface->conf->ieee80211n && | |||||
iface->conf->secondary_channel == -1) { | |||||
wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); | |||||
return NULL; | |||||
} | |||||
if (iface->conf->ieee80211n && | |||||
iface->conf->secondary_channel) | |||||
n_chans = 2; | |||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { | |||||
switch (hostapd_get_oper_chwidth(iface->conf)) { | |||||
case CHANWIDTH_80MHZ: | |||||
n_chans = 4; | |||||
break; | |||||
case CHANWIDTH_160MHZ: | |||||
n_chans = 8; | |||||
break; | |||||
} | |||||
} | |||||
bw = num_chan_to_bw(n_chans); | |||||
/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */ | |||||
wpa_printf(MSG_DEBUG, | |||||
"ACS: Survey analysis for selected bandwidth %d MHz", bw); | |||||
for (i = 0; i < iface->current_mode->num_channels; i++) { | |||||
double total_weight; | double total_weight; | ||||
struct acs_bias *bias, tmp_bias; | struct acs_bias *bias, tmp_bias; | ||||
chan = &iface->current_mode->channels[i]; | chan = &mode->channels[i]; | ||||
/* Since in the current ACS implementation the first channel is | /* Since in the current ACS implementation the first channel is | ||||
* always a primary channel, skip channels not available as | * always a primary channel, skip channels not available as | ||||
Context not available. | |||||
if (!is_in_chanlist(iface, chan)) | if (!is_in_chanlist(iface, chan)) | ||||
continue; | continue; | ||||
if (!is_in_freqlist(iface, chan)) | |||||
continue; | |||||
if (!chan_bw_allowed(chan, bw, 1, 1)) { | if (!chan_bw_allowed(chan, bw, 1, 1)) { | ||||
wpa_printf(MSG_DEBUG, | wpa_printf(MSG_DEBUG, | ||||
"ACS: Channel %d: BW %u is not supported", | "ACS: Channel %d: BW %u is not supported", | ||||
Context not available. | |||||
/* HT40 on 5 GHz has a limited set of primary channels as per | /* HT40 on 5 GHz has a limited set of primary channels as per | ||||
* 11n Annex J */ | * 11n Annex J */ | ||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && | if (mode->mode == HOSTAPD_MODE_IEEE80211A && | ||||
iface->conf->ieee80211n && | iface->conf->ieee80211n && | ||||
iface->conf->secondary_channel && | iface->conf->secondary_channel && | ||||
!acs_usable_ht40_chan(chan)) { | !acs_usable_ht40_chan(chan)) { | ||||
Context not available. | |||||
continue; | continue; | ||||
} | } | ||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && | if (mode->mode == HOSTAPD_MODE_IEEE80211A && | ||||
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) { | (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) { | ||||
if (hostapd_get_oper_chwidth(iface->conf) == | if (hostapd_get_oper_chwidth(iface->conf) == | ||||
CHANWIDTH_80MHZ && | CHANWIDTH_80MHZ && | ||||
Context not available. | |||||
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent | /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent | ||||
* channel interference factor. */ | * channel interference factor. */ | ||||
if (is_24ghz_mode(iface->current_mode->mode)) { | if (is_24ghz_mode(mode->mode)) { | ||||
for (j = 0; j < n_chans; j++) { | for (j = 0; j < n_chans; j++) { | ||||
adj_chan = acs_find_chan(iface, chan->freq + | adj_chan = acs_find_chan(iface, chan->freq + | ||||
(j * 20) - 5); | (j * 20) - 5); | ||||
Context not available. | |||||
break; | break; | ||||
bias = NULL; | bias = NULL; | ||||
} | } | ||||
} else if (is_24ghz_mode(iface->current_mode->mode) && | } else if (is_24ghz_mode(mode->mode) && | ||||
is_common_24ghz_chan(chan->chan)) { | is_common_24ghz_chan(chan->chan)) { | ||||
tmp_bias.channel = chan->chan; | tmp_bias.channel = chan->chan; | ||||
tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11; | tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11; | ||||
Context not available. | |||||
} | } | ||||
if (acs_usable_chan(chan) && | if (acs_usable_chan(chan) && | ||||
(!ideal_chan || factor < ideal_factor)) { | (!*ideal_chan || factor < *ideal_factor)) { | ||||
ideal_factor = factor; | *ideal_factor = factor; | ||||
ideal_chan = chan; | *ideal_chan = chan; | ||||
} | } | ||||
/* This channel would at least be usable */ | /* This channel would at least be usable */ | ||||
if (!rand_chan) | if (!(*rand_chan)) | ||||
rand_chan = chan; | *rand_chan = chan; | ||||
} | |||||
} | |||||
/* | |||||
* At this point it's assumed chan->interference_factor has been computed. | |||||
* This function should be reusable regardless of interference computation | |||||
* option (survey, BSS, spectral, ...). chan->interference factor must be | |||||
* summable (i.e., must be always greater than zero). | |||||
*/ | |||||
static struct hostapd_channel_data * | |||||
acs_find_ideal_chan(struct hostapd_iface *iface) | |||||
{ | |||||
struct hostapd_channel_data *ideal_chan = NULL, | |||||
*rand_chan = NULL; | |||||
long double ideal_factor = 0; | |||||
int i; | |||||
int n_chans = 1; | |||||
u32 bw; | |||||
struct hostapd_hw_modes *mode; | |||||
/* TODO: HT40- support */ | |||||
if (iface->conf->ieee80211n && | |||||
iface->conf->secondary_channel == -1) { | |||||
wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); | |||||
return NULL; | |||||
} | |||||
if (iface->conf->ieee80211n && | |||||
iface->conf->secondary_channel) | |||||
n_chans = 2; | |||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { | |||||
switch (hostapd_get_oper_chwidth(iface->conf)) { | |||||
case CHANWIDTH_80MHZ: | |||||
n_chans = 4; | |||||
break; | |||||
case CHANWIDTH_160MHZ: | |||||
n_chans = 8; | |||||
break; | |||||
} | |||||
} | |||||
bw = num_chan_to_bw(n_chans); | |||||
/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */ | |||||
wpa_printf(MSG_DEBUG, | |||||
"ACS: Survey analysis for selected bandwidth %d MHz", bw); | |||||
for (i = 0; i < iface->num_hw_features; i++) { | |||||
mode = &iface->hw_features[i]; | |||||
if (!hostapd_hw_skip_mode(iface, mode)) | |||||
acs_find_ideal_chan_mode(iface, mode, n_chans, bw, | |||||
&rand_chan, &ideal_chan, | |||||
&ideal_factor); | |||||
} | } | ||||
if (ideal_chan) { | if (ideal_chan) { | ||||
Context not available. | |||||
return -1; | return -1; | ||||
} | } | ||||
acs_survey_all_chans_intereference_factor(iface); | acs_survey_all_chans_interference_factor(iface); | ||||
return 0; | return 0; | ||||
} | } | ||||
Context not available. | |||||
} | } | ||||
iface->conf->channel = ideal_chan->chan; | iface->conf->channel = ideal_chan->chan; | ||||
iface->freq = ideal_chan->freq; | |||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) | if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) | ||||
acs_adjust_center_freq(iface); | acs_adjust_center_freq(iface); | ||||
Context not available. | |||||
} | } | ||||
static int * acs_request_scan_add_freqs(struct hostapd_iface *iface, | |||||
struct hostapd_hw_modes *mode, | |||||
int *freq) | |||||
{ | |||||
struct hostapd_channel_data *chan; | |||||
int i; | |||||
for (i = 0; i < mode->num_channels; i++) { | |||||
chan = &mode->channels[i]; | |||||
if (chan->flag & HOSTAPD_CHAN_DISABLED) | |||||
continue; | |||||
if (!is_in_chanlist(iface, chan)) | |||||
continue; | |||||
if (!is_in_freqlist(iface, chan)) | |||||
continue; | |||||
*freq++ = chan->freq; | |||||
} | |||||
return freq; | |||||
} | |||||
static int acs_request_scan(struct hostapd_iface *iface) | static int acs_request_scan(struct hostapd_iface *iface) | ||||
{ | { | ||||
struct wpa_driver_scan_params params; | struct wpa_driver_scan_params params; | ||||
struct hostapd_channel_data *chan; | |||||
int i, *freq; | int i, *freq; | ||||
int num_channels; | |||||
struct hostapd_hw_modes *mode; | |||||
os_memset(¶ms, 0, sizeof(params)); | os_memset(¶ms, 0, sizeof(params)); | ||||
params.freqs = os_calloc(iface->current_mode->num_channels + 1, | |||||
sizeof(params.freqs[0])); | num_channels = 0; | ||||
for (i = 0; i < iface->num_hw_features; i++) { | |||||
mode = &iface->hw_features[i]; | |||||
if (!hostapd_hw_skip_mode(iface, mode)) | |||||
num_channels += mode->num_channels; | |||||
} | |||||
params.freqs = os_calloc(num_channels + 1, sizeof(params.freqs[0])); | |||||
if (params.freqs == NULL) | if (params.freqs == NULL) | ||||
return -1; | return -1; | ||||
freq = params.freqs; | freq = params.freqs; | ||||
for (i = 0; i < iface->current_mode->num_channels; i++) { | |||||
chan = &iface->current_mode->channels[i]; | |||||
if (chan->flag & HOSTAPD_CHAN_DISABLED) | |||||
continue; | |||||
if (!is_in_chanlist(iface, chan)) | |||||
continue; | |||||
*freq++ = chan->freq; | for (i = 0; i < iface->num_hw_features; i++) { | ||||
mode = &iface->hw_features[i]; | |||||
if (!hostapd_hw_skip_mode(iface, mode)) | |||||
freq = acs_request_scan_add_freqs(iface, mode, freq); | |||||
} | } | ||||
*freq = 0; | *freq = 0; | ||||
if (params.freqs == freq) { | |||||
wpa_printf(MSG_ERROR, "ACS: No available channels found"); | |||||
os_free(params.freqs); | |||||
return -1; | |||||
} | |||||
iface->scan_cb = acs_scan_complete; | iface->scan_cb = acs_scan_complete; | ||||
wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", | wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", | ||||
Context not available. | |||||
return HOSTAPD_CHAN_ACS; | return HOSTAPD_CHAN_ACS; | ||||
} | } | ||||
if (!iface->current_mode) | if (!iface->current_mode && | ||||
iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY) | |||||
return HOSTAPD_CHAN_INVALID; | return HOSTAPD_CHAN_INVALID; | ||||
acs_cleanup(iface); | acs_cleanup(iface); | ||||
Context not available. |