LCOV - code coverage report
Current view: top level - src/ap - dfs.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1401264779 Lines: 107 445 24.0 %
Date: 2014-05-28 Functions: 9 23 39.1 %

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

Generated by: LCOV version 1.10