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 1422976643 Lines: 383 483 79.3 %
Date: 2015-02-03 Functions: 23 24 95.8 %

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

Generated by: LCOV version 1.10