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