blob: 8ee2e04a2c35c3ffc32649158bd996d7c41caba1 [file] [log] [blame]
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001/*
2 * ACS - Automatic Channel Selection module
3 * Copyright (c) 2011, Atheros Communications
4 * Copyright (c) 2013, Qualcomm Atheros, Inc.
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "utils/includes.h"
11#include <math.h>
12
13#include "utils/common.h"
14#include "utils/list.h"
15#include "common/ieee802_11_defs.h"
Hai Shalom74f70d42019-02-11 14:42:39 -080016#include "common/hw_features_common.h"
Dmitry Shmidtcce06662013-11-04 18:44:24 -080017#include "common/wpa_ctrl.h"
Dmitry Shmidt391c59f2013-09-03 12:16:28 -070018#include "drivers/driver.h"
19#include "hostapd.h"
20#include "ap_drv_ops.h"
21#include "ap_config.h"
22#include "hw_features.h"
23#include "acs.h"
24
25/*
26 * Automatic Channel Selection
27 * ===========================
28 *
29 * More info at
30 * ------------
31 * http://wireless.kernel.org/en/users/Documentation/acs
32 *
33 * How to use
34 * ----------
35 * - make sure you have CONFIG_ACS=y in hostapd's .config
36 * - use channel=0 or channel=acs to enable ACS
37 *
38 * How does it work
39 * ----------------
40 * 1. passive scans are used to collect survey data
41 * (it is assumed that scan trigger collection of survey data in driver)
42 * 2. interference factor is calculated for each channel
43 * 3. ideal channel is picked depending on channel width by using adjacent
44 * channel interference factors
45 *
46 * Known limitations
47 * -----------------
48 * - Current implementation depends heavily on the amount of time willing to
49 * spend gathering survey data during hostapd startup. Short traffic bursts
50 * may be missed and a suboptimal channel may be picked.
51 * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
52 *
53 * Todo / Ideas
54 * ------------
55 * - implement other interference computation methods
56 * - BSS/RSSI based
57 * - spectral scan based
58 * (should be possibly to hook this up with current ACS scans)
59 * - add wpa_supplicant support (for P2P)
60 * - collect a histogram of interference over time allowing more educated
61 * guess about an ideal channel (perhaps CSA could be used to migrate AP to a
62 * new "better" channel while running)
63 * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
64 * when choosing the ideal channel
65 *
66 * Survey interference factor implementation details
67 * -------------------------------------------------
68 * Generic interference_factor in struct hostapd_channel_data is used.
69 *
70 * The survey interference factor is defined as the ratio of the
71 * observed busy time over the time we spent on the channel,
72 * this value is then amplified by the observed noise floor on
73 * the channel in comparison to the lowest noise floor observed
74 * on the entire band.
75 *
76 * This corresponds to:
77 * ---
78 * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
79 * ---
80 *
81 * The coefficient of 2 reflects the way power in "far-field"
82 * radiation decreases as the square of distance from the antenna [1].
83 * What this does is it decreases the observed busy time ratio if the
84 * noise observed was low but increases it if the noise was high,
85 * proportionally to the way "far field" radiation changes over
86 * distance.
87 *
88 * If channel busy time is not available the fallback is to use channel RX time.
89 *
90 * Since noise floor is in dBm it is necessary to convert it into Watts so that
91 * combined channel interference (e.g., HT40, which uses two channels) can be
92 * calculated easily.
93 * ---
94 * (busy time - tx time) / (active time - tx time) *
95 * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
96 * ---
97 *
98 * However to account for cases where busy/rx time is 0 (channel load is then
99 * 0%) channel noise floor signal power is combined into the equation so a
100 * channel with lower noise floor is preferred. The equation becomes:
101 * ---
102 * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
103 * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
104 * ---
105 *
106 * All this "interference factor" is purely subjective and only time
107 * will tell how usable this is. By using the minimum noise floor we
108 * remove any possible issues due to card calibration. The computation
109 * of the interference factor then is dependent on what the card itself
110 * picks up as the minimum noise, not an actual real possible card
111 * noise value.
112 *
113 * Total interference computation details
114 * --------------------------------------
115 * The above channel interference factor is calculated with no respect to
116 * target operational bandwidth.
117 *
118 * To find an ideal channel the above data is combined by taking into account
119 * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
120 * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
121 * on 5 GHz.
122 *
123 * Each valid and possible channel spec (i.e., channel + width) is taken and its
124 * interference factor is computed by summing up interferences of each channel
125 * it overlaps. The one with least total interference is picked up.
126 *
127 * Note: This implies base channel interference factor must be non-negative
128 * allowing easy summing up.
129 *
130 * Example ACS analysis printout
131 * -----------------------------
132 *
133 * ACS: Trying survey-based ACS
134 * ACS: Survey analysis for channel 1 (2412 MHz)
135 * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
136 * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
137 * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
138 * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
139 * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
140 * ACS: * interference factor average: 0.0557166
141 * ACS: Survey analysis for channel 2 (2417 MHz)
142 * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
143 * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
144 * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
145 * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
146 * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
147 * ACS: * interference factor average: 0.050832
148 * ACS: Survey analysis for channel 3 (2422 MHz)
149 * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
150 * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
151 * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
152 * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
153 * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
154 * ACS: * interference factor average: 0.0148838
155 * ACS: Survey analysis for channel 4 (2427 MHz)
156 * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
157 * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
158 * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
159 * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
160 * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
161 * ACS: * interference factor average: 0.0160801
162 * ACS: Survey analysis for channel 5 (2432 MHz)
163 * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
164 * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
165 * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
166 * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
167 * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
168 * ACS: * interference factor average: 0.232244
169 * ACS: Survey analysis for channel 6 (2437 MHz)
170 * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
171 * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
172 * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
173 * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
174 * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
175 * ACS: * interference factor average: 0.232298
176 * ACS: Survey analysis for channel 7 (2442 MHz)
177 * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
178 * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
179 * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
180 * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
181 * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
182 * ACS: * interference factor average: 0.195031
183 * ACS: Survey analysis for channel 8 (2447 MHz)
184 * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
185 * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
186 * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
187 * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
188 * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
189 * ACS: * interference factor average: 0.0865885
190 * ACS: Survey analysis for channel 9 (2452 MHz)
191 * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
192 * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
193 * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
194 * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
195 * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
196 * ACS: * interference factor average: 0.00993022
197 * ACS: Survey analysis for channel 10 (2457 MHz)
198 * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
199 * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
200 * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
201 * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
202 * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
203 * ACS: * interference factor average: 0.0136033
204 * ACS: Survey analysis for channel 11 (2462 MHz)
205 * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
206 * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
207 * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
208 * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
209 * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
210 * ACS: * interference factor average: 0.0271605
211 * ACS: Survey analysis for channel 12 (2467 MHz)
212 * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
213 * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
214 * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
215 * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
216 * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
217 * ACS: * interference factor average: 0.0148992
218 * ACS: Survey analysis for channel 13 (2472 MHz)
219 * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
220 * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
221 * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
222 * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
223 * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
224 * ACS: * interference factor average: 0.0260179
225 * ACS: Survey analysis for selected bandwidth 20MHz
226 * ACS: * channel 1: total interference = 0.121432
227 * ACS: * channel 2: total interference = 0.137512
228 * ACS: * channel 3: total interference = 0.369757
229 * ACS: * channel 4: total interference = 0.546338
230 * ACS: * channel 5: total interference = 0.690538
231 * ACS: * channel 6: total interference = 0.762242
232 * ACS: * channel 7: total interference = 0.756092
233 * ACS: * channel 8: total interference = 0.537451
234 * ACS: * channel 9: total interference = 0.332313
235 * ACS: * channel 10: total interference = 0.152182
236 * ACS: * channel 11: total interference = 0.0916111
237 * ACS: * channel 12: total interference = 0.0816809
238 * ACS: * channel 13: total interference = 0.0680776
239 * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
240 *
241 * [1] http://en.wikipedia.org/wiki/Near_and_far_field
242 */
243
244
245static int acs_request_scan(struct hostapd_iface *iface);
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800246static int acs_survey_is_sufficient(struct freq_survey *survey);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700247
248
249static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
250{
251 struct freq_survey *survey, *tmp;
252
253 if (dl_list_empty(&chan->survey_list))
254 return;
255
256 dl_list_for_each_safe(survey, tmp, &chan->survey_list,
257 struct freq_survey, list) {
258 dl_list_del(&survey->list);
259 os_free(survey);
260 }
261}
262
263
Hai Shalomfdcde762020-04-02 11:19:20 -0700264static void acs_cleanup_mode(struct hostapd_hw_modes *mode)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700265{
266 int i;
267 struct hostapd_channel_data *chan;
268
Hai Shalomfdcde762020-04-02 11:19:20 -0700269 for (i = 0; i < mode->num_channels; i++) {
270 chan = &mode->channels[i];
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700271
272 if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
273 acs_clean_chan_surveys(chan);
274
275 dl_list_init(&chan->survey_list);
276 chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
277 chan->min_nf = 0;
278 }
Hai Shalomfdcde762020-04-02 11:19:20 -0700279}
280
281
282void acs_cleanup(struct hostapd_iface *iface)
283{
284 int i;
285
286 for (i = 0; i < iface->num_hw_features; i++)
287 acs_cleanup_mode(&iface->hw_features[i]);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700288
289 iface->chans_surveyed = 0;
290 iface->acs_num_completed_scans = 0;
291}
292
293
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800294static void acs_fail(struct hostapd_iface *iface)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700295{
296 wpa_printf(MSG_ERROR, "ACS: Failed to start");
297 acs_cleanup(iface);
Dmitry Shmidt2ac5f602014-03-07 10:08:21 -0800298 hostapd_disable_iface(iface);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700299}
300
301
302static long double
303acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
304{
305 long double factor, busy, total;
306
307 if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
308 busy = survey->channel_time_busy;
309 else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
310 busy = survey->channel_time_rx;
311 else {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700312 wpa_printf(MSG_ERROR, "ACS: Survey data missing");
313 return 0;
314 }
315
316 total = survey->channel_time;
317
318 if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
319 busy -= survey->channel_time_tx;
320 total -= survey->channel_time_tx;
321 }
322
323 /* TODO: figure out the best multiplier for noise floor base */
324 factor = pow(10, survey->nf / 5.0L) +
Roshan Pius3a1667e2018-07-03 15:17:14 -0700325 (total ? (busy / total) : 0) *
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700326 pow(2, pow(10, (long double) survey->nf / 10.0L) -
327 pow(10, (long double) min_nf / 10.0L));
328
329 return factor;
330}
331
332
333static void
334acs_survey_chan_interference_factor(struct hostapd_iface *iface,
335 struct hostapd_channel_data *chan)
336{
337 struct freq_survey *survey;
338 unsigned int i = 0;
339 long double int_factor = 0;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800340 unsigned count = 0;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700341
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700342 if (dl_list_empty(&chan->survey_list) ||
343 (chan->flag & HOSTAPD_CHAN_DISABLED))
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700344 return;
345
346 chan->interference_factor = 0;
347
348 dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
349 {
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800350 i++;
351
352 if (!acs_survey_is_sufficient(survey)) {
353 wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
354 continue;
355 }
356
357 count++;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700358 int_factor = acs_survey_interference_factor(survey,
359 iface->lowest_nf);
360 chan->interference_factor += int_factor;
361 wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800362 i, chan->min_nf, int_factor,
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700363 survey->nf, (unsigned long) survey->channel_time,
364 (unsigned long) survey->channel_time_busy,
365 (unsigned long) survey->channel_time_rx);
366 }
367
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700368 if (count)
369 chan->interference_factor /= count;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700370}
371
372
Hai Shaloma20dcd72022-02-04 13:43:00 -0800373static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700374{
Hai Shaloma20dcd72022-02-04 13:43:00 -0800375 const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
376 5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
377 6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
378 6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
379 6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
380 7035, 7075 };
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700381 unsigned int i;
382
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700383 for (i = 0; i < ARRAY_SIZE(allowed); i++)
Hai Shaloma20dcd72022-02-04 13:43:00 -0800384 if (chan->freq == allowed[i])
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700385 return 1;
386
387 return 0;
388}
389
390
Hai Shaloma20dcd72022-02-04 13:43:00 -0800391static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800392{
Hai Shaloma20dcd72022-02-04 13:43:00 -0800393 const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
394 6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
395 6755, 6835, 6915, 6995 };
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800396 unsigned int i;
397
398 for (i = 0; i < ARRAY_SIZE(allowed); i++)
Hai Shaloma20dcd72022-02-04 13:43:00 -0800399 if (chan->freq == allowed[i])
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800400 return 1;
401
402 return 0;
403}
404
405
Hai Shaloma20dcd72022-02-04 13:43:00 -0800406static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
Hai Shalom74f70d42019-02-11 14:42:39 -0800407{
Hai Shaloma20dcd72022-02-04 13:43:00 -0800408 const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
409 6915 };
Hai Shalom74f70d42019-02-11 14:42:39 -0800410 unsigned int i;
411
412 for (i = 0; i < ARRAY_SIZE(allowed); i++)
Hai Shaloma20dcd72022-02-04 13:43:00 -0800413 if (chan->freq == allowed[i])
Hai Shalom74f70d42019-02-11 14:42:39 -0800414 return 1;
415
416 return 0;
417}
418
419
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700420static int acs_survey_is_sufficient(struct freq_survey *survey)
421{
422 if (!(survey->filled & SURVEY_HAS_NF)) {
Sunil Ravi89eba102022-09-13 21:04:37 -0700423 wpa_printf(MSG_INFO,
424 "ACS: Survey for freq %d is missing noise floor",
425 survey->freq);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700426 return 0;
427 }
428
429 if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
Sunil Ravi89eba102022-09-13 21:04:37 -0700430 wpa_printf(MSG_INFO,
431 "ACS: Survey for freq %d is missing channel time",
432 survey->freq);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700433 return 0;
434 }
435
436 if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
437 !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800438 wpa_printf(MSG_INFO,
Sunil Ravi89eba102022-09-13 21:04:37 -0700439 "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
440 survey->freq);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700441 return 0;
442 }
443
444 return 1;
445}
446
447
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700448static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
449{
450 struct freq_survey *survey;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800451 int ret = -1;
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700452
453 dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
454 {
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800455 if (acs_survey_is_sufficient(survey)) {
456 ret = 1;
457 break;
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700458 }
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800459 ret = 0;
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700460 }
461
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800462 if (ret == -1)
463 ret = 1; /* no survey list entries */
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700464
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800465 if (!ret) {
466 wpa_printf(MSG_INFO,
467 "ACS: Channel %d has insufficient survey data",
468 chan->chan);
469 }
470
471 return ret;
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700472}
473
474
Hai Shalomfdcde762020-04-02 11:19:20 -0700475static int acs_surveys_are_sufficient_mode(struct hostapd_hw_modes *mode)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700476{
477 int i;
478 struct hostapd_channel_data *chan;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700479
Hai Shalomfdcde762020-04-02 11:19:20 -0700480 for (i = 0; i < mode->num_channels; i++) {
481 chan = &mode->channels[i];
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700482 if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
483 acs_survey_list_is_sufficient(chan))
Hai Shalomfdcde762020-04-02 11:19:20 -0700484 return 1;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700485 }
486
Hai Shalomfdcde762020-04-02 11:19:20 -0700487 return 0;
488}
489
490
491static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
492{
493 int i;
494 struct hostapd_hw_modes *mode;
495
496 for (i = 0; i < iface->num_hw_features; i++) {
497 mode = &iface->hw_features[i];
498 if (!hostapd_hw_skip_mode(iface, mode) &&
499 acs_surveys_are_sufficient_mode(mode))
500 return 1;
501 }
502
503 return 0;
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700504}
505
506
507static int acs_usable_chan(struct hostapd_channel_data *chan)
508{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700509 return !dl_list_empty(&chan->survey_list) &&
510 !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
511 acs_survey_list_is_sufficient(chan);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700512}
513
514
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800515static int is_in_chanlist(struct hostapd_iface *iface,
516 struct hostapd_channel_data *chan)
517{
Dmitry Shmidtdda10c22015-03-24 16:05:01 -0700518 if (!iface->conf->acs_ch_list.num)
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800519 return 1;
520
Dmitry Shmidtdda10c22015-03-24 16:05:01 -0700521 return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800522}
523
524
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700525static int is_in_freqlist(struct hostapd_iface *iface,
526 struct hostapd_channel_data *chan)
527{
528 if (!iface->conf->acs_freq_list.num)
529 return 1;
530
531 return freq_range_list_includes(&iface->conf->acs_freq_list,
532 chan->freq);
533}
534
535
Hai Shalomfdcde762020-04-02 11:19:20 -0700536static void acs_survey_mode_interference_factor(
537 struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700538{
539 int i;
540 struct hostapd_channel_data *chan;
541
Hai Shalomfdcde762020-04-02 11:19:20 -0700542 for (i = 0; i < mode->num_channels; i++) {
543 chan = &mode->channels[i];
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700544
545 if (!acs_usable_chan(chan))
546 continue;
547
Sunil Ravia04bd252022-05-02 22:54:18 -0700548 if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
549 iface->conf->acs_exclude_dfs)
550 continue;
551
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800552 if (!is_in_chanlist(iface, chan))
553 continue;
554
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700555 if (!is_in_freqlist(iface, chan))
556 continue;
557
Hai Shaloma20dcd72022-02-04 13:43:00 -0800558 if (chan->max_tx_power < iface->conf->min_tx_power)
559 continue;
560
Sunil Ravi89eba102022-09-13 21:04:37 -0700561 if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
562 iface->conf->country[2] == 0x4f)
563 continue;
564
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700565 wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
566 chan->chan, chan->freq);
567
568 acs_survey_chan_interference_factor(iface, chan);
569
570 wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
571 chan->interference_factor);
572 }
573}
574
575
Hai Shalomfdcde762020-04-02 11:19:20 -0700576static void acs_survey_all_chans_interference_factor(
577 struct hostapd_iface *iface)
578{
579 int i;
580 struct hostapd_hw_modes *mode;
581
582 for (i = 0; i < iface->num_hw_features; i++) {
583 mode = &iface->hw_features[i];
584 if (!hostapd_hw_skip_mode(iface, mode))
585 acs_survey_mode_interference_factor(iface, mode);
586 }
587}
588
589
590static struct hostapd_channel_data *
591acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700592{
593 struct hostapd_channel_data *chan;
594 int i;
595
Hai Shalomfdcde762020-04-02 11:19:20 -0700596 for (i = 0; i < mode->num_channels; i++) {
597 chan = &mode->channels[i];
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700598
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700599 if (chan->flag & HOSTAPD_CHAN_DISABLED)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700600 continue;
601
602 if (chan->freq == freq)
603 return chan;
604 }
605
606 return NULL;
607}
608
609
Hai Shalomfdcde762020-04-02 11:19:20 -0700610static struct hostapd_channel_data *
611acs_find_chan(struct hostapd_iface *iface, int freq)
612{
613 int i;
614 struct hostapd_hw_modes *mode;
615 struct hostapd_channel_data *chan;
616
617 for (i = 0; i < iface->num_hw_features; i++) {
618 mode = &iface->hw_features[i];
619 if (!hostapd_hw_skip_mode(iface, mode)) {
620 chan = acs_find_chan_mode(mode, freq);
621 if (chan)
622 return chan;
623 }
624 }
625
626 return NULL;
627}
628
629
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800630static int is_24ghz_mode(enum hostapd_hw_mode mode)
631{
632 return mode == HOSTAPD_MODE_IEEE80211B ||
633 mode == HOSTAPD_MODE_IEEE80211G;
634}
635
636
637static int is_common_24ghz_chan(int chan)
638{
639 return chan == 1 || chan == 6 || chan == 11;
640}
641
642
643#ifndef ACS_ADJ_WEIGHT
644#define ACS_ADJ_WEIGHT 0.85
645#endif /* ACS_ADJ_WEIGHT */
646
647#ifndef ACS_NEXT_ADJ_WEIGHT
648#define ACS_NEXT_ADJ_WEIGHT 0.55
649#endif /* ACS_NEXT_ADJ_WEIGHT */
650
651#ifndef ACS_24GHZ_PREFER_1_6_11
652/*
653 * Select commonly used channels 1, 6, 11 by default even if a neighboring
654 * channel has a smaller interference factor as long as it is not better by more
655 * than this multiplier.
656 */
657#define ACS_24GHZ_PREFER_1_6_11 0.8
658#endif /* ACS_24GHZ_PREFER_1_6_11 */
659
Hai Shalomfdcde762020-04-02 11:19:20 -0700660static void
661acs_find_ideal_chan_mode(struct hostapd_iface *iface,
662 struct hostapd_hw_modes *mode,
663 int n_chans, u32 bw,
664 struct hostapd_channel_data **rand_chan,
665 struct hostapd_channel_data **ideal_chan,
666 long double *ideal_factor)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700667{
Hai Shalomfdcde762020-04-02 11:19:20 -0700668 struct hostapd_channel_data *chan, *adj_chan = NULL;
669 long double factor;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700670 int i, j;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800671 unsigned int k;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700672
Hai Shalomfdcde762020-04-02 11:19:20 -0700673 for (i = 0; i < mode->num_channels; i++) {
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800674 double total_weight;
675 struct acs_bias *bias, tmp_bias;
676
Hai Shalomfdcde762020-04-02 11:19:20 -0700677 chan = &mode->channels[i];
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700678
Hai Shalom74f70d42019-02-11 14:42:39 -0800679 /* Since in the current ACS implementation the first channel is
680 * always a primary channel, skip channels not available as
681 * primary until more sophisticated channel selection is
682 * implemented. */
683 if (!chan_pri_allowed(chan))
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700684 continue;
685
Sunil Ravia04bd252022-05-02 22:54:18 -0700686 if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
687 iface->conf->acs_exclude_dfs)
688 continue;
689
Dmitry Shmidt2f74e362015-01-21 13:19:05 -0800690 if (!is_in_chanlist(iface, chan))
691 continue;
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700692
Hai Shalom4fbc08f2020-05-18 12:37:00 -0700693 if (!is_in_freqlist(iface, chan))
694 continue;
695
Hai Shaloma20dcd72022-02-04 13:43:00 -0800696 if (chan->max_tx_power < iface->conf->min_tx_power)
697 continue;
698
Sunil Ravi89eba102022-09-13 21:04:37 -0700699 if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
700 iface->conf->country[2] == 0x4f)
701 continue;
702
Hai Shalom74f70d42019-02-11 14:42:39 -0800703 if (!chan_bw_allowed(chan, bw, 1, 1)) {
704 wpa_printf(MSG_DEBUG,
705 "ACS: Channel %d: BW %u is not supported",
706 chan->chan, bw);
707 continue;
708 }
709
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700710 /* HT40 on 5 GHz has a limited set of primary channels as per
711 * 11n Annex J */
Hai Shalomfdcde762020-04-02 11:19:20 -0700712 if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
Hai Shaloma20dcd72022-02-04 13:43:00 -0800713 ((iface->conf->ieee80211n &&
714 iface->conf->secondary_channel) ||
715 is_6ghz_freq(chan->freq)) &&
716 !acs_usable_bw40_chan(chan)) {
717 wpa_printf(MSG_DEBUG,
718 "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700719 chan->chan);
720 continue;
721 }
722
Hai Shalomfdcde762020-04-02 11:19:20 -0700723 if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
Hai Shalom81f62d82019-07-22 12:10:00 -0700724 (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
725 if (hostapd_get_oper_chwidth(iface->conf) ==
Sunil8cd6f4d2022-06-28 18:40:46 +0000726 CONF_OPER_CHWIDTH_80MHZ &&
Hai Shaloma20dcd72022-02-04 13:43:00 -0800727 !acs_usable_bw80_chan(chan)) {
Hai Shalom74f70d42019-02-11 14:42:39 -0800728 wpa_printf(MSG_DEBUG,
Hai Shaloma20dcd72022-02-04 13:43:00 -0800729 "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
Hai Shalom74f70d42019-02-11 14:42:39 -0800730 chan->chan);
731 continue;
732 }
733
Hai Shalom81f62d82019-07-22 12:10:00 -0700734 if (hostapd_get_oper_chwidth(iface->conf) ==
Sunil8cd6f4d2022-06-28 18:40:46 +0000735 CONF_OPER_CHWIDTH_160MHZ &&
Hai Shaloma20dcd72022-02-04 13:43:00 -0800736 !acs_usable_bw160_chan(chan)) {
Hai Shalom74f70d42019-02-11 14:42:39 -0800737 wpa_printf(MSG_DEBUG,
Hai Shaloma20dcd72022-02-04 13:43:00 -0800738 "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
Hai Shalom74f70d42019-02-11 14:42:39 -0800739 chan->chan);
740 continue;
741 }
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800742 }
743
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700744 factor = 0;
745 if (acs_usable_chan(chan))
746 factor = chan->interference_factor;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800747 total_weight = 1;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700748
749 for (j = 1; j < n_chans; j++) {
750 adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
751 if (!adj_chan)
752 break;
753
Hai Shalom74f70d42019-02-11 14:42:39 -0800754 if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
755 wpa_printf(MSG_DEBUG,
756 "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
757 chan->chan, adj_chan->chan, bw);
758 break;
759 }
760
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800761 if (acs_usable_chan(adj_chan)) {
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700762 factor += adj_chan->interference_factor;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800763 total_weight += 1;
764 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700765 }
766
767 if (j != n_chans) {
768 wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
769 chan->chan);
770 continue;
771 }
772
773 /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
774 * channel interference factor. */
Hai Shalomfdcde762020-04-02 11:19:20 -0700775 if (is_24ghz_mode(mode->mode)) {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700776 for (j = 0; j < n_chans; j++) {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700777 adj_chan = acs_find_chan(iface, chan->freq +
778 (j * 20) - 5);
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800779 if (adj_chan && acs_usable_chan(adj_chan)) {
780 factor += ACS_ADJ_WEIGHT *
781 adj_chan->interference_factor;
782 total_weight += ACS_ADJ_WEIGHT;
783 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700784
785 adj_chan = acs_find_chan(iface, chan->freq +
786 (j * 20) - 10);
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800787 if (adj_chan && acs_usable_chan(adj_chan)) {
788 factor += ACS_NEXT_ADJ_WEIGHT *
789 adj_chan->interference_factor;
790 total_weight += ACS_NEXT_ADJ_WEIGHT;
791 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700792
793 adj_chan = acs_find_chan(iface, chan->freq +
794 (j * 20) + 5);
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800795 if (adj_chan && acs_usable_chan(adj_chan)) {
796 factor += ACS_ADJ_WEIGHT *
797 adj_chan->interference_factor;
798 total_weight += ACS_ADJ_WEIGHT;
799 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700800
801 adj_chan = acs_find_chan(iface, chan->freq +
802 (j * 20) + 10);
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800803 if (adj_chan && acs_usable_chan(adj_chan)) {
804 factor += ACS_NEXT_ADJ_WEIGHT *
805 adj_chan->interference_factor;
806 total_weight += ACS_NEXT_ADJ_WEIGHT;
807 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700808 }
809 }
810
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800811 factor /= total_weight;
812
813 bias = NULL;
814 if (iface->conf->acs_chan_bias) {
815 for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
816 bias = &iface->conf->acs_chan_bias[k];
817 if (bias->channel == chan->chan)
818 break;
819 bias = NULL;
820 }
Hai Shalomfdcde762020-04-02 11:19:20 -0700821 } else if (is_24ghz_mode(mode->mode) &&
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800822 is_common_24ghz_chan(chan->chan)) {
823 tmp_bias.channel = chan->chan;
824 tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
825 bias = &tmp_bias;
826 }
827
828 if (bias) {
829 factor *= bias->bias;
830 wpa_printf(MSG_DEBUG,
831 "ACS: * channel %d: total interference = %Lg (%f bias)",
832 chan->chan, factor, bias->bias);
833 } else {
834 wpa_printf(MSG_DEBUG,
835 "ACS: * channel %d: total interference = %Lg",
836 chan->chan, factor);
837 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700838
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700839 if (acs_usable_chan(chan) &&
Hai Shalomfdcde762020-04-02 11:19:20 -0700840 (!*ideal_chan || factor < *ideal_factor)) {
841 *ideal_factor = factor;
842 *ideal_chan = chan;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700843 }
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700844
845 /* This channel would at least be usable */
Hai Shalomfdcde762020-04-02 11:19:20 -0700846 if (!(*rand_chan))
847 *rand_chan = chan;
848 }
849}
850
851
852/*
853 * At this point it's assumed chan->interference_factor has been computed.
854 * This function should be reusable regardless of interference computation
855 * option (survey, BSS, spectral, ...). chan->interference factor must be
856 * summable (i.e., must be always greater than zero).
857 */
858static struct hostapd_channel_data *
859acs_find_ideal_chan(struct hostapd_iface *iface)
860{
861 struct hostapd_channel_data *ideal_chan = NULL,
862 *rand_chan = NULL;
863 long double ideal_factor = 0;
864 int i;
865 int n_chans = 1;
866 u32 bw;
867 struct hostapd_hw_modes *mode;
868
Hai Shaloma20dcd72022-02-04 13:43:00 -0800869 if (is_6ghz_op_class(iface->conf->op_class)) {
870 bw = op_class_to_bandwidth(iface->conf->op_class);
871 n_chans = bw / 20;
872 goto bw_selected;
873 }
874
Hai Shalomfdcde762020-04-02 11:19:20 -0700875 /* TODO: HT40- support */
876
877 if (iface->conf->ieee80211n &&
878 iface->conf->secondary_channel == -1) {
879 wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
880 return NULL;
881 }
882
883 if (iface->conf->ieee80211n &&
884 iface->conf->secondary_channel)
885 n_chans = 2;
886
887 if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
888 switch (hostapd_get_oper_chwidth(iface->conf)) {
Sunil8cd6f4d2022-06-28 18:40:46 +0000889 case CONF_OPER_CHWIDTH_80MHZ:
Hai Shalomfdcde762020-04-02 11:19:20 -0700890 n_chans = 4;
891 break;
Sunil8cd6f4d2022-06-28 18:40:46 +0000892 case CONF_OPER_CHWIDTH_160MHZ:
Hai Shalomfdcde762020-04-02 11:19:20 -0700893 n_chans = 8;
894 break;
Sunil8cd6f4d2022-06-28 18:40:46 +0000895 default:
896 break;
Hai Shalomfdcde762020-04-02 11:19:20 -0700897 }
898 }
899
900 bw = num_chan_to_bw(n_chans);
901
Hai Shaloma20dcd72022-02-04 13:43:00 -0800902bw_selected:
Hai Shalomfdcde762020-04-02 11:19:20 -0700903 /* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
904
905 wpa_printf(MSG_DEBUG,
906 "ACS: Survey analysis for selected bandwidth %d MHz", bw);
907
908 for (i = 0; i < iface->num_hw_features; i++) {
909 mode = &iface->hw_features[i];
910 if (!hostapd_hw_skip_mode(iface, mode))
911 acs_find_ideal_chan_mode(iface, mode, n_chans, bw,
912 &rand_chan, &ideal_chan,
913 &ideal_factor);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700914 }
915
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700916 if (ideal_chan) {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700917 wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
918 ideal_chan->chan, ideal_chan->freq, ideal_factor);
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700919 return ideal_chan;
920 }
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700921
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -0700922 return rand_chan;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700923}
924
925
Hai Shalom81f62d82019-07-22 12:10:00 -0700926static void acs_adjust_center_freq(struct hostapd_iface *iface)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700927{
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700928 int offset;
929
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700930 wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
931
Hai Shalom81f62d82019-07-22 12:10:00 -0700932 switch (hostapd_get_oper_chwidth(iface->conf)) {
Sunil8cd6f4d2022-06-28 18:40:46 +0000933 case CONF_OPER_CHWIDTH_USE_HT:
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700934 offset = 2 * iface->conf->secondary_channel;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700935 break;
Sunil8cd6f4d2022-06-28 18:40:46 +0000936 case CONF_OPER_CHWIDTH_80MHZ:
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700937 offset = 6;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700938 break;
Sunil8cd6f4d2022-06-28 18:40:46 +0000939 case CONF_OPER_CHWIDTH_160MHZ:
Hai Shalom74f70d42019-02-11 14:42:39 -0800940 offset = 14;
941 break;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700942 default:
943 /* TODO: How can this be calculated? Adjust
944 * acs_find_ideal_chan() */
Hai Shalom74f70d42019-02-11 14:42:39 -0800945 wpa_printf(MSG_INFO,
946 "ACS: Only VHT20/40/80/160 is supported now");
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700947 return;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700948 }
Dmitry Shmidtb36ed7c2014-03-17 10:57:26 -0700949
Hai Shalom81f62d82019-07-22 12:10:00 -0700950 hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
951 iface->conf->channel + offset);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700952}
953
954
955static int acs_study_survey_based(struct hostapd_iface *iface)
956{
957 wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
958
959 if (!iface->chans_surveyed) {
960 wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
961 return -1;
962 }
963
964 if (!acs_surveys_are_sufficient(iface)) {
965 wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
966 return -1;
967 }
968
Hai Shalomfdcde762020-04-02 11:19:20 -0700969 acs_survey_all_chans_interference_factor(iface);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700970 return 0;
971}
972
973
974static int acs_study_options(struct hostapd_iface *iface)
975{
Dmitry Shmidtd2986c22017-10-23 14:22:09 -0700976 if (acs_study_survey_based(iface) == 0)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -0700977 return 0;
978
979 /* TODO: If no surveys are available/sufficient this is a good
980 * place to fallback to BSS-based ACS */
981
982 return -1;
983}
984
985
986static void acs_study(struct hostapd_iface *iface)
987{
988 struct hostapd_channel_data *ideal_chan;
989 int err;
990
991 err = acs_study_options(iface);
992 if (err < 0) {
993 wpa_printf(MSG_ERROR, "ACS: All study options have failed");
994 goto fail;
995 }
996
997 ideal_chan = acs_find_ideal_chan(iface);
998 if (!ideal_chan) {
999 wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -07001000 err = -1;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001001 goto fail;
1002 }
1003
1004 iface->conf->channel = ideal_chan->chan;
Ahmed ElArabawy0ff61c52019-12-26 12:38:39 -08001005 iface->freq = ideal_chan->freq;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001006
Hai Shalom81f62d82019-07-22 12:10:00 -07001007 if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
1008 acs_adjust_center_freq(iface);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001009
Sunil Ravi3e88b0e2022-08-10 22:56:55 +00001010 err = hostapd_select_hw_mode(iface);
1011 if (err) {
Sunil Ravi89eba102022-09-13 21:04:37 -07001012 wpa_printf(MSG_ERROR,
1013 "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
Sunil Ravi3e88b0e2022-08-10 22:56:55 +00001014 err, iface->freq, iface->conf->channel);
1015 err = -1;
1016 goto fail;
1017 }
1018
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -07001019 err = 0;
1020fail:
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001021 /*
1022 * hostapd_setup_interface_complete() will return -1 on failure,
1023 * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
1024 */
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -07001025 if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001026 acs_cleanup(iface);
1027 return;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001028 }
1029
Dmitry Shmidt4ce9c872013-10-24 11:08:13 -07001030 /* This can possibly happen if channel parameters (secondary
1031 * channel, center frequencies) are misconfigured */
1032 wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001033 acs_fail(iface);
1034}
1035
1036
1037static void acs_scan_complete(struct hostapd_iface *iface)
1038{
1039 int err;
1040
1041 iface->scan_cb = NULL;
1042
1043 wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
1044 iface->conf->acs_num_scans);
1045
1046 err = hostapd_drv_get_survey(iface->bss[0], 0);
1047 if (err) {
1048 wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
Dmitry Shmidt15907092014-03-25 10:42:57 -07001049 goto fail;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001050 }
1051
1052 if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
1053 err = acs_request_scan(iface);
1054 if (err) {
1055 wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001056 goto fail;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001057 }
1058
1059 return;
1060 }
1061
1062 acs_study(iface);
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001063 return;
1064fail:
1065 hostapd_acs_completed(iface, 1);
1066 acs_fail(iface);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001067}
1068
1069
Hai Shalomfdcde762020-04-02 11:19:20 -07001070static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
1071 struct hostapd_hw_modes *mode,
1072 int *freq)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001073{
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001074 struct hostapd_channel_data *chan;
Hai Shalomfdcde762020-04-02 11:19:20 -07001075 int i;
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001076
Hai Shalomfdcde762020-04-02 11:19:20 -07001077 for (i = 0; i < mode->num_channels; i++) {
1078 chan = &mode->channels[i];
Sunil Ravia04bd252022-05-02 22:54:18 -07001079 if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
1080 ((chan->flag & HOSTAPD_CHAN_RADAR) &&
1081 iface->conf->acs_exclude_dfs))
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001082 continue;
1083
Dmitry Shmidt8bd70b72015-05-26 16:02:19 -07001084 if (!is_in_chanlist(iface, chan))
1085 continue;
1086
Hai Shalom4fbc08f2020-05-18 12:37:00 -07001087 if (!is_in_freqlist(iface, chan))
1088 continue;
1089
Hai Shaloma20dcd72022-02-04 13:43:00 -08001090 if (chan->max_tx_power < iface->conf->min_tx_power)
1091 continue;
1092
Sunil Ravi89eba102022-09-13 21:04:37 -07001093 if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
1094 iface->conf->country[2] == 0x4f)
1095 continue;
1096
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001097 *freq++ = chan->freq;
1098 }
Hai Shalomfdcde762020-04-02 11:19:20 -07001099
1100 return freq;
1101}
1102
1103
1104static int acs_request_scan(struct hostapd_iface *iface)
1105{
1106 struct wpa_driver_scan_params params;
1107 int i, *freq;
1108 int num_channels;
1109 struct hostapd_hw_modes *mode;
1110
1111 os_memset(&params, 0, sizeof(params));
1112
1113 num_channels = 0;
1114 for (i = 0; i < iface->num_hw_features; i++) {
1115 mode = &iface->hw_features[i];
1116 if (!hostapd_hw_skip_mode(iface, mode))
1117 num_channels += mode->num_channels;
1118 }
1119
1120 params.freqs = os_calloc(num_channels + 1, sizeof(params.freqs[0]));
1121 if (params.freqs == NULL)
1122 return -1;
1123
1124 freq = params.freqs;
1125
1126 for (i = 0; i < iface->num_hw_features; i++) {
1127 mode = &iface->hw_features[i];
1128 if (!hostapd_hw_skip_mode(iface, mode))
1129 freq = acs_request_scan_add_freqs(iface, mode, freq);
1130 }
1131
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001132 *freq = 0;
1133
Neo Joue2881702019-10-17 11:32:28 +08001134 if (params.freqs == freq) {
1135 wpa_printf(MSG_ERROR, "ACS: No available channels found");
1136 os_free(params.freqs);
1137 return -1;
1138 }
1139
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001140 iface->scan_cb = acs_scan_complete;
1141
1142 wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
1143 iface->acs_num_completed_scans + 1,
1144 iface->conf->acs_num_scans);
1145
1146 if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
1147 wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
1148 acs_cleanup(iface);
Dmitry Shmidt15907092014-03-25 10:42:57 -07001149 os_free(params.freqs);
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001150 return -1;
1151 }
1152
1153 os_free(params.freqs);
1154 return 0;
1155}
1156
1157
1158enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
1159{
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001160 wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
1161
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001162 if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
1163 wpa_printf(MSG_INFO, "ACS: Offloading to driver");
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001164 if (hostapd_drv_do_acs(iface->bss[0]))
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001165 return HOSTAPD_CHAN_INVALID;
1166 return HOSTAPD_CHAN_ACS;
1167 }
1168
Hai Shalomfdcde762020-04-02 11:19:20 -07001169 if (!iface->current_mode &&
1170 iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
Dmitry Shmidtde47be72016-01-07 12:52:55 -08001171 return HOSTAPD_CHAN_INVALID;
1172
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001173 acs_cleanup(iface);
1174
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001175 if (acs_request_scan(iface) < 0)
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001176 return HOSTAPD_CHAN_INVALID;
1177
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001178 hostapd_set_state(iface, HAPD_IFACE_ACS);
1179 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
1180
Dmitry Shmidt391c59f2013-09-03 12:16:28 -07001181 return HOSTAPD_CHAN_ACS;
1182}