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