blob: 37bbd203cf8a4754fa76e88a12ac6d157a64d737 [file] [log] [blame]
Dmitry Shmidt051af732013-10-22 13:52:46 -07001/*
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 "hostapd.h"
15#include "ap_drv_ops.h"
16#include "drivers/driver.h"
17#include "dfs.h"
18
19
20static int dfs_get_used_n_chans(struct hostapd_data *hapd)
21{
22 int n_chans = 1;
23
24 if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel)
25 n_chans = 2;
26
27 if (hapd->iconf->ieee80211ac) {
28 switch (hapd->iconf->vht_oper_chwidth) {
29 case VHT_CHANWIDTH_USE_HT:
30 break;
31 case VHT_CHANWIDTH_80MHZ:
32 n_chans = 4;
33 break;
34 case VHT_CHANWIDTH_160MHZ:
35 n_chans = 8;
36 break;
37 default:
38 break;
39 }
40 }
41
42 return n_chans;
43}
44
45
46static int dfs_channel_available(struct hostapd_channel_data *chan)
47{
48 if (chan->flag & HOSTAPD_CHAN_DISABLED)
49 return 0;
50 if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
51 ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
52 HOSTAPD_CHAN_DFS_UNAVAILABLE))
53 return 0;
54 return 1;
55}
56
57
58static int dfs_is_ht40_allowed(struct hostapd_channel_data *chan)
59{
60 int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
61 184, 192 };
62 unsigned int i;
63
64 for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++) {
65 if (chan->chan == allowed[i])
66 return 1;
67 }
68
69 return 0;
70}
71
72
73static int dfs_find_channel(struct hostapd_data *hapd,
74 struct hostapd_channel_data **ret_chan,
75 int idx)
76{
77 struct hostapd_hw_modes *mode;
78 struct hostapd_channel_data *chan, *next_chan;
79 int i, j, channel_idx = 0, n_chans;
80
81 mode = hapd->iface->current_mode;
82 n_chans = dfs_get_used_n_chans(hapd);
83
84 wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
85 for (i = 0; i < mode->num_channels; i++) {
86 chan = &mode->channels[i];
87
88 /* Skip not available channels */
89 if (!dfs_channel_available(chan))
90 continue;
91
92 /* Skip HT40/VHT uncompatible channels */
93 if (hapd->iconf->ieee80211n &&
94 hapd->iconf->secondary_channel) {
95 if (!dfs_is_ht40_allowed(chan))
96 continue;
97
98 for (j = 1; j < n_chans; j++) {
99 next_chan = &mode->channels[i + j];
100 if (!dfs_channel_available(next_chan))
101 break;
102 }
103 if (j != n_chans)
104 continue;
105
106 /* Set HT40+ */
107 hapd->iconf->secondary_channel = 1;
108 }
109
110 if (ret_chan && idx == channel_idx) {
111 wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
112 *ret_chan = chan;
113 return idx;
114 }
115 wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
116 channel_idx++;
117 }
118 return channel_idx;
119}
120
121
122static void dfs_adjust_vht_center_freq(struct hostapd_data *hapd,
123 struct hostapd_channel_data *chan)
124{
125 if (!hapd->iconf->ieee80211ac)
126 return;
127
128 if (!chan)
129 return;
130
131 switch (hapd->iconf->vht_oper_chwidth) {
132 case VHT_CHANWIDTH_USE_HT:
133 hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 2;
134 break;
135 case VHT_CHANWIDTH_80MHZ:
136 hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6;
137 break;
138 case VHT_CHANWIDTH_160MHZ:
139 hapd->iconf->vht_oper_centr_freq_seg0_idx =
140 chan->chan + 14;
141 default:
142 wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
143 break;
144 }
145
146 wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d",
147 hapd->iconf->vht_oper_centr_freq_seg0_idx);
148}
149
150
151/* Return start channel idx we will use for mode->channels[idx] */
152static int dfs_get_start_chan_idx(struct hostapd_data *hapd)
153{
154 struct hostapd_hw_modes *mode;
155 struct hostapd_channel_data *chan;
156 int channel_no = hapd->iconf->channel;
157 int res = -1, i;
158
159 /* HT40- */
160 if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1)
161 channel_no -= 4;
162
163 /* VHT */
164 if (hapd->iconf->ieee80211ac) {
165 switch (hapd->iconf->vht_oper_chwidth) {
166 case VHT_CHANWIDTH_USE_HT:
167 break;
168 case VHT_CHANWIDTH_80MHZ:
169 channel_no =
170 hapd->iconf->vht_oper_centr_freq_seg0_idx - 6;
171 break;
172 case VHT_CHANWIDTH_160MHZ:
173 channel_no =
174 hapd->iconf->vht_oper_centr_freq_seg0_idx - 14;
175 break;
176 default:
177 wpa_printf(MSG_INFO,
178 "DFS only VHT20/40/80/160 is supported now");
179 channel_no = -1;
180 break;
181 }
182 }
183
184 /* Get idx */
185 mode = hapd->iface->current_mode;
186 for (i = 0; i < mode->num_channels; i++) {
187 chan = &mode->channels[i];
188 if (chan->chan == channel_no) {
189 res = i;
190 break;
191 }
192 }
193
194 if (res == -1)
195 wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
196
197 return res;
198}
199
200
201/* At least one channel have radar flag */
202static int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx,
203 int n_chans)
204{
205 struct hostapd_channel_data *channel;
206 struct hostapd_hw_modes *mode;
207 int i, res = 0;
208
209 mode = hapd->iface->current_mode;
210
211 for (i = 0; i < n_chans; i++) {
212 channel = &mode->channels[start_chan_idx + i];
213 if (channel->flag & HOSTAPD_CHAN_RADAR)
214 res++;
215 }
216
217 return res;
218}
219
220
221/* All channels available */
222static int dfs_check_chans_available(struct hostapd_data *hapd,
223 int start_chan_idx, int n_chans)
224{
225 struct hostapd_channel_data *channel;
226 struct hostapd_hw_modes *mode;
227 int i;
228
229 mode = hapd->iface->current_mode;
230
231 for(i = 0; i < n_chans; i++) {
232 channel = &mode->channels[start_chan_idx + i];
233 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
234 HOSTAPD_CHAN_DFS_AVAILABLE)
235 break;
236 }
237
238 return i == n_chans;
239}
240
241
242/* At least one channel unavailable */
243static int dfs_check_chans_unavailable(struct hostapd_data *hapd,
244 int start_chan_idx,
245 int n_chans)
246{
247 struct hostapd_channel_data *channel;
248 struct hostapd_hw_modes *mode;
249 int i, res = 0;
250
251 mode = hapd->iface->current_mode;
252
253 for(i = 0; i < n_chans; i++) {
254 channel = &mode->channels[start_chan_idx + i];
255 if (channel->flag & HOSTAPD_CHAN_DISABLED)
256 res++;
257 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
258 HOSTAPD_CHAN_DFS_UNAVAILABLE)
259 res++;
260 }
261
262 return res;
263}
264
265
266static struct hostapd_channel_data * dfs_get_valid_channel(
267 struct hostapd_data *hapd)
268{
269 struct hostapd_hw_modes *mode;
270 struct hostapd_channel_data *chan = NULL;
271 int channel_idx, new_channel_idx;
272 u32 _rand;
273
274 wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
275
276 if (hapd->iface->current_mode == NULL)
277 return NULL;
278
279 mode = hapd->iface->current_mode;
280 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
281 return NULL;
282
283 /* get random available channel */
284 channel_idx = dfs_find_channel(hapd, NULL, 0);
285 if (channel_idx > 0) {
286 os_get_random((u8 *) &_rand, sizeof(_rand));
287 new_channel_idx = _rand % channel_idx;
288 dfs_find_channel(hapd, &chan, new_channel_idx);
289 }
290
291 /* VHT */
292 dfs_adjust_vht_center_freq(hapd, chan);
293
294 return chan;
295}
296
297
298static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state)
299{
300 struct hostapd_hw_modes *mode;
301 struct hostapd_channel_data *chan = NULL;
302 int i;
303
304 mode = hapd->iface->current_mode;
305 if (mode == NULL)
306 return 0;
307
308 wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
309 for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
310 chan = &hapd->iface->current_mode->channels[i];
311 if (chan->freq == freq) {
312 if (chan->flag & HOSTAPD_CHAN_RADAR) {
313 chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
314 chan->flag |= state;
315 return 1; /* Channel found */
316 }
317 }
318 }
319 wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
320 return 0;
321}
322
323
324static int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled,
325 int chan_offset, int chan_width, int cf1,
326 int cf2, u32 state)
327{
328 int n_chans = 1, i;
329 struct hostapd_hw_modes *mode;
330 int frequency = freq;
331 int ret = 0;
332
333 mode = hapd->iface->current_mode;
334 if (mode == NULL)
335 return 0;
336
337 if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
338 wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
339 return 0;
340 }
341
342 /* Seems cf1 and chan_width is enough here */
343 switch (chan_width) {
344 case CHAN_WIDTH_20_NOHT:
345 case CHAN_WIDTH_20:
346 n_chans = 1;
347 frequency = cf1;
348 break;
349 case CHAN_WIDTH_40:
350 n_chans = 2;
351 frequency = cf1 - 10;
352 break;
353 case CHAN_WIDTH_80:
354 n_chans = 4;
355 frequency = cf1 - 30;
356 break;
357 case CHAN_WIDTH_160:
358 n_chans = 8;
359 frequency = cf1 - 70;
360 break;
361 default:
362 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
363 chan_width);
364 break;
365 }
366
367 wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
368 n_chans);
369 for (i = 0; i < n_chans; i++) {
370 ret += set_dfs_state_freq(hapd, frequency, state);
371 frequency = frequency + 20;
372 }
373
374 return ret;
375}
376
377
378static int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq,
379 int chan_width, int cf1, int cf2)
380{
381 int start_chan_idx;
382 struct hostapd_hw_modes *mode;
383 struct hostapd_channel_data *chan;
384 int n_chans, i, j, frequency = freq, radar_n_chans = 1;
385 u8 radar_chan;
386 int res = 0;
387
388 if (hapd->iface->freq == freq)
389 res++;
390
391 /* Our configuration */
392 mode = hapd->iface->current_mode;
393 start_chan_idx = dfs_get_start_chan_idx(hapd);
394 n_chans = dfs_get_used_n_chans(hapd);
395
396 /* Reported via radar event */
397 switch (chan_width) {
398 case CHAN_WIDTH_20_NOHT:
399 case CHAN_WIDTH_20:
400 radar_n_chans = 1;
401 frequency = cf1;
402 break;
403 case CHAN_WIDTH_40:
404 radar_n_chans = 2;
405 frequency = cf1 - 10;
406 break;
407 case CHAN_WIDTH_80:
408 radar_n_chans = 4;
409 frequency = cf1 - 30;
410 break;
411 case CHAN_WIDTH_160:
412 radar_n_chans = 8;
413 frequency = cf1 - 70;
414 break;
415 default:
416 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
417 chan_width);
418 break;
419 }
420
421 ieee80211_freq_to_chan(frequency, &radar_chan);
422
423 for (i = 0; i < n_chans; i++) {
424 chan = &mode->channels[start_chan_idx + i];
425 for (j = 0; j < radar_n_chans; j++) {
426 wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
427 chan->chan, radar_chan + j * 4);
428 if (chan->chan == radar_chan + j * 4)
429 res++;
430 }
431 }
432
433 wpa_printf(MSG_DEBUG, "overlapped: %d", res);
434
435 return res;
436}
437
438
439/*
440 * Main DFS handler
441 * 1 - continue channel/ap setup
442 * 0 - channel/ap setup will be continued after CAC
443 * -1 - hit critical error
444 */
445int hostapd_handle_dfs(struct hostapd_data *hapd)
446{
447 struct hostapd_channel_data *channel;
448 int res, n_chans, start_chan_idx;
449
450 do {
451 /* Get start (first) channel for current configuration */
452 start_chan_idx = dfs_get_start_chan_idx(hapd);
453 if (start_chan_idx == -1)
454 return -1;
455
456 /* Get number of used channels, depend on width */
457 n_chans = dfs_get_used_n_chans(hapd);
458
459 /* Check if any of configured channels require DFS */
460 res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans);
461 wpa_printf(MSG_DEBUG,
462 "DFS %d channels required radar detection",
463 res);
464 if (!res)
465 return 1;
466
467 /* Check if all channels are DFS available */
468 res = dfs_check_chans_available(hapd, start_chan_idx, n_chans);
469 wpa_printf(MSG_DEBUG,
470 "DFS all channels available, (SKIP CAC): %s",
471 res ? "yes" : "no");
472 if (res)
473 return 1;
474
475 /* Check if any of configured channels is unavailable */
476 res = dfs_check_chans_unavailable(hapd, start_chan_idx,
477 n_chans);
478 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
479 res, res ? "yes": "no");
480 if (res) {
481 channel = dfs_get_valid_channel(hapd);
482 if (!channel) {
483 wpa_printf(MSG_ERROR, "could not get valid channel");
484 return -1;
485 }
486 hapd->iconf->channel = channel->chan;
487 hapd->iface->freq = channel->freq;
488 }
489 } while (res);
490
491 /* Finally start CAC */
492 wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq);
493 if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode,
494 hapd->iface->freq,
495 hapd->iconf->channel,
496 hapd->iconf->ieee80211n,
497 hapd->iconf->ieee80211ac,
498 hapd->iconf->secondary_channel,
499 hapd->iconf->vht_oper_chwidth,
500 hapd->iconf->vht_oper_centr_freq_seg0_idx,
501 hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
502 wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
503 return -1;
504 }
505
506 return 0;
507}
508
509
510int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
511 int ht_enabled, int chan_offset, int chan_width,
512 int cf1, int cf2)
513{
514 struct hostapd_channel_data *channel;
515 int err = 1;
516
517 if (success) {
518 /* Complete iface/ap configuration */
519 set_dfs_state(hapd, freq, ht_enabled, chan_offset,
520 chan_width, cf1, cf2,
521 HOSTAPD_CHAN_DFS_AVAILABLE);
522 hostapd_setup_interface_complete(hapd->iface, 0);
523 } else {
524 /* Switch to new channel */
525 set_dfs_state(hapd, freq, ht_enabled, chan_offset,
526 chan_width, cf1, cf2,
527 HOSTAPD_CHAN_DFS_UNAVAILABLE);
528 channel = dfs_get_valid_channel(hapd);
529 if (channel) {
530 hapd->iconf->channel = channel->chan;
531 hapd->iface->freq = channel->freq;
532 err = 0;
533 } else
534 wpa_printf(MSG_ERROR, "No valid channel available");
535
536 hostapd_setup_interface_complete(hapd->iface, err);
537 }
538
539 return 0;
540}
541
542
543static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd)
544{
545 struct hostapd_channel_data *channel;
546 int err = 1;
547
548 wpa_printf(MSG_DEBUG, "%s called", __func__);
549 channel = dfs_get_valid_channel(hapd);
550 if (channel) {
551 hapd->iconf->channel = channel->chan;
552 hapd->iface->freq = channel->freq;
553 err = 0;
554 }
555
556 hapd->driver->stop_ap(hapd->drv_priv);
557
558 hostapd_setup_interface_complete(hapd->iface, err);
559 return 0;
560}
561
562
563int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
564 int ht_enabled, int chan_offset, int chan_width,
565 int cf1, int cf2)
566{
567 int res;
568
569 if (!hapd->iconf->ieee80211h)
570 return 0;
571
572 /* mark radar frequency as invalid */
573 res = set_dfs_state(hapd, freq, ht_enabled, chan_offset,
574 chan_width, cf1, cf2,
575 HOSTAPD_CHAN_DFS_UNAVAILABLE);
576
577 /* Skip if reported radar event not overlapped our channels */
578 res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2);
579 if (!res)
580 return 0;
581
582 /* we are working on non-DFS channel - skip event */
583 if (res == 0)
584 return 0;
585
586 /* radar detected while operating, switch the channel. */
587 res = hostapd_dfs_start_channel_switch(hapd);
588
589 return res;
590}
591
592
593int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
594 int ht_enabled, int chan_offset, int chan_width,
595 int cf1, int cf2)
596{
597 /* TODO add correct implementation here */
598 set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
599 HOSTAPD_CHAN_DFS_USABLE);
600 return 0;
601}