LCOV - code coverage report
Current view: top level - ap - dfs.c (source / functions) Hit Total Coverage
Test: hostapd hwsim test run 1412854115 Lines: 332 479 69.3 %
Date: 2014-10-09 Functions: 22 24 91.7 %

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

Generated by: LCOV version 1.10