blob: 0a909f4af010949dc2b10c458dafdb6c23bfd6f1 [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"
Dmitry Shmidtcce06662013-11-04 18:44:24 -080014#include "common/wpa_ctrl.h"
Dmitry Shmidt051af732013-10-22 13:52:46 -070015#include "hostapd.h"
16#include "ap_drv_ops.h"
17#include "drivers/driver.h"
18#include "dfs.h"
19
20
Dmitry Shmidtcce06662013-11-04 18:44:24 -080021static int dfs_get_used_n_chans(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -070022{
23 int n_chans = 1;
24
Dmitry Shmidtcce06662013-11-04 18:44:24 -080025 if (iface->conf->ieee80211n && iface->conf->secondary_channel)
Dmitry Shmidt051af732013-10-22 13:52:46 -070026 n_chans = 2;
27
Dmitry Shmidtcce06662013-11-04 18:44:24 -080028 if (iface->conf->ieee80211ac) {
29 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -070030 case VHT_CHANWIDTH_USE_HT:
31 break;
32 case VHT_CHANWIDTH_80MHZ:
33 n_chans = 4;
34 break;
35 case VHT_CHANWIDTH_160MHZ:
36 n_chans = 8;
37 break;
38 default:
39 break;
40 }
41 }
42
43 return n_chans;
44}
45
46
47static int dfs_channel_available(struct hostapd_channel_data *chan)
48{
49 if (chan->flag & HOSTAPD_CHAN_DISABLED)
50 return 0;
51 if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
52 ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
53 HOSTAPD_CHAN_DFS_UNAVAILABLE))
54 return 0;
55 return 1;
56}
57
58
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070059static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
Dmitry Shmidt051af732013-10-22 13:52:46 -070060{
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070061 /*
62 * The tables contain first valid channel number based on channel width.
63 * We will also choose this first channel as the control one.
64 */
65 int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
66 184, 192 };
67 /*
68 * VHT80, valid channels based on center frequency:
69 * 42, 58, 106, 122, 138, 155
70 */
71 int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
72 int *allowed = allowed_40;
73 unsigned int i, allowed_no = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -070074
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070075 switch (n_chans) {
76 case 2:
77 allowed = allowed_40;
78 allowed_no = ARRAY_SIZE(allowed_40);
79 break;
80 case 4:
81 allowed = allowed_80;
82 allowed_no = ARRAY_SIZE(allowed_80);
83 break;
84 default:
85 wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
86 break;
87 }
88
89 for (i = 0; i < allowed_no; i++) {
Dmitry Shmidt051af732013-10-22 13:52:46 -070090 if (chan->chan == allowed[i])
91 return 1;
92 }
93
94 return 0;
95}
96
97
Dmitry Shmidtcce06662013-11-04 18:44:24 -080098static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
99 int first_chan_idx, int num_chans)
100{
101 struct hostapd_channel_data *first_chan, *chan;
102 int i;
103
104 if (first_chan_idx + num_chans >= mode->num_channels)
105 return 0;
106
107 first_chan = &mode->channels[first_chan_idx];
108
109 for (i = 0; i < num_chans; i++) {
110 chan = &mode->channels[first_chan_idx + i];
111
112 if (first_chan->freq + i * 20 != chan->freq)
113 return 0;
114
115 if (!dfs_channel_available(chan))
116 return 0;
117 }
118
119 return 1;
120}
121
122
123/*
124 * The function assumes HT40+ operation.
125 * Make sure to adjust the following variables after calling this:
126 * - hapd->secondary_channel
127 * - hapd->vht_oper_centr_freq_seg0_idx
128 * - hapd->vht_oper_centr_freq_seg1_idx
129 */
130static int dfs_find_channel(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700131 struct hostapd_channel_data **ret_chan,
132 int idx)
133{
134 struct hostapd_hw_modes *mode;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800135 struct hostapd_channel_data *chan;
136 int i, channel_idx = 0, n_chans;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700137
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800138 mode = iface->current_mode;
139 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700140
141 wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
142 for (i = 0; i < mode->num_channels; i++) {
143 chan = &mode->channels[i];
144
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800145 /* Skip HT40/VHT incompatible channels */
146 if (iface->conf->ieee80211n &&
147 iface->conf->secondary_channel &&
148 !dfs_is_chan_allowed(chan, n_chans))
Dmitry Shmidt051af732013-10-22 13:52:46 -0700149 continue;
150
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800151 /* Skip incompatible chandefs */
152 if (!dfs_chan_range_available(mode, i, n_chans))
153 continue;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700154
155 if (ret_chan && idx == channel_idx) {
156 wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
157 *ret_chan = chan;
158 return idx;
159 }
160 wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
161 channel_idx++;
162 }
163 return channel_idx;
164}
165
166
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800167static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
168 struct hostapd_channel_data *chan,
169 u8 *vht_oper_centr_freq_seg0_idx,
170 u8 *vht_oper_centr_freq_seg1_idx)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700171{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800172 if (!iface->conf->ieee80211ac)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700173 return;
174
175 if (!chan)
176 return;
177
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800178 *vht_oper_centr_freq_seg1_idx = 0;
179
180 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700181 case VHT_CHANWIDTH_USE_HT:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800182 if (iface->conf->secondary_channel == 1)
183 *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
184 else if (iface->conf->secondary_channel == -1)
185 *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700186 else
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800187 *vht_oper_centr_freq_seg0_idx = chan->chan;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700188 break;
189 case VHT_CHANWIDTH_80MHZ:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800190 *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700191 break;
192 case VHT_CHANWIDTH_160MHZ:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800193 *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700194 break;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700195 default:
196 wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
197 break;
198 }
199
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800200 wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
201 *vht_oper_centr_freq_seg0_idx,
202 *vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700203}
204
205
206/* Return start channel idx we will use for mode->channels[idx] */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800207static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700208{
209 struct hostapd_hw_modes *mode;
210 struct hostapd_channel_data *chan;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800211 int channel_no = iface->conf->channel;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700212 int res = -1, i;
213
214 /* HT40- */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800215 if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700216 channel_no -= 4;
217
218 /* VHT */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800219 if (iface->conf->ieee80211ac) {
220 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700221 case VHT_CHANWIDTH_USE_HT:
222 break;
223 case VHT_CHANWIDTH_80MHZ:
224 channel_no =
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800225 iface->conf->vht_oper_centr_freq_seg0_idx - 6;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700226 break;
227 case VHT_CHANWIDTH_160MHZ:
228 channel_no =
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800229 iface->conf->vht_oper_centr_freq_seg0_idx - 14;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700230 break;
231 default:
232 wpa_printf(MSG_INFO,
233 "DFS only VHT20/40/80/160 is supported now");
234 channel_no = -1;
235 break;
236 }
237 }
238
239 /* Get idx */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800240 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700241 for (i = 0; i < mode->num_channels; i++) {
242 chan = &mode->channels[i];
243 if (chan->chan == channel_no) {
244 res = i;
245 break;
246 }
247 }
248
249 if (res == -1)
250 wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
251
252 return res;
253}
254
255
256/* At least one channel have radar flag */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800257static int dfs_check_chans_radar(struct hostapd_iface *iface,
258 int start_chan_idx, int n_chans)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700259{
260 struct hostapd_channel_data *channel;
261 struct hostapd_hw_modes *mode;
262 int i, res = 0;
263
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800264 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700265
266 for (i = 0; i < n_chans; i++) {
267 channel = &mode->channels[start_chan_idx + i];
268 if (channel->flag & HOSTAPD_CHAN_RADAR)
269 res++;
270 }
271
272 return res;
273}
274
275
276/* All channels available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800277static int dfs_check_chans_available(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700278 int start_chan_idx, int n_chans)
279{
280 struct hostapd_channel_data *channel;
281 struct hostapd_hw_modes *mode;
282 int i;
283
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800284 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700285
286 for(i = 0; i < n_chans; i++) {
287 channel = &mode->channels[start_chan_idx + i];
288 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
289 HOSTAPD_CHAN_DFS_AVAILABLE)
290 break;
291 }
292
293 return i == n_chans;
294}
295
296
297/* At least one channel unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800298static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700299 int start_chan_idx,
300 int n_chans)
301{
302 struct hostapd_channel_data *channel;
303 struct hostapd_hw_modes *mode;
304 int i, res = 0;
305
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800306 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700307
308 for(i = 0; i < n_chans; i++) {
309 channel = &mode->channels[start_chan_idx + i];
310 if (channel->flag & HOSTAPD_CHAN_DISABLED)
311 res++;
312 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
313 HOSTAPD_CHAN_DFS_UNAVAILABLE)
314 res++;
315 }
316
317 return res;
318}
319
320
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800321static struct hostapd_channel_data *
322dfs_get_valid_channel(struct hostapd_iface *iface,
323 int *secondary_channel,
324 u8 *vht_oper_centr_freq_seg0_idx,
325 u8 *vht_oper_centr_freq_seg1_idx)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700326{
327 struct hostapd_hw_modes *mode;
328 struct hostapd_channel_data *chan = NULL;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800329 int num_available_chandefs;
330 int chan_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700331 u32 _rand;
332
333 wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
334
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800335 if (iface->current_mode == NULL)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700336 return NULL;
337
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800338 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700339 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
340 return NULL;
341
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800342 /* Get the count first */
343 num_available_chandefs = dfs_find_channel(iface, NULL, 0);
344 if (num_available_chandefs == 0)
345 return NULL;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700346
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800347 os_get_random((u8 *) &_rand, sizeof(_rand));
348 chan_idx = _rand % num_available_chandefs;
349 dfs_find_channel(iface, &chan, chan_idx);
350
351 /* dfs_find_channel() calculations assume HT40+ */
352 if (iface->conf->secondary_channel)
353 *secondary_channel = 1;
354 else
355 *secondary_channel = 0;
356
357 dfs_adjust_vht_center_freq(iface, chan,
358 vht_oper_centr_freq_seg0_idx,
359 vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700360
361 return chan;
362}
363
364
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800365static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700366{
367 struct hostapd_hw_modes *mode;
368 struct hostapd_channel_data *chan = NULL;
369 int i;
370
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800371 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700372 if (mode == NULL)
373 return 0;
374
375 wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800376 for (i = 0; i < iface->current_mode->num_channels; i++) {
377 chan = &iface->current_mode->channels[i];
Dmitry Shmidt051af732013-10-22 13:52:46 -0700378 if (chan->freq == freq) {
379 if (chan->flag & HOSTAPD_CHAN_RADAR) {
380 chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
381 chan->flag |= state;
382 return 1; /* Channel found */
383 }
384 }
385 }
386 wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
387 return 0;
388}
389
390
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800391static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700392 int chan_offset, int chan_width, int cf1,
393 int cf2, u32 state)
394{
395 int n_chans = 1, i;
396 struct hostapd_hw_modes *mode;
397 int frequency = freq;
398 int ret = 0;
399
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800400 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700401 if (mode == NULL)
402 return 0;
403
404 if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
405 wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
406 return 0;
407 }
408
409 /* Seems cf1 and chan_width is enough here */
410 switch (chan_width) {
411 case CHAN_WIDTH_20_NOHT:
412 case CHAN_WIDTH_20:
413 n_chans = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800414 if (frequency == 0)
415 frequency = cf1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700416 break;
417 case CHAN_WIDTH_40:
418 n_chans = 2;
419 frequency = cf1 - 10;
420 break;
421 case CHAN_WIDTH_80:
422 n_chans = 4;
423 frequency = cf1 - 30;
424 break;
425 case CHAN_WIDTH_160:
426 n_chans = 8;
427 frequency = cf1 - 70;
428 break;
429 default:
430 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
431 chan_width);
432 break;
433 }
434
435 wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
436 n_chans);
437 for (i = 0; i < n_chans; i++) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800438 ret += set_dfs_state_freq(iface, frequency, state);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700439 frequency = frequency + 20;
440 }
441
442 return ret;
443}
444
445
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800446static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700447 int chan_width, int cf1, int cf2)
448{
449 int start_chan_idx;
450 struct hostapd_hw_modes *mode;
451 struct hostapd_channel_data *chan;
452 int n_chans, i, j, frequency = freq, radar_n_chans = 1;
453 u8 radar_chan;
454 int res = 0;
455
Dmitry Shmidt051af732013-10-22 13:52:46 -0700456 /* Our configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800457 mode = iface->current_mode;
458 start_chan_idx = dfs_get_start_chan_idx(iface);
459 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700460
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700461 /* Check we are on DFS channel(s) */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800462 if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700463 return 0;
464
Dmitry Shmidt051af732013-10-22 13:52:46 -0700465 /* Reported via radar event */
466 switch (chan_width) {
467 case CHAN_WIDTH_20_NOHT:
468 case CHAN_WIDTH_20:
469 radar_n_chans = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800470 if (frequency == 0)
471 frequency = cf1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700472 break;
473 case CHAN_WIDTH_40:
474 radar_n_chans = 2;
475 frequency = cf1 - 10;
476 break;
477 case CHAN_WIDTH_80:
478 radar_n_chans = 4;
479 frequency = cf1 - 30;
480 break;
481 case CHAN_WIDTH_160:
482 radar_n_chans = 8;
483 frequency = cf1 - 70;
484 break;
485 default:
486 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
487 chan_width);
488 break;
489 }
490
491 ieee80211_freq_to_chan(frequency, &radar_chan);
492
493 for (i = 0; i < n_chans; i++) {
494 chan = &mode->channels[start_chan_idx + i];
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700495 if (!(chan->flag & HOSTAPD_CHAN_RADAR))
496 continue;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700497 for (j = 0; j < radar_n_chans; j++) {
498 wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
499 chan->chan, radar_chan + j * 4);
500 if (chan->chan == radar_chan + j * 4)
501 res++;
502 }
503 }
504
505 wpa_printf(MSG_DEBUG, "overlapped: %d", res);
506
507 return res;
508}
509
510
511/*
512 * Main DFS handler
513 * 1 - continue channel/ap setup
514 * 0 - channel/ap setup will be continued after CAC
515 * -1 - hit critical error
516 */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800517int hostapd_handle_dfs(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700518{
519 struct hostapd_channel_data *channel;
520 int res, n_chans, start_chan_idx;
521
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800522 iface->cac_started = 0;
523
Dmitry Shmidt051af732013-10-22 13:52:46 -0700524 do {
525 /* Get start (first) channel for current configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800526 start_chan_idx = dfs_get_start_chan_idx(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700527 if (start_chan_idx == -1)
528 return -1;
529
530 /* Get number of used channels, depend on width */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800531 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700532
533 /* Check if any of configured channels require DFS */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800534 res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700535 wpa_printf(MSG_DEBUG,
536 "DFS %d channels required radar detection",
537 res);
538 if (!res)
539 return 1;
540
541 /* Check if all channels are DFS available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800542 res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700543 wpa_printf(MSG_DEBUG,
544 "DFS all channels available, (SKIP CAC): %s",
545 res ? "yes" : "no");
546 if (res)
547 return 1;
548
549 /* Check if any of configured channels is unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800550 res = dfs_check_chans_unavailable(iface, start_chan_idx,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700551 n_chans);
552 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
553 res, res ? "yes": "no");
554 if (res) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800555 int sec;
556 u8 cf1, cf2;
557
558 channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700559 if (!channel) {
560 wpa_printf(MSG_ERROR, "could not get valid channel");
561 return -1;
562 }
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800563
564 iface->freq = channel->freq;
565 iface->conf->channel = channel->chan;
566 iface->conf->secondary_channel = sec;
567 iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
568 iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700569 }
570 } while (res);
571
572 /* Finally start CAC */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800573 hostapd_set_state(iface, HAPD_IFACE_DFS);
574 wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
575 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
576 "freq=%d chan=%d sec_chan=%d",
577 iface->freq,
578 iface->conf->channel, iface->conf->secondary_channel);
579 if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
580 iface->freq,
581 iface->conf->channel,
582 iface->conf->ieee80211n,
583 iface->conf->ieee80211ac,
584 iface->conf->secondary_channel,
585 iface->conf->vht_oper_chwidth,
586 iface->conf->vht_oper_centr_freq_seg0_idx,
587 iface->conf->vht_oper_centr_freq_seg1_idx)) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700588 wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
589 return -1;
590 }
591
592 return 0;
593}
594
595
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800596int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700597 int ht_enabled, int chan_offset, int chan_width,
598 int cf1, int cf2)
599{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800600 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
601 "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
602 success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
603
Dmitry Shmidt051af732013-10-22 13:52:46 -0700604 if (success) {
605 /* Complete iface/ap configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800606 set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700607 chan_width, cf1, cf2,
608 HOSTAPD_CHAN_DFS_AVAILABLE);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800609 iface->cac_started = 0;
610 hostapd_setup_interface_complete(iface, 0);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700611 }
612
613 return 0;
614}
615
616
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800617static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700618{
619 struct hostapd_channel_data *channel;
620 int err = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800621 int secondary_channel;
622 u8 vht_oper_centr_freq_seg0_idx;
623 u8 vht_oper_centr_freq_seg1_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700624
625 wpa_printf(MSG_DEBUG, "%s called", __func__);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800626 channel = dfs_get_valid_channel(iface, &secondary_channel,
627 &vht_oper_centr_freq_seg0_idx,
628 &vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700629 if (channel) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800630 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
631 channel->chan);
632 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
633 "freq=%d chan=%d sec_chan=%d", channel->freq,
634 channel->chan, secondary_channel);
635
636 iface->freq = channel->freq;
637 iface->conf->channel = channel->chan;
638 iface->conf->secondary_channel = secondary_channel;
639 iface->conf->vht_oper_centr_freq_seg0_idx =
640 vht_oper_centr_freq_seg0_idx;
641 iface->conf->vht_oper_centr_freq_seg1_idx =
642 vht_oper_centr_freq_seg1_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700643 err = 0;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700644 } else {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800645 wpa_printf(MSG_ERROR, "No valid channel available");
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700646 }
Dmitry Shmidt051af732013-10-22 13:52:46 -0700647
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800648 if (iface->cac_started) {
649 wpa_printf(MSG_DEBUG, "DFS radar detected during CAC");
650 iface->cac_started = 0;
651 /* FIXME: Wait for channel(s) to become available if no channel
652 * has been found */
653 hostapd_setup_interface_complete(iface, err);
654 return err;
655 }
656
657 if (err) {
658 /* FIXME: Wait for channel(s) to become available */
659 hostapd_disable_iface(iface);
660 return err;
661 }
662
663 wpa_printf(MSG_DEBUG, "DFS radar detected");
664 hostapd_disable_iface(iface);
665 hostapd_enable_iface(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700666 return 0;
667}
668
669
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800670int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700671 int ht_enabled, int chan_offset, int chan_width,
672 int cf1, int cf2)
673{
674 int res;
675
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800676 if (!iface->conf->ieee80211h)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700677 return 0;
678
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800679 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
680 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
681 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
682
Dmitry Shmidt051af732013-10-22 13:52:46 -0700683 /* mark radar frequency as invalid */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800684 res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700685 chan_width, cf1, cf2,
686 HOSTAPD_CHAN_DFS_UNAVAILABLE);
687
688 /* Skip if reported radar event not overlapped our channels */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800689 res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700690 if (!res)
691 return 0;
692
Dmitry Shmidt051af732013-10-22 13:52:46 -0700693 /* radar detected while operating, switch the channel. */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800694 res = hostapd_dfs_start_channel_switch(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700695
696 return res;
697}
698
699
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800700int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700701 int ht_enabled, int chan_offset, int chan_width,
702 int cf1, int cf2)
703{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800704 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
705 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
706 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700707 /* TODO add correct implementation here */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800708 set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
709 cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700710 return 0;
711}