Branch data Line data Source code
1 : : /*
2 : : * DFS - Dynamic Frequency Selection
3 : : * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
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 : :
12 : : #include "utils/common.h"
13 : : #include "common/ieee802_11_defs.h"
14 : : #include "common/wpa_ctrl.h"
15 : : #include "hostapd.h"
16 : : #include "ap_drv_ops.h"
17 : : #include "drivers/driver.h"
18 : : #include "dfs.h"
19 : :
20 : :
21 : 183 : static int dfs_get_used_n_chans(struct hostapd_iface *iface)
22 : : {
23 : 183 : int n_chans = 1;
24 : :
25 [ + - ][ + + ]: 183 : if (iface->conf->ieee80211n && iface->conf->secondary_channel)
26 : 4 : n_chans = 2;
27 : :
28 [ - + ]: 183 : if (iface->conf->ieee80211ac) {
29 [ # # # # ]: 0 : switch (iface->conf->vht_oper_chwidth) {
30 : : case VHT_CHANWIDTH_USE_HT:
31 : 0 : break;
32 : : case VHT_CHANWIDTH_80MHZ:
33 : 0 : n_chans = 4;
34 : 0 : break;
35 : : case VHT_CHANWIDTH_160MHZ:
36 : 0 : n_chans = 8;
37 : 0 : break;
38 : : default:
39 : 0 : break;
40 : : }
41 : : }
42 : :
43 : 183 : return n_chans;
44 : : }
45 : :
46 : :
47 : 0 : static int dfs_channel_available(struct hostapd_channel_data *chan,
48 : : int skip_radar)
49 : : {
50 : : /*
51 : : * When radar detection happens, CSA is performed. However, there's no
52 : : * time for CAC, so radar channels must be skipped when finding a new
53 : : * channel for CSA.
54 : : */
55 [ # # ][ # # ]: 0 : if (skip_radar && chan->flag & HOSTAPD_CHAN_RADAR)
56 : 0 : return 0;
57 : :
58 [ # # ]: 0 : if (chan->flag & HOSTAPD_CHAN_DISABLED)
59 : 0 : return 0;
60 [ # # ][ # # ]: 0 : if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
61 : 0 : ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
62 : : HOSTAPD_CHAN_DFS_UNAVAILABLE))
63 : 0 : return 0;
64 : 0 : return 1;
65 : : }
66 : :
67 : :
68 : 0 : static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
69 : : {
70 : : /*
71 : : * The tables contain first valid channel number based on channel width.
72 : : * We will also choose this first channel as the control one.
73 : : */
74 : 0 : int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
75 : : 184, 192 };
76 : : /*
77 : : * VHT80, valid channels based on center frequency:
78 : : * 42, 58, 106, 122, 138, 155
79 : : */
80 : 0 : int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
81 : 0 : int *allowed = allowed_40;
82 : 0 : unsigned int i, allowed_no = 0;
83 : :
84 [ # # # ]: 0 : switch (n_chans) {
85 : : case 2:
86 : 0 : allowed = allowed_40;
87 : 0 : allowed_no = ARRAY_SIZE(allowed_40);
88 : 0 : break;
89 : : case 4:
90 : 0 : allowed = allowed_80;
91 : 0 : allowed_no = ARRAY_SIZE(allowed_80);
92 : 0 : break;
93 : : default:
94 : 0 : wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
95 : 0 : break;
96 : : }
97 : :
98 [ # # ]: 0 : for (i = 0; i < allowed_no; i++) {
99 [ # # ]: 0 : if (chan->chan == allowed[i])
100 : 0 : return 1;
101 : : }
102 : :
103 : 0 : return 0;
104 : : }
105 : :
106 : :
107 : 0 : static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
108 : : int first_chan_idx, int num_chans,
109 : : int skip_radar)
110 : : {
111 : : struct hostapd_channel_data *first_chan, *chan;
112 : : int i;
113 : :
114 [ # # ]: 0 : if (first_chan_idx + num_chans >= mode->num_channels)
115 : 0 : return 0;
116 : :
117 : 0 : first_chan = &mode->channels[first_chan_idx];
118 : :
119 [ # # ]: 0 : for (i = 0; i < num_chans; i++) {
120 : 0 : chan = &mode->channels[first_chan_idx + i];
121 : :
122 [ # # ]: 0 : if (first_chan->freq + i * 20 != chan->freq)
123 : 0 : return 0;
124 : :
125 [ # # ]: 0 : if (!dfs_channel_available(chan, skip_radar))
126 : 0 : return 0;
127 : : }
128 : :
129 : 0 : return 1;
130 : : }
131 : :
132 : :
133 : : /*
134 : : * The function assumes HT40+ operation.
135 : : * Make sure to adjust the following variables after calling this:
136 : : * - hapd->secondary_channel
137 : : * - hapd->vht_oper_centr_freq_seg0_idx
138 : : * - hapd->vht_oper_centr_freq_seg1_idx
139 : : */
140 : 0 : static int dfs_find_channel(struct hostapd_iface *iface,
141 : : struct hostapd_channel_data **ret_chan,
142 : : int idx, int skip_radar)
143 : : {
144 : : struct hostapd_hw_modes *mode;
145 : : struct hostapd_channel_data *chan;
146 : 0 : int i, channel_idx = 0, n_chans;
147 : :
148 : 0 : mode = iface->current_mode;
149 : 0 : n_chans = dfs_get_used_n_chans(iface);
150 : :
151 : 0 : wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
152 [ # # ]: 0 : for (i = 0; i < mode->num_channels; i++) {
153 : 0 : chan = &mode->channels[i];
154 : :
155 : : /* Skip HT40/VHT incompatible channels */
156 [ # # ][ # # ]: 0 : if (iface->conf->ieee80211n &&
157 [ # # ]: 0 : iface->conf->secondary_channel &&
158 : 0 : !dfs_is_chan_allowed(chan, n_chans))
159 : 0 : continue;
160 : :
161 : : /* Skip incompatible chandefs */
162 [ # # ]: 0 : if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
163 : 0 : continue;
164 : :
165 [ # # ][ # # ]: 0 : if (ret_chan && idx == channel_idx) {
166 : 0 : wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
167 : 0 : *ret_chan = chan;
168 : 0 : return idx;
169 : : }
170 : 0 : wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
171 : 0 : channel_idx++;
172 : : }
173 : 0 : return channel_idx;
174 : : }
175 : :
176 : :
177 : 0 : static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
178 : : struct hostapd_channel_data *chan,
179 : : u8 *vht_oper_centr_freq_seg0_idx,
180 : : u8 *vht_oper_centr_freq_seg1_idx)
181 : : {
182 [ # # ]: 0 : if (!iface->conf->ieee80211ac)
183 : 0 : return;
184 : :
185 [ # # ]: 0 : if (!chan)
186 : 0 : return;
187 : :
188 : 0 : *vht_oper_centr_freq_seg1_idx = 0;
189 : :
190 [ # # # # ]: 0 : switch (iface->conf->vht_oper_chwidth) {
191 : : case VHT_CHANWIDTH_USE_HT:
192 [ # # ]: 0 : if (iface->conf->secondary_channel == 1)
193 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
194 [ # # ]: 0 : else if (iface->conf->secondary_channel == -1)
195 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
196 : : else
197 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan;
198 : 0 : break;
199 : : case VHT_CHANWIDTH_80MHZ:
200 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
201 : 0 : break;
202 : : case VHT_CHANWIDTH_160MHZ:
203 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
204 : 0 : break;
205 : : default:
206 : 0 : wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
207 : 0 : break;
208 : : }
209 : :
210 : 0 : wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
211 : 0 : *vht_oper_centr_freq_seg0_idx,
212 : 0 : *vht_oper_centr_freq_seg1_idx);
213 : : }
214 : :
215 : :
216 : : /* Return start channel idx we will use for mode->channels[idx] */
217 : 183 : static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
218 : : {
219 : : struct hostapd_hw_modes *mode;
220 : : struct hostapd_channel_data *chan;
221 : 183 : int channel_no = iface->conf->channel;
222 : 183 : int res = -1, i;
223 : :
224 : : /* HT40- */
225 [ + - ][ + + ]: 183 : if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
226 : 1 : channel_no -= 4;
227 : :
228 : : /* VHT */
229 [ - + ]: 183 : if (iface->conf->ieee80211ac) {
230 [ # # # # ]: 0 : switch (iface->conf->vht_oper_chwidth) {
231 : : case VHT_CHANWIDTH_USE_HT:
232 : 0 : break;
233 : : case VHT_CHANWIDTH_80MHZ:
234 : 0 : channel_no =
235 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx - 6;
236 : 0 : break;
237 : : case VHT_CHANWIDTH_160MHZ:
238 : 0 : channel_no =
239 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx - 14;
240 : 0 : break;
241 : : default:
242 : 0 : wpa_printf(MSG_INFO,
243 : : "DFS only VHT20/40/80/160 is supported now");
244 : 0 : channel_no = -1;
245 : 0 : break;
246 : : }
247 : : }
248 : :
249 : : /* Get idx */
250 : 183 : mode = iface->current_mode;
251 [ + - ]: 183 : for (i = 0; i < mode->num_channels; i++) {
252 : 183 : chan = &mode->channels[i];
253 [ + - ]: 183 : if (chan->chan == channel_no) {
254 : 183 : res = i;
255 : 183 : break;
256 : : }
257 : : }
258 : :
259 [ - + ]: 183 : if (res == -1)
260 : 0 : wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
261 : :
262 : 183 : return res;
263 : : }
264 : :
265 : :
266 : : /* At least one channel have radar flag */
267 : 183 : static int dfs_check_chans_radar(struct hostapd_iface *iface,
268 : : int start_chan_idx, int n_chans)
269 : : {
270 : : struct hostapd_channel_data *channel;
271 : : struct hostapd_hw_modes *mode;
272 : 183 : int i, res = 0;
273 : :
274 : 183 : mode = iface->current_mode;
275 : :
276 [ + + ]: 370 : for (i = 0; i < n_chans; i++) {
277 : 187 : channel = &mode->channels[start_chan_idx + i];
278 [ - + ]: 187 : if (channel->flag & HOSTAPD_CHAN_RADAR)
279 : 0 : res++;
280 : : }
281 : :
282 : 183 : return res;
283 : : }
284 : :
285 : :
286 : : /* All channels available */
287 : 0 : static int dfs_check_chans_available(struct hostapd_iface *iface,
288 : : int start_chan_idx, int n_chans)
289 : : {
290 : : struct hostapd_channel_data *channel;
291 : : struct hostapd_hw_modes *mode;
292 : : int i;
293 : :
294 : 0 : mode = iface->current_mode;
295 : :
296 [ # # ]: 0 : for(i = 0; i < n_chans; i++) {
297 : 0 : channel = &mode->channels[start_chan_idx + i];
298 [ # # ]: 0 : if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
299 : : HOSTAPD_CHAN_DFS_AVAILABLE)
300 : 0 : break;
301 : : }
302 : :
303 : 0 : return i == n_chans;
304 : : }
305 : :
306 : :
307 : : /* At least one channel unavailable */
308 : 0 : static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
309 : : int start_chan_idx,
310 : : int n_chans)
311 : : {
312 : : struct hostapd_channel_data *channel;
313 : : struct hostapd_hw_modes *mode;
314 : 0 : int i, res = 0;
315 : :
316 : 0 : mode = iface->current_mode;
317 : :
318 [ # # ]: 0 : for(i = 0; i < n_chans; i++) {
319 : 0 : channel = &mode->channels[start_chan_idx + i];
320 [ # # ]: 0 : if (channel->flag & HOSTAPD_CHAN_DISABLED)
321 : 0 : res++;
322 [ # # ]: 0 : if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
323 : : HOSTAPD_CHAN_DFS_UNAVAILABLE)
324 : 0 : res++;
325 : : }
326 : :
327 : 0 : return res;
328 : : }
329 : :
330 : :
331 : : static struct hostapd_channel_data *
332 : 0 : dfs_get_valid_channel(struct hostapd_iface *iface,
333 : : int *secondary_channel,
334 : : u8 *vht_oper_centr_freq_seg0_idx,
335 : : u8 *vht_oper_centr_freq_seg1_idx,
336 : : int skip_radar)
337 : : {
338 : : struct hostapd_hw_modes *mode;
339 : 0 : struct hostapd_channel_data *chan = NULL;
340 : : int num_available_chandefs;
341 : : int chan_idx;
342 : : u32 _rand;
343 : :
344 : 0 : wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
345 : :
346 [ # # ]: 0 : if (iface->current_mode == NULL)
347 : 0 : return NULL;
348 : :
349 : 0 : mode = iface->current_mode;
350 [ # # ]: 0 : if (mode->mode != HOSTAPD_MODE_IEEE80211A)
351 : 0 : return NULL;
352 : :
353 : : /* Get the count first */
354 : 0 : num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
355 [ # # ]: 0 : if (num_available_chandefs == 0)
356 : 0 : return NULL;
357 : :
358 : 0 : os_get_random((u8 *) &_rand, sizeof(_rand));
359 : 0 : chan_idx = _rand % num_available_chandefs;
360 : 0 : dfs_find_channel(iface, &chan, chan_idx, skip_radar);
361 : :
362 : : /* dfs_find_channel() calculations assume HT40+ */
363 [ # # ]: 0 : if (iface->conf->secondary_channel)
364 : 0 : *secondary_channel = 1;
365 : : else
366 : 0 : *secondary_channel = 0;
367 : :
368 : 0 : dfs_adjust_vht_center_freq(iface, chan,
369 : : vht_oper_centr_freq_seg0_idx,
370 : : vht_oper_centr_freq_seg1_idx);
371 : :
372 : 0 : return chan;
373 : : }
374 : :
375 : :
376 : 0 : static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
377 : : {
378 : : struct hostapd_hw_modes *mode;
379 : 0 : struct hostapd_channel_data *chan = NULL;
380 : : int i;
381 : :
382 : 0 : mode = iface->current_mode;
383 [ # # ]: 0 : if (mode == NULL)
384 : 0 : return 0;
385 : :
386 : 0 : wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
387 [ # # ]: 0 : for (i = 0; i < iface->current_mode->num_channels; i++) {
388 : 0 : chan = &iface->current_mode->channels[i];
389 [ # # ]: 0 : if (chan->freq == freq) {
390 [ # # ]: 0 : if (chan->flag & HOSTAPD_CHAN_RADAR) {
391 : 0 : chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
392 : 0 : chan->flag |= state;
393 : 0 : return 1; /* Channel found */
394 : : }
395 : : }
396 : : }
397 : 0 : wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
398 : 0 : return 0;
399 : : }
400 : :
401 : :
402 : 0 : static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
403 : : int chan_offset, int chan_width, int cf1,
404 : : int cf2, u32 state)
405 : : {
406 : 0 : int n_chans = 1, i;
407 : : struct hostapd_hw_modes *mode;
408 : 0 : int frequency = freq;
409 : 0 : int ret = 0;
410 : :
411 : 0 : mode = iface->current_mode;
412 [ # # ]: 0 : if (mode == NULL)
413 : 0 : return 0;
414 : :
415 [ # # ]: 0 : if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
416 : 0 : wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
417 : 0 : return 0;
418 : : }
419 : :
420 : : /* Seems cf1 and chan_width is enough here */
421 [ # # # # : 0 : switch (chan_width) {
# ]
422 : : case CHAN_WIDTH_20_NOHT:
423 : : case CHAN_WIDTH_20:
424 : 0 : n_chans = 1;
425 [ # # ]: 0 : if (frequency == 0)
426 : 0 : frequency = cf1;
427 : 0 : break;
428 : : case CHAN_WIDTH_40:
429 : 0 : n_chans = 2;
430 : 0 : frequency = cf1 - 10;
431 : 0 : break;
432 : : case CHAN_WIDTH_80:
433 : 0 : n_chans = 4;
434 : 0 : frequency = cf1 - 30;
435 : 0 : break;
436 : : case CHAN_WIDTH_160:
437 : 0 : n_chans = 8;
438 : 0 : frequency = cf1 - 70;
439 : 0 : break;
440 : : default:
441 : 0 : wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
442 : : chan_width);
443 : 0 : break;
444 : : }
445 : :
446 : 0 : wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
447 : : n_chans);
448 [ # # ]: 0 : for (i = 0; i < n_chans; i++) {
449 : 0 : ret += set_dfs_state_freq(iface, frequency, state);
450 : 0 : frequency = frequency + 20;
451 : : }
452 : :
453 : 0 : return ret;
454 : : }
455 : :
456 : :
457 : 0 : static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
458 : : int chan_width, int cf1, int cf2)
459 : : {
460 : : int start_chan_idx;
461 : : struct hostapd_hw_modes *mode;
462 : : struct hostapd_channel_data *chan;
463 : 0 : int n_chans, i, j, frequency = freq, radar_n_chans = 1;
464 : : u8 radar_chan;
465 : 0 : int res = 0;
466 : :
467 : : /* Our configuration */
468 : 0 : mode = iface->current_mode;
469 : 0 : start_chan_idx = dfs_get_start_chan_idx(iface);
470 : 0 : n_chans = dfs_get_used_n_chans(iface);
471 : :
472 : : /* Check we are on DFS channel(s) */
473 [ # # ]: 0 : if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
474 : 0 : return 0;
475 : :
476 : : /* Reported via radar event */
477 [ # # # # : 0 : switch (chan_width) {
# ]
478 : : case CHAN_WIDTH_20_NOHT:
479 : : case CHAN_WIDTH_20:
480 : 0 : radar_n_chans = 1;
481 [ # # ]: 0 : if (frequency == 0)
482 : 0 : frequency = cf1;
483 : 0 : break;
484 : : case CHAN_WIDTH_40:
485 : 0 : radar_n_chans = 2;
486 : 0 : frequency = cf1 - 10;
487 : 0 : break;
488 : : case CHAN_WIDTH_80:
489 : 0 : radar_n_chans = 4;
490 : 0 : frequency = cf1 - 30;
491 : 0 : break;
492 : : case CHAN_WIDTH_160:
493 : 0 : radar_n_chans = 8;
494 : 0 : frequency = cf1 - 70;
495 : 0 : break;
496 : : default:
497 : 0 : wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
498 : : chan_width);
499 : 0 : break;
500 : : }
501 : :
502 : 0 : ieee80211_freq_to_chan(frequency, &radar_chan);
503 : :
504 [ # # ]: 0 : for (i = 0; i < n_chans; i++) {
505 : 0 : chan = &mode->channels[start_chan_idx + i];
506 [ # # ]: 0 : if (!(chan->flag & HOSTAPD_CHAN_RADAR))
507 : 0 : continue;
508 [ # # ]: 0 : for (j = 0; j < radar_n_chans; j++) {
509 : 0 : wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
510 : 0 : chan->chan, radar_chan + j * 4);
511 [ # # ]: 0 : if (chan->chan == radar_chan + j * 4)
512 : 0 : res++;
513 : : }
514 : : }
515 : :
516 : 0 : wpa_printf(MSG_DEBUG, "overlapped: %d", res);
517 : :
518 : 0 : return res;
519 : : }
520 : :
521 : :
522 : : /*
523 : : * Main DFS handler
524 : : * 1 - continue channel/ap setup
525 : : * 0 - channel/ap setup will be continued after CAC
526 : : * -1 - hit critical error
527 : : */
528 : 183 : int hostapd_handle_dfs(struct hostapd_iface *iface)
529 : : {
530 : : struct hostapd_channel_data *channel;
531 : : int res, n_chans, start_chan_idx;
532 : 183 : int skip_radar = 0;
533 : :
534 : 183 : iface->cac_started = 0;
535 : :
536 : : do {
537 : : /* Get start (first) channel for current configuration */
538 : 183 : start_chan_idx = dfs_get_start_chan_idx(iface);
539 [ - + ]: 183 : if (start_chan_idx == -1)
540 : 0 : return -1;
541 : :
542 : : /* Get number of used channels, depend on width */
543 : 183 : n_chans = dfs_get_used_n_chans(iface);
544 : :
545 : : /* Check if any of configured channels require DFS */
546 : 183 : res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
547 : 183 : wpa_printf(MSG_DEBUG,
548 : : "DFS %d channels required radar detection",
549 : : res);
550 [ + - ]: 183 : if (!res)
551 : 183 : return 1;
552 : :
553 : : /* Check if all channels are DFS available */
554 : 0 : res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
555 [ # # ]: 0 : wpa_printf(MSG_DEBUG,
556 : : "DFS all channels available, (SKIP CAC): %s",
557 : : res ? "yes" : "no");
558 [ # # ]: 0 : if (res)
559 : 0 : return 1;
560 : :
561 : : /* Check if any of configured channels is unavailable */
562 : 0 : res = dfs_check_chans_unavailable(iface, start_chan_idx,
563 : : n_chans);
564 [ # # ]: 0 : wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
565 : : res, res ? "yes": "no");
566 [ # # ]: 0 : if (res) {
567 : : int sec;
568 : : u8 cf1, cf2;
569 : :
570 : 0 : channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
571 : : skip_radar);
572 [ # # ]: 0 : if (!channel) {
573 : 0 : wpa_printf(MSG_ERROR, "could not get valid channel");
574 : 0 : return -1;
575 : : }
576 : :
577 : 0 : iface->freq = channel->freq;
578 : 0 : iface->conf->channel = channel->chan;
579 : 0 : iface->conf->secondary_channel = sec;
580 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
581 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
582 : : }
583 [ # # ]: 0 : } while (res);
584 : :
585 : : /* Finally start CAC */
586 : 0 : hostapd_set_state(iface, HAPD_IFACE_DFS);
587 : 0 : wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
588 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
589 : : "freq=%d chan=%d sec_chan=%d",
590 : : iface->freq,
591 : 0 : iface->conf->channel, iface->conf->secondary_channel);
592 [ # # ]: 0 : if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
593 : : iface->freq,
594 : 0 : iface->conf->channel,
595 : 0 : iface->conf->ieee80211n,
596 : 0 : iface->conf->ieee80211ac,
597 : 0 : iface->conf->secondary_channel,
598 : 0 : iface->conf->vht_oper_chwidth,
599 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx,
600 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx)) {
601 : 0 : wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
602 : 0 : return -1;
603 : : }
604 : :
605 : 183 : return 0;
606 : : }
607 : :
608 : :
609 : 0 : int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
610 : : int ht_enabled, int chan_offset, int chan_width,
611 : : int cf1, int cf2)
612 : : {
613 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
614 : : "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
615 : : success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
616 : :
617 [ # # ]: 0 : if (success) {
618 : : /* Complete iface/ap configuration */
619 : 0 : set_dfs_state(iface, freq, ht_enabled, chan_offset,
620 : : chan_width, cf1, cf2,
621 : : HOSTAPD_CHAN_DFS_AVAILABLE);
622 : 0 : iface->cac_started = 0;
623 : 0 : hostapd_setup_interface_complete(iface, 0);
624 : : }
625 : :
626 : 0 : return 0;
627 : : }
628 : :
629 : :
630 : 0 : static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
631 : : {
632 : : struct hostapd_channel_data *channel;
633 : : int secondary_channel;
634 : : u8 vht_oper_centr_freq_seg0_idx;
635 : : u8 vht_oper_centr_freq_seg1_idx;
636 : 0 : int skip_radar = 0;
637 : 0 : int err = 1;
638 : :
639 : : /* Radar detected during active CAC */
640 : 0 : iface->cac_started = 0;
641 : 0 : channel = dfs_get_valid_channel(iface, &secondary_channel,
642 : : &vht_oper_centr_freq_seg0_idx,
643 : : &vht_oper_centr_freq_seg1_idx,
644 : : skip_radar);
645 : :
646 [ # # ]: 0 : if (!channel) {
647 : 0 : wpa_printf(MSG_ERROR, "No valid channel available");
648 : 0 : hostapd_setup_interface_complete(iface, err);
649 : 0 : return err;
650 : : }
651 : :
652 : 0 : wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
653 : 0 : channel->chan);
654 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
655 : : "freq=%d chan=%d sec_chan=%d", channel->freq,
656 : 0 : channel->chan, secondary_channel);
657 : :
658 : 0 : iface->freq = channel->freq;
659 : 0 : iface->conf->channel = channel->chan;
660 : 0 : iface->conf->secondary_channel = secondary_channel;
661 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx =
662 : : vht_oper_centr_freq_seg0_idx;
663 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx =
664 : : vht_oper_centr_freq_seg1_idx;
665 : 0 : err = 0;
666 : :
667 : 0 : hostapd_setup_interface_complete(iface, err);
668 : 0 : return err;
669 : : }
670 : :
671 : :
672 : 0 : static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
673 : : {
674 : : struct hostapd_channel_data *channel;
675 : : int secondary_channel;
676 : : u8 vht_oper_centr_freq_seg0_idx;
677 : : u8 vht_oper_centr_freq_seg1_idx;
678 : 0 : int skip_radar = 1;
679 : : struct csa_settings csa_settings;
680 : 0 : struct hostapd_data *hapd = iface->bss[0];
681 : 0 : int err = 1;
682 : :
683 [ # # ]: 0 : wpa_printf(MSG_DEBUG, "%s called (CAC active: %s)", __func__,
684 : 0 : iface->cac_started ? "yes" : "no");
685 : :
686 : : /* Check if active CAC */
687 [ # # ]: 0 : if (iface->cac_started)
688 : 0 : return hostapd_dfs_start_channel_switch_cac(iface);
689 : :
690 : :
691 : : /* Perform channel switch/CSA */
692 : 0 : channel = dfs_get_valid_channel(iface, &secondary_channel,
693 : : &vht_oper_centr_freq_seg0_idx,
694 : : &vht_oper_centr_freq_seg1_idx,
695 : : skip_radar);
696 : :
697 [ # # ]: 0 : if (!channel) {
698 : : /* FIXME: Wait for channel(s) to become available */
699 : 0 : hostapd_disable_iface(iface);
700 : 0 : return err;
701 : : }
702 : :
703 : 0 : wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
704 : 0 : channel->chan);
705 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
706 : : "freq=%d chan=%d sec_chan=%d", channel->freq,
707 : 0 : channel->chan, secondary_channel);
708 : :
709 : : /* Setup CSA request */
710 : 0 : os_memset(&csa_settings, 0, sizeof(csa_settings));
711 : 0 : csa_settings.cs_count = 5;
712 : 0 : csa_settings.block_tx = 1;
713 : 0 : err = hostapd_set_freq_params(&csa_settings.freq_params,
714 : 0 : iface->conf->hw_mode,
715 : : channel->freq,
716 : 0 : channel->chan,
717 : 0 : iface->conf->ieee80211n,
718 : 0 : iface->conf->ieee80211ac,
719 : : secondary_channel,
720 : 0 : iface->conf->vht_oper_chwidth,
721 : : vht_oper_centr_freq_seg0_idx,
722 : : vht_oper_centr_freq_seg1_idx,
723 : 0 : iface->current_mode->vht_capab);
724 : :
725 [ # # ]: 0 : if (err) {
726 : 0 : wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
727 : 0 : hostapd_disable_iface(iface);
728 : 0 : return err;
729 : : }
730 : :
731 : 0 : err = hostapd_switch_channel(hapd, &csa_settings);
732 [ # # ]: 0 : if (err) {
733 : 0 : wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
734 : : err);
735 : 0 : iface->freq = channel->freq;
736 : 0 : iface->conf->channel = channel->chan;
737 : 0 : iface->conf->secondary_channel = secondary_channel;
738 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx =
739 : : vht_oper_centr_freq_seg0_idx;
740 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx =
741 : : vht_oper_centr_freq_seg1_idx;
742 : :
743 : 0 : hostapd_disable_iface(iface);
744 : 0 : hostapd_enable_iface(iface);
745 : 0 : return 0;
746 : : }
747 : :
748 : : /* Channel configuration will be updated once CSA completes and
749 : : * ch_switch_notify event is received */
750 : :
751 : 0 : wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
752 : 0 : return 0;
753 : : }
754 : :
755 : :
756 : 0 : int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
757 : : int ht_enabled, int chan_offset, int chan_width,
758 : : int cf1, int cf2)
759 : : {
760 : : int res;
761 : :
762 [ # # ]: 0 : if (!iface->conf->ieee80211h)
763 : 0 : return 0;
764 : :
765 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
766 : : "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
767 : : freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
768 : :
769 : : /* mark radar frequency as invalid */
770 : 0 : res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
771 : : chan_width, cf1, cf2,
772 : : HOSTAPD_CHAN_DFS_UNAVAILABLE);
773 : :
774 : : /* Skip if reported radar event not overlapped our channels */
775 : 0 : res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
776 [ # # ]: 0 : if (!res)
777 : 0 : return 0;
778 : :
779 : : /* radar detected while operating, switch the channel. */
780 : 0 : res = hostapd_dfs_start_channel_switch(iface);
781 : :
782 : 0 : return res;
783 : : }
784 : :
785 : :
786 : 0 : int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
787 : : int ht_enabled, int chan_offset, int chan_width,
788 : : int cf1, int cf2)
789 : : {
790 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
791 : : "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
792 : : freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
793 : : /* TODO add correct implementation here */
794 : 0 : set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
795 : : cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
796 : 0 : return 0;
797 : : }
|