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