15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * DFS - Dynamic Frequency Selection 35b9c547cSRui Paulo * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 485732ac8SCy Schubert * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. 55b9c547cSRui Paulo * 65b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 75b9c547cSRui Paulo * See README for more details. 85b9c547cSRui Paulo */ 95b9c547cSRui Paulo 105b9c547cSRui Paulo #include "utils/includes.h" 115b9c547cSRui Paulo 125b9c547cSRui Paulo #include "utils/common.h" 135b9c547cSRui Paulo #include "common/ieee802_11_defs.h" 145b9c547cSRui Paulo #include "common/hw_features_common.h" 155b9c547cSRui Paulo #include "common/wpa_ctrl.h" 165b9c547cSRui Paulo #include "hostapd.h" 17*a90b9d01SCy Schubert #include "beacon.h" 185b9c547cSRui Paulo #include "ap_drv_ops.h" 195b9c547cSRui Paulo #include "drivers/driver.h" 205b9c547cSRui Paulo #include "dfs.h" 215b9c547cSRui Paulo 225b9c547cSRui Paulo 23*a90b9d01SCy Schubert enum dfs_channel_type { 24*a90b9d01SCy Schubert DFS_ANY_CHANNEL, 25*a90b9d01SCy Schubert DFS_AVAILABLE, /* non-radar or radar-available */ 26*a90b9d01SCy Schubert DFS_NO_CAC_YET, /* radar-not-yet-available */ 27*a90b9d01SCy Schubert }; 28*a90b9d01SCy Schubert 29*a90b9d01SCy Schubert static struct hostapd_channel_data * 30*a90b9d01SCy Schubert dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel, 31*a90b9d01SCy Schubert u8 *oper_centr_freq_seg0_idx, 32*a90b9d01SCy Schubert u8 *oper_centr_freq_seg1_idx, 33*a90b9d01SCy Schubert enum dfs_channel_type *channel_type); 34*a90b9d01SCy Schubert 35*a90b9d01SCy Schubert 36*a90b9d01SCy Schubert static bool dfs_use_radar_background(struct hostapd_iface *iface) 37*a90b9d01SCy Schubert { 38*a90b9d01SCy Schubert return (iface->drv_flags2 & WPA_DRIVER_FLAGS2_RADAR_BACKGROUND) && 39*a90b9d01SCy Schubert iface->conf->enable_background_radar; 40*a90b9d01SCy Schubert } 41*a90b9d01SCy Schubert 42*a90b9d01SCy Schubert 435b9c547cSRui Paulo static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) 445b9c547cSRui Paulo { 455b9c547cSRui Paulo int n_chans = 1; 465b9c547cSRui Paulo 475b9c547cSRui Paulo *seg1 = 0; 485b9c547cSRui Paulo 495b9c547cSRui Paulo if (iface->conf->ieee80211n && iface->conf->secondary_channel) 505b9c547cSRui Paulo n_chans = 2; 515b9c547cSRui Paulo 52206b73d0SCy Schubert if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { 53206b73d0SCy Schubert switch (hostapd_get_oper_chwidth(iface->conf)) { 54*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_USE_HT: 555b9c547cSRui Paulo break; 56*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_80MHZ: 575b9c547cSRui Paulo n_chans = 4; 585b9c547cSRui Paulo break; 59*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_160MHZ: 605b9c547cSRui Paulo n_chans = 8; 615b9c547cSRui Paulo break; 62*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_80P80MHZ: 635b9c547cSRui Paulo n_chans = 4; 645b9c547cSRui Paulo *seg1 = 4; 655b9c547cSRui Paulo break; 665b9c547cSRui Paulo default: 675b9c547cSRui Paulo break; 685b9c547cSRui Paulo } 695b9c547cSRui Paulo } 705b9c547cSRui Paulo 715b9c547cSRui Paulo return n_chans; 725b9c547cSRui Paulo } 735b9c547cSRui Paulo 745b9c547cSRui Paulo 75*a90b9d01SCy Schubert /* dfs_channel_available: select new channel according to type parameter */ 765b9c547cSRui Paulo static int dfs_channel_available(struct hostapd_channel_data *chan, 77*a90b9d01SCy Schubert enum dfs_channel_type type) 785b9c547cSRui Paulo { 79*a90b9d01SCy Schubert if (type == DFS_NO_CAC_YET) { 80*a90b9d01SCy Schubert /* Select only radar channel where CAC has not been 81*a90b9d01SCy Schubert * performed yet 82*a90b9d01SCy Schubert */ 83*a90b9d01SCy Schubert if ((chan->flag & HOSTAPD_CHAN_RADAR) && 84*a90b9d01SCy Schubert (chan->flag & HOSTAPD_CHAN_DFS_MASK) == 85*a90b9d01SCy Schubert HOSTAPD_CHAN_DFS_USABLE) 86*a90b9d01SCy Schubert return 1; 87*a90b9d01SCy Schubert return 0; 88*a90b9d01SCy Schubert } 89*a90b9d01SCy Schubert 905b9c547cSRui Paulo /* 915b9c547cSRui Paulo * When radar detection happens, CSA is performed. However, there's no 925b9c547cSRui Paulo * time for CAC, so radar channels must be skipped when finding a new 935b9c547cSRui Paulo * channel for CSA, unless they are available for immediate use. 945b9c547cSRui Paulo */ 95*a90b9d01SCy Schubert if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) && 965b9c547cSRui Paulo ((chan->flag & HOSTAPD_CHAN_DFS_MASK) != 975b9c547cSRui Paulo HOSTAPD_CHAN_DFS_AVAILABLE)) 985b9c547cSRui Paulo return 0; 995b9c547cSRui Paulo 1005b9c547cSRui Paulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 1015b9c547cSRui Paulo return 0; 1025b9c547cSRui Paulo if ((chan->flag & HOSTAPD_CHAN_RADAR) && 1035b9c547cSRui Paulo ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == 1045b9c547cSRui Paulo HOSTAPD_CHAN_DFS_UNAVAILABLE)) 1055b9c547cSRui Paulo return 0; 1065b9c547cSRui Paulo return 1; 1075b9c547cSRui Paulo } 1085b9c547cSRui Paulo 1095b9c547cSRui Paulo 1105b9c547cSRui Paulo static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) 1115b9c547cSRui Paulo { 1125b9c547cSRui Paulo /* 1135b9c547cSRui Paulo * The tables contain first valid channel number based on channel width. 1145b9c547cSRui Paulo * We will also choose this first channel as the control one. 1155b9c547cSRui Paulo */ 1165b9c547cSRui Paulo int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 117c1d255d3SCy Schubert 165, 173, 184, 192 }; 1185b9c547cSRui Paulo /* 1195b9c547cSRui Paulo * VHT80, valid channels based on center frequency: 120c1d255d3SCy Schubert * 42, 58, 106, 122, 138, 155, 171 1215b9c547cSRui Paulo */ 122c1d255d3SCy Schubert int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 }; 1235b9c547cSRui Paulo /* 1245b9c547cSRui Paulo * VHT160 valid channels based on center frequency: 125c1d255d3SCy Schubert * 50, 114, 163 1265b9c547cSRui Paulo */ 127c1d255d3SCy Schubert int allowed_160[] = { 36, 100, 149 }; 1285b9c547cSRui Paulo int *allowed = allowed_40; 1295b9c547cSRui Paulo unsigned int i, allowed_no = 0; 1305b9c547cSRui Paulo 1315b9c547cSRui Paulo switch (n_chans) { 1325b9c547cSRui Paulo case 2: 1335b9c547cSRui Paulo allowed = allowed_40; 1345b9c547cSRui Paulo allowed_no = ARRAY_SIZE(allowed_40); 1355b9c547cSRui Paulo break; 1365b9c547cSRui Paulo case 4: 1375b9c547cSRui Paulo allowed = allowed_80; 1385b9c547cSRui Paulo allowed_no = ARRAY_SIZE(allowed_80); 1395b9c547cSRui Paulo break; 1405b9c547cSRui Paulo case 8: 1415b9c547cSRui Paulo allowed = allowed_160; 1425b9c547cSRui Paulo allowed_no = ARRAY_SIZE(allowed_160); 1435b9c547cSRui Paulo break; 1445b9c547cSRui Paulo default: 1455b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); 1465b9c547cSRui Paulo break; 1475b9c547cSRui Paulo } 1485b9c547cSRui Paulo 1495b9c547cSRui Paulo for (i = 0; i < allowed_no; i++) { 1505b9c547cSRui Paulo if (chan->chan == allowed[i]) 1515b9c547cSRui Paulo return 1; 1525b9c547cSRui Paulo } 1535b9c547cSRui Paulo 1545b9c547cSRui Paulo return 0; 1555b9c547cSRui Paulo } 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo 158325151a3SRui Paulo static struct hostapd_channel_data * 159325151a3SRui Paulo dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) 160325151a3SRui Paulo { 161325151a3SRui Paulo int i; 162325151a3SRui Paulo 163325151a3SRui Paulo for (i = first_chan_idx; i < mode->num_channels; i++) { 164325151a3SRui Paulo if (mode->channels[i].freq == freq) 165325151a3SRui Paulo return &mode->channels[i]; 166325151a3SRui Paulo } 167325151a3SRui Paulo 168325151a3SRui Paulo return NULL; 169325151a3SRui Paulo } 170325151a3SRui Paulo 171325151a3SRui Paulo 1725b9c547cSRui Paulo static int dfs_chan_range_available(struct hostapd_hw_modes *mode, 1735b9c547cSRui Paulo int first_chan_idx, int num_chans, 174*a90b9d01SCy Schubert enum dfs_channel_type type) 1755b9c547cSRui Paulo { 1765b9c547cSRui Paulo struct hostapd_channel_data *first_chan, *chan; 1775b9c547cSRui Paulo int i; 1784bc52338SCy Schubert u32 bw = num_chan_to_bw(num_chans); 1795b9c547cSRui Paulo 180c1d255d3SCy Schubert if (first_chan_idx + num_chans > mode->num_channels) { 181c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 182c1d255d3SCy Schubert "DFS: some channels in range not defined"); 1835b9c547cSRui Paulo return 0; 184c1d255d3SCy Schubert } 1855b9c547cSRui Paulo 1865b9c547cSRui Paulo first_chan = &mode->channels[first_chan_idx]; 1875b9c547cSRui Paulo 1884bc52338SCy Schubert /* hostapd DFS implementation assumes the first channel as primary. 1894bc52338SCy Schubert * If it's not allowed to use the first channel as primary, decline the 1904bc52338SCy Schubert * whole channel range. */ 191c1d255d3SCy Schubert if (!chan_pri_allowed(first_chan)) { 192*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DFS: primary channel not allowed"); 1934bc52338SCy Schubert return 0; 194c1d255d3SCy Schubert } 1954bc52338SCy Schubert 1965b9c547cSRui Paulo for (i = 0; i < num_chans; i++) { 197325151a3SRui Paulo chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, 198325151a3SRui Paulo first_chan_idx); 199c1d255d3SCy Schubert if (!chan) { 200c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: no channel data for %d", 201c1d255d3SCy Schubert first_chan->freq + i * 20); 2025b9c547cSRui Paulo return 0; 203c1d255d3SCy Schubert } 2045b9c547cSRui Paulo 2054bc52338SCy Schubert /* HT 40 MHz secondary channel availability checked only for 2064bc52338SCy Schubert * primary channel */ 207c1d255d3SCy Schubert if (!chan_bw_allowed(chan, bw, 1, !i)) { 208c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: bw now allowed for %d", 209c1d255d3SCy Schubert first_chan->freq + i * 20); 2104bc52338SCy Schubert return 0; 211c1d255d3SCy Schubert } 2124bc52338SCy Schubert 213*a90b9d01SCy Schubert if (!dfs_channel_available(chan, type)) { 214c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: channel not available %d", 215c1d255d3SCy Schubert first_chan->freq + i * 20); 2165b9c547cSRui Paulo return 0; 2175b9c547cSRui Paulo } 218c1d255d3SCy Schubert } 2195b9c547cSRui Paulo 2205b9c547cSRui Paulo return 1; 2215b9c547cSRui Paulo } 2225b9c547cSRui Paulo 2235b9c547cSRui Paulo 2245b9c547cSRui Paulo static int is_in_chanlist(struct hostapd_iface *iface, 2255b9c547cSRui Paulo struct hostapd_channel_data *chan) 2265b9c547cSRui Paulo { 227325151a3SRui Paulo if (!iface->conf->acs_ch_list.num) 2285b9c547cSRui Paulo return 1; 2295b9c547cSRui Paulo 230325151a3SRui Paulo return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); 2315b9c547cSRui Paulo } 2325b9c547cSRui Paulo 2335b9c547cSRui Paulo 2345b9c547cSRui Paulo /* 2355b9c547cSRui Paulo * The function assumes HT40+ operation. 2365b9c547cSRui Paulo * Make sure to adjust the following variables after calling this: 2375b9c547cSRui Paulo * - hapd->secondary_channel 238206b73d0SCy Schubert * - hapd->vht/he_oper_centr_freq_seg0_idx 239206b73d0SCy Schubert * - hapd->vht/he_oper_centr_freq_seg1_idx 2405b9c547cSRui Paulo */ 2415b9c547cSRui Paulo static int dfs_find_channel(struct hostapd_iface *iface, 2425b9c547cSRui Paulo struct hostapd_channel_data **ret_chan, 243*a90b9d01SCy Schubert int idx, enum dfs_channel_type type) 2445b9c547cSRui Paulo { 2455b9c547cSRui Paulo struct hostapd_hw_modes *mode; 2465b9c547cSRui Paulo struct hostapd_channel_data *chan; 2475b9c547cSRui Paulo int i, channel_idx = 0, n_chans, n_chans1; 2485b9c547cSRui Paulo 2495b9c547cSRui Paulo mode = iface->current_mode; 2505b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 2515b9c547cSRui Paulo 2525b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); 2535b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 2545b9c547cSRui Paulo chan = &mode->channels[i]; 2555b9c547cSRui Paulo 2565b9c547cSRui Paulo /* Skip HT40/VHT incompatible channels */ 2575b9c547cSRui Paulo if (iface->conf->ieee80211n && 2585b9c547cSRui Paulo iface->conf->secondary_channel && 2594bc52338SCy Schubert (!dfs_is_chan_allowed(chan, n_chans) || 260c1d255d3SCy Schubert !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) { 261c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 262c1d255d3SCy Schubert "DFS: channel %d (%d) is incompatible", 263c1d255d3SCy Schubert chan->freq, chan->chan); 2645b9c547cSRui Paulo continue; 265c1d255d3SCy Schubert } 2665b9c547cSRui Paulo 2675b9c547cSRui Paulo /* Skip incompatible chandefs */ 268*a90b9d01SCy Schubert if (!dfs_chan_range_available(mode, i, n_chans, type)) { 269c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 270c1d255d3SCy Schubert "DFS: range not available for %d (%d)", 271c1d255d3SCy Schubert chan->freq, chan->chan); 2725b9c547cSRui Paulo continue; 273c1d255d3SCy Schubert } 2745b9c547cSRui Paulo 275c1d255d3SCy Schubert if (!is_in_chanlist(iface, chan)) { 276c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 277c1d255d3SCy Schubert "DFS: channel %d (%d) not in chanlist", 278c1d255d3SCy Schubert chan->freq, chan->chan); 2795b9c547cSRui Paulo continue; 280c1d255d3SCy Schubert } 2815b9c547cSRui Paulo 28232a95656SCy Schubert if (chan->max_tx_power < iface->conf->min_tx_power) 28332a95656SCy Schubert continue; 28432a95656SCy Schubert 285*a90b9d01SCy Schubert if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) && 286*a90b9d01SCy Schubert iface->conf->country[2] == 0x4f) 287*a90b9d01SCy Schubert continue; 288*a90b9d01SCy Schubert 2895b9c547cSRui Paulo if (ret_chan && idx == channel_idx) { 290c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "Selected channel %d (%d)", 291c1d255d3SCy Schubert chan->freq, chan->chan); 2925b9c547cSRui Paulo *ret_chan = chan; 2935b9c547cSRui Paulo return idx; 2945b9c547cSRui Paulo } 295c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "Adding channel %d (%d)", 296c1d255d3SCy Schubert chan->freq, chan->chan); 2975b9c547cSRui Paulo channel_idx++; 2985b9c547cSRui Paulo } 2995b9c547cSRui Paulo return channel_idx; 3005b9c547cSRui Paulo } 3015b9c547cSRui Paulo 3025b9c547cSRui Paulo 303206b73d0SCy Schubert static void dfs_adjust_center_freq(struct hostapd_iface *iface, 3045b9c547cSRui Paulo struct hostapd_channel_data *chan, 3055b9c547cSRui Paulo int secondary_channel, 306c1d255d3SCy Schubert int sec_chan_idx_80p80, 307206b73d0SCy Schubert u8 *oper_centr_freq_seg0_idx, 308206b73d0SCy Schubert u8 *oper_centr_freq_seg1_idx) 3095b9c547cSRui Paulo { 310206b73d0SCy Schubert if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax) 3115b9c547cSRui Paulo return; 3125b9c547cSRui Paulo 3135b9c547cSRui Paulo if (!chan) 3145b9c547cSRui Paulo return; 3155b9c547cSRui Paulo 316206b73d0SCy Schubert *oper_centr_freq_seg1_idx = 0; 3175b9c547cSRui Paulo 318206b73d0SCy Schubert switch (hostapd_get_oper_chwidth(iface->conf)) { 319*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_USE_HT: 3205b9c547cSRui Paulo if (secondary_channel == 1) 321206b73d0SCy Schubert *oper_centr_freq_seg0_idx = chan->chan + 2; 3225b9c547cSRui Paulo else if (secondary_channel == -1) 323206b73d0SCy Schubert *oper_centr_freq_seg0_idx = chan->chan - 2; 3245b9c547cSRui Paulo else 325206b73d0SCy Schubert *oper_centr_freq_seg0_idx = chan->chan; 3265b9c547cSRui Paulo break; 327*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_80MHZ: 328206b73d0SCy Schubert *oper_centr_freq_seg0_idx = chan->chan + 6; 3295b9c547cSRui Paulo break; 330*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_160MHZ: 331206b73d0SCy Schubert *oper_centr_freq_seg0_idx = chan->chan + 14; 3325b9c547cSRui Paulo break; 333*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_80P80MHZ: 334c1d255d3SCy Schubert *oper_centr_freq_seg0_idx = chan->chan + 6; 335c1d255d3SCy Schubert *oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6; 336c1d255d3SCy Schubert break; 337c1d255d3SCy Schubert 3385b9c547cSRui Paulo default: 339c1d255d3SCy Schubert wpa_printf(MSG_INFO, 340c1d255d3SCy Schubert "DFS: Unsupported channel width configuration"); 341206b73d0SCy Schubert *oper_centr_freq_seg0_idx = 0; 3425b9c547cSRui Paulo break; 3435b9c547cSRui Paulo } 3445b9c547cSRui Paulo 3455b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", 346206b73d0SCy Schubert *oper_centr_freq_seg0_idx, 347206b73d0SCy Schubert *oper_centr_freq_seg1_idx); 3485b9c547cSRui Paulo } 3495b9c547cSRui Paulo 3505b9c547cSRui Paulo 3515b9c547cSRui Paulo /* Return start channel idx we will use for mode->channels[idx] */ 3525b9c547cSRui Paulo static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) 3535b9c547cSRui Paulo { 3545b9c547cSRui Paulo struct hostapd_hw_modes *mode; 3555b9c547cSRui Paulo struct hostapd_channel_data *chan; 3565b9c547cSRui Paulo int channel_no = iface->conf->channel; 3575b9c547cSRui Paulo int res = -1, i; 3585b9c547cSRui Paulo int chan_seg1 = -1; 3595b9c547cSRui Paulo 3605b9c547cSRui Paulo *seg1_start = -1; 3615b9c547cSRui Paulo 3625b9c547cSRui Paulo /* HT40- */ 3635b9c547cSRui Paulo if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) 3645b9c547cSRui Paulo channel_no -= 4; 3655b9c547cSRui Paulo 366*a90b9d01SCy Schubert /* VHT/HE/EHT */ 367*a90b9d01SCy Schubert if (iface->conf->ieee80211ac || iface->conf->ieee80211ax || 368*a90b9d01SCy Schubert iface->conf->ieee80211be) { 369206b73d0SCy Schubert switch (hostapd_get_oper_chwidth(iface->conf)) { 370*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_USE_HT: 3715b9c547cSRui Paulo break; 372*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_80MHZ: 373206b73d0SCy Schubert channel_no = hostapd_get_oper_centr_freq_seg0_idx( 374206b73d0SCy Schubert iface->conf) - 6; 3755b9c547cSRui Paulo break; 376*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_160MHZ: 377206b73d0SCy Schubert channel_no = hostapd_get_oper_centr_freq_seg0_idx( 378206b73d0SCy Schubert iface->conf) - 14; 3795b9c547cSRui Paulo break; 380*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_80P80MHZ: 381206b73d0SCy Schubert channel_no = hostapd_get_oper_centr_freq_seg0_idx( 382206b73d0SCy Schubert iface->conf) - 6; 383206b73d0SCy Schubert chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx( 384206b73d0SCy Schubert iface->conf) - 6; 3855b9c547cSRui Paulo break; 386*a90b9d01SCy Schubert case CONF_OPER_CHWIDTH_320MHZ: 387*a90b9d01SCy Schubert channel_no = hostapd_get_oper_centr_freq_seg0_idx( 388*a90b9d01SCy Schubert iface->conf) - 30; 389*a90b9d01SCy Schubert break; 3905b9c547cSRui Paulo default: 3915b9c547cSRui Paulo wpa_printf(MSG_INFO, 392*a90b9d01SCy Schubert "DFS only EHT20/40/80/160/80+80/320 is supported now"); 3935b9c547cSRui Paulo channel_no = -1; 3945b9c547cSRui Paulo break; 3955b9c547cSRui Paulo } 3965b9c547cSRui Paulo } 3975b9c547cSRui Paulo 3985b9c547cSRui Paulo /* Get idx */ 3995b9c547cSRui Paulo mode = iface->current_mode; 4005b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 4015b9c547cSRui Paulo chan = &mode->channels[i]; 4025b9c547cSRui Paulo if (chan->chan == channel_no) { 4035b9c547cSRui Paulo res = i; 4045b9c547cSRui Paulo break; 4055b9c547cSRui Paulo } 4065b9c547cSRui Paulo } 4075b9c547cSRui Paulo 4085b9c547cSRui Paulo if (res != -1 && chan_seg1 > -1) { 4095b9c547cSRui Paulo int found = 0; 4105b9c547cSRui Paulo 4115b9c547cSRui Paulo /* Get idx for seg1 */ 4125b9c547cSRui Paulo mode = iface->current_mode; 4135b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 4145b9c547cSRui Paulo chan = &mode->channels[i]; 4155b9c547cSRui Paulo if (chan->chan == chan_seg1) { 4165b9c547cSRui Paulo *seg1_start = i; 4175b9c547cSRui Paulo found = 1; 4185b9c547cSRui Paulo break; 4195b9c547cSRui Paulo } 4205b9c547cSRui Paulo } 4215b9c547cSRui Paulo if (!found) 4225b9c547cSRui Paulo res = -1; 4235b9c547cSRui Paulo } 4245b9c547cSRui Paulo 4255b9c547cSRui Paulo if (res == -1) { 4265b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 4275b9c547cSRui Paulo "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d", 4285b9c547cSRui Paulo mode->num_channels, channel_no, iface->conf->channel, 4295b9c547cSRui Paulo iface->conf->ieee80211n, 4305b9c547cSRui Paulo iface->conf->secondary_channel, 431206b73d0SCy Schubert hostapd_get_oper_chwidth(iface->conf)); 4325b9c547cSRui Paulo 4335b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 4345b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Available channel: %d", 4355b9c547cSRui Paulo mode->channels[i].chan); 4365b9c547cSRui Paulo } 4375b9c547cSRui Paulo } 4385b9c547cSRui Paulo 4395b9c547cSRui Paulo return res; 4405b9c547cSRui Paulo } 4415b9c547cSRui Paulo 4425b9c547cSRui Paulo 4435b9c547cSRui Paulo /* At least one channel have radar flag */ 4445b9c547cSRui Paulo static int dfs_check_chans_radar(struct hostapd_iface *iface, 4455b9c547cSRui Paulo int start_chan_idx, int n_chans) 4465b9c547cSRui Paulo { 4475b9c547cSRui Paulo struct hostapd_channel_data *channel; 4485b9c547cSRui Paulo struct hostapd_hw_modes *mode; 4495b9c547cSRui Paulo int i, res = 0; 4505b9c547cSRui Paulo 4515b9c547cSRui Paulo mode = iface->current_mode; 4525b9c547cSRui Paulo 4535b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 454*a90b9d01SCy Schubert if (start_chan_idx + i >= mode->num_channels) 455*a90b9d01SCy Schubert break; 4565b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 4575b9c547cSRui Paulo if (channel->flag & HOSTAPD_CHAN_RADAR) 4585b9c547cSRui Paulo res++; 4595b9c547cSRui Paulo } 4605b9c547cSRui Paulo 4615b9c547cSRui Paulo return res; 4625b9c547cSRui Paulo } 4635b9c547cSRui Paulo 4645b9c547cSRui Paulo 4655b9c547cSRui Paulo /* All channels available */ 4665b9c547cSRui Paulo static int dfs_check_chans_available(struct hostapd_iface *iface, 4675b9c547cSRui Paulo int start_chan_idx, int n_chans) 4685b9c547cSRui Paulo { 4695b9c547cSRui Paulo struct hostapd_channel_data *channel; 4705b9c547cSRui Paulo struct hostapd_hw_modes *mode; 4715b9c547cSRui Paulo int i; 4725b9c547cSRui Paulo 4735b9c547cSRui Paulo mode = iface->current_mode; 4745b9c547cSRui Paulo 4755b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 4765b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 4775b9c547cSRui Paulo 4785b9c547cSRui Paulo if (channel->flag & HOSTAPD_CHAN_DISABLED) 4795b9c547cSRui Paulo break; 4805b9c547cSRui Paulo 4815b9c547cSRui Paulo if (!(channel->flag & HOSTAPD_CHAN_RADAR)) 4825b9c547cSRui Paulo continue; 4835b9c547cSRui Paulo 4845b9c547cSRui Paulo if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != 4855b9c547cSRui Paulo HOSTAPD_CHAN_DFS_AVAILABLE) 4865b9c547cSRui Paulo break; 4875b9c547cSRui Paulo } 4885b9c547cSRui Paulo 4895b9c547cSRui Paulo return i == n_chans; 4905b9c547cSRui Paulo } 4915b9c547cSRui Paulo 4925b9c547cSRui Paulo 4935b9c547cSRui Paulo /* At least one channel unavailable */ 4945b9c547cSRui Paulo static int dfs_check_chans_unavailable(struct hostapd_iface *iface, 4955b9c547cSRui Paulo int start_chan_idx, 4965b9c547cSRui Paulo int n_chans) 4975b9c547cSRui Paulo { 4985b9c547cSRui Paulo struct hostapd_channel_data *channel; 4995b9c547cSRui Paulo struct hostapd_hw_modes *mode; 5005b9c547cSRui Paulo int i, res = 0; 5015b9c547cSRui Paulo 5025b9c547cSRui Paulo mode = iface->current_mode; 5035b9c547cSRui Paulo 5045b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 5055b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 5065b9c547cSRui Paulo if (channel->flag & HOSTAPD_CHAN_DISABLED) 5075b9c547cSRui Paulo res++; 5085b9c547cSRui Paulo if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == 5095b9c547cSRui Paulo HOSTAPD_CHAN_DFS_UNAVAILABLE) 5105b9c547cSRui Paulo res++; 5115b9c547cSRui Paulo } 5125b9c547cSRui Paulo 5135b9c547cSRui Paulo return res; 5145b9c547cSRui Paulo } 5155b9c547cSRui Paulo 5165b9c547cSRui Paulo 5175b9c547cSRui Paulo static struct hostapd_channel_data * 5185b9c547cSRui Paulo dfs_get_valid_channel(struct hostapd_iface *iface, 5195b9c547cSRui Paulo int *secondary_channel, 520206b73d0SCy Schubert u8 *oper_centr_freq_seg0_idx, 521206b73d0SCy Schubert u8 *oper_centr_freq_seg1_idx, 522*a90b9d01SCy Schubert enum dfs_channel_type type) 5235b9c547cSRui Paulo { 5245b9c547cSRui Paulo struct hostapd_hw_modes *mode; 5255b9c547cSRui Paulo struct hostapd_channel_data *chan = NULL; 526c1d255d3SCy Schubert struct hostapd_channel_data *chan2 = NULL; 5275b9c547cSRui Paulo int num_available_chandefs; 528c1d255d3SCy Schubert int chan_idx, chan_idx2; 529c1d255d3SCy Schubert int sec_chan_idx_80p80 = -1; 530c1d255d3SCy Schubert int i; 5315b9c547cSRui Paulo u32 _rand; 5325b9c547cSRui Paulo 5335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); 5345b9c547cSRui Paulo *secondary_channel = 0; 535206b73d0SCy Schubert *oper_centr_freq_seg0_idx = 0; 536206b73d0SCy Schubert *oper_centr_freq_seg1_idx = 0; 5375b9c547cSRui Paulo 5385b9c547cSRui Paulo if (iface->current_mode == NULL) 5395b9c547cSRui Paulo return NULL; 5405b9c547cSRui Paulo 5415b9c547cSRui Paulo mode = iface->current_mode; 5425b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) 5435b9c547cSRui Paulo return NULL; 5445b9c547cSRui Paulo 5455b9c547cSRui Paulo /* Get the count first */ 546*a90b9d01SCy Schubert num_available_chandefs = dfs_find_channel(iface, NULL, 0, type); 547c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d", 548c1d255d3SCy Schubert num_available_chandefs); 5495b9c547cSRui Paulo if (num_available_chandefs == 0) 5505b9c547cSRui Paulo return NULL; 5515b9c547cSRui Paulo 5525b9c547cSRui Paulo if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) 553780fb4a2SCy Schubert return NULL; 5545b9c547cSRui Paulo chan_idx = _rand % num_available_chandefs; 555*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d", 556*a90b9d01SCy Schubert chan_idx, num_available_chandefs); 557*a90b9d01SCy Schubert dfs_find_channel(iface, &chan, chan_idx, type); 558c1d255d3SCy Schubert if (!chan) { 559c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: no random channel found"); 560c1d255d3SCy Schubert return NULL; 561c1d255d3SCy Schubert } 562c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: got random channel %d (%d)", 563c1d255d3SCy Schubert chan->freq, chan->chan); 5645b9c547cSRui Paulo 5655b9c547cSRui Paulo /* dfs_find_channel() calculations assume HT40+ */ 5665b9c547cSRui Paulo if (iface->conf->secondary_channel) 5675b9c547cSRui Paulo *secondary_channel = 1; 5685b9c547cSRui Paulo else 5695b9c547cSRui Paulo *secondary_channel = 0; 5705b9c547cSRui Paulo 571c1d255d3SCy Schubert /* Get secondary channel for HT80P80 */ 572*a90b9d01SCy Schubert if (hostapd_get_oper_chwidth(iface->conf) == 573*a90b9d01SCy Schubert CONF_OPER_CHWIDTH_80P80MHZ) { 574c1d255d3SCy Schubert if (num_available_chandefs <= 1) { 575c1d255d3SCy Schubert wpa_printf(MSG_ERROR, 576c1d255d3SCy Schubert "only 1 valid chan, can't support 80+80"); 577c1d255d3SCy Schubert return NULL; 578c1d255d3SCy Schubert } 579c1d255d3SCy Schubert 580c1d255d3SCy Schubert /* 581c1d255d3SCy Schubert * Loop all channels except channel1 to find a valid channel2 582c1d255d3SCy Schubert * that is not adjacent to channel1. 583c1d255d3SCy Schubert */ 584c1d255d3SCy Schubert for (i = 0; i < num_available_chandefs - 1; i++) { 585c1d255d3SCy Schubert /* start from chan_idx + 1, end when chan_idx - 1 */ 586c1d255d3SCy Schubert chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs; 587*a90b9d01SCy Schubert dfs_find_channel(iface, &chan2, chan_idx2, type); 588c1d255d3SCy Schubert if (chan2 && abs(chan2->chan - chan->chan) > 12) { 589c1d255d3SCy Schubert /* two channels are not adjacent */ 590c1d255d3SCy Schubert sec_chan_idx_80p80 = chan2->chan; 591c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 592c1d255d3SCy Schubert "DFS: got second chan: %d (%d)", 593c1d255d3SCy Schubert chan2->freq, chan2->chan); 594c1d255d3SCy Schubert break; 595c1d255d3SCy Schubert } 596c1d255d3SCy Schubert } 597c1d255d3SCy Schubert 598c1d255d3SCy Schubert /* Check if we got a valid secondary channel which is not 599c1d255d3SCy Schubert * adjacent to the first channel. 600c1d255d3SCy Schubert */ 601c1d255d3SCy Schubert if (sec_chan_idx_80p80 == -1) { 602c1d255d3SCy Schubert wpa_printf(MSG_INFO, 603c1d255d3SCy Schubert "DFS: failed to get chan2 for 80+80"); 604c1d255d3SCy Schubert return NULL; 605c1d255d3SCy Schubert } 606c1d255d3SCy Schubert } 607c1d255d3SCy Schubert 608206b73d0SCy Schubert dfs_adjust_center_freq(iface, chan, 6095b9c547cSRui Paulo *secondary_channel, 610c1d255d3SCy Schubert sec_chan_idx_80p80, 611206b73d0SCy Schubert oper_centr_freq_seg0_idx, 612206b73d0SCy Schubert oper_centr_freq_seg1_idx); 6135b9c547cSRui Paulo 6145b9c547cSRui Paulo return chan; 6155b9c547cSRui Paulo } 6165b9c547cSRui Paulo 6175b9c547cSRui Paulo 618*a90b9d01SCy Schubert static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar) 619*a90b9d01SCy Schubert { 620*a90b9d01SCy Schubert struct hostapd_channel_data *channel; 621*a90b9d01SCy Schubert u8 cf1 = 0, cf2 = 0; 622*a90b9d01SCy Schubert int sec = 0; 623*a90b9d01SCy Schubert 624*a90b9d01SCy Schubert channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 625*a90b9d01SCy Schubert skip_radar ? DFS_AVAILABLE : 626*a90b9d01SCy Schubert DFS_ANY_CHANNEL); 627*a90b9d01SCy Schubert if (!channel) { 628*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "could not get valid channel"); 629*a90b9d01SCy Schubert return -1; 630*a90b9d01SCy Schubert } 631*a90b9d01SCy Schubert 632*a90b9d01SCy Schubert iface->freq = channel->freq; 633*a90b9d01SCy Schubert iface->conf->channel = channel->chan; 634*a90b9d01SCy Schubert iface->conf->secondary_channel = sec; 635*a90b9d01SCy Schubert hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1); 636*a90b9d01SCy Schubert hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2); 637*a90b9d01SCy Schubert 638*a90b9d01SCy Schubert return 0; 639*a90b9d01SCy Schubert } 640*a90b9d01SCy Schubert 641*a90b9d01SCy Schubert 6425b9c547cSRui Paulo static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) 6435b9c547cSRui Paulo { 6445b9c547cSRui Paulo struct hostapd_hw_modes *mode; 6455b9c547cSRui Paulo struct hostapd_channel_data *chan = NULL; 6465b9c547cSRui Paulo int i; 6475b9c547cSRui Paulo 6485b9c547cSRui Paulo mode = iface->current_mode; 6495b9c547cSRui Paulo if (mode == NULL) 6505b9c547cSRui Paulo return 0; 6515b9c547cSRui Paulo 6525b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); 6535b9c547cSRui Paulo for (i = 0; i < iface->current_mode->num_channels; i++) { 6545b9c547cSRui Paulo chan = &iface->current_mode->channels[i]; 6555b9c547cSRui Paulo if (chan->freq == freq) { 6565b9c547cSRui Paulo if (chan->flag & HOSTAPD_CHAN_RADAR) { 6575b9c547cSRui Paulo chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; 6585b9c547cSRui Paulo chan->flag |= state; 6595b9c547cSRui Paulo return 1; /* Channel found */ 6605b9c547cSRui Paulo } 6615b9c547cSRui Paulo } 6625b9c547cSRui Paulo } 6635b9c547cSRui Paulo wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); 6645b9c547cSRui Paulo return 0; 6655b9c547cSRui Paulo } 6665b9c547cSRui Paulo 6675b9c547cSRui Paulo 6685b9c547cSRui Paulo static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, 6695b9c547cSRui Paulo int chan_offset, int chan_width, int cf1, 6705b9c547cSRui Paulo int cf2, u32 state) 6715b9c547cSRui Paulo { 6725b9c547cSRui Paulo int n_chans = 1, i; 6735b9c547cSRui Paulo struct hostapd_hw_modes *mode; 6745b9c547cSRui Paulo int frequency = freq; 675c1d255d3SCy Schubert int frequency2 = 0; 6765b9c547cSRui Paulo int ret = 0; 6775b9c547cSRui Paulo 6785b9c547cSRui Paulo mode = iface->current_mode; 6795b9c547cSRui Paulo if (mode == NULL) 6805b9c547cSRui Paulo return 0; 6815b9c547cSRui Paulo 6825b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) { 6835b9c547cSRui Paulo wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); 6845b9c547cSRui Paulo return 0; 6855b9c547cSRui Paulo } 6865b9c547cSRui Paulo 6875b9c547cSRui Paulo /* Seems cf1 and chan_width is enough here */ 6885b9c547cSRui Paulo switch (chan_width) { 6895b9c547cSRui Paulo case CHAN_WIDTH_20_NOHT: 6905b9c547cSRui Paulo case CHAN_WIDTH_20: 6915b9c547cSRui Paulo n_chans = 1; 6925b9c547cSRui Paulo if (frequency == 0) 6935b9c547cSRui Paulo frequency = cf1; 6945b9c547cSRui Paulo break; 6955b9c547cSRui Paulo case CHAN_WIDTH_40: 6965b9c547cSRui Paulo n_chans = 2; 6975b9c547cSRui Paulo frequency = cf1 - 10; 6985b9c547cSRui Paulo break; 6995b9c547cSRui Paulo case CHAN_WIDTH_80: 7005b9c547cSRui Paulo n_chans = 4; 7015b9c547cSRui Paulo frequency = cf1 - 30; 7025b9c547cSRui Paulo break; 703c1d255d3SCy Schubert case CHAN_WIDTH_80P80: 704c1d255d3SCy Schubert n_chans = 4; 705c1d255d3SCy Schubert frequency = cf1 - 30; 706c1d255d3SCy Schubert frequency2 = cf2 - 30; 707c1d255d3SCy Schubert break; 7085b9c547cSRui Paulo case CHAN_WIDTH_160: 7095b9c547cSRui Paulo n_chans = 8; 7105b9c547cSRui Paulo frequency = cf1 - 70; 7115b9c547cSRui Paulo break; 7125b9c547cSRui Paulo default: 7135b9c547cSRui Paulo wpa_printf(MSG_INFO, "DFS chan_width %d not supported", 7145b9c547cSRui Paulo chan_width); 7155b9c547cSRui Paulo break; 7165b9c547cSRui Paulo } 7175b9c547cSRui Paulo 7185b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, 7195b9c547cSRui Paulo n_chans); 7205b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 7215b9c547cSRui Paulo ret += set_dfs_state_freq(iface, frequency, state); 7225b9c547cSRui Paulo frequency = frequency + 20; 723c1d255d3SCy Schubert 724c1d255d3SCy Schubert if (chan_width == CHAN_WIDTH_80P80) { 725c1d255d3SCy Schubert ret += set_dfs_state_freq(iface, frequency2, state); 726c1d255d3SCy Schubert frequency2 = frequency2 + 20; 727c1d255d3SCy Schubert } 7285b9c547cSRui Paulo } 7295b9c547cSRui Paulo 7305b9c547cSRui Paulo return ret; 7315b9c547cSRui Paulo } 7325b9c547cSRui Paulo 7335b9c547cSRui Paulo 7345b9c547cSRui Paulo static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, 7355b9c547cSRui Paulo int chan_width, int cf1, int cf2) 7365b9c547cSRui Paulo { 7375b9c547cSRui Paulo int start_chan_idx, start_chan_idx1; 7385b9c547cSRui Paulo struct hostapd_hw_modes *mode; 7395b9c547cSRui Paulo struct hostapd_channel_data *chan; 7405b9c547cSRui Paulo int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1; 7415b9c547cSRui Paulo u8 radar_chan; 7425b9c547cSRui Paulo int res = 0; 7435b9c547cSRui Paulo 7445b9c547cSRui Paulo /* Our configuration */ 7455b9c547cSRui Paulo mode = iface->current_mode; 7465b9c547cSRui Paulo start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 7475b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 7485b9c547cSRui Paulo 7495b9c547cSRui Paulo /* Check we are on DFS channel(s) */ 7505b9c547cSRui Paulo if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) 7515b9c547cSRui Paulo return 0; 7525b9c547cSRui Paulo 7535b9c547cSRui Paulo /* Reported via radar event */ 7545b9c547cSRui Paulo switch (chan_width) { 7555b9c547cSRui Paulo case CHAN_WIDTH_20_NOHT: 7565b9c547cSRui Paulo case CHAN_WIDTH_20: 7575b9c547cSRui Paulo radar_n_chans = 1; 7585b9c547cSRui Paulo if (frequency == 0) 7595b9c547cSRui Paulo frequency = cf1; 7605b9c547cSRui Paulo break; 7615b9c547cSRui Paulo case CHAN_WIDTH_40: 7625b9c547cSRui Paulo radar_n_chans = 2; 7635b9c547cSRui Paulo frequency = cf1 - 10; 7645b9c547cSRui Paulo break; 7655b9c547cSRui Paulo case CHAN_WIDTH_80: 7665b9c547cSRui Paulo radar_n_chans = 4; 7675b9c547cSRui Paulo frequency = cf1 - 30; 7685b9c547cSRui Paulo break; 7695b9c547cSRui Paulo case CHAN_WIDTH_160: 7705b9c547cSRui Paulo radar_n_chans = 8; 7715b9c547cSRui Paulo frequency = cf1 - 70; 7725b9c547cSRui Paulo break; 7735b9c547cSRui Paulo default: 7745b9c547cSRui Paulo wpa_printf(MSG_INFO, "DFS chan_width %d not supported", 7755b9c547cSRui Paulo chan_width); 7765b9c547cSRui Paulo break; 7775b9c547cSRui Paulo } 7785b9c547cSRui Paulo 7795b9c547cSRui Paulo ieee80211_freq_to_chan(frequency, &radar_chan); 7805b9c547cSRui Paulo 7815b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 7825b9c547cSRui Paulo chan = &mode->channels[start_chan_idx + i]; 7835b9c547cSRui Paulo if (!(chan->flag & HOSTAPD_CHAN_RADAR)) 7845b9c547cSRui Paulo continue; 7855b9c547cSRui Paulo for (j = 0; j < radar_n_chans; j++) { 7865b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", 7875b9c547cSRui Paulo chan->chan, radar_chan + j * 4); 7885b9c547cSRui Paulo if (chan->chan == radar_chan + j * 4) 7895b9c547cSRui Paulo res++; 7905b9c547cSRui Paulo } 7915b9c547cSRui Paulo } 7925b9c547cSRui Paulo 7935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "overlapped: %d", res); 7945b9c547cSRui Paulo 7955b9c547cSRui Paulo return res; 7965b9c547cSRui Paulo } 7975b9c547cSRui Paulo 7985b9c547cSRui Paulo 7995b9c547cSRui Paulo static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, 8005b9c547cSRui Paulo int start_chan_idx, int n_chans) 8015b9c547cSRui Paulo { 8025b9c547cSRui Paulo struct hostapd_channel_data *channel; 8035b9c547cSRui Paulo struct hostapd_hw_modes *mode; 8045b9c547cSRui Paulo int i; 8055b9c547cSRui Paulo unsigned int cac_time_ms = 0; 8065b9c547cSRui Paulo 8075b9c547cSRui Paulo mode = iface->current_mode; 8085b9c547cSRui Paulo 8095b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 810*a90b9d01SCy Schubert if (start_chan_idx + i >= mode->num_channels) 811*a90b9d01SCy Schubert break; 8125b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 8135b9c547cSRui Paulo if (!(channel->flag & HOSTAPD_CHAN_RADAR)) 8145b9c547cSRui Paulo continue; 8155b9c547cSRui Paulo if (channel->dfs_cac_ms > cac_time_ms) 8165b9c547cSRui Paulo cac_time_ms = channel->dfs_cac_ms; 8175b9c547cSRui Paulo } 8185b9c547cSRui Paulo 8195b9c547cSRui Paulo return cac_time_ms; 8205b9c547cSRui Paulo } 8215b9c547cSRui Paulo 8225b9c547cSRui Paulo 8235b9c547cSRui Paulo /* 8245b9c547cSRui Paulo * Main DFS handler 8255b9c547cSRui Paulo * 1 - continue channel/ap setup 8265b9c547cSRui Paulo * 0 - channel/ap setup will be continued after CAC 8275b9c547cSRui Paulo * -1 - hit critical error 8285b9c547cSRui Paulo */ 8295b9c547cSRui Paulo int hostapd_handle_dfs(struct hostapd_iface *iface) 8305b9c547cSRui Paulo { 8315b9c547cSRui Paulo int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; 8325b9c547cSRui Paulo int skip_radar = 0; 8335b9c547cSRui Paulo 834c1d255d3SCy Schubert if (is_6ghz_freq(iface->freq)) 835c1d255d3SCy Schubert return 1; 836c1d255d3SCy Schubert 8375b9c547cSRui Paulo if (!iface->current_mode) { 8385b9c547cSRui Paulo /* 8395b9c547cSRui Paulo * This can happen with drivers that do not provide mode 8405b9c547cSRui Paulo * information and as such, cannot really use hostapd for DFS. 8415b9c547cSRui Paulo */ 8425b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 8435b9c547cSRui Paulo "DFS: No current_mode information - assume no need to perform DFS operations by hostapd"); 8445b9c547cSRui Paulo return 1; 8455b9c547cSRui Paulo } 8465b9c547cSRui Paulo 8475b9c547cSRui Paulo iface->cac_started = 0; 8485b9c547cSRui Paulo 8495b9c547cSRui Paulo do { 8505b9c547cSRui Paulo /* Get start (first) channel for current configuration */ 8515b9c547cSRui Paulo start_chan_idx = dfs_get_start_chan_idx(iface, 8525b9c547cSRui Paulo &start_chan_idx1); 8535b9c547cSRui Paulo if (start_chan_idx == -1) 8545b9c547cSRui Paulo return -1; 8555b9c547cSRui Paulo 8565b9c547cSRui Paulo /* Get number of used channels, depend on width */ 8575b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 8585b9c547cSRui Paulo 8595b9c547cSRui Paulo /* Setup CAC time */ 8605b9c547cSRui Paulo iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx, 8615b9c547cSRui Paulo n_chans); 8625b9c547cSRui Paulo 8635b9c547cSRui Paulo /* Check if any of configured channels require DFS */ 8645b9c547cSRui Paulo res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); 8655b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 8665b9c547cSRui Paulo "DFS %d channels required radar detection", 8675b9c547cSRui Paulo res); 8685b9c547cSRui Paulo if (!res) 8695b9c547cSRui Paulo return 1; 8705b9c547cSRui Paulo 8715b9c547cSRui Paulo /* Check if all channels are DFS available */ 8725b9c547cSRui Paulo res = dfs_check_chans_available(iface, start_chan_idx, n_chans); 8735b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 8745b9c547cSRui Paulo "DFS all channels available, (SKIP CAC): %s", 8755b9c547cSRui Paulo res ? "yes" : "no"); 8765b9c547cSRui Paulo if (res) 8775b9c547cSRui Paulo return 1; 8785b9c547cSRui Paulo 8795b9c547cSRui Paulo /* Check if any of configured channels is unavailable */ 8805b9c547cSRui Paulo res = dfs_check_chans_unavailable(iface, start_chan_idx, 8815b9c547cSRui Paulo n_chans); 8825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", 8835b9c547cSRui Paulo res, res ? "yes": "no"); 8845b9c547cSRui Paulo if (res) { 885*a90b9d01SCy Schubert if (dfs_set_valid_channel(iface, skip_radar) < 0) { 886780fb4a2SCy Schubert hostapd_set_state(iface, HAPD_IFACE_DFS); 887780fb4a2SCy Schubert return 0; 8885b9c547cSRui Paulo } 8895b9c547cSRui Paulo } 8905b9c547cSRui Paulo } while (res); 8915b9c547cSRui Paulo 8925b9c547cSRui Paulo /* Finally start CAC */ 8935b9c547cSRui Paulo hostapd_set_state(iface, HAPD_IFACE_DFS); 894*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq, 895*a90b9d01SCy Schubert dfs_use_radar_background(iface) ? " (background)" : ""); 8965b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START 8975b9c547cSRui Paulo "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", 8985b9c547cSRui Paulo iface->freq, 8995b9c547cSRui Paulo iface->conf->channel, iface->conf->secondary_channel, 900206b73d0SCy Schubert hostapd_get_oper_chwidth(iface->conf), 901206b73d0SCy Schubert hostapd_get_oper_centr_freq_seg0_idx(iface->conf), 902206b73d0SCy Schubert hostapd_get_oper_centr_freq_seg1_idx(iface->conf), 9035b9c547cSRui Paulo iface->dfs_cac_ms / 1000); 9045b9c547cSRui Paulo 905206b73d0SCy Schubert res = hostapd_start_dfs_cac( 906206b73d0SCy Schubert iface, iface->conf->hw_mode, iface->freq, iface->conf->channel, 907206b73d0SCy Schubert iface->conf->ieee80211n, iface->conf->ieee80211ac, 908*a90b9d01SCy Schubert iface->conf->ieee80211ax, iface->conf->ieee80211be, 9095b9c547cSRui Paulo iface->conf->secondary_channel, 910206b73d0SCy Schubert hostapd_get_oper_chwidth(iface->conf), 911206b73d0SCy Schubert hostapd_get_oper_centr_freq_seg0_idx(iface->conf), 912*a90b9d01SCy Schubert hostapd_get_oper_centr_freq_seg1_idx(iface->conf), 913*a90b9d01SCy Schubert dfs_use_radar_background(iface)); 9145b9c547cSRui Paulo 9155b9c547cSRui Paulo if (res) { 9165b9c547cSRui Paulo wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); 9175b9c547cSRui Paulo return -1; 9185b9c547cSRui Paulo } 9195b9c547cSRui Paulo 920*a90b9d01SCy Schubert if (dfs_use_radar_background(iface)) { 921*a90b9d01SCy Schubert /* Cache background radar parameters. */ 922*a90b9d01SCy Schubert iface->radar_background.channel = iface->conf->channel; 923*a90b9d01SCy Schubert iface->radar_background.secondary_channel = 924*a90b9d01SCy Schubert iface->conf->secondary_channel; 925*a90b9d01SCy Schubert iface->radar_background.freq = iface->freq; 926*a90b9d01SCy Schubert iface->radar_background.centr_freq_seg0_idx = 927*a90b9d01SCy Schubert hostapd_get_oper_centr_freq_seg0_idx(iface->conf); 928*a90b9d01SCy Schubert iface->radar_background.centr_freq_seg1_idx = 929*a90b9d01SCy Schubert hostapd_get_oper_centr_freq_seg1_idx(iface->conf); 930*a90b9d01SCy Schubert 931*a90b9d01SCy Schubert /* 932*a90b9d01SCy Schubert * Let's select a random channel according to the 933*a90b9d01SCy Schubert * regulations and perform CAC on dedicated radar chain. 934*a90b9d01SCy Schubert */ 935*a90b9d01SCy Schubert res = dfs_set_valid_channel(iface, 1); 936*a90b9d01SCy Schubert if (res < 0) 937*a90b9d01SCy Schubert return res; 938*a90b9d01SCy Schubert 939*a90b9d01SCy Schubert iface->radar_background.temp_ch = 1; 940*a90b9d01SCy Schubert return 1; 941*a90b9d01SCy Schubert } 942*a90b9d01SCy Schubert 9435b9c547cSRui Paulo return 0; 9445b9c547cSRui Paulo } 9455b9c547cSRui Paulo 9465b9c547cSRui Paulo 947c1d255d3SCy Schubert int hostapd_is_dfs_chan_available(struct hostapd_iface *iface) 94885732ac8SCy Schubert { 94985732ac8SCy Schubert int n_chans, n_chans1, start_chan_idx, start_chan_idx1; 95085732ac8SCy Schubert 95185732ac8SCy Schubert /* Get the start (first) channel for current configuration */ 95285732ac8SCy Schubert start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 95385732ac8SCy Schubert if (start_chan_idx < 0) 95485732ac8SCy Schubert return 0; 95585732ac8SCy Schubert 95685732ac8SCy Schubert /* Get the number of used channels, depending on width */ 95785732ac8SCy Schubert n_chans = dfs_get_used_n_chans(iface, &n_chans1); 95885732ac8SCy Schubert 95985732ac8SCy Schubert /* Check if all channels are DFS available */ 96085732ac8SCy Schubert return dfs_check_chans_available(iface, start_chan_idx, n_chans); 96185732ac8SCy Schubert } 96285732ac8SCy Schubert 96385732ac8SCy Schubert 964*a90b9d01SCy Schubert static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface, 965*a90b9d01SCy Schubert int channel, int freq, 966*a90b9d01SCy Schubert int secondary_channel, 967*a90b9d01SCy Schubert u8 current_vht_oper_chwidth, 968*a90b9d01SCy Schubert u8 oper_centr_freq_seg0_idx, 969*a90b9d01SCy Schubert u8 oper_centr_freq_seg1_idx) 970*a90b9d01SCy Schubert { 971*a90b9d01SCy Schubert struct hostapd_hw_modes *cmode = iface->current_mode; 972*a90b9d01SCy Schubert int ieee80211_mode = IEEE80211_MODE_AP, err; 973*a90b9d01SCy Schubert struct csa_settings csa_settings; 974*a90b9d01SCy Schubert u8 new_vht_oper_chwidth; 975*a90b9d01SCy Schubert unsigned int i; 976*a90b9d01SCy Schubert unsigned int num_err = 0; 977*a90b9d01SCy Schubert 978*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel); 979*a90b9d01SCy Schubert wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL 980*a90b9d01SCy Schubert "freq=%d chan=%d sec_chan=%d", freq, channel, 981*a90b9d01SCy Schubert secondary_channel); 982*a90b9d01SCy Schubert 983*a90b9d01SCy Schubert new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 984*a90b9d01SCy Schubert hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth); 985*a90b9d01SCy Schubert 986*a90b9d01SCy Schubert /* Setup CSA request */ 987*a90b9d01SCy Schubert os_memset(&csa_settings, 0, sizeof(csa_settings)); 988*a90b9d01SCy Schubert csa_settings.cs_count = 5; 989*a90b9d01SCy Schubert csa_settings.block_tx = 1; 990*a90b9d01SCy Schubert csa_settings.link_id = -1; 991*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE 992*a90b9d01SCy Schubert if (iface->bss[0]->conf->mld_ap) 993*a90b9d01SCy Schubert csa_settings.link_id = iface->bss[0]->mld_link_id; 994*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */ 995*a90b9d01SCy Schubert #ifdef CONFIG_MESH 996*a90b9d01SCy Schubert if (iface->mconf) 997*a90b9d01SCy Schubert ieee80211_mode = IEEE80211_MODE_MESH; 998*a90b9d01SCy Schubert #endif /* CONFIG_MESH */ 999*a90b9d01SCy Schubert err = hostapd_set_freq_params(&csa_settings.freq_params, 1000*a90b9d01SCy Schubert iface->conf->hw_mode, 1001*a90b9d01SCy Schubert freq, channel, 1002*a90b9d01SCy Schubert iface->conf->enable_edmg, 1003*a90b9d01SCy Schubert iface->conf->edmg_channel, 1004*a90b9d01SCy Schubert iface->conf->ieee80211n, 1005*a90b9d01SCy Schubert iface->conf->ieee80211ac, 1006*a90b9d01SCy Schubert iface->conf->ieee80211ax, 1007*a90b9d01SCy Schubert iface->conf->ieee80211be, 1008*a90b9d01SCy Schubert secondary_channel, 1009*a90b9d01SCy Schubert new_vht_oper_chwidth, 1010*a90b9d01SCy Schubert oper_centr_freq_seg0_idx, 1011*a90b9d01SCy Schubert oper_centr_freq_seg1_idx, 1012*a90b9d01SCy Schubert cmode->vht_capab, 1013*a90b9d01SCy Schubert &cmode->he_capab[ieee80211_mode], 1014*a90b9d01SCy Schubert &cmode->eht_capab[ieee80211_mode], 1015*a90b9d01SCy Schubert hostapd_get_punct_bitmap(iface->bss[0])); 1016*a90b9d01SCy Schubert 1017*a90b9d01SCy Schubert if (err) { 1018*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 1019*a90b9d01SCy Schubert "DFS failed to calculate CSA freq params"); 1020*a90b9d01SCy Schubert hostapd_disable_iface(iface); 1021*a90b9d01SCy Schubert return err; 1022*a90b9d01SCy Schubert } 1023*a90b9d01SCy Schubert 1024*a90b9d01SCy Schubert for (i = 0; i < iface->num_bss; i++) { 1025*a90b9d01SCy Schubert err = hostapd_switch_channel(iface->bss[i], &csa_settings); 1026*a90b9d01SCy Schubert if (err) 1027*a90b9d01SCy Schubert num_err++; 1028*a90b9d01SCy Schubert } 1029*a90b9d01SCy Schubert 1030*a90b9d01SCy Schubert if (num_err == iface->num_bss) { 1031*a90b9d01SCy Schubert wpa_printf(MSG_WARNING, 1032*a90b9d01SCy Schubert "DFS failed to schedule CSA (%d) - trying fallback", 1033*a90b9d01SCy Schubert err); 1034*a90b9d01SCy Schubert iface->freq = freq; 1035*a90b9d01SCy Schubert iface->conf->channel = channel; 1036*a90b9d01SCy Schubert iface->conf->secondary_channel = secondary_channel; 1037*a90b9d01SCy Schubert hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth); 1038*a90b9d01SCy Schubert hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 1039*a90b9d01SCy Schubert oper_centr_freq_seg0_idx); 1040*a90b9d01SCy Schubert hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 1041*a90b9d01SCy Schubert oper_centr_freq_seg1_idx); 1042*a90b9d01SCy Schubert 1043*a90b9d01SCy Schubert hostapd_disable_iface(iface); 1044*a90b9d01SCy Schubert hostapd_enable_iface(iface); 1045*a90b9d01SCy Schubert 1046*a90b9d01SCy Schubert return 0; 1047*a90b9d01SCy Schubert } 1048*a90b9d01SCy Schubert 1049*a90b9d01SCy Schubert /* Channel configuration will be updated once CSA completes and 1050*a90b9d01SCy Schubert * ch_switch_notify event is received */ 1051*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); 1052*a90b9d01SCy Schubert 1053*a90b9d01SCy Schubert return 0; 1054*a90b9d01SCy Schubert } 1055*a90b9d01SCy Schubert 1056*a90b9d01SCy Schubert 1057*a90b9d01SCy Schubert static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface) 1058*a90b9d01SCy Schubert { 1059*a90b9d01SCy Schubert int sec = 0; 1060*a90b9d01SCy Schubert enum dfs_channel_type channel_type = DFS_NO_CAC_YET; 1061*a90b9d01SCy Schubert struct hostapd_channel_data *channel; 1062*a90b9d01SCy Schubert u8 oper_centr_freq_seg0_idx = 0; 1063*a90b9d01SCy Schubert u8 oper_centr_freq_seg1_idx = 0; 1064*a90b9d01SCy Schubert 1065*a90b9d01SCy Schubert /* 1066*a90b9d01SCy Schubert * Allow selection of DFS channel in ETSI to comply with 1067*a90b9d01SCy Schubert * uniform spreading. 1068*a90b9d01SCy Schubert */ 1069*a90b9d01SCy Schubert if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) 1070*a90b9d01SCy Schubert channel_type = DFS_ANY_CHANNEL; 1071*a90b9d01SCy Schubert 1072*a90b9d01SCy Schubert channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx, 1073*a90b9d01SCy Schubert &oper_centr_freq_seg1_idx, 1074*a90b9d01SCy Schubert channel_type); 1075*a90b9d01SCy Schubert if (!channel || 1076*a90b9d01SCy Schubert channel->chan == iface->conf->channel || 1077*a90b9d01SCy Schubert channel->chan == iface->radar_background.channel) 1078*a90b9d01SCy Schubert channel = dfs_downgrade_bandwidth(iface, &sec, 1079*a90b9d01SCy Schubert &oper_centr_freq_seg0_idx, 1080*a90b9d01SCy Schubert &oper_centr_freq_seg1_idx, 1081*a90b9d01SCy Schubert &channel_type); 1082*a90b9d01SCy Schubert if (!channel || 1083*a90b9d01SCy Schubert hostapd_start_dfs_cac(iface, iface->conf->hw_mode, 1084*a90b9d01SCy Schubert channel->freq, channel->chan, 1085*a90b9d01SCy Schubert iface->conf->ieee80211n, 1086*a90b9d01SCy Schubert iface->conf->ieee80211ac, 1087*a90b9d01SCy Schubert iface->conf->ieee80211ax, 1088*a90b9d01SCy Schubert iface->conf->ieee80211be, 1089*a90b9d01SCy Schubert sec, hostapd_get_oper_chwidth(iface->conf), 1090*a90b9d01SCy Schubert oper_centr_freq_seg0_idx, 1091*a90b9d01SCy Schubert oper_centr_freq_seg1_idx, true)) { 1092*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel"); 1093*a90b9d01SCy Schubert iface->radar_background.channel = -1; 1094*a90b9d01SCy Schubert return; 1095*a90b9d01SCy Schubert } 1096*a90b9d01SCy Schubert 1097*a90b9d01SCy Schubert iface->radar_background.channel = channel->chan; 1098*a90b9d01SCy Schubert iface->radar_background.freq = channel->freq; 1099*a90b9d01SCy Schubert iface->radar_background.secondary_channel = sec; 1100*a90b9d01SCy Schubert iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx; 1101*a90b9d01SCy Schubert iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx; 1102*a90b9d01SCy Schubert 1103*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1104*a90b9d01SCy Schubert "%s: setting background chain to chan %d (%d MHz)", 1105*a90b9d01SCy Schubert __func__, channel->chan, channel->freq); 1106*a90b9d01SCy Schubert } 1107*a90b9d01SCy Schubert 1108*a90b9d01SCy Schubert 1109*a90b9d01SCy Schubert static bool 1110*a90b9d01SCy Schubert hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq) 1111*a90b9d01SCy Schubert { 1112*a90b9d01SCy Schubert return dfs_use_radar_background(iface) && 1113*a90b9d01SCy Schubert iface->radar_background.channel != -1 && 1114*a90b9d01SCy Schubert iface->radar_background.freq == freq; 1115*a90b9d01SCy Schubert } 1116*a90b9d01SCy Schubert 1117*a90b9d01SCy Schubert 1118*a90b9d01SCy Schubert static int 1119*a90b9d01SCy Schubert hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface) 1120*a90b9d01SCy Schubert { 1121*a90b9d01SCy Schubert u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 1122*a90b9d01SCy Schubert 1123*a90b9d01SCy Schubert iface->conf->channel = iface->radar_background.channel; 1124*a90b9d01SCy Schubert iface->freq = iface->radar_background.freq; 1125*a90b9d01SCy Schubert iface->conf->secondary_channel = 1126*a90b9d01SCy Schubert iface->radar_background.secondary_channel; 1127*a90b9d01SCy Schubert hostapd_set_oper_centr_freq_seg0_idx( 1128*a90b9d01SCy Schubert iface->conf, iface->radar_background.centr_freq_seg0_idx); 1129*a90b9d01SCy Schubert hostapd_set_oper_centr_freq_seg1_idx( 1130*a90b9d01SCy Schubert iface->conf, iface->radar_background.centr_freq_seg1_idx); 1131*a90b9d01SCy Schubert 1132*a90b9d01SCy Schubert hostapd_dfs_update_background_chain(iface); 1133*a90b9d01SCy Schubert 1134*a90b9d01SCy Schubert return hostapd_dfs_request_channel_switch( 1135*a90b9d01SCy Schubert iface, iface->conf->channel, iface->freq, 1136*a90b9d01SCy Schubert iface->conf->secondary_channel, current_vht_oper_chwidth, 1137*a90b9d01SCy Schubert hostapd_get_oper_centr_freq_seg0_idx(iface->conf), 1138*a90b9d01SCy Schubert hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); 1139*a90b9d01SCy Schubert } 1140*a90b9d01SCy Schubert 1141*a90b9d01SCy Schubert 11425b9c547cSRui Paulo int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, 11435b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 11445b9c547cSRui Paulo int cf1, int cf2) 11455b9c547cSRui Paulo { 11465b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED 1147*a90b9d01SCy Schubert "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d radar_detected=%d", 1148*a90b9d01SCy Schubert success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, 1149*a90b9d01SCy Schubert iface->radar_detected); 11505b9c547cSRui Paulo 11515b9c547cSRui Paulo if (success) { 11525b9c547cSRui Paulo /* Complete iface/ap configuration */ 11535b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) { 1154*a90b9d01SCy Schubert /* Complete AP configuration for the first bring up. If 1155*a90b9d01SCy Schubert * a radar was detected in this channel, interface setup 1156*a90b9d01SCy Schubert * will be handled in 1157*a90b9d01SCy Schubert * 1. hostapd_event_ch_switch() if switching to a 1158*a90b9d01SCy Schubert * non-DFS channel 1159*a90b9d01SCy Schubert * 2. on next CAC complete event if switching to another 1160*a90b9d01SCy Schubert * DFS channel. 1161*a90b9d01SCy Schubert */ 1162*a90b9d01SCy Schubert if (iface->state != HAPD_IFACE_ENABLED && 1163*a90b9d01SCy Schubert !iface->radar_detected) 11645b9c547cSRui Paulo hostapd_setup_interface_complete(iface, 0); 11655b9c547cSRui Paulo else 11665b9c547cSRui Paulo iface->cac_started = 0; 11675b9c547cSRui Paulo } else { 11685b9c547cSRui Paulo set_dfs_state(iface, freq, ht_enabled, chan_offset, 11695b9c547cSRui Paulo chan_width, cf1, cf2, 11705b9c547cSRui Paulo HOSTAPD_CHAN_DFS_AVAILABLE); 1171*a90b9d01SCy Schubert 1172*a90b9d01SCy Schubert /* 1173*a90b9d01SCy Schubert * Radar event from background chain for the selected 1174*a90b9d01SCy Schubert * channel. Perform CSA, move the main chain to the 1175*a90b9d01SCy Schubert * selected channel and configure the background chain 1176*a90b9d01SCy Schubert * to a new DFS channel. 1177*a90b9d01SCy Schubert */ 1178*a90b9d01SCy Schubert if (hostapd_dfs_is_background_event(iface, freq)) { 1179*a90b9d01SCy Schubert iface->radar_background.cac_started = 0; 1180*a90b9d01SCy Schubert if (!iface->radar_background.temp_ch) 1181*a90b9d01SCy Schubert return 0; 1182*a90b9d01SCy Schubert 1183*a90b9d01SCy Schubert iface->radar_background.temp_ch = 0; 1184*a90b9d01SCy Schubert return hostapd_dfs_start_channel_switch_background(iface); 1185*a90b9d01SCy Schubert } 1186*a90b9d01SCy Schubert 118785732ac8SCy Schubert /* 118885732ac8SCy Schubert * Just mark the channel available when CAC completion 118985732ac8SCy Schubert * event is received in enabled state. CAC result could 119085732ac8SCy Schubert * have been propagated from another radio having the 119185732ac8SCy Schubert * same regulatory configuration. When CAC completion is 119285732ac8SCy Schubert * received during non-HAPD_IFACE_ENABLED state, make 119385732ac8SCy Schubert * sure the configured channel is available because this 119485732ac8SCy Schubert * CAC completion event could have been propagated from 119585732ac8SCy Schubert * another radio. 119685732ac8SCy Schubert */ 119785732ac8SCy Schubert if (iface->state != HAPD_IFACE_ENABLED && 1198c1d255d3SCy Schubert hostapd_is_dfs_chan_available(iface)) { 11995b9c547cSRui Paulo hostapd_setup_interface_complete(iface, 0); 120085732ac8SCy Schubert iface->cac_started = 0; 12015b9c547cSRui Paulo } 12025b9c547cSRui Paulo } 1203*a90b9d01SCy Schubert } else if (hostapd_dfs_is_background_event(iface, freq)) { 1204*a90b9d01SCy Schubert iface->radar_background.cac_started = 0; 1205*a90b9d01SCy Schubert hostapd_dfs_update_background_chain(iface); 120685732ac8SCy Schubert } 120785732ac8SCy Schubert 1208*a90b9d01SCy Schubert iface->radar_detected = false; 120985732ac8SCy Schubert return 0; 121085732ac8SCy Schubert } 121185732ac8SCy Schubert 121285732ac8SCy Schubert 121385732ac8SCy Schubert int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, 121485732ac8SCy Schubert int ht_enabled, int chan_offset, int chan_width, 121585732ac8SCy Schubert int cf1, int cf2) 121685732ac8SCy Schubert { 121785732ac8SCy Schubert wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED 121885732ac8SCy Schubert "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 121985732ac8SCy Schubert freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 122085732ac8SCy Schubert 122185732ac8SCy Schubert /* Proceed only if DFS is not offloaded to the driver */ 122285732ac8SCy Schubert if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 122385732ac8SCy Schubert return 0; 122485732ac8SCy Schubert 122585732ac8SCy Schubert set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 122685732ac8SCy Schubert cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); 12275b9c547cSRui Paulo 12285b9c547cSRui Paulo return 0; 12295b9c547cSRui Paulo } 12305b9c547cSRui Paulo 12315b9c547cSRui Paulo 1232c1d255d3SCy Schubert static struct hostapd_channel_data * 1233c1d255d3SCy Schubert dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel, 1234c1d255d3SCy Schubert u8 *oper_centr_freq_seg0_idx, 1235*a90b9d01SCy Schubert u8 *oper_centr_freq_seg1_idx, 1236*a90b9d01SCy Schubert enum dfs_channel_type *channel_type) 1237c1d255d3SCy Schubert { 1238c1d255d3SCy Schubert struct hostapd_channel_data *channel; 1239c1d255d3SCy Schubert 1240c1d255d3SCy Schubert for (;;) { 1241c1d255d3SCy Schubert channel = dfs_get_valid_channel(iface, secondary_channel, 1242c1d255d3SCy Schubert oper_centr_freq_seg0_idx, 1243c1d255d3SCy Schubert oper_centr_freq_seg1_idx, 1244*a90b9d01SCy Schubert *channel_type); 1245c1d255d3SCy Schubert if (channel) { 1246c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d", 1247c1d255d3SCy Schubert channel->chan); 1248c1d255d3SCy Schubert return channel; 1249c1d255d3SCy Schubert } 1250c1d255d3SCy Schubert 1251*a90b9d01SCy Schubert if (*channel_type != DFS_ANY_CHANNEL) { 1252*a90b9d01SCy Schubert *channel_type = DFS_ANY_CHANNEL; 1253c1d255d3SCy Schubert } else { 1254c1d255d3SCy Schubert int oper_chwidth; 1255c1d255d3SCy Schubert 1256c1d255d3SCy Schubert oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 1257*a90b9d01SCy Schubert if (oper_chwidth == CONF_OPER_CHWIDTH_USE_HT) 1258c1d255d3SCy Schubert break; 1259*a90b9d01SCy Schubert *channel_type = DFS_AVAILABLE; 1260c1d255d3SCy Schubert hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1); 1261c1d255d3SCy Schubert } 1262c1d255d3SCy Schubert } 1263c1d255d3SCy Schubert 1264c1d255d3SCy Schubert wpa_printf(MSG_INFO, 1265c1d255d3SCy Schubert "%s: no DFS channels left, waiting for NOP to finish", 1266c1d255d3SCy Schubert __func__); 1267c1d255d3SCy Schubert return NULL; 1268c1d255d3SCy Schubert } 1269c1d255d3SCy Schubert 1270c1d255d3SCy Schubert 12715b9c547cSRui Paulo static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) 12725b9c547cSRui Paulo { 12735b9c547cSRui Paulo struct hostapd_channel_data *channel; 12745b9c547cSRui Paulo int secondary_channel; 1275206b73d0SCy Schubert u8 oper_centr_freq_seg0_idx = 0; 1276206b73d0SCy Schubert u8 oper_centr_freq_seg1_idx = 0; 1277*a90b9d01SCy Schubert enum dfs_channel_type channel_type = DFS_ANY_CHANNEL; 12785b9c547cSRui Paulo int err = 1; 12795b9c547cSRui Paulo 12805b9c547cSRui Paulo /* Radar detected during active CAC */ 12815b9c547cSRui Paulo iface->cac_started = 0; 12825b9c547cSRui Paulo channel = dfs_get_valid_channel(iface, &secondary_channel, 1283206b73d0SCy Schubert &oper_centr_freq_seg0_idx, 1284206b73d0SCy Schubert &oper_centr_freq_seg1_idx, 1285*a90b9d01SCy Schubert channel_type); 12865b9c547cSRui Paulo 12875b9c547cSRui Paulo if (!channel) { 1288c1d255d3SCy Schubert channel = dfs_downgrade_bandwidth(iface, &secondary_channel, 1289c1d255d3SCy Schubert &oper_centr_freq_seg0_idx, 1290c1d255d3SCy Schubert &oper_centr_freq_seg1_idx, 1291*a90b9d01SCy Schubert &channel_type); 1292c1d255d3SCy Schubert if (!channel) { 12935b9c547cSRui Paulo wpa_printf(MSG_ERROR, "No valid channel available"); 12945b9c547cSRui Paulo return err; 12955b9c547cSRui Paulo } 1296c1d255d3SCy Schubert } 12975b9c547cSRui Paulo 12985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", 12995b9c547cSRui Paulo channel->chan); 13005b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL 13015b9c547cSRui Paulo "freq=%d chan=%d sec_chan=%d", channel->freq, 13025b9c547cSRui Paulo channel->chan, secondary_channel); 13035b9c547cSRui Paulo 13045b9c547cSRui Paulo iface->freq = channel->freq; 13055b9c547cSRui Paulo iface->conf->channel = channel->chan; 13065b9c547cSRui Paulo iface->conf->secondary_channel = secondary_channel; 1307206b73d0SCy Schubert hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 1308206b73d0SCy Schubert oper_centr_freq_seg0_idx); 1309206b73d0SCy Schubert hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 1310206b73d0SCy Schubert oper_centr_freq_seg1_idx); 13115b9c547cSRui Paulo err = 0; 13125b9c547cSRui Paulo 13135b9c547cSRui Paulo hostapd_setup_interface_complete(iface, err); 13145b9c547cSRui Paulo return err; 13155b9c547cSRui Paulo } 13165b9c547cSRui Paulo 13175b9c547cSRui Paulo 1318*a90b9d01SCy Schubert static int 1319*a90b9d01SCy Schubert hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface, 1320*a90b9d01SCy Schubert int freq) 1321*a90b9d01SCy Schubert { 1322*a90b9d01SCy Schubert if (!dfs_use_radar_background(iface)) 1323*a90b9d01SCy Schubert return -1; /* Background radar chain not supported. */ 1324*a90b9d01SCy Schubert 1325*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1326*a90b9d01SCy Schubert "%s called (background CAC active: %s, CSA active: %s)", 1327*a90b9d01SCy Schubert __func__, iface->radar_background.cac_started ? "yes" : "no", 1328*a90b9d01SCy Schubert hostapd_csa_in_progress(iface) ? "yes" : "no"); 1329*a90b9d01SCy Schubert 1330*a90b9d01SCy Schubert /* Check if CSA in progress */ 1331*a90b9d01SCy Schubert if (hostapd_csa_in_progress(iface)) 1332*a90b9d01SCy Schubert return 0; 1333*a90b9d01SCy Schubert 1334*a90b9d01SCy Schubert if (hostapd_dfs_is_background_event(iface, freq)) { 1335*a90b9d01SCy Schubert /* 1336*a90b9d01SCy Schubert * Radar pattern is reported on the background chain. 1337*a90b9d01SCy Schubert * Just select a new random channel according to the 1338*a90b9d01SCy Schubert * regulations for monitoring. 1339*a90b9d01SCy Schubert */ 1340*a90b9d01SCy Schubert hostapd_dfs_update_background_chain(iface); 1341*a90b9d01SCy Schubert return 0; 1342*a90b9d01SCy Schubert } 1343*a90b9d01SCy Schubert 1344*a90b9d01SCy Schubert /* 1345*a90b9d01SCy Schubert * If background radar detection is supported and the radar channel 1346*a90b9d01SCy Schubert * monitored by the background chain is available switch to it without 1347*a90b9d01SCy Schubert * waiting for the CAC. 1348*a90b9d01SCy Schubert */ 1349*a90b9d01SCy Schubert if (iface->radar_background.channel == -1) 1350*a90b9d01SCy Schubert return -1; /* Background radar chain not available. */ 1351*a90b9d01SCy Schubert 1352*a90b9d01SCy Schubert if (iface->radar_background.cac_started) { 1353*a90b9d01SCy Schubert /* 1354*a90b9d01SCy Schubert * Background channel not available yet. Perform CAC on the 1355*a90b9d01SCy Schubert * main chain. 1356*a90b9d01SCy Schubert */ 1357*a90b9d01SCy Schubert iface->radar_background.temp_ch = 1; 1358*a90b9d01SCy Schubert return -1; 1359*a90b9d01SCy Schubert } 1360*a90b9d01SCy Schubert 1361*a90b9d01SCy Schubert return hostapd_dfs_start_channel_switch_background(iface); 1362*a90b9d01SCy Schubert } 1363*a90b9d01SCy Schubert 1364*a90b9d01SCy Schubert 13655b9c547cSRui Paulo static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) 13665b9c547cSRui Paulo { 13675b9c547cSRui Paulo struct hostapd_channel_data *channel; 13685b9c547cSRui Paulo int secondary_channel; 1369206b73d0SCy Schubert u8 oper_centr_freq_seg0_idx; 1370206b73d0SCy Schubert u8 oper_centr_freq_seg1_idx; 1371*a90b9d01SCy Schubert enum dfs_channel_type channel_type = DFS_AVAILABLE; 1372c1d255d3SCy Schubert u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 13735b9c547cSRui Paulo 13745b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", 13755b9c547cSRui Paulo __func__, iface->cac_started ? "yes" : "no", 13765b9c547cSRui Paulo hostapd_csa_in_progress(iface) ? "yes" : "no"); 13775b9c547cSRui Paulo 13785b9c547cSRui Paulo /* Check if CSA in progress */ 13795b9c547cSRui Paulo if (hostapd_csa_in_progress(iface)) 13805b9c547cSRui Paulo return 0; 13815b9c547cSRui Paulo 13825b9c547cSRui Paulo /* Check if active CAC */ 13835b9c547cSRui Paulo if (iface->cac_started) 13845b9c547cSRui Paulo return hostapd_dfs_start_channel_switch_cac(iface); 13855b9c547cSRui Paulo 138685732ac8SCy Schubert /* 138785732ac8SCy Schubert * Allow selection of DFS channel in ETSI to comply with 138885732ac8SCy Schubert * uniform spreading. 138985732ac8SCy Schubert */ 139085732ac8SCy Schubert if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) 1391*a90b9d01SCy Schubert channel_type = DFS_ANY_CHANNEL; 139285732ac8SCy Schubert 13935b9c547cSRui Paulo /* Perform channel switch/CSA */ 13945b9c547cSRui Paulo channel = dfs_get_valid_channel(iface, &secondary_channel, 1395206b73d0SCy Schubert &oper_centr_freq_seg0_idx, 1396206b73d0SCy Schubert &oper_centr_freq_seg1_idx, 1397*a90b9d01SCy Schubert channel_type); 13985b9c547cSRui Paulo 13995b9c547cSRui Paulo if (!channel) { 14005b9c547cSRui Paulo /* 14015b9c547cSRui Paulo * If there is no channel to switch immediately to, check if 14025b9c547cSRui Paulo * there is another channel where we can switch even if it 14035b9c547cSRui Paulo * requires to perform a CAC first. 14045b9c547cSRui Paulo */ 1405*a90b9d01SCy Schubert channel_type = DFS_ANY_CHANNEL; 1406c1d255d3SCy Schubert channel = dfs_downgrade_bandwidth(iface, &secondary_channel, 1407206b73d0SCy Schubert &oper_centr_freq_seg0_idx, 1408206b73d0SCy Schubert &oper_centr_freq_seg1_idx, 1409*a90b9d01SCy Schubert &channel_type); 14105b9c547cSRui Paulo if (!channel) { 1411c1d255d3SCy Schubert /* 1412c1d255d3SCy Schubert * Toggle interface state to enter DFS state 1413c1d255d3SCy Schubert * until NOP is finished. 1414c1d255d3SCy Schubert */ 1415c1d255d3SCy Schubert hostapd_disable_iface(iface); 1416c1d255d3SCy Schubert hostapd_enable_iface(iface); 1417c1d255d3SCy Schubert return 0; 14185b9c547cSRui Paulo } 14195b9c547cSRui Paulo 1420*a90b9d01SCy Schubert if (channel_type == DFS_ANY_CHANNEL) { 14215b9c547cSRui Paulo iface->freq = channel->freq; 14225b9c547cSRui Paulo iface->conf->channel = channel->chan; 14235b9c547cSRui Paulo iface->conf->secondary_channel = secondary_channel; 1424c1d255d3SCy Schubert hostapd_set_oper_centr_freq_seg0_idx( 1425c1d255d3SCy Schubert iface->conf, oper_centr_freq_seg0_idx); 1426c1d255d3SCy Schubert hostapd_set_oper_centr_freq_seg1_idx( 1427c1d255d3SCy Schubert iface->conf, oper_centr_freq_seg1_idx); 14285b9c547cSRui Paulo 14295b9c547cSRui Paulo hostapd_disable_iface(iface); 14305b9c547cSRui Paulo hostapd_enable_iface(iface); 14315b9c547cSRui Paulo return 0; 14325b9c547cSRui Paulo } 1433c1d255d3SCy Schubert } 14345b9c547cSRui Paulo 1435*a90b9d01SCy Schubert return hostapd_dfs_request_channel_switch(iface, channel->chan, 14365b9c547cSRui Paulo channel->freq, 14375b9c547cSRui Paulo secondary_channel, 1438*a90b9d01SCy Schubert current_vht_oper_chwidth, 1439206b73d0SCy Schubert oper_centr_freq_seg0_idx, 1440206b73d0SCy Schubert oper_centr_freq_seg1_idx); 14415b9c547cSRui Paulo } 14425b9c547cSRui Paulo 14435b9c547cSRui Paulo 14445b9c547cSRui Paulo int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, 14455b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 14465b9c547cSRui Paulo int cf1, int cf2) 14475b9c547cSRui Paulo { 14485b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED 14495b9c547cSRui Paulo "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 14505b9c547cSRui Paulo freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 14515b9c547cSRui Paulo 1452*a90b9d01SCy Schubert iface->radar_detected = true; 1453*a90b9d01SCy Schubert 14545b9c547cSRui Paulo /* Proceed only if DFS is not offloaded to the driver */ 14555b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 14565b9c547cSRui Paulo return 0; 14575b9c547cSRui Paulo 14585b9c547cSRui Paulo if (!iface->conf->ieee80211h) 14595b9c547cSRui Paulo return 0; 14605b9c547cSRui Paulo 14615b9c547cSRui Paulo /* mark radar frequency as invalid */ 1462*a90b9d01SCy Schubert if (!set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 1463*a90b9d01SCy Schubert cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE)) 1464c1d255d3SCy Schubert return 0; 14655b9c547cSRui Paulo 1466*a90b9d01SCy Schubert if (!hostapd_dfs_is_background_event(iface, freq)) { 14675b9c547cSRui Paulo /* Skip if reported radar event not overlapped our channels */ 1468*a90b9d01SCy Schubert if (!dfs_are_channels_overlapped(iface, freq, chan_width, 1469*a90b9d01SCy Schubert cf1, cf2)) 14705b9c547cSRui Paulo return 0; 1471*a90b9d01SCy Schubert } 14725b9c547cSRui Paulo 1473*a90b9d01SCy Schubert if (hostapd_dfs_background_start_channel_switch(iface, freq)) { 1474*a90b9d01SCy Schubert /* Radar detected while operating, switch the channel. */ 1475*a90b9d01SCy Schubert return hostapd_dfs_start_channel_switch(iface); 1476*a90b9d01SCy Schubert } 14775b9c547cSRui Paulo 1478*a90b9d01SCy Schubert return 0; 14795b9c547cSRui Paulo } 14805b9c547cSRui Paulo 14815b9c547cSRui Paulo 14825b9c547cSRui Paulo int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, 14835b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 14845b9c547cSRui Paulo int cf1, int cf2) 14855b9c547cSRui Paulo { 14865b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED 14875b9c547cSRui Paulo "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 14885b9c547cSRui Paulo freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 14895b9c547cSRui Paulo 14905b9c547cSRui Paulo /* Proceed only if DFS is not offloaded to the driver */ 14915b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 14925b9c547cSRui Paulo return 0; 14935b9c547cSRui Paulo 14945b9c547cSRui Paulo /* TODO add correct implementation here */ 14955b9c547cSRui Paulo set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 14965b9c547cSRui Paulo cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); 1497780fb4a2SCy Schubert 1498*a90b9d01SCy Schubert if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) { 1499780fb4a2SCy Schubert /* Handle cases where all channels were initially unavailable */ 1500780fb4a2SCy Schubert hostapd_handle_dfs(iface); 1501*a90b9d01SCy Schubert } else if (dfs_use_radar_background(iface) && 1502*a90b9d01SCy Schubert iface->radar_background.channel == -1) { 1503*a90b9d01SCy Schubert /* Reset radar background chain if disabled */ 1504*a90b9d01SCy Schubert hostapd_dfs_update_background_chain(iface); 1505*a90b9d01SCy Schubert } 1506780fb4a2SCy Schubert 15075b9c547cSRui Paulo return 0; 15085b9c547cSRui Paulo } 15095b9c547cSRui Paulo 15105b9c547cSRui Paulo 15115b9c547cSRui Paulo int hostapd_is_dfs_required(struct hostapd_iface *iface) 15125b9c547cSRui Paulo { 15135b9c547cSRui Paulo int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res; 15145b9c547cSRui Paulo 1515c1d255d3SCy Schubert if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && 1516c1d255d3SCy Schubert !iface->conf->ieee80211h) || 1517c1d255d3SCy Schubert !iface->current_mode || 15185b9c547cSRui Paulo iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) 15195b9c547cSRui Paulo return 0; 15205b9c547cSRui Paulo 15215b9c547cSRui Paulo /* Get start (first) channel for current configuration */ 15225b9c547cSRui Paulo start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 15235b9c547cSRui Paulo if (start_chan_idx == -1) 15245b9c547cSRui Paulo return -1; 15255b9c547cSRui Paulo 15265b9c547cSRui Paulo /* Get number of used channels, depend on width */ 15275b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 15285b9c547cSRui Paulo 15295b9c547cSRui Paulo /* Check if any of configured channels require DFS */ 15305b9c547cSRui Paulo res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); 15315b9c547cSRui Paulo if (res) 15325b9c547cSRui Paulo return res; 15335b9c547cSRui Paulo if (start_chan_idx1 >= 0 && n_chans1 > 0) 15345b9c547cSRui Paulo res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1); 15355b9c547cSRui Paulo return res; 15365b9c547cSRui Paulo } 15375b9c547cSRui Paulo 15385b9c547cSRui Paulo 15395b9c547cSRui Paulo int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, 15405b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 15415b9c547cSRui Paulo int cf1, int cf2) 15425b9c547cSRui Paulo { 1543*a90b9d01SCy Schubert if (hostapd_dfs_is_background_event(iface, freq)) { 1544*a90b9d01SCy Schubert iface->radar_background.cac_started = 1; 1545*a90b9d01SCy Schubert } else { 1546*a90b9d01SCy Schubert /* This is called when the driver indicates that an offloaded 1547*a90b9d01SCy Schubert * DFS has started CAC. radar_detected might be set for previous 1548*a90b9d01SCy Schubert * DFS channel. Clear it for this new CAC process. */ 1549c1d255d3SCy Schubert hostapd_set_state(iface, HAPD_IFACE_DFS); 1550*a90b9d01SCy Schubert iface->cac_started = 1; 1551*a90b9d01SCy Schubert 1552*a90b9d01SCy Schubert /* Clear radar_detected in case it is for the previous 1553*a90b9d01SCy Schubert * frequency. Also remove disabled link's information in RNR 1554*a90b9d01SCy Schubert * element from other links. */ 1555*a90b9d01SCy Schubert iface->radar_detected = false; 1556*a90b9d01SCy Schubert if (iface->interfaces && iface->interfaces->count > 1) 1557*a90b9d01SCy Schubert ieee802_11_set_beacons(iface); 1558*a90b9d01SCy Schubert } 1559c1d255d3SCy Schubert /* TODO: How to check CAC time for ETSI weather channels? */ 1560c1d255d3SCy Schubert iface->dfs_cac_ms = 60000; 15615b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START 15625b9c547cSRui Paulo "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " 1563*a90b9d01SCy Schubert "seg1=%d cac_time=%ds%s", 1564c1d255d3SCy Schubert freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 1565*a90b9d01SCy Schubert iface->dfs_cac_ms / 1000, 1566*a90b9d01SCy Schubert hostapd_dfs_is_background_event(iface, freq) ? 1567*a90b9d01SCy Schubert " (background)" : ""); 1568*a90b9d01SCy Schubert 1569c1d255d3SCy Schubert os_get_reltime(&iface->dfs_cac_start); 15705b9c547cSRui Paulo return 0; 15715b9c547cSRui Paulo } 15725b9c547cSRui Paulo 15735b9c547cSRui Paulo 15745b9c547cSRui Paulo /* 15755b9c547cSRui Paulo * Main DFS handler for offloaded case. 15765b9c547cSRui Paulo * 2 - continue channel/AP setup for non-DFS channel 15775b9c547cSRui Paulo * 1 - continue channel/AP setup for DFS channel 15785b9c547cSRui Paulo * 0 - channel/AP setup will be continued after CAC 15795b9c547cSRui Paulo * -1 - hit critical error 15805b9c547cSRui Paulo */ 15815b9c547cSRui Paulo int hostapd_handle_dfs_offload(struct hostapd_iface *iface) 15825b9c547cSRui Paulo { 1583c1d255d3SCy Schubert int dfs_res; 1584c1d255d3SCy Schubert 15855b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", 15865b9c547cSRui Paulo __func__, iface->cac_started); 15875b9c547cSRui Paulo 15885b9c547cSRui Paulo /* 15895b9c547cSRui Paulo * If DFS has already been started, then we are being called from a 15905b9c547cSRui Paulo * callback to continue AP/channel setup. Reset the CAC start flag and 15915b9c547cSRui Paulo * return. 15925b9c547cSRui Paulo */ 15935b9c547cSRui Paulo if (iface->cac_started) { 15945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", 15955b9c547cSRui Paulo __func__, iface->cac_started); 15965b9c547cSRui Paulo iface->cac_started = 0; 15975b9c547cSRui Paulo return 1; 15985b9c547cSRui Paulo } 15995b9c547cSRui Paulo 1600c1d255d3SCy Schubert dfs_res = hostapd_is_dfs_required(iface); 1601c1d255d3SCy Schubert if (dfs_res > 0) { 1602c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1603c1d255d3SCy Schubert "%s: freq %d MHz requires DFS for %d chans", 1604c1d255d3SCy Schubert __func__, iface->freq, dfs_res); 16055b9c547cSRui Paulo return 0; 16065b9c547cSRui Paulo } 16075b9c547cSRui Paulo 16085b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 16095b9c547cSRui Paulo "%s: freq %d MHz does not require DFS. Continue channel/AP setup", 16105b9c547cSRui Paulo __func__, iface->freq); 16115b9c547cSRui Paulo return 2; 16125b9c547cSRui Paulo } 1613c1d255d3SCy Schubert 1614c1d255d3SCy Schubert 1615c1d255d3SCy Schubert int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width, 1616c1d255d3SCy Schubert int center_freq) 1617c1d255d3SCy Schubert { 1618c1d255d3SCy Schubert struct hostapd_channel_data *chan; 1619c1d255d3SCy Schubert struct hostapd_hw_modes *mode = iface->current_mode; 1620c1d255d3SCy Schubert int half_width; 1621c1d255d3SCy Schubert int res = 0; 1622c1d255d3SCy Schubert int i; 1623c1d255d3SCy Schubert 1624c1d255d3SCy Schubert if (!iface->conf->ieee80211h || !mode || 1625c1d255d3SCy Schubert mode->mode != HOSTAPD_MODE_IEEE80211A) 1626c1d255d3SCy Schubert return 0; 1627c1d255d3SCy Schubert 1628c1d255d3SCy Schubert switch (width) { 1629c1d255d3SCy Schubert case CHAN_WIDTH_20_NOHT: 1630c1d255d3SCy Schubert case CHAN_WIDTH_20: 1631c1d255d3SCy Schubert half_width = 10; 1632c1d255d3SCy Schubert break; 1633c1d255d3SCy Schubert case CHAN_WIDTH_40: 1634c1d255d3SCy Schubert half_width = 20; 1635c1d255d3SCy Schubert break; 1636c1d255d3SCy Schubert case CHAN_WIDTH_80: 1637c1d255d3SCy Schubert case CHAN_WIDTH_80P80: 1638c1d255d3SCy Schubert half_width = 40; 1639c1d255d3SCy Schubert break; 1640c1d255d3SCy Schubert case CHAN_WIDTH_160: 1641c1d255d3SCy Schubert half_width = 80; 1642c1d255d3SCy Schubert break; 1643c1d255d3SCy Schubert default: 1644c1d255d3SCy Schubert wpa_printf(MSG_WARNING, "DFS chanwidth %d not supported", 1645c1d255d3SCy Schubert width); 1646c1d255d3SCy Schubert return 0; 1647c1d255d3SCy Schubert } 1648c1d255d3SCy Schubert 1649c1d255d3SCy Schubert for (i = 0; i < mode->num_channels; i++) { 1650c1d255d3SCy Schubert chan = &mode->channels[i]; 1651c1d255d3SCy Schubert 1652c1d255d3SCy Schubert if (!(chan->flag & HOSTAPD_CHAN_RADAR)) 1653c1d255d3SCy Schubert continue; 1654c1d255d3SCy Schubert 1655c1d255d3SCy Schubert if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == 1656c1d255d3SCy Schubert HOSTAPD_CHAN_DFS_AVAILABLE) 1657c1d255d3SCy Schubert continue; 1658c1d255d3SCy Schubert 1659c1d255d3SCy Schubert if (center_freq - chan->freq < half_width && 1660c1d255d3SCy Schubert chan->freq - center_freq < half_width) 1661c1d255d3SCy Schubert res++; 1662c1d255d3SCy Schubert } 1663c1d255d3SCy Schubert 1664c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s", 1665c1d255d3SCy Schubert center_freq - half_width, center_freq + half_width, 1666c1d255d3SCy Schubert res ? "yes" : "no"); 1667c1d255d3SCy Schubert 1668c1d255d3SCy Schubert return res; 1669c1d255d3SCy Schubert } 1670