acs.c revision 281681
1281681Srpaulo/* 2281681Srpaulo * ACS - Automatic Channel Selection module 3281681Srpaulo * Copyright (c) 2011, Atheros Communications 4281681Srpaulo * Copyright (c) 2013, Qualcomm Atheros, Inc. 5281681Srpaulo * 6281681Srpaulo * This software may be distributed under the terms of the BSD license. 7281681Srpaulo * See README for more details. 8281681Srpaulo */ 9281681Srpaulo 10281681Srpaulo#include "utils/includes.h" 11281681Srpaulo#include <math.h> 12281681Srpaulo 13281681Srpaulo#include "utils/common.h" 14281681Srpaulo#include "utils/list.h" 15281681Srpaulo#include "common/ieee802_11_defs.h" 16281681Srpaulo#include "common/wpa_ctrl.h" 17281681Srpaulo#include "drivers/driver.h" 18281681Srpaulo#include "hostapd.h" 19281681Srpaulo#include "ap_drv_ops.h" 20281681Srpaulo#include "ap_config.h" 21281681Srpaulo#include "hw_features.h" 22281681Srpaulo#include "acs.h" 23281681Srpaulo 24281681Srpaulo/* 25281681Srpaulo * Automatic Channel Selection 26281681Srpaulo * =========================== 27281681Srpaulo * 28281681Srpaulo * More info at 29281681Srpaulo * ------------ 30281681Srpaulo * http://wireless.kernel.org/en/users/Documentation/acs 31281681Srpaulo * 32281681Srpaulo * How to use 33281681Srpaulo * ---------- 34281681Srpaulo * - make sure you have CONFIG_ACS=y in hostapd's .config 35281681Srpaulo * - use channel=0 or channel=acs to enable ACS 36281681Srpaulo * 37281681Srpaulo * How does it work 38281681Srpaulo * ---------------- 39281681Srpaulo * 1. passive scans are used to collect survey data 40281681Srpaulo * (it is assumed that scan trigger collection of survey data in driver) 41281681Srpaulo * 2. interference factor is calculated for each channel 42281681Srpaulo * 3. ideal channel is picked depending on channel width by using adjacent 43281681Srpaulo * channel interference factors 44281681Srpaulo * 45281681Srpaulo * Known limitations 46281681Srpaulo * ----------------- 47281681Srpaulo * - Current implementation depends heavily on the amount of time willing to 48281681Srpaulo * spend gathering survey data during hostapd startup. Short traffic bursts 49281681Srpaulo * may be missed and a suboptimal channel may be picked. 50281681Srpaulo * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS 51281681Srpaulo * 52281681Srpaulo * Todo / Ideas 53281681Srpaulo * ------------ 54281681Srpaulo * - implement other interference computation methods 55281681Srpaulo * - BSS/RSSI based 56281681Srpaulo * - spectral scan based 57281681Srpaulo * (should be possibly to hook this up with current ACS scans) 58281681Srpaulo * - add wpa_supplicant support (for P2P) 59281681Srpaulo * - collect a histogram of interference over time allowing more educated 60281681Srpaulo * guess about an ideal channel (perhaps CSA could be used to migrate AP to a 61281681Srpaulo * new "better" channel while running) 62281681Srpaulo * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs 63281681Srpaulo * when choosing the ideal channel 64281681Srpaulo * 65281681Srpaulo * Survey interference factor implementation details 66281681Srpaulo * ------------------------------------------------- 67281681Srpaulo * Generic interference_factor in struct hostapd_channel_data is used. 68281681Srpaulo * 69281681Srpaulo * The survey interference factor is defined as the ratio of the 70281681Srpaulo * observed busy time over the time we spent on the channel, 71281681Srpaulo * this value is then amplified by the observed noise floor on 72281681Srpaulo * the channel in comparison to the lowest noise floor observed 73281681Srpaulo * on the entire band. 74281681Srpaulo * 75281681Srpaulo * This corresponds to: 76281681Srpaulo * --- 77281681Srpaulo * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf) 78281681Srpaulo * --- 79281681Srpaulo * 80281681Srpaulo * The coefficient of 2 reflects the way power in "far-field" 81281681Srpaulo * radiation decreases as the square of distance from the antenna [1]. 82281681Srpaulo * What this does is it decreases the observed busy time ratio if the 83281681Srpaulo * noise observed was low but increases it if the noise was high, 84281681Srpaulo * proportionally to the way "far field" radiation changes over 85281681Srpaulo * distance. 86281681Srpaulo * 87281681Srpaulo * If channel busy time is not available the fallback is to use channel RX time. 88281681Srpaulo * 89281681Srpaulo * Since noise floor is in dBm it is necessary to convert it into Watts so that 90281681Srpaulo * combined channel interference (e.g., HT40, which uses two channels) can be 91281681Srpaulo * calculated easily. 92281681Srpaulo * --- 93281681Srpaulo * (busy time - tx time) / (active time - tx time) * 94281681Srpaulo * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) 95281681Srpaulo * --- 96281681Srpaulo * 97281681Srpaulo * However to account for cases where busy/rx time is 0 (channel load is then 98281681Srpaulo * 0%) channel noise floor signal power is combined into the equation so a 99281681Srpaulo * channel with lower noise floor is preferred. The equation becomes: 100281681Srpaulo * --- 101281681Srpaulo * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) * 102281681Srpaulo * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) 103281681Srpaulo * --- 104281681Srpaulo * 105281681Srpaulo * All this "interference factor" is purely subjective and only time 106281681Srpaulo * will tell how usable this is. By using the minimum noise floor we 107281681Srpaulo * remove any possible issues due to card calibration. The computation 108281681Srpaulo * of the interference factor then is dependent on what the card itself 109281681Srpaulo * picks up as the minimum noise, not an actual real possible card 110281681Srpaulo * noise value. 111281681Srpaulo * 112281681Srpaulo * Total interference computation details 113281681Srpaulo * -------------------------------------- 114281681Srpaulo * The above channel interference factor is calculated with no respect to 115281681Srpaulo * target operational bandwidth. 116281681Srpaulo * 117281681Srpaulo * To find an ideal channel the above data is combined by taking into account 118281681Srpaulo * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels 119281681Srpaulo * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth 120281681Srpaulo * on 5 GHz. 121281681Srpaulo * 122281681Srpaulo * Each valid and possible channel spec (i.e., channel + width) is taken and its 123281681Srpaulo * interference factor is computed by summing up interferences of each channel 124281681Srpaulo * it overlaps. The one with least total interference is picked up. 125281681Srpaulo * 126281681Srpaulo * Note: This implies base channel interference factor must be non-negative 127281681Srpaulo * allowing easy summing up. 128281681Srpaulo * 129281681Srpaulo * Example ACS analysis printout 130281681Srpaulo * ----------------------------- 131281681Srpaulo * 132281681Srpaulo * ACS: Trying survey-based ACS 133281681Srpaulo * ACS: Survey analysis for channel 1 (2412 MHz) 134281681Srpaulo * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13 135281681Srpaulo * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 136281681Srpaulo * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11 137281681Srpaulo * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 138281681Srpaulo * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 139281681Srpaulo * ACS: * interference factor average: 0.0557166 140281681Srpaulo * ACS: Survey analysis for channel 2 (2417 MHz) 141281681Srpaulo * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 142281681Srpaulo * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4 143281681Srpaulo * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6 144281681Srpaulo * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24 145281681Srpaulo * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 146281681Srpaulo * ACS: * interference factor average: 0.050832 147281681Srpaulo * ACS: Survey analysis for channel 3 (2422 MHz) 148281681Srpaulo * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 149281681Srpaulo * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 150281681Srpaulo * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 151281681Srpaulo * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 152281681Srpaulo * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 153281681Srpaulo * ACS: * interference factor average: 0.0148838 154281681Srpaulo * ACS: Survey analysis for channel 4 (2427 MHz) 155281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 156281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 157281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 158281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 159281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 160281681Srpaulo * ACS: * interference factor average: 0.0160801 161281681Srpaulo * ACS: Survey analysis for channel 5 (2432 MHz) 162281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66 163281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7 164281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2 165281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109 166281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 167281681Srpaulo * ACS: * interference factor average: 0.232244 168281681Srpaulo * ACS: Survey analysis for channel 6 (2437 MHz) 169281681Srpaulo * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89 170281681Srpaulo * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13 171281681Srpaulo * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 172281681Srpaulo * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70 173281681Srpaulo * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 174281681Srpaulo * ACS: * interference factor average: 0.232298 175281681Srpaulo * ACS: Survey analysis for channel 7 (2442 MHz) 176281681Srpaulo * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71 177281681Srpaulo * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62 178281681Srpaulo * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 179281681Srpaulo * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 180281681Srpaulo * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 181281681Srpaulo * ACS: * interference factor average: 0.195031 182281681Srpaulo * ACS: Survey analysis for channel 8 (2447 MHz) 183281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8 184281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8 185281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 186281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21 187281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27 188281681Srpaulo * ACS: * interference factor average: 0.0865885 189281681Srpaulo * ACS: Survey analysis for channel 9 (2452 MHz) 190281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2 191281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5 192281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 193281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1 194281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 195281681Srpaulo * ACS: * interference factor average: 0.00993022 196281681Srpaulo * ACS: Survey analysis for channel 10 (2457 MHz) 197281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 198281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 199281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 200281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8 201281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 202281681Srpaulo * ACS: * interference factor average: 0.0136033 203281681Srpaulo * ACS: Survey analysis for channel 11 (2462 MHz) 204281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 205281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 206281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 207281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7 208281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15 209281681Srpaulo * ACS: * interference factor average: 0.0271605 210281681Srpaulo * ACS: Survey analysis for channel 12 (2467 MHz) 211281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 212281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 213281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 214281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 215281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1 216281681Srpaulo * ACS: * interference factor average: 0.0148992 217281681Srpaulo * ACS: Survey analysis for channel 13 (2472 MHz) 218281681Srpaulo * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12 219281681Srpaulo * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 220281681Srpaulo * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 221281681Srpaulo * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 222281681Srpaulo * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 223281681Srpaulo * ACS: * interference factor average: 0.0260179 224281681Srpaulo * ACS: Survey analysis for selected bandwidth 20MHz 225281681Srpaulo * ACS: * channel 1: total interference = 0.121432 226281681Srpaulo * ACS: * channel 2: total interference = 0.137512 227281681Srpaulo * ACS: * channel 3: total interference = 0.369757 228281681Srpaulo * ACS: * channel 4: total interference = 0.546338 229281681Srpaulo * ACS: * channel 5: total interference = 0.690538 230281681Srpaulo * ACS: * channel 6: total interference = 0.762242 231281681Srpaulo * ACS: * channel 7: total interference = 0.756092 232281681Srpaulo * ACS: * channel 8: total interference = 0.537451 233281681Srpaulo * ACS: * channel 9: total interference = 0.332313 234281681Srpaulo * ACS: * channel 10: total interference = 0.152182 235281681Srpaulo * ACS: * channel 11: total interference = 0.0916111 236281681Srpaulo * ACS: * channel 12: total interference = 0.0816809 237281681Srpaulo * ACS: * channel 13: total interference = 0.0680776 238281681Srpaulo * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776 239281681Srpaulo * 240281681Srpaulo * [1] http://en.wikipedia.org/wiki/Near_and_far_field 241281681Srpaulo */ 242281681Srpaulo 243281681Srpaulo 244281681Srpaulostatic int acs_request_scan(struct hostapd_iface *iface); 245281681Srpaulostatic int acs_survey_is_sufficient(struct freq_survey *survey); 246281681Srpaulo 247281681Srpaulo 248281681Srpaulostatic void acs_clean_chan_surveys(struct hostapd_channel_data *chan) 249281681Srpaulo{ 250281681Srpaulo struct freq_survey *survey, *tmp; 251281681Srpaulo 252281681Srpaulo if (dl_list_empty(&chan->survey_list)) 253281681Srpaulo return; 254281681Srpaulo 255281681Srpaulo dl_list_for_each_safe(survey, tmp, &chan->survey_list, 256281681Srpaulo struct freq_survey, list) { 257281681Srpaulo dl_list_del(&survey->list); 258281681Srpaulo os_free(survey); 259281681Srpaulo } 260281681Srpaulo} 261281681Srpaulo 262281681Srpaulo 263281681Srpaulostatic void acs_cleanup(struct hostapd_iface *iface) 264281681Srpaulo{ 265281681Srpaulo int i; 266281681Srpaulo struct hostapd_channel_data *chan; 267281681Srpaulo 268281681Srpaulo for (i = 0; i < iface->current_mode->num_channels; i++) { 269281681Srpaulo chan = &iface->current_mode->channels[i]; 270281681Srpaulo 271281681Srpaulo if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) 272281681Srpaulo acs_clean_chan_surveys(chan); 273281681Srpaulo 274281681Srpaulo dl_list_init(&chan->survey_list); 275281681Srpaulo chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; 276281681Srpaulo chan->min_nf = 0; 277281681Srpaulo } 278281681Srpaulo 279281681Srpaulo iface->chans_surveyed = 0; 280281681Srpaulo iface->acs_num_completed_scans = 0; 281281681Srpaulo} 282281681Srpaulo 283281681Srpaulo 284281681Srpaulostatic void acs_fail(struct hostapd_iface *iface) 285281681Srpaulo{ 286281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Failed to start"); 287281681Srpaulo acs_cleanup(iface); 288281681Srpaulo hostapd_disable_iface(iface); 289281681Srpaulo} 290281681Srpaulo 291281681Srpaulo 292281681Srpaulostatic long double 293281681Srpauloacs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) 294281681Srpaulo{ 295281681Srpaulo long double factor, busy, total; 296281681Srpaulo 297281681Srpaulo if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) 298281681Srpaulo busy = survey->channel_time_busy; 299281681Srpaulo else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX) 300281681Srpaulo busy = survey->channel_time_rx; 301281681Srpaulo else { 302281681Srpaulo /* This shouldn't really happen as survey data is checked in 303281681Srpaulo * acs_sanity_check() */ 304281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Survey data missing"); 305281681Srpaulo return 0; 306281681Srpaulo } 307281681Srpaulo 308281681Srpaulo total = survey->channel_time; 309281681Srpaulo 310281681Srpaulo if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) { 311281681Srpaulo busy -= survey->channel_time_tx; 312281681Srpaulo total -= survey->channel_time_tx; 313281681Srpaulo } 314281681Srpaulo 315281681Srpaulo /* TODO: figure out the best multiplier for noise floor base */ 316281681Srpaulo factor = pow(10, survey->nf / 5.0L) + 317281681Srpaulo (busy / total) * 318281681Srpaulo pow(2, pow(10, (long double) survey->nf / 10.0L) - 319281681Srpaulo pow(10, (long double) min_nf / 10.0L)); 320281681Srpaulo 321281681Srpaulo return factor; 322281681Srpaulo} 323281681Srpaulo 324281681Srpaulo 325281681Srpaulostatic void 326281681Srpauloacs_survey_chan_interference_factor(struct hostapd_iface *iface, 327281681Srpaulo struct hostapd_channel_data *chan) 328281681Srpaulo{ 329281681Srpaulo struct freq_survey *survey; 330281681Srpaulo unsigned int i = 0; 331281681Srpaulo long double int_factor = 0; 332281681Srpaulo unsigned count = 0; 333281681Srpaulo 334281681Srpaulo if (dl_list_empty(&chan->survey_list)) 335281681Srpaulo return; 336281681Srpaulo 337281681Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 338281681Srpaulo return; 339281681Srpaulo 340281681Srpaulo chan->interference_factor = 0; 341281681Srpaulo 342281681Srpaulo dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) 343281681Srpaulo { 344281681Srpaulo i++; 345281681Srpaulo 346281681Srpaulo if (!acs_survey_is_sufficient(survey)) { 347281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i); 348281681Srpaulo continue; 349281681Srpaulo } 350281681Srpaulo 351281681Srpaulo count++; 352281681Srpaulo int_factor = acs_survey_interference_factor(survey, 353281681Srpaulo iface->lowest_nf); 354281681Srpaulo chan->interference_factor += int_factor; 355281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu", 356281681Srpaulo i, chan->min_nf, int_factor, 357281681Srpaulo survey->nf, (unsigned long) survey->channel_time, 358281681Srpaulo (unsigned long) survey->channel_time_busy, 359281681Srpaulo (unsigned long) survey->channel_time_rx); 360281681Srpaulo } 361281681Srpaulo 362281681Srpaulo if (!count) 363281681Srpaulo return; 364281681Srpaulo chan->interference_factor /= count; 365281681Srpaulo} 366281681Srpaulo 367281681Srpaulo 368281681Srpaulostatic int acs_usable_ht40_chan(struct hostapd_channel_data *chan) 369281681Srpaulo{ 370281681Srpaulo const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 371281681Srpaulo 157, 184, 192 }; 372281681Srpaulo unsigned int i; 373281681Srpaulo 374281681Srpaulo for (i = 0; i < ARRAY_SIZE(allowed); i++) 375281681Srpaulo if (chan->chan == allowed[i]) 376281681Srpaulo return 1; 377281681Srpaulo 378281681Srpaulo return 0; 379281681Srpaulo} 380281681Srpaulo 381281681Srpaulo 382281681Srpaulostatic int acs_usable_vht80_chan(struct hostapd_channel_data *chan) 383281681Srpaulo{ 384281681Srpaulo const int allowed[] = { 36, 52, 100, 116, 132, 149 }; 385281681Srpaulo unsigned int i; 386281681Srpaulo 387281681Srpaulo for (i = 0; i < ARRAY_SIZE(allowed); i++) 388281681Srpaulo if (chan->chan == allowed[i]) 389281681Srpaulo return 1; 390281681Srpaulo 391281681Srpaulo return 0; 392281681Srpaulo} 393281681Srpaulo 394281681Srpaulo 395281681Srpaulostatic int acs_survey_is_sufficient(struct freq_survey *survey) 396281681Srpaulo{ 397281681Srpaulo if (!(survey->filled & SURVEY_HAS_NF)) { 398281681Srpaulo wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor"); 399281681Srpaulo return 0; 400281681Srpaulo } 401281681Srpaulo 402281681Srpaulo if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) { 403281681Srpaulo wpa_printf(MSG_INFO, "ACS: Survey is missing channel time"); 404281681Srpaulo return 0; 405281681Srpaulo } 406281681Srpaulo 407281681Srpaulo if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) && 408281681Srpaulo !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) { 409281681Srpaulo wpa_printf(MSG_INFO, 410281681Srpaulo "ACS: Survey is missing RX and busy time (at least one is required)"); 411281681Srpaulo return 0; 412281681Srpaulo } 413281681Srpaulo 414281681Srpaulo return 1; 415281681Srpaulo} 416281681Srpaulo 417281681Srpaulo 418281681Srpaulostatic int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan) 419281681Srpaulo{ 420281681Srpaulo struct freq_survey *survey; 421281681Srpaulo int ret = -1; 422281681Srpaulo 423281681Srpaulo dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) 424281681Srpaulo { 425281681Srpaulo if (acs_survey_is_sufficient(survey)) { 426281681Srpaulo ret = 1; 427281681Srpaulo break; 428281681Srpaulo } 429281681Srpaulo ret = 0; 430281681Srpaulo } 431281681Srpaulo 432281681Srpaulo if (ret == -1) 433281681Srpaulo ret = 1; /* no survey list entries */ 434281681Srpaulo 435281681Srpaulo if (!ret) { 436281681Srpaulo wpa_printf(MSG_INFO, 437281681Srpaulo "ACS: Channel %d has insufficient survey data", 438281681Srpaulo chan->chan); 439281681Srpaulo } 440281681Srpaulo 441281681Srpaulo return ret; 442281681Srpaulo} 443281681Srpaulo 444281681Srpaulo 445281681Srpaulostatic int acs_surveys_are_sufficient(struct hostapd_iface *iface) 446281681Srpaulo{ 447281681Srpaulo int i; 448281681Srpaulo struct hostapd_channel_data *chan; 449281681Srpaulo int valid = 0; 450281681Srpaulo 451281681Srpaulo for (i = 0; i < iface->current_mode->num_channels; i++) { 452281681Srpaulo chan = &iface->current_mode->channels[i]; 453281681Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 454281681Srpaulo continue; 455281681Srpaulo 456281681Srpaulo if (!acs_survey_list_is_sufficient(chan)) 457281681Srpaulo continue; 458281681Srpaulo 459281681Srpaulo valid++; 460281681Srpaulo } 461281681Srpaulo 462281681Srpaulo /* We need at least survey data for one channel */ 463281681Srpaulo return !!valid; 464281681Srpaulo} 465281681Srpaulo 466281681Srpaulo 467281681Srpaulostatic int acs_usable_chan(struct hostapd_channel_data *chan) 468281681Srpaulo{ 469281681Srpaulo if (dl_list_empty(&chan->survey_list)) 470281681Srpaulo return 0; 471281681Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 472281681Srpaulo return 0; 473281681Srpaulo if (!acs_survey_list_is_sufficient(chan)) 474281681Srpaulo return 0; 475281681Srpaulo return 1; 476281681Srpaulo} 477281681Srpaulo 478281681Srpaulo 479281681Srpaulostatic int is_in_chanlist(struct hostapd_iface *iface, 480281681Srpaulo struct hostapd_channel_data *chan) 481281681Srpaulo{ 482281681Srpaulo int *entry; 483281681Srpaulo 484281681Srpaulo if (!iface->conf->chanlist) 485281681Srpaulo return 1; 486281681Srpaulo 487281681Srpaulo for (entry = iface->conf->chanlist; *entry != -1; entry++) { 488281681Srpaulo if (*entry == chan->chan) 489281681Srpaulo return 1; 490281681Srpaulo } 491281681Srpaulo return 0; 492281681Srpaulo} 493281681Srpaulo 494281681Srpaulo 495281681Srpaulostatic void acs_survey_all_chans_intereference_factor( 496281681Srpaulo struct hostapd_iface *iface) 497281681Srpaulo{ 498281681Srpaulo int i; 499281681Srpaulo struct hostapd_channel_data *chan; 500281681Srpaulo 501281681Srpaulo for (i = 0; i < iface->current_mode->num_channels; i++) { 502281681Srpaulo chan = &iface->current_mode->channels[i]; 503281681Srpaulo 504281681Srpaulo if (!acs_usable_chan(chan)) 505281681Srpaulo continue; 506281681Srpaulo 507281681Srpaulo if (!is_in_chanlist(iface, chan)) 508281681Srpaulo continue; 509281681Srpaulo 510281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", 511281681Srpaulo chan->chan, chan->freq); 512281681Srpaulo 513281681Srpaulo acs_survey_chan_interference_factor(iface, chan); 514281681Srpaulo 515281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg", 516281681Srpaulo chan->interference_factor); 517281681Srpaulo } 518281681Srpaulo} 519281681Srpaulo 520281681Srpaulo 521281681Srpaulostatic struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, 522281681Srpaulo int freq) 523281681Srpaulo{ 524281681Srpaulo struct hostapd_channel_data *chan; 525281681Srpaulo int i; 526281681Srpaulo 527281681Srpaulo for (i = 0; i < iface->current_mode->num_channels; i++) { 528281681Srpaulo chan = &iface->current_mode->channels[i]; 529281681Srpaulo 530281681Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 531281681Srpaulo continue; 532281681Srpaulo 533281681Srpaulo if (chan->freq == freq) 534281681Srpaulo return chan; 535281681Srpaulo } 536281681Srpaulo 537281681Srpaulo return NULL; 538281681Srpaulo} 539281681Srpaulo 540281681Srpaulo 541281681Srpaulostatic int is_24ghz_mode(enum hostapd_hw_mode mode) 542281681Srpaulo{ 543281681Srpaulo return mode == HOSTAPD_MODE_IEEE80211B || 544281681Srpaulo mode == HOSTAPD_MODE_IEEE80211G; 545281681Srpaulo} 546281681Srpaulo 547281681Srpaulo 548281681Srpaulostatic int is_common_24ghz_chan(int chan) 549281681Srpaulo{ 550281681Srpaulo return chan == 1 || chan == 6 || chan == 11; 551281681Srpaulo} 552281681Srpaulo 553281681Srpaulo 554281681Srpaulo#ifndef ACS_ADJ_WEIGHT 555281681Srpaulo#define ACS_ADJ_WEIGHT 0.85 556281681Srpaulo#endif /* ACS_ADJ_WEIGHT */ 557281681Srpaulo 558281681Srpaulo#ifndef ACS_NEXT_ADJ_WEIGHT 559281681Srpaulo#define ACS_NEXT_ADJ_WEIGHT 0.55 560281681Srpaulo#endif /* ACS_NEXT_ADJ_WEIGHT */ 561281681Srpaulo 562281681Srpaulo#ifndef ACS_24GHZ_PREFER_1_6_11 563281681Srpaulo/* 564281681Srpaulo * Select commonly used channels 1, 6, 11 by default even if a neighboring 565281681Srpaulo * channel has a smaller interference factor as long as it is not better by more 566281681Srpaulo * than this multiplier. 567281681Srpaulo */ 568281681Srpaulo#define ACS_24GHZ_PREFER_1_6_11 0.8 569281681Srpaulo#endif /* ACS_24GHZ_PREFER_1_6_11 */ 570281681Srpaulo 571281681Srpaulo/* 572281681Srpaulo * At this point it's assumed chan->interface_factor has been computed. 573281681Srpaulo * This function should be reusable regardless of interference computation 574281681Srpaulo * option (survey, BSS, spectral, ...). chan->interference factor must be 575281681Srpaulo * summable (i.e., must be always greater than zero). 576281681Srpaulo */ 577281681Srpaulostatic struct hostapd_channel_data * 578281681Srpauloacs_find_ideal_chan(struct hostapd_iface *iface) 579281681Srpaulo{ 580281681Srpaulo struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL, 581281681Srpaulo *rand_chan = NULL; 582281681Srpaulo long double factor, ideal_factor = 0; 583281681Srpaulo int i, j; 584281681Srpaulo int n_chans = 1; 585281681Srpaulo unsigned int k; 586281681Srpaulo 587281681Srpaulo /* TODO: HT40- support */ 588281681Srpaulo 589281681Srpaulo if (iface->conf->ieee80211n && 590281681Srpaulo iface->conf->secondary_channel == -1) { 591281681Srpaulo wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); 592281681Srpaulo return NULL; 593281681Srpaulo } 594281681Srpaulo 595281681Srpaulo if (iface->conf->ieee80211n && 596281681Srpaulo iface->conf->secondary_channel) 597281681Srpaulo n_chans = 2; 598281681Srpaulo 599281681Srpaulo if (iface->conf->ieee80211ac && 600281681Srpaulo iface->conf->vht_oper_chwidth == 1) 601281681Srpaulo n_chans = 4; 602281681Srpaulo 603281681Srpaulo /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ 604281681Srpaulo 605281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", 606281681Srpaulo n_chans == 1 ? 20 : 607281681Srpaulo n_chans == 2 ? 40 : 608281681Srpaulo n_chans == 4 ? 80 : 609281681Srpaulo -1); 610281681Srpaulo 611281681Srpaulo for (i = 0; i < iface->current_mode->num_channels; i++) { 612281681Srpaulo double total_weight; 613281681Srpaulo struct acs_bias *bias, tmp_bias; 614281681Srpaulo 615281681Srpaulo chan = &iface->current_mode->channels[i]; 616281681Srpaulo 617281681Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 618281681Srpaulo continue; 619281681Srpaulo 620281681Srpaulo if (!is_in_chanlist(iface, chan)) 621281681Srpaulo continue; 622281681Srpaulo 623281681Srpaulo /* HT40 on 5 GHz has a limited set of primary channels as per 624281681Srpaulo * 11n Annex J */ 625281681Srpaulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && 626281681Srpaulo iface->conf->ieee80211n && 627281681Srpaulo iface->conf->secondary_channel && 628281681Srpaulo !acs_usable_ht40_chan(chan)) { 629281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40", 630281681Srpaulo chan->chan); 631281681Srpaulo continue; 632281681Srpaulo } 633281681Srpaulo 634281681Srpaulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && 635281681Srpaulo iface->conf->ieee80211ac && 636281681Srpaulo iface->conf->vht_oper_chwidth == 1 && 637281681Srpaulo !acs_usable_vht80_chan(chan)) { 638281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80", 639281681Srpaulo chan->chan); 640281681Srpaulo continue; 641281681Srpaulo } 642281681Srpaulo 643281681Srpaulo factor = 0; 644281681Srpaulo if (acs_usable_chan(chan)) 645281681Srpaulo factor = chan->interference_factor; 646281681Srpaulo total_weight = 1; 647281681Srpaulo 648281681Srpaulo for (j = 1; j < n_chans; j++) { 649281681Srpaulo adj_chan = acs_find_chan(iface, chan->freq + (j * 20)); 650281681Srpaulo if (!adj_chan) 651281681Srpaulo break; 652281681Srpaulo 653281681Srpaulo if (acs_usable_chan(adj_chan)) { 654281681Srpaulo factor += adj_chan->interference_factor; 655281681Srpaulo total_weight += 1; 656281681Srpaulo } 657281681Srpaulo } 658281681Srpaulo 659281681Srpaulo if (j != n_chans) { 660281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth", 661281681Srpaulo chan->chan); 662281681Srpaulo continue; 663281681Srpaulo } 664281681Srpaulo 665281681Srpaulo /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent 666281681Srpaulo * channel interference factor. */ 667281681Srpaulo if (is_24ghz_mode(iface->current_mode->mode)) { 668281681Srpaulo for (j = 0; j < n_chans; j++) { 669281681Srpaulo adj_chan = acs_find_chan(iface, chan->freq + 670281681Srpaulo (j * 20) - 5); 671281681Srpaulo if (adj_chan && acs_usable_chan(adj_chan)) { 672281681Srpaulo factor += ACS_ADJ_WEIGHT * 673281681Srpaulo adj_chan->interference_factor; 674281681Srpaulo total_weight += ACS_ADJ_WEIGHT; 675281681Srpaulo } 676281681Srpaulo 677281681Srpaulo adj_chan = acs_find_chan(iface, chan->freq + 678281681Srpaulo (j * 20) - 10); 679281681Srpaulo if (adj_chan && acs_usable_chan(adj_chan)) { 680281681Srpaulo factor += ACS_NEXT_ADJ_WEIGHT * 681281681Srpaulo adj_chan->interference_factor; 682281681Srpaulo total_weight += ACS_NEXT_ADJ_WEIGHT; 683281681Srpaulo } 684281681Srpaulo 685281681Srpaulo adj_chan = acs_find_chan(iface, chan->freq + 686281681Srpaulo (j * 20) + 5); 687281681Srpaulo if (adj_chan && acs_usable_chan(adj_chan)) { 688281681Srpaulo factor += ACS_ADJ_WEIGHT * 689281681Srpaulo adj_chan->interference_factor; 690281681Srpaulo total_weight += ACS_ADJ_WEIGHT; 691281681Srpaulo } 692281681Srpaulo 693281681Srpaulo adj_chan = acs_find_chan(iface, chan->freq + 694281681Srpaulo (j * 20) + 10); 695281681Srpaulo if (adj_chan && acs_usable_chan(adj_chan)) { 696281681Srpaulo factor += ACS_NEXT_ADJ_WEIGHT * 697281681Srpaulo adj_chan->interference_factor; 698281681Srpaulo total_weight += ACS_NEXT_ADJ_WEIGHT; 699281681Srpaulo } 700281681Srpaulo } 701281681Srpaulo } 702281681Srpaulo 703281681Srpaulo factor /= total_weight; 704281681Srpaulo 705281681Srpaulo bias = NULL; 706281681Srpaulo if (iface->conf->acs_chan_bias) { 707281681Srpaulo for (k = 0; k < iface->conf->num_acs_chan_bias; k++) { 708281681Srpaulo bias = &iface->conf->acs_chan_bias[k]; 709281681Srpaulo if (bias->channel == chan->chan) 710281681Srpaulo break; 711281681Srpaulo bias = NULL; 712281681Srpaulo } 713281681Srpaulo } else if (is_24ghz_mode(iface->current_mode->mode) && 714281681Srpaulo is_common_24ghz_chan(chan->chan)) { 715281681Srpaulo tmp_bias.channel = chan->chan; 716281681Srpaulo tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11; 717281681Srpaulo bias = &tmp_bias; 718281681Srpaulo } 719281681Srpaulo 720281681Srpaulo if (bias) { 721281681Srpaulo factor *= bias->bias; 722281681Srpaulo wpa_printf(MSG_DEBUG, 723281681Srpaulo "ACS: * channel %d: total interference = %Lg (%f bias)", 724281681Srpaulo chan->chan, factor, bias->bias); 725281681Srpaulo } else { 726281681Srpaulo wpa_printf(MSG_DEBUG, 727281681Srpaulo "ACS: * channel %d: total interference = %Lg", 728281681Srpaulo chan->chan, factor); 729281681Srpaulo } 730281681Srpaulo 731281681Srpaulo if (acs_usable_chan(chan) && 732281681Srpaulo (!ideal_chan || factor < ideal_factor)) { 733281681Srpaulo ideal_factor = factor; 734281681Srpaulo ideal_chan = chan; 735281681Srpaulo } 736281681Srpaulo 737281681Srpaulo /* This channel would at least be usable */ 738281681Srpaulo if (!rand_chan) 739281681Srpaulo rand_chan = chan; 740281681Srpaulo } 741281681Srpaulo 742281681Srpaulo if (ideal_chan) { 743281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg", 744281681Srpaulo ideal_chan->chan, ideal_chan->freq, ideal_factor); 745281681Srpaulo return ideal_chan; 746281681Srpaulo } 747281681Srpaulo 748281681Srpaulo return rand_chan; 749281681Srpaulo} 750281681Srpaulo 751281681Srpaulo 752281681Srpaulostatic void acs_adjust_vht_center_freq(struct hostapd_iface *iface) 753281681Srpaulo{ 754281681Srpaulo int offset; 755281681Srpaulo 756281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency"); 757281681Srpaulo 758281681Srpaulo switch (iface->conf->vht_oper_chwidth) { 759281681Srpaulo case VHT_CHANWIDTH_USE_HT: 760281681Srpaulo offset = 2 * iface->conf->secondary_channel; 761281681Srpaulo break; 762281681Srpaulo case VHT_CHANWIDTH_80MHZ: 763281681Srpaulo offset = 6; 764281681Srpaulo break; 765281681Srpaulo default: 766281681Srpaulo /* TODO: How can this be calculated? Adjust 767281681Srpaulo * acs_find_ideal_chan() */ 768281681Srpaulo wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); 769281681Srpaulo return; 770281681Srpaulo } 771281681Srpaulo 772281681Srpaulo iface->conf->vht_oper_centr_freq_seg0_idx = 773281681Srpaulo iface->conf->channel + offset; 774281681Srpaulo} 775281681Srpaulo 776281681Srpaulo 777281681Srpaulostatic int acs_study_survey_based(struct hostapd_iface *iface) 778281681Srpaulo{ 779281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS"); 780281681Srpaulo 781281681Srpaulo if (!iface->chans_surveyed) { 782281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data"); 783281681Srpaulo return -1; 784281681Srpaulo } 785281681Srpaulo 786281681Srpaulo if (!acs_surveys_are_sufficient(iface)) { 787281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data"); 788281681Srpaulo return -1; 789281681Srpaulo } 790281681Srpaulo 791281681Srpaulo acs_survey_all_chans_intereference_factor(iface); 792281681Srpaulo return 0; 793281681Srpaulo} 794281681Srpaulo 795281681Srpaulo 796281681Srpaulostatic int acs_study_options(struct hostapd_iface *iface) 797281681Srpaulo{ 798281681Srpaulo int err; 799281681Srpaulo 800281681Srpaulo err = acs_study_survey_based(iface); 801281681Srpaulo if (err == 0) 802281681Srpaulo return 0; 803281681Srpaulo 804281681Srpaulo /* TODO: If no surveys are available/sufficient this is a good 805281681Srpaulo * place to fallback to BSS-based ACS */ 806281681Srpaulo 807281681Srpaulo return -1; 808281681Srpaulo} 809281681Srpaulo 810281681Srpaulo 811281681Srpaulostatic void acs_study(struct hostapd_iface *iface) 812281681Srpaulo{ 813281681Srpaulo struct hostapd_channel_data *ideal_chan; 814281681Srpaulo int err; 815281681Srpaulo 816281681Srpaulo err = acs_study_options(iface); 817281681Srpaulo if (err < 0) { 818281681Srpaulo wpa_printf(MSG_ERROR, "ACS: All study options have failed"); 819281681Srpaulo goto fail; 820281681Srpaulo } 821281681Srpaulo 822281681Srpaulo ideal_chan = acs_find_ideal_chan(iface); 823281681Srpaulo if (!ideal_chan) { 824281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel"); 825281681Srpaulo err = -1; 826281681Srpaulo goto fail; 827281681Srpaulo } 828281681Srpaulo 829281681Srpaulo iface->conf->channel = ideal_chan->chan; 830281681Srpaulo 831281681Srpaulo if (iface->conf->ieee80211ac) 832281681Srpaulo acs_adjust_vht_center_freq(iface); 833281681Srpaulo 834281681Srpaulo err = 0; 835281681Srpaulofail: 836281681Srpaulo /* 837281681Srpaulo * hostapd_setup_interface_complete() will return -1 on failure, 838281681Srpaulo * 0 on success and 0 is HOSTAPD_CHAN_VALID :) 839281681Srpaulo */ 840281681Srpaulo if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) { 841281681Srpaulo acs_cleanup(iface); 842281681Srpaulo return; 843281681Srpaulo } 844281681Srpaulo 845281681Srpaulo /* This can possibly happen if channel parameters (secondary 846281681Srpaulo * channel, center frequencies) are misconfigured */ 847281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file."); 848281681Srpaulo acs_fail(iface); 849281681Srpaulo} 850281681Srpaulo 851281681Srpaulo 852281681Srpaulostatic void acs_scan_complete(struct hostapd_iface *iface) 853281681Srpaulo{ 854281681Srpaulo int err; 855281681Srpaulo 856281681Srpaulo iface->scan_cb = NULL; 857281681Srpaulo 858281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)", 859281681Srpaulo iface->conf->acs_num_scans); 860281681Srpaulo 861281681Srpaulo err = hostapd_drv_get_survey(iface->bss[0], 0); 862281681Srpaulo if (err) { 863281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Failed to get survey data"); 864281681Srpaulo goto fail; 865281681Srpaulo } 866281681Srpaulo 867281681Srpaulo if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) { 868281681Srpaulo err = acs_request_scan(iface); 869281681Srpaulo if (err) { 870281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Failed to request scan"); 871281681Srpaulo goto fail; 872281681Srpaulo } 873281681Srpaulo 874281681Srpaulo return; 875281681Srpaulo } 876281681Srpaulo 877281681Srpaulo acs_study(iface); 878281681Srpaulo return; 879281681Srpaulofail: 880281681Srpaulo hostapd_acs_completed(iface, 1); 881281681Srpaulo acs_fail(iface); 882281681Srpaulo} 883281681Srpaulo 884281681Srpaulo 885281681Srpaulostatic int acs_request_scan(struct hostapd_iface *iface) 886281681Srpaulo{ 887281681Srpaulo struct wpa_driver_scan_params params; 888281681Srpaulo struct hostapd_channel_data *chan; 889281681Srpaulo int i, *freq; 890281681Srpaulo 891281681Srpaulo os_memset(¶ms, 0, sizeof(params)); 892281681Srpaulo params.freqs = os_calloc(iface->current_mode->num_channels + 1, 893281681Srpaulo sizeof(params.freqs[0])); 894281681Srpaulo if (params.freqs == NULL) 895281681Srpaulo return -1; 896281681Srpaulo 897281681Srpaulo freq = params.freqs; 898281681Srpaulo for (i = 0; i < iface->current_mode->num_channels; i++) { 899281681Srpaulo chan = &iface->current_mode->channels[i]; 900281681Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 901281681Srpaulo continue; 902281681Srpaulo 903281681Srpaulo *freq++ = chan->freq; 904281681Srpaulo } 905281681Srpaulo *freq = 0; 906281681Srpaulo 907281681Srpaulo iface->scan_cb = acs_scan_complete; 908281681Srpaulo 909281681Srpaulo wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", 910281681Srpaulo iface->acs_num_completed_scans + 1, 911281681Srpaulo iface->conf->acs_num_scans); 912281681Srpaulo 913281681Srpaulo if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { 914281681Srpaulo wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan"); 915281681Srpaulo acs_cleanup(iface); 916281681Srpaulo os_free(params.freqs); 917281681Srpaulo return -1; 918281681Srpaulo } 919281681Srpaulo 920281681Srpaulo os_free(params.freqs); 921281681Srpaulo return 0; 922281681Srpaulo} 923281681Srpaulo 924281681Srpaulo 925281681Srpauloenum hostapd_chan_status acs_init(struct hostapd_iface *iface) 926281681Srpaulo{ 927281681Srpaulo int err; 928281681Srpaulo 929281681Srpaulo wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); 930281681Srpaulo 931281681Srpaulo if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { 932281681Srpaulo wpa_printf(MSG_INFO, "ACS: Offloading to driver"); 933281681Srpaulo err = hostapd_drv_do_acs(iface->bss[0]); 934281681Srpaulo if (err) 935281681Srpaulo return HOSTAPD_CHAN_INVALID; 936281681Srpaulo return HOSTAPD_CHAN_ACS; 937281681Srpaulo } 938281681Srpaulo 939281681Srpaulo acs_cleanup(iface); 940281681Srpaulo 941281681Srpaulo err = acs_request_scan(iface); 942281681Srpaulo if (err < 0) 943281681Srpaulo return HOSTAPD_CHAN_INVALID; 944281681Srpaulo 945281681Srpaulo hostapd_set_state(iface, HAPD_IFACE_ACS); 946281681Srpaulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED); 947281681Srpaulo 948281681Srpaulo return HOSTAPD_CHAN_ACS; 949281681Srpaulo} 950