blob: caf40927f4c59d7200ff2f70c53ae2cb4579d636 [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
Dmitry Shmidt04f534e2013-12-09 15:50:16 -080047static int dfs_channel_available(struct hostapd_channel_data *chan,
48 int skip_radar)
Dmitry Shmidt051af732013-10-22 13:52:46 -070049{
Dmitry Shmidt04f534e2013-12-09 15:50:16 -080050 /*
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 if (skip_radar && chan->flag & HOSTAPD_CHAN_RADAR)
56 return 0;
57
Dmitry Shmidt051af732013-10-22 13:52:46 -070058 if (chan->flag & HOSTAPD_CHAN_DISABLED)
59 return 0;
60 if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
61 ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
62 HOSTAPD_CHAN_DFS_UNAVAILABLE))
63 return 0;
64 return 1;
65}
66
67
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070068static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
Dmitry Shmidt051af732013-10-22 13:52:46 -070069{
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070070 /*
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 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 int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
81 int *allowed = allowed_40;
82 unsigned int i, allowed_no = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -070083
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070084 switch (n_chans) {
85 case 2:
86 allowed = allowed_40;
87 allowed_no = ARRAY_SIZE(allowed_40);
88 break;
89 case 4:
90 allowed = allowed_80;
91 allowed_no = ARRAY_SIZE(allowed_80);
92 break;
93 default:
94 wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
95 break;
96 }
97
98 for (i = 0; i < allowed_no; i++) {
Dmitry Shmidt051af732013-10-22 13:52:46 -070099 if (chan->chan == allowed[i])
100 return 1;
101 }
102
103 return 0;
104}
105
106
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800107static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800108 int first_chan_idx, int num_chans,
109 int skip_radar)
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800110{
111 struct hostapd_channel_data *first_chan, *chan;
112 int i;
113
114 if (first_chan_idx + num_chans >= mode->num_channels)
115 return 0;
116
117 first_chan = &mode->channels[first_chan_idx];
118
119 for (i = 0; i < num_chans; i++) {
120 chan = &mode->channels[first_chan_idx + i];
121
122 if (first_chan->freq + i * 20 != chan->freq)
123 return 0;
124
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800125 if (!dfs_channel_available(chan, skip_radar))
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800126 return 0;
127 }
128
129 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 */
140static int dfs_find_channel(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700141 struct hostapd_channel_data **ret_chan,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800142 int idx, int skip_radar)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700143{
144 struct hostapd_hw_modes *mode;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800145 struct hostapd_channel_data *chan;
146 int i, channel_idx = 0, n_chans;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700147
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800148 mode = iface->current_mode;
149 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700150
151 wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
152 for (i = 0; i < mode->num_channels; i++) {
153 chan = &mode->channels[i];
154
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800155 /* Skip HT40/VHT incompatible channels */
156 if (iface->conf->ieee80211n &&
157 iface->conf->secondary_channel &&
158 !dfs_is_chan_allowed(chan, n_chans))
Dmitry Shmidt051af732013-10-22 13:52:46 -0700159 continue;
160
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800161 /* Skip incompatible chandefs */
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800162 if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800163 continue;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700164
165 if (ret_chan && idx == channel_idx) {
166 wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
167 *ret_chan = chan;
168 return idx;
169 }
170 wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
171 channel_idx++;
172 }
173 return channel_idx;
174}
175
176
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800177static 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)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700181{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800182 if (!iface->conf->ieee80211ac)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700183 return;
184
185 if (!chan)
186 return;
187
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800188 *vht_oper_centr_freq_seg1_idx = 0;
189
190 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700191 case VHT_CHANWIDTH_USE_HT:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800192 if (iface->conf->secondary_channel == 1)
193 *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
194 else if (iface->conf->secondary_channel == -1)
195 *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700196 else
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800197 *vht_oper_centr_freq_seg0_idx = chan->chan;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700198 break;
199 case VHT_CHANWIDTH_80MHZ:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800200 *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700201 break;
202 case VHT_CHANWIDTH_160MHZ:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800203 *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700204 break;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700205 default:
206 wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
207 break;
208 }
209
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800210 wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
211 *vht_oper_centr_freq_seg0_idx,
212 *vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700213}
214
215
216/* Return start channel idx we will use for mode->channels[idx] */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800217static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700218{
219 struct hostapd_hw_modes *mode;
220 struct hostapd_channel_data *chan;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800221 int channel_no = iface->conf->channel;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700222 int res = -1, i;
223
224 /* HT40- */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800225 if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700226 channel_no -= 4;
227
228 /* VHT */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800229 if (iface->conf->ieee80211ac) {
230 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700231 case VHT_CHANWIDTH_USE_HT:
232 break;
233 case VHT_CHANWIDTH_80MHZ:
234 channel_no =
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800235 iface->conf->vht_oper_centr_freq_seg0_idx - 6;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700236 break;
237 case VHT_CHANWIDTH_160MHZ:
238 channel_no =
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800239 iface->conf->vht_oper_centr_freq_seg0_idx - 14;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700240 break;
241 default:
242 wpa_printf(MSG_INFO,
243 "DFS only VHT20/40/80/160 is supported now");
244 channel_no = -1;
245 break;
246 }
247 }
248
249 /* Get idx */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800250 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700251 for (i = 0; i < mode->num_channels; i++) {
252 chan = &mode->channels[i];
253 if (chan->chan == channel_no) {
254 res = i;
255 break;
256 }
257 }
258
259 if (res == -1)
260 wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
261
262 return res;
263}
264
265
266/* At least one channel have radar flag */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800267static int dfs_check_chans_radar(struct hostapd_iface *iface,
268 int start_chan_idx, int n_chans)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700269{
270 struct hostapd_channel_data *channel;
271 struct hostapd_hw_modes *mode;
272 int i, res = 0;
273
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800274 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700275
276 for (i = 0; i < n_chans; i++) {
277 channel = &mode->channels[start_chan_idx + i];
278 if (channel->flag & HOSTAPD_CHAN_RADAR)
279 res++;
280 }
281
282 return res;
283}
284
285
286/* All channels available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800287static int dfs_check_chans_available(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700288 int start_chan_idx, int n_chans)
289{
290 struct hostapd_channel_data *channel;
291 struct hostapd_hw_modes *mode;
292 int i;
293
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800294 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700295
296 for(i = 0; i < n_chans; i++) {
297 channel = &mode->channels[start_chan_idx + i];
298 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
299 HOSTAPD_CHAN_DFS_AVAILABLE)
300 break;
301 }
302
303 return i == n_chans;
304}
305
306
307/* At least one channel unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800308static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700309 int start_chan_idx,
310 int n_chans)
311{
312 struct hostapd_channel_data *channel;
313 struct hostapd_hw_modes *mode;
314 int i, res = 0;
315
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800316 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700317
318 for(i = 0; i < n_chans; i++) {
319 channel = &mode->channels[start_chan_idx + i];
320 if (channel->flag & HOSTAPD_CHAN_DISABLED)
321 res++;
322 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
323 HOSTAPD_CHAN_DFS_UNAVAILABLE)
324 res++;
325 }
326
327 return res;
328}
329
330
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800331static struct hostapd_channel_data *
332dfs_get_valid_channel(struct hostapd_iface *iface,
333 int *secondary_channel,
334 u8 *vht_oper_centr_freq_seg0_idx,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800335 u8 *vht_oper_centr_freq_seg1_idx,
336 int skip_radar)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700337{
338 struct hostapd_hw_modes *mode;
339 struct hostapd_channel_data *chan = NULL;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800340 int num_available_chandefs;
341 int chan_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700342 u32 _rand;
343
344 wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
345
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800346 if (iface->current_mode == NULL)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700347 return NULL;
348
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800349 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700350 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
351 return NULL;
352
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800353 /* Get the count first */
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800354 num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800355 if (num_available_chandefs == 0)
356 return NULL;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700357
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800358 os_get_random((u8 *) &_rand, sizeof(_rand));
359 chan_idx = _rand % num_available_chandefs;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800360 dfs_find_channel(iface, &chan, chan_idx, skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800361
362 /* dfs_find_channel() calculations assume HT40+ */
363 if (iface->conf->secondary_channel)
364 *secondary_channel = 1;
365 else
366 *secondary_channel = 0;
367
368 dfs_adjust_vht_center_freq(iface, chan,
369 vht_oper_centr_freq_seg0_idx,
370 vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700371
372 return chan;
373}
374
375
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800376static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700377{
378 struct hostapd_hw_modes *mode;
379 struct hostapd_channel_data *chan = NULL;
380 int i;
381
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800382 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700383 if (mode == NULL)
384 return 0;
385
386 wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800387 for (i = 0; i < iface->current_mode->num_channels; i++) {
388 chan = &iface->current_mode->channels[i];
Dmitry Shmidt051af732013-10-22 13:52:46 -0700389 if (chan->freq == freq) {
390 if (chan->flag & HOSTAPD_CHAN_RADAR) {
391 chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
392 chan->flag |= state;
393 return 1; /* Channel found */
394 }
395 }
396 }
397 wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
398 return 0;
399}
400
401
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800402static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700403 int chan_offset, int chan_width, int cf1,
404 int cf2, u32 state)
405{
406 int n_chans = 1, i;
407 struct hostapd_hw_modes *mode;
408 int frequency = freq;
409 int ret = 0;
410
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800411 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700412 if (mode == NULL)
413 return 0;
414
415 if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
416 wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
417 return 0;
418 }
419
420 /* Seems cf1 and chan_width is enough here */
421 switch (chan_width) {
422 case CHAN_WIDTH_20_NOHT:
423 case CHAN_WIDTH_20:
424 n_chans = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800425 if (frequency == 0)
426 frequency = cf1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700427 break;
428 case CHAN_WIDTH_40:
429 n_chans = 2;
430 frequency = cf1 - 10;
431 break;
432 case CHAN_WIDTH_80:
433 n_chans = 4;
434 frequency = cf1 - 30;
435 break;
436 case CHAN_WIDTH_160:
437 n_chans = 8;
438 frequency = cf1 - 70;
439 break;
440 default:
441 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
442 chan_width);
443 break;
444 }
445
446 wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
447 n_chans);
448 for (i = 0; i < n_chans; i++) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800449 ret += set_dfs_state_freq(iface, frequency, state);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700450 frequency = frequency + 20;
451 }
452
453 return ret;
454}
455
456
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800457static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700458 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 int n_chans, i, j, frequency = freq, radar_n_chans = 1;
464 u8 radar_chan;
465 int res = 0;
466
Dmitry Shmidt051af732013-10-22 13:52:46 -0700467 /* Our configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800468 mode = iface->current_mode;
469 start_chan_idx = dfs_get_start_chan_idx(iface);
470 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700471
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700472 /* Check we are on DFS channel(s) */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800473 if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700474 return 0;
475
Dmitry Shmidt051af732013-10-22 13:52:46 -0700476 /* Reported via radar event */
477 switch (chan_width) {
478 case CHAN_WIDTH_20_NOHT:
479 case CHAN_WIDTH_20:
480 radar_n_chans = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800481 if (frequency == 0)
482 frequency = cf1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700483 break;
484 case CHAN_WIDTH_40:
485 radar_n_chans = 2;
486 frequency = cf1 - 10;
487 break;
488 case CHAN_WIDTH_80:
489 radar_n_chans = 4;
490 frequency = cf1 - 30;
491 break;
492 case CHAN_WIDTH_160:
493 radar_n_chans = 8;
494 frequency = cf1 - 70;
495 break;
496 default:
497 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
498 chan_width);
499 break;
500 }
501
502 ieee80211_freq_to_chan(frequency, &radar_chan);
503
504 for (i = 0; i < n_chans; i++) {
505 chan = &mode->channels[start_chan_idx + i];
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700506 if (!(chan->flag & HOSTAPD_CHAN_RADAR))
507 continue;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700508 for (j = 0; j < radar_n_chans; j++) {
509 wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
510 chan->chan, radar_chan + j * 4);
511 if (chan->chan == radar_chan + j * 4)
512 res++;
513 }
514 }
515
516 wpa_printf(MSG_DEBUG, "overlapped: %d", res);
517
518 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 */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800528int hostapd_handle_dfs(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700529{
530 struct hostapd_channel_data *channel;
531 int res, n_chans, start_chan_idx;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800532 int skip_radar = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700533
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800534 iface->cac_started = 0;
535
Dmitry Shmidt051af732013-10-22 13:52:46 -0700536 do {
537 /* Get start (first) channel for current configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800538 start_chan_idx = dfs_get_start_chan_idx(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700539 if (start_chan_idx == -1)
540 return -1;
541
542 /* Get number of used channels, depend on width */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800543 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700544
545 /* Check if any of configured channels require DFS */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800546 res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700547 wpa_printf(MSG_DEBUG,
548 "DFS %d channels required radar detection",
549 res);
550 if (!res)
551 return 1;
552
553 /* Check if all channels are DFS available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800554 res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700555 wpa_printf(MSG_DEBUG,
556 "DFS all channels available, (SKIP CAC): %s",
557 res ? "yes" : "no");
558 if (res)
559 return 1;
560
561 /* Check if any of configured channels is unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800562 res = dfs_check_chans_unavailable(iface, start_chan_idx,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700563 n_chans);
564 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
565 res, res ? "yes": "no");
566 if (res) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800567 int sec;
568 u8 cf1, cf2;
569
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800570 channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
571 skip_radar);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700572 if (!channel) {
573 wpa_printf(MSG_ERROR, "could not get valid channel");
574 return -1;
575 }
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800576
577 iface->freq = channel->freq;
578 iface->conf->channel = channel->chan;
579 iface->conf->secondary_channel = sec;
580 iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
581 iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700582 }
583 } while (res);
584
585 /* Finally start CAC */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800586 hostapd_set_state(iface, HAPD_IFACE_DFS);
587 wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
588 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 iface->conf->channel, iface->conf->secondary_channel);
592 if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
593 iface->freq,
594 iface->conf->channel,
595 iface->conf->ieee80211n,
596 iface->conf->ieee80211ac,
597 iface->conf->secondary_channel,
598 iface->conf->vht_oper_chwidth,
599 iface->conf->vht_oper_centr_freq_seg0_idx,
600 iface->conf->vht_oper_centr_freq_seg1_idx)) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700601 wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
602 return -1;
603 }
604
605 return 0;
606}
607
608
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800609int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700610 int ht_enabled, int chan_offset, int chan_width,
611 int cf1, int cf2)
612{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800613 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
Dmitry Shmidt051af732013-10-22 13:52:46 -0700617 if (success) {
618 /* Complete iface/ap configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800619 set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700620 chan_width, cf1, cf2,
621 HOSTAPD_CHAN_DFS_AVAILABLE);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800622 iface->cac_started = 0;
623 hostapd_setup_interface_complete(iface, 0);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700624 }
625
626 return 0;
627}
628
629
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800630static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700631{
632 struct hostapd_channel_data *channel;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800633 int secondary_channel;
634 u8 vht_oper_centr_freq_seg0_idx;
635 u8 vht_oper_centr_freq_seg1_idx;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800636 int skip_radar = 0;
637 int err = 1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700638
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800639 /* Radar detected during active CAC */
640 iface->cac_started = 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800641 channel = dfs_get_valid_channel(iface, &secondary_channel,
642 &vht_oper_centr_freq_seg0_idx,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800643 &vht_oper_centr_freq_seg1_idx,
644 skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800645
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800646 if (!channel) {
647 wpa_printf(MSG_ERROR, "No valid channel available");
648 hostapd_setup_interface_complete(iface, err);
649 return err;
650 }
651
652 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
653 channel->chan);
654 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
655 "freq=%d chan=%d sec_chan=%d", channel->freq,
656 channel->chan, secondary_channel);
657
658 iface->freq = channel->freq;
659 iface->conf->channel = channel->chan;
660 iface->conf->secondary_channel = secondary_channel;
661 iface->conf->vht_oper_centr_freq_seg0_idx =
662 vht_oper_centr_freq_seg0_idx;
663 iface->conf->vht_oper_centr_freq_seg1_idx =
664 vht_oper_centr_freq_seg1_idx;
665 err = 0;
666
667 hostapd_setup_interface_complete(iface, err);
668 return err;
669}
670
671
672static 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 int skip_radar = 1;
679 struct csa_settings csa_settings;
680 struct hostapd_data *hapd = iface->bss[0];
681 int err = 1;
682
683 wpa_printf(MSG_DEBUG, "%s called (CAC active: %s)", __func__,
684 iface->cac_started ? "yes" : "no");
685
686 /* Check if active CAC */
687 if (iface->cac_started)
688 return hostapd_dfs_start_channel_switch_cac(iface);
689
690
691 /* Perform channel switch/CSA */
692 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 if (!channel) {
698 /* FIXME: Wait for channel(s) to become available */
699 hostapd_disable_iface(iface);
700 return err;
701 }
702
703 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
704 channel->chan);
705 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
706 "freq=%d chan=%d sec_chan=%d", channel->freq,
707 channel->chan, secondary_channel);
708
709 /* Setup CSA request */
710 os_memset(&csa_settings, 0, sizeof(csa_settings));
711 csa_settings.cs_count = 5;
712 csa_settings.block_tx = 1;
713 err = hostapd_set_freq_params(&csa_settings.freq_params,
714 iface->conf->hw_mode,
715 channel->freq,
716 channel->chan,
717 iface->conf->ieee80211n,
718 iface->conf->ieee80211ac,
719 secondary_channel,
720 iface->conf->vht_oper_chwidth,
721 vht_oper_centr_freq_seg0_idx,
722 vht_oper_centr_freq_seg1_idx,
723 iface->current_mode->vht_capab);
724
725 if (err) {
726 wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
727 hostapd_disable_iface(iface);
728 return err;
729 }
730
731 err = hostapd_switch_channel(hapd, &csa_settings);
732 if (err) {
733 wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
734 err);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800735 iface->freq = channel->freq;
736 iface->conf->channel = channel->chan;
737 iface->conf->secondary_channel = secondary_channel;
738 iface->conf->vht_oper_centr_freq_seg0_idx =
739 vht_oper_centr_freq_seg0_idx;
740 iface->conf->vht_oper_centr_freq_seg1_idx =
741 vht_oper_centr_freq_seg1_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700742
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800743 hostapd_disable_iface(iface);
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800744 hostapd_enable_iface(iface);
745 return 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800746 }
747
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800748 /* Channel configuration will be updated once CSA completes and
749 * ch_switch_notify event is received */
750
751 wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
Dmitry Shmidt051af732013-10-22 13:52:46 -0700752 return 0;
753}
754
755
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800756int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700757 int ht_enabled, int chan_offset, int chan_width,
758 int cf1, int cf2)
759{
760 int res;
761
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800762 if (!iface->conf->ieee80211h)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700763 return 0;
764
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800765 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
Dmitry Shmidt051af732013-10-22 13:52:46 -0700769 /* mark radar frequency as invalid */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800770 res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700771 chan_width, cf1, cf2,
772 HOSTAPD_CHAN_DFS_UNAVAILABLE);
773
774 /* Skip if reported radar event not overlapped our channels */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800775 res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700776 if (!res)
777 return 0;
778
Dmitry Shmidt051af732013-10-22 13:52:46 -0700779 /* radar detected while operating, switch the channel. */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800780 res = hostapd_dfs_start_channel_switch(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700781
782 return res;
783}
784
785
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800786int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700787 int ht_enabled, int chan_offset, int chan_width,
788 int cf1, int cf2)
789{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800790 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);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700793 /* TODO add correct implementation here */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800794 set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
795 cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700796 return 0;
797}