15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * DFS - Dynamic Frequency Selection 35b9c547cSRui Paulo * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 45b9c547cSRui Paulo * Copyright (c) 2013-2015, 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" 175b9c547cSRui Paulo #include "ap_drv_ops.h" 185b9c547cSRui Paulo #include "drivers/driver.h" 195b9c547cSRui Paulo #include "dfs.h" 205b9c547cSRui Paulo 215b9c547cSRui Paulo 225b9c547cSRui Paulo static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) 235b9c547cSRui Paulo { 245b9c547cSRui Paulo int n_chans = 1; 255b9c547cSRui Paulo 265b9c547cSRui Paulo *seg1 = 0; 275b9c547cSRui Paulo 285b9c547cSRui Paulo if (iface->conf->ieee80211n && iface->conf->secondary_channel) 295b9c547cSRui Paulo n_chans = 2; 305b9c547cSRui Paulo 315b9c547cSRui Paulo if (iface->conf->ieee80211ac) { 325b9c547cSRui Paulo switch (iface->conf->vht_oper_chwidth) { 335b9c547cSRui Paulo case VHT_CHANWIDTH_USE_HT: 345b9c547cSRui Paulo break; 355b9c547cSRui Paulo case VHT_CHANWIDTH_80MHZ: 365b9c547cSRui Paulo n_chans = 4; 375b9c547cSRui Paulo break; 385b9c547cSRui Paulo case VHT_CHANWIDTH_160MHZ: 395b9c547cSRui Paulo n_chans = 8; 405b9c547cSRui Paulo break; 415b9c547cSRui Paulo case VHT_CHANWIDTH_80P80MHZ: 425b9c547cSRui Paulo n_chans = 4; 435b9c547cSRui Paulo *seg1 = 4; 445b9c547cSRui Paulo break; 455b9c547cSRui Paulo default: 465b9c547cSRui Paulo break; 475b9c547cSRui Paulo } 485b9c547cSRui Paulo } 495b9c547cSRui Paulo 505b9c547cSRui Paulo return n_chans; 515b9c547cSRui Paulo } 525b9c547cSRui Paulo 535b9c547cSRui Paulo 545b9c547cSRui Paulo static int dfs_channel_available(struct hostapd_channel_data *chan, 555b9c547cSRui Paulo int skip_radar) 565b9c547cSRui Paulo { 575b9c547cSRui Paulo /* 585b9c547cSRui Paulo * When radar detection happens, CSA is performed. However, there's no 595b9c547cSRui Paulo * time for CAC, so radar channels must be skipped when finding a new 605b9c547cSRui Paulo * channel for CSA, unless they are available for immediate use. 615b9c547cSRui Paulo */ 625b9c547cSRui Paulo if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) && 635b9c547cSRui Paulo ((chan->flag & HOSTAPD_CHAN_DFS_MASK) != 645b9c547cSRui Paulo HOSTAPD_CHAN_DFS_AVAILABLE)) 655b9c547cSRui Paulo return 0; 665b9c547cSRui Paulo 675b9c547cSRui Paulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 685b9c547cSRui Paulo return 0; 695b9c547cSRui Paulo if ((chan->flag & HOSTAPD_CHAN_RADAR) && 705b9c547cSRui Paulo ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == 715b9c547cSRui Paulo HOSTAPD_CHAN_DFS_UNAVAILABLE)) 725b9c547cSRui Paulo return 0; 735b9c547cSRui Paulo return 1; 745b9c547cSRui Paulo } 755b9c547cSRui Paulo 765b9c547cSRui Paulo 775b9c547cSRui Paulo static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) 785b9c547cSRui Paulo { 795b9c547cSRui Paulo /* 805b9c547cSRui Paulo * The tables contain first valid channel number based on channel width. 815b9c547cSRui Paulo * We will also choose this first channel as the control one. 825b9c547cSRui Paulo */ 835b9c547cSRui Paulo int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 845b9c547cSRui Paulo 184, 192 }; 855b9c547cSRui Paulo /* 865b9c547cSRui Paulo * VHT80, valid channels based on center frequency: 875b9c547cSRui Paulo * 42, 58, 106, 122, 138, 155 885b9c547cSRui Paulo */ 895b9c547cSRui Paulo int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; 905b9c547cSRui Paulo /* 915b9c547cSRui Paulo * VHT160 valid channels based on center frequency: 925b9c547cSRui Paulo * 50, 114 935b9c547cSRui Paulo */ 945b9c547cSRui Paulo int allowed_160[] = { 36, 100 }; 955b9c547cSRui Paulo int *allowed = allowed_40; 965b9c547cSRui Paulo unsigned int i, allowed_no = 0; 975b9c547cSRui Paulo 985b9c547cSRui Paulo switch (n_chans) { 995b9c547cSRui Paulo case 2: 1005b9c547cSRui Paulo allowed = allowed_40; 1015b9c547cSRui Paulo allowed_no = ARRAY_SIZE(allowed_40); 1025b9c547cSRui Paulo break; 1035b9c547cSRui Paulo case 4: 1045b9c547cSRui Paulo allowed = allowed_80; 1055b9c547cSRui Paulo allowed_no = ARRAY_SIZE(allowed_80); 1065b9c547cSRui Paulo break; 1075b9c547cSRui Paulo case 8: 1085b9c547cSRui Paulo allowed = allowed_160; 1095b9c547cSRui Paulo allowed_no = ARRAY_SIZE(allowed_160); 1105b9c547cSRui Paulo break; 1115b9c547cSRui Paulo default: 1125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); 1135b9c547cSRui Paulo break; 1145b9c547cSRui Paulo } 1155b9c547cSRui Paulo 1165b9c547cSRui Paulo for (i = 0; i < allowed_no; i++) { 1175b9c547cSRui Paulo if (chan->chan == allowed[i]) 1185b9c547cSRui Paulo return 1; 1195b9c547cSRui Paulo } 1205b9c547cSRui Paulo 1215b9c547cSRui Paulo return 0; 1225b9c547cSRui Paulo } 1235b9c547cSRui Paulo 1245b9c547cSRui Paulo 125*325151a3SRui Paulo static struct hostapd_channel_data * 126*325151a3SRui Paulo dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) 127*325151a3SRui Paulo { 128*325151a3SRui Paulo int i; 129*325151a3SRui Paulo 130*325151a3SRui Paulo for (i = first_chan_idx; i < mode->num_channels; i++) { 131*325151a3SRui Paulo if (mode->channels[i].freq == freq) 132*325151a3SRui Paulo return &mode->channels[i]; 133*325151a3SRui Paulo } 134*325151a3SRui Paulo 135*325151a3SRui Paulo return NULL; 136*325151a3SRui Paulo } 137*325151a3SRui Paulo 138*325151a3SRui Paulo 1395b9c547cSRui Paulo static int dfs_chan_range_available(struct hostapd_hw_modes *mode, 1405b9c547cSRui Paulo int first_chan_idx, int num_chans, 1415b9c547cSRui Paulo int skip_radar) 1425b9c547cSRui Paulo { 1435b9c547cSRui Paulo struct hostapd_channel_data *first_chan, *chan; 1445b9c547cSRui Paulo int i; 1455b9c547cSRui Paulo 146*325151a3SRui Paulo if (first_chan_idx + num_chans > mode->num_channels) 1475b9c547cSRui Paulo return 0; 1485b9c547cSRui Paulo 1495b9c547cSRui Paulo first_chan = &mode->channels[first_chan_idx]; 1505b9c547cSRui Paulo 1515b9c547cSRui Paulo for (i = 0; i < num_chans; i++) { 152*325151a3SRui Paulo chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, 153*325151a3SRui Paulo first_chan_idx); 154*325151a3SRui Paulo if (!chan) 1555b9c547cSRui Paulo return 0; 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo if (!dfs_channel_available(chan, skip_radar)) 1585b9c547cSRui Paulo return 0; 1595b9c547cSRui Paulo } 1605b9c547cSRui Paulo 1615b9c547cSRui Paulo return 1; 1625b9c547cSRui Paulo } 1635b9c547cSRui Paulo 1645b9c547cSRui Paulo 1655b9c547cSRui Paulo static int is_in_chanlist(struct hostapd_iface *iface, 1665b9c547cSRui Paulo struct hostapd_channel_data *chan) 1675b9c547cSRui Paulo { 168*325151a3SRui Paulo if (!iface->conf->acs_ch_list.num) 1695b9c547cSRui Paulo return 1; 1705b9c547cSRui Paulo 171*325151a3SRui Paulo return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); 1725b9c547cSRui Paulo } 1735b9c547cSRui Paulo 1745b9c547cSRui Paulo 1755b9c547cSRui Paulo /* 1765b9c547cSRui Paulo * The function assumes HT40+ operation. 1775b9c547cSRui Paulo * Make sure to adjust the following variables after calling this: 1785b9c547cSRui Paulo * - hapd->secondary_channel 1795b9c547cSRui Paulo * - hapd->vht_oper_centr_freq_seg0_idx 1805b9c547cSRui Paulo * - hapd->vht_oper_centr_freq_seg1_idx 1815b9c547cSRui Paulo */ 1825b9c547cSRui Paulo static int dfs_find_channel(struct hostapd_iface *iface, 1835b9c547cSRui Paulo struct hostapd_channel_data **ret_chan, 1845b9c547cSRui Paulo int idx, int skip_radar) 1855b9c547cSRui Paulo { 1865b9c547cSRui Paulo struct hostapd_hw_modes *mode; 1875b9c547cSRui Paulo struct hostapd_channel_data *chan; 1885b9c547cSRui Paulo int i, channel_idx = 0, n_chans, n_chans1; 1895b9c547cSRui Paulo 1905b9c547cSRui Paulo mode = iface->current_mode; 1915b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 1925b9c547cSRui Paulo 1935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); 1945b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 1955b9c547cSRui Paulo chan = &mode->channels[i]; 1965b9c547cSRui Paulo 1975b9c547cSRui Paulo /* Skip HT40/VHT incompatible channels */ 1985b9c547cSRui Paulo if (iface->conf->ieee80211n && 1995b9c547cSRui Paulo iface->conf->secondary_channel && 2005b9c547cSRui Paulo !dfs_is_chan_allowed(chan, n_chans)) 2015b9c547cSRui Paulo continue; 2025b9c547cSRui Paulo 2035b9c547cSRui Paulo /* Skip incompatible chandefs */ 2045b9c547cSRui Paulo if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) 2055b9c547cSRui Paulo continue; 2065b9c547cSRui Paulo 2075b9c547cSRui Paulo if (!is_in_chanlist(iface, chan)) 2085b9c547cSRui Paulo continue; 2095b9c547cSRui Paulo 2105b9c547cSRui Paulo if (ret_chan && idx == channel_idx) { 2115b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); 2125b9c547cSRui Paulo *ret_chan = chan; 2135b9c547cSRui Paulo return idx; 2145b9c547cSRui Paulo } 2155b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); 2165b9c547cSRui Paulo channel_idx++; 2175b9c547cSRui Paulo } 2185b9c547cSRui Paulo return channel_idx; 2195b9c547cSRui Paulo } 2205b9c547cSRui Paulo 2215b9c547cSRui Paulo 2225b9c547cSRui Paulo static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, 2235b9c547cSRui Paulo struct hostapd_channel_data *chan, 2245b9c547cSRui Paulo int secondary_channel, 2255b9c547cSRui Paulo u8 *vht_oper_centr_freq_seg0_idx, 2265b9c547cSRui Paulo u8 *vht_oper_centr_freq_seg1_idx) 2275b9c547cSRui Paulo { 2285b9c547cSRui Paulo if (!iface->conf->ieee80211ac) 2295b9c547cSRui Paulo return; 2305b9c547cSRui Paulo 2315b9c547cSRui Paulo if (!chan) 2325b9c547cSRui Paulo return; 2335b9c547cSRui Paulo 2345b9c547cSRui Paulo *vht_oper_centr_freq_seg1_idx = 0; 2355b9c547cSRui Paulo 2365b9c547cSRui Paulo switch (iface->conf->vht_oper_chwidth) { 2375b9c547cSRui Paulo case VHT_CHANWIDTH_USE_HT: 2385b9c547cSRui Paulo if (secondary_channel == 1) 2395b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = chan->chan + 2; 2405b9c547cSRui Paulo else if (secondary_channel == -1) 2415b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = chan->chan - 2; 2425b9c547cSRui Paulo else 2435b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = chan->chan; 2445b9c547cSRui Paulo break; 2455b9c547cSRui Paulo case VHT_CHANWIDTH_80MHZ: 2465b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = chan->chan + 6; 2475b9c547cSRui Paulo break; 2485b9c547cSRui Paulo case VHT_CHANWIDTH_160MHZ: 2495b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = chan->chan + 14; 2505b9c547cSRui Paulo break; 2515b9c547cSRui Paulo default: 2525b9c547cSRui Paulo wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); 2535b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = 0; 2545b9c547cSRui Paulo break; 2555b9c547cSRui Paulo } 2565b9c547cSRui Paulo 2575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", 2585b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx, 2595b9c547cSRui Paulo *vht_oper_centr_freq_seg1_idx); 2605b9c547cSRui Paulo } 2615b9c547cSRui Paulo 2625b9c547cSRui Paulo 2635b9c547cSRui Paulo /* Return start channel idx we will use for mode->channels[idx] */ 2645b9c547cSRui Paulo static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) 2655b9c547cSRui Paulo { 2665b9c547cSRui Paulo struct hostapd_hw_modes *mode; 2675b9c547cSRui Paulo struct hostapd_channel_data *chan; 2685b9c547cSRui Paulo int channel_no = iface->conf->channel; 2695b9c547cSRui Paulo int res = -1, i; 2705b9c547cSRui Paulo int chan_seg1 = -1; 2715b9c547cSRui Paulo 2725b9c547cSRui Paulo *seg1_start = -1; 2735b9c547cSRui Paulo 2745b9c547cSRui Paulo /* HT40- */ 2755b9c547cSRui Paulo if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) 2765b9c547cSRui Paulo channel_no -= 4; 2775b9c547cSRui Paulo 2785b9c547cSRui Paulo /* VHT */ 2795b9c547cSRui Paulo if (iface->conf->ieee80211ac) { 2805b9c547cSRui Paulo switch (iface->conf->vht_oper_chwidth) { 2815b9c547cSRui Paulo case VHT_CHANWIDTH_USE_HT: 2825b9c547cSRui Paulo break; 2835b9c547cSRui Paulo case VHT_CHANWIDTH_80MHZ: 2845b9c547cSRui Paulo channel_no = 2855b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx - 6; 2865b9c547cSRui Paulo break; 2875b9c547cSRui Paulo case VHT_CHANWIDTH_160MHZ: 2885b9c547cSRui Paulo channel_no = 2895b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx - 14; 2905b9c547cSRui Paulo break; 2915b9c547cSRui Paulo case VHT_CHANWIDTH_80P80MHZ: 2925b9c547cSRui Paulo channel_no = 2935b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx - 6; 2945b9c547cSRui Paulo chan_seg1 = 2955b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx - 6; 2965b9c547cSRui Paulo break; 2975b9c547cSRui Paulo default: 2985b9c547cSRui Paulo wpa_printf(MSG_INFO, 2995b9c547cSRui Paulo "DFS only VHT20/40/80/160/80+80 is supported now"); 3005b9c547cSRui Paulo channel_no = -1; 3015b9c547cSRui Paulo break; 3025b9c547cSRui Paulo } 3035b9c547cSRui Paulo } 3045b9c547cSRui Paulo 3055b9c547cSRui Paulo /* Get idx */ 3065b9c547cSRui Paulo mode = iface->current_mode; 3075b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 3085b9c547cSRui Paulo chan = &mode->channels[i]; 3095b9c547cSRui Paulo if (chan->chan == channel_no) { 3105b9c547cSRui Paulo res = i; 3115b9c547cSRui Paulo break; 3125b9c547cSRui Paulo } 3135b9c547cSRui Paulo } 3145b9c547cSRui Paulo 3155b9c547cSRui Paulo if (res != -1 && chan_seg1 > -1) { 3165b9c547cSRui Paulo int found = 0; 3175b9c547cSRui Paulo 3185b9c547cSRui Paulo /* Get idx for seg1 */ 3195b9c547cSRui Paulo mode = iface->current_mode; 3205b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 3215b9c547cSRui Paulo chan = &mode->channels[i]; 3225b9c547cSRui Paulo if (chan->chan == chan_seg1) { 3235b9c547cSRui Paulo *seg1_start = i; 3245b9c547cSRui Paulo found = 1; 3255b9c547cSRui Paulo break; 3265b9c547cSRui Paulo } 3275b9c547cSRui Paulo } 3285b9c547cSRui Paulo if (!found) 3295b9c547cSRui Paulo res = -1; 3305b9c547cSRui Paulo } 3315b9c547cSRui Paulo 3325b9c547cSRui Paulo if (res == -1) { 3335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 3345b9c547cSRui 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", 3355b9c547cSRui Paulo mode->num_channels, channel_no, iface->conf->channel, 3365b9c547cSRui Paulo iface->conf->ieee80211n, 3375b9c547cSRui Paulo iface->conf->secondary_channel, 3385b9c547cSRui Paulo iface->conf->vht_oper_chwidth); 3395b9c547cSRui Paulo 3405b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 3415b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Available channel: %d", 3425b9c547cSRui Paulo mode->channels[i].chan); 3435b9c547cSRui Paulo } 3445b9c547cSRui Paulo } 3455b9c547cSRui Paulo 3465b9c547cSRui Paulo return res; 3475b9c547cSRui Paulo } 3485b9c547cSRui Paulo 3495b9c547cSRui Paulo 3505b9c547cSRui Paulo /* At least one channel have radar flag */ 3515b9c547cSRui Paulo static int dfs_check_chans_radar(struct hostapd_iface *iface, 3525b9c547cSRui Paulo int start_chan_idx, int n_chans) 3535b9c547cSRui Paulo { 3545b9c547cSRui Paulo struct hostapd_channel_data *channel; 3555b9c547cSRui Paulo struct hostapd_hw_modes *mode; 3565b9c547cSRui Paulo int i, res = 0; 3575b9c547cSRui Paulo 3585b9c547cSRui Paulo mode = iface->current_mode; 3595b9c547cSRui Paulo 3605b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 3615b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 3625b9c547cSRui Paulo if (channel->flag & HOSTAPD_CHAN_RADAR) 3635b9c547cSRui Paulo res++; 3645b9c547cSRui Paulo } 3655b9c547cSRui Paulo 3665b9c547cSRui Paulo return res; 3675b9c547cSRui Paulo } 3685b9c547cSRui Paulo 3695b9c547cSRui Paulo 3705b9c547cSRui Paulo /* All channels available */ 3715b9c547cSRui Paulo static int dfs_check_chans_available(struct hostapd_iface *iface, 3725b9c547cSRui Paulo int start_chan_idx, int n_chans) 3735b9c547cSRui Paulo { 3745b9c547cSRui Paulo struct hostapd_channel_data *channel; 3755b9c547cSRui Paulo struct hostapd_hw_modes *mode; 3765b9c547cSRui Paulo int i; 3775b9c547cSRui Paulo 3785b9c547cSRui Paulo mode = iface->current_mode; 3795b9c547cSRui Paulo 3805b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 3815b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 3825b9c547cSRui Paulo 3835b9c547cSRui Paulo if (channel->flag & HOSTAPD_CHAN_DISABLED) 3845b9c547cSRui Paulo break; 3855b9c547cSRui Paulo 3865b9c547cSRui Paulo if (!(channel->flag & HOSTAPD_CHAN_RADAR)) 3875b9c547cSRui Paulo continue; 3885b9c547cSRui Paulo 3895b9c547cSRui Paulo if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != 3905b9c547cSRui Paulo HOSTAPD_CHAN_DFS_AVAILABLE) 3915b9c547cSRui Paulo break; 3925b9c547cSRui Paulo } 3935b9c547cSRui Paulo 3945b9c547cSRui Paulo return i == n_chans; 3955b9c547cSRui Paulo } 3965b9c547cSRui Paulo 3975b9c547cSRui Paulo 3985b9c547cSRui Paulo /* At least one channel unavailable */ 3995b9c547cSRui Paulo static int dfs_check_chans_unavailable(struct hostapd_iface *iface, 4005b9c547cSRui Paulo int start_chan_idx, 4015b9c547cSRui Paulo int n_chans) 4025b9c547cSRui Paulo { 4035b9c547cSRui Paulo struct hostapd_channel_data *channel; 4045b9c547cSRui Paulo struct hostapd_hw_modes *mode; 4055b9c547cSRui Paulo int i, res = 0; 4065b9c547cSRui Paulo 4075b9c547cSRui Paulo mode = iface->current_mode; 4085b9c547cSRui Paulo 4095b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 4105b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 4115b9c547cSRui Paulo if (channel->flag & HOSTAPD_CHAN_DISABLED) 4125b9c547cSRui Paulo res++; 4135b9c547cSRui Paulo if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == 4145b9c547cSRui Paulo HOSTAPD_CHAN_DFS_UNAVAILABLE) 4155b9c547cSRui Paulo res++; 4165b9c547cSRui Paulo } 4175b9c547cSRui Paulo 4185b9c547cSRui Paulo return res; 4195b9c547cSRui Paulo } 4205b9c547cSRui Paulo 4215b9c547cSRui Paulo 4225b9c547cSRui Paulo static struct hostapd_channel_data * 4235b9c547cSRui Paulo dfs_get_valid_channel(struct hostapd_iface *iface, 4245b9c547cSRui Paulo int *secondary_channel, 4255b9c547cSRui Paulo u8 *vht_oper_centr_freq_seg0_idx, 4265b9c547cSRui Paulo u8 *vht_oper_centr_freq_seg1_idx, 4275b9c547cSRui Paulo int skip_radar) 4285b9c547cSRui Paulo { 4295b9c547cSRui Paulo struct hostapd_hw_modes *mode; 4305b9c547cSRui Paulo struct hostapd_channel_data *chan = NULL; 4315b9c547cSRui Paulo int num_available_chandefs; 4325b9c547cSRui Paulo int chan_idx; 4335b9c547cSRui Paulo u32 _rand; 4345b9c547cSRui Paulo 4355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); 4365b9c547cSRui Paulo *secondary_channel = 0; 4375b9c547cSRui Paulo *vht_oper_centr_freq_seg0_idx = 0; 4385b9c547cSRui Paulo *vht_oper_centr_freq_seg1_idx = 0; 4395b9c547cSRui Paulo 4405b9c547cSRui Paulo if (iface->current_mode == NULL) 4415b9c547cSRui Paulo return NULL; 4425b9c547cSRui Paulo 4435b9c547cSRui Paulo mode = iface->current_mode; 4445b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) 4455b9c547cSRui Paulo return NULL; 4465b9c547cSRui Paulo 4475b9c547cSRui Paulo /* Get the count first */ 4485b9c547cSRui Paulo num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); 4495b9c547cSRui Paulo if (num_available_chandefs == 0) 4505b9c547cSRui Paulo return NULL; 4515b9c547cSRui Paulo 4525b9c547cSRui Paulo if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) 4535b9c547cSRui Paulo _rand = os_random(); 4545b9c547cSRui Paulo chan_idx = _rand % num_available_chandefs; 4555b9c547cSRui Paulo dfs_find_channel(iface, &chan, chan_idx, skip_radar); 4565b9c547cSRui Paulo 4575b9c547cSRui Paulo /* dfs_find_channel() calculations assume HT40+ */ 4585b9c547cSRui Paulo if (iface->conf->secondary_channel) 4595b9c547cSRui Paulo *secondary_channel = 1; 4605b9c547cSRui Paulo else 4615b9c547cSRui Paulo *secondary_channel = 0; 4625b9c547cSRui Paulo 4635b9c547cSRui Paulo dfs_adjust_vht_center_freq(iface, chan, 4645b9c547cSRui Paulo *secondary_channel, 4655b9c547cSRui Paulo vht_oper_centr_freq_seg0_idx, 4665b9c547cSRui Paulo vht_oper_centr_freq_seg1_idx); 4675b9c547cSRui Paulo 4685b9c547cSRui Paulo return chan; 4695b9c547cSRui Paulo } 4705b9c547cSRui Paulo 4715b9c547cSRui Paulo 4725b9c547cSRui Paulo static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) 4735b9c547cSRui Paulo { 4745b9c547cSRui Paulo struct hostapd_hw_modes *mode; 4755b9c547cSRui Paulo struct hostapd_channel_data *chan = NULL; 4765b9c547cSRui Paulo int i; 4775b9c547cSRui Paulo 4785b9c547cSRui Paulo mode = iface->current_mode; 4795b9c547cSRui Paulo if (mode == NULL) 4805b9c547cSRui Paulo return 0; 4815b9c547cSRui Paulo 4825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); 4835b9c547cSRui Paulo for (i = 0; i < iface->current_mode->num_channels; i++) { 4845b9c547cSRui Paulo chan = &iface->current_mode->channels[i]; 4855b9c547cSRui Paulo if (chan->freq == freq) { 4865b9c547cSRui Paulo if (chan->flag & HOSTAPD_CHAN_RADAR) { 4875b9c547cSRui Paulo chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; 4885b9c547cSRui Paulo chan->flag |= state; 4895b9c547cSRui Paulo return 1; /* Channel found */ 4905b9c547cSRui Paulo } 4915b9c547cSRui Paulo } 4925b9c547cSRui Paulo } 4935b9c547cSRui Paulo wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); 4945b9c547cSRui Paulo return 0; 4955b9c547cSRui Paulo } 4965b9c547cSRui Paulo 4975b9c547cSRui Paulo 4985b9c547cSRui Paulo static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, 4995b9c547cSRui Paulo int chan_offset, int chan_width, int cf1, 5005b9c547cSRui Paulo int cf2, u32 state) 5015b9c547cSRui Paulo { 5025b9c547cSRui Paulo int n_chans = 1, i; 5035b9c547cSRui Paulo struct hostapd_hw_modes *mode; 5045b9c547cSRui Paulo int frequency = freq; 5055b9c547cSRui Paulo int ret = 0; 5065b9c547cSRui Paulo 5075b9c547cSRui Paulo mode = iface->current_mode; 5085b9c547cSRui Paulo if (mode == NULL) 5095b9c547cSRui Paulo return 0; 5105b9c547cSRui Paulo 5115b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) { 5125b9c547cSRui Paulo wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); 5135b9c547cSRui Paulo return 0; 5145b9c547cSRui Paulo } 5155b9c547cSRui Paulo 5165b9c547cSRui Paulo /* Seems cf1 and chan_width is enough here */ 5175b9c547cSRui Paulo switch (chan_width) { 5185b9c547cSRui Paulo case CHAN_WIDTH_20_NOHT: 5195b9c547cSRui Paulo case CHAN_WIDTH_20: 5205b9c547cSRui Paulo n_chans = 1; 5215b9c547cSRui Paulo if (frequency == 0) 5225b9c547cSRui Paulo frequency = cf1; 5235b9c547cSRui Paulo break; 5245b9c547cSRui Paulo case CHAN_WIDTH_40: 5255b9c547cSRui Paulo n_chans = 2; 5265b9c547cSRui Paulo frequency = cf1 - 10; 5275b9c547cSRui Paulo break; 5285b9c547cSRui Paulo case CHAN_WIDTH_80: 5295b9c547cSRui Paulo n_chans = 4; 5305b9c547cSRui Paulo frequency = cf1 - 30; 5315b9c547cSRui Paulo break; 5325b9c547cSRui Paulo case CHAN_WIDTH_160: 5335b9c547cSRui Paulo n_chans = 8; 5345b9c547cSRui Paulo frequency = cf1 - 70; 5355b9c547cSRui Paulo break; 5365b9c547cSRui Paulo default: 5375b9c547cSRui Paulo wpa_printf(MSG_INFO, "DFS chan_width %d not supported", 5385b9c547cSRui Paulo chan_width); 5395b9c547cSRui Paulo break; 5405b9c547cSRui Paulo } 5415b9c547cSRui Paulo 5425b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, 5435b9c547cSRui Paulo n_chans); 5445b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 5455b9c547cSRui Paulo ret += set_dfs_state_freq(iface, frequency, state); 5465b9c547cSRui Paulo frequency = frequency + 20; 5475b9c547cSRui Paulo } 5485b9c547cSRui Paulo 5495b9c547cSRui Paulo return ret; 5505b9c547cSRui Paulo } 5515b9c547cSRui Paulo 5525b9c547cSRui Paulo 5535b9c547cSRui Paulo static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, 5545b9c547cSRui Paulo int chan_width, int cf1, int cf2) 5555b9c547cSRui Paulo { 5565b9c547cSRui Paulo int start_chan_idx, start_chan_idx1; 5575b9c547cSRui Paulo struct hostapd_hw_modes *mode; 5585b9c547cSRui Paulo struct hostapd_channel_data *chan; 5595b9c547cSRui Paulo int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1; 5605b9c547cSRui Paulo u8 radar_chan; 5615b9c547cSRui Paulo int res = 0; 5625b9c547cSRui Paulo 5635b9c547cSRui Paulo /* Our configuration */ 5645b9c547cSRui Paulo mode = iface->current_mode; 5655b9c547cSRui Paulo start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 5665b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 5675b9c547cSRui Paulo 5685b9c547cSRui Paulo /* Check we are on DFS channel(s) */ 5695b9c547cSRui Paulo if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) 5705b9c547cSRui Paulo return 0; 5715b9c547cSRui Paulo 5725b9c547cSRui Paulo /* Reported via radar event */ 5735b9c547cSRui Paulo switch (chan_width) { 5745b9c547cSRui Paulo case CHAN_WIDTH_20_NOHT: 5755b9c547cSRui Paulo case CHAN_WIDTH_20: 5765b9c547cSRui Paulo radar_n_chans = 1; 5775b9c547cSRui Paulo if (frequency == 0) 5785b9c547cSRui Paulo frequency = cf1; 5795b9c547cSRui Paulo break; 5805b9c547cSRui Paulo case CHAN_WIDTH_40: 5815b9c547cSRui Paulo radar_n_chans = 2; 5825b9c547cSRui Paulo frequency = cf1 - 10; 5835b9c547cSRui Paulo break; 5845b9c547cSRui Paulo case CHAN_WIDTH_80: 5855b9c547cSRui Paulo radar_n_chans = 4; 5865b9c547cSRui Paulo frequency = cf1 - 30; 5875b9c547cSRui Paulo break; 5885b9c547cSRui Paulo case CHAN_WIDTH_160: 5895b9c547cSRui Paulo radar_n_chans = 8; 5905b9c547cSRui Paulo frequency = cf1 - 70; 5915b9c547cSRui Paulo break; 5925b9c547cSRui Paulo default: 5935b9c547cSRui Paulo wpa_printf(MSG_INFO, "DFS chan_width %d not supported", 5945b9c547cSRui Paulo chan_width); 5955b9c547cSRui Paulo break; 5965b9c547cSRui Paulo } 5975b9c547cSRui Paulo 5985b9c547cSRui Paulo ieee80211_freq_to_chan(frequency, &radar_chan); 5995b9c547cSRui Paulo 6005b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 6015b9c547cSRui Paulo chan = &mode->channels[start_chan_idx + i]; 6025b9c547cSRui Paulo if (!(chan->flag & HOSTAPD_CHAN_RADAR)) 6035b9c547cSRui Paulo continue; 6045b9c547cSRui Paulo for (j = 0; j < radar_n_chans; j++) { 6055b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", 6065b9c547cSRui Paulo chan->chan, radar_chan + j * 4); 6075b9c547cSRui Paulo if (chan->chan == radar_chan + j * 4) 6085b9c547cSRui Paulo res++; 6095b9c547cSRui Paulo } 6105b9c547cSRui Paulo } 6115b9c547cSRui Paulo 6125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "overlapped: %d", res); 6135b9c547cSRui Paulo 6145b9c547cSRui Paulo return res; 6155b9c547cSRui Paulo } 6165b9c547cSRui Paulo 6175b9c547cSRui Paulo 6185b9c547cSRui Paulo static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, 6195b9c547cSRui Paulo int start_chan_idx, int n_chans) 6205b9c547cSRui Paulo { 6215b9c547cSRui Paulo struct hostapd_channel_data *channel; 6225b9c547cSRui Paulo struct hostapd_hw_modes *mode; 6235b9c547cSRui Paulo int i; 6245b9c547cSRui Paulo unsigned int cac_time_ms = 0; 6255b9c547cSRui Paulo 6265b9c547cSRui Paulo mode = iface->current_mode; 6275b9c547cSRui Paulo 6285b9c547cSRui Paulo for (i = 0; i < n_chans; i++) { 6295b9c547cSRui Paulo channel = &mode->channels[start_chan_idx + i]; 6305b9c547cSRui Paulo if (!(channel->flag & HOSTAPD_CHAN_RADAR)) 6315b9c547cSRui Paulo continue; 6325b9c547cSRui Paulo if (channel->dfs_cac_ms > cac_time_ms) 6335b9c547cSRui Paulo cac_time_ms = channel->dfs_cac_ms; 6345b9c547cSRui Paulo } 6355b9c547cSRui Paulo 6365b9c547cSRui Paulo return cac_time_ms; 6375b9c547cSRui Paulo } 6385b9c547cSRui Paulo 6395b9c547cSRui Paulo 6405b9c547cSRui Paulo /* 6415b9c547cSRui Paulo * Main DFS handler 6425b9c547cSRui Paulo * 1 - continue channel/ap setup 6435b9c547cSRui Paulo * 0 - channel/ap setup will be continued after CAC 6445b9c547cSRui Paulo * -1 - hit critical error 6455b9c547cSRui Paulo */ 6465b9c547cSRui Paulo int hostapd_handle_dfs(struct hostapd_iface *iface) 6475b9c547cSRui Paulo { 6485b9c547cSRui Paulo struct hostapd_channel_data *channel; 6495b9c547cSRui Paulo int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; 6505b9c547cSRui Paulo int skip_radar = 0; 6515b9c547cSRui Paulo 6525b9c547cSRui Paulo if (!iface->current_mode) { 6535b9c547cSRui Paulo /* 6545b9c547cSRui Paulo * This can happen with drivers that do not provide mode 6555b9c547cSRui Paulo * information and as such, cannot really use hostapd for DFS. 6565b9c547cSRui Paulo */ 6575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 6585b9c547cSRui Paulo "DFS: No current_mode information - assume no need to perform DFS operations by hostapd"); 6595b9c547cSRui Paulo return 1; 6605b9c547cSRui Paulo } 6615b9c547cSRui Paulo 6625b9c547cSRui Paulo iface->cac_started = 0; 6635b9c547cSRui Paulo 6645b9c547cSRui Paulo do { 6655b9c547cSRui Paulo /* Get start (first) channel for current configuration */ 6665b9c547cSRui Paulo start_chan_idx = dfs_get_start_chan_idx(iface, 6675b9c547cSRui Paulo &start_chan_idx1); 6685b9c547cSRui Paulo if (start_chan_idx == -1) 6695b9c547cSRui Paulo return -1; 6705b9c547cSRui Paulo 6715b9c547cSRui Paulo /* Get number of used channels, depend on width */ 6725b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 6735b9c547cSRui Paulo 6745b9c547cSRui Paulo /* Setup CAC time */ 6755b9c547cSRui Paulo iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx, 6765b9c547cSRui Paulo n_chans); 6775b9c547cSRui Paulo 6785b9c547cSRui Paulo /* Check if any of configured channels require DFS */ 6795b9c547cSRui Paulo res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); 6805b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 6815b9c547cSRui Paulo "DFS %d channels required radar detection", 6825b9c547cSRui Paulo res); 6835b9c547cSRui Paulo if (!res) 6845b9c547cSRui Paulo return 1; 6855b9c547cSRui Paulo 6865b9c547cSRui Paulo /* Check if all channels are DFS available */ 6875b9c547cSRui Paulo res = dfs_check_chans_available(iface, start_chan_idx, n_chans); 6885b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 6895b9c547cSRui Paulo "DFS all channels available, (SKIP CAC): %s", 6905b9c547cSRui Paulo res ? "yes" : "no"); 6915b9c547cSRui Paulo if (res) 6925b9c547cSRui Paulo return 1; 6935b9c547cSRui Paulo 6945b9c547cSRui Paulo /* Check if any of configured channels is unavailable */ 6955b9c547cSRui Paulo res = dfs_check_chans_unavailable(iface, start_chan_idx, 6965b9c547cSRui Paulo n_chans); 6975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", 6985b9c547cSRui Paulo res, res ? "yes": "no"); 6995b9c547cSRui Paulo if (res) { 7005b9c547cSRui Paulo int sec = 0; 7015b9c547cSRui Paulo u8 cf1 = 0, cf2 = 0; 7025b9c547cSRui Paulo 7035b9c547cSRui Paulo channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 7045b9c547cSRui Paulo skip_radar); 7055b9c547cSRui Paulo if (!channel) { 7065b9c547cSRui Paulo wpa_printf(MSG_ERROR, "could not get valid channel"); 7075b9c547cSRui Paulo return -1; 7085b9c547cSRui Paulo } 7095b9c547cSRui Paulo 7105b9c547cSRui Paulo iface->freq = channel->freq; 7115b9c547cSRui Paulo iface->conf->channel = channel->chan; 7125b9c547cSRui Paulo iface->conf->secondary_channel = sec; 7135b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx = cf1; 7145b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx = cf2; 7155b9c547cSRui Paulo } 7165b9c547cSRui Paulo } while (res); 7175b9c547cSRui Paulo 7185b9c547cSRui Paulo /* Finally start CAC */ 7195b9c547cSRui Paulo hostapd_set_state(iface, HAPD_IFACE_DFS); 7205b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); 7215b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START 7225b9c547cSRui Paulo "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", 7235b9c547cSRui Paulo iface->freq, 7245b9c547cSRui Paulo iface->conf->channel, iface->conf->secondary_channel, 7255b9c547cSRui Paulo iface->conf->vht_oper_chwidth, 7265b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx, 7275b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx, 7285b9c547cSRui Paulo iface->dfs_cac_ms / 1000); 7295b9c547cSRui Paulo 7305b9c547cSRui Paulo res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode, 7315b9c547cSRui Paulo iface->freq, 7325b9c547cSRui Paulo iface->conf->channel, 7335b9c547cSRui Paulo iface->conf->ieee80211n, 7345b9c547cSRui Paulo iface->conf->ieee80211ac, 7355b9c547cSRui Paulo iface->conf->secondary_channel, 7365b9c547cSRui Paulo iface->conf->vht_oper_chwidth, 7375b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx, 7385b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx); 7395b9c547cSRui Paulo 7405b9c547cSRui Paulo if (res) { 7415b9c547cSRui Paulo wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); 7425b9c547cSRui Paulo return -1; 7435b9c547cSRui Paulo } 7445b9c547cSRui Paulo 7455b9c547cSRui Paulo return 0; 7465b9c547cSRui Paulo } 7475b9c547cSRui Paulo 7485b9c547cSRui Paulo 7495b9c547cSRui Paulo int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, 7505b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 7515b9c547cSRui Paulo int cf1, int cf2) 7525b9c547cSRui Paulo { 7535b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED 7545b9c547cSRui Paulo "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 7555b9c547cSRui Paulo success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 7565b9c547cSRui Paulo 7575b9c547cSRui Paulo if (success) { 7585b9c547cSRui Paulo /* Complete iface/ap configuration */ 7595b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) { 7605b9c547cSRui Paulo /* Complete AP configuration for the first bring up. */ 7615b9c547cSRui Paulo if (iface->state != HAPD_IFACE_ENABLED) 7625b9c547cSRui Paulo hostapd_setup_interface_complete(iface, 0); 7635b9c547cSRui Paulo else 7645b9c547cSRui Paulo iface->cac_started = 0; 7655b9c547cSRui Paulo } else { 7665b9c547cSRui Paulo set_dfs_state(iface, freq, ht_enabled, chan_offset, 7675b9c547cSRui Paulo chan_width, cf1, cf2, 7685b9c547cSRui Paulo HOSTAPD_CHAN_DFS_AVAILABLE); 7695b9c547cSRui Paulo iface->cac_started = 0; 7705b9c547cSRui Paulo hostapd_setup_interface_complete(iface, 0); 7715b9c547cSRui Paulo } 7725b9c547cSRui Paulo } 7735b9c547cSRui Paulo 7745b9c547cSRui Paulo return 0; 7755b9c547cSRui Paulo } 7765b9c547cSRui Paulo 7775b9c547cSRui Paulo 7785b9c547cSRui Paulo static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) 7795b9c547cSRui Paulo { 7805b9c547cSRui Paulo struct hostapd_channel_data *channel; 7815b9c547cSRui Paulo int secondary_channel; 7825b9c547cSRui Paulo u8 vht_oper_centr_freq_seg0_idx = 0; 7835b9c547cSRui Paulo u8 vht_oper_centr_freq_seg1_idx = 0; 7845b9c547cSRui Paulo int skip_radar = 0; 7855b9c547cSRui Paulo int err = 1; 7865b9c547cSRui Paulo 7875b9c547cSRui Paulo /* Radar detected during active CAC */ 7885b9c547cSRui Paulo iface->cac_started = 0; 7895b9c547cSRui Paulo channel = dfs_get_valid_channel(iface, &secondary_channel, 7905b9c547cSRui Paulo &vht_oper_centr_freq_seg0_idx, 7915b9c547cSRui Paulo &vht_oper_centr_freq_seg1_idx, 7925b9c547cSRui Paulo skip_radar); 7935b9c547cSRui Paulo 7945b9c547cSRui Paulo if (!channel) { 7955b9c547cSRui Paulo wpa_printf(MSG_ERROR, "No valid channel available"); 7965b9c547cSRui Paulo hostapd_setup_interface_complete(iface, err); 7975b9c547cSRui Paulo return err; 7985b9c547cSRui Paulo } 7995b9c547cSRui Paulo 8005b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", 8015b9c547cSRui Paulo channel->chan); 8025b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL 8035b9c547cSRui Paulo "freq=%d chan=%d sec_chan=%d", channel->freq, 8045b9c547cSRui Paulo channel->chan, secondary_channel); 8055b9c547cSRui Paulo 8065b9c547cSRui Paulo iface->freq = channel->freq; 8075b9c547cSRui Paulo iface->conf->channel = channel->chan; 8085b9c547cSRui Paulo iface->conf->secondary_channel = secondary_channel; 8095b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx = 8105b9c547cSRui Paulo vht_oper_centr_freq_seg0_idx; 8115b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx = 8125b9c547cSRui Paulo vht_oper_centr_freq_seg1_idx; 8135b9c547cSRui Paulo err = 0; 8145b9c547cSRui Paulo 8155b9c547cSRui Paulo hostapd_setup_interface_complete(iface, err); 8165b9c547cSRui Paulo return err; 8175b9c547cSRui Paulo } 8185b9c547cSRui Paulo 8195b9c547cSRui Paulo 8205b9c547cSRui Paulo static int hostapd_csa_in_progress(struct hostapd_iface *iface) 8215b9c547cSRui Paulo { 8225b9c547cSRui Paulo unsigned int i; 8235b9c547cSRui Paulo for (i = 0; i < iface->num_bss; i++) 8245b9c547cSRui Paulo if (iface->bss[i]->csa_in_progress) 8255b9c547cSRui Paulo return 1; 8265b9c547cSRui Paulo return 0; 8275b9c547cSRui Paulo } 8285b9c547cSRui Paulo 8295b9c547cSRui Paulo 8305b9c547cSRui Paulo static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) 8315b9c547cSRui Paulo { 8325b9c547cSRui Paulo struct hostapd_channel_data *channel; 8335b9c547cSRui Paulo int secondary_channel; 8345b9c547cSRui Paulo u8 vht_oper_centr_freq_seg0_idx; 8355b9c547cSRui Paulo u8 vht_oper_centr_freq_seg1_idx; 8365b9c547cSRui Paulo int skip_radar = 1; 8375b9c547cSRui Paulo struct csa_settings csa_settings; 8385b9c547cSRui Paulo unsigned int i; 8395b9c547cSRui Paulo int err = 1; 8405b9c547cSRui Paulo 8415b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", 8425b9c547cSRui Paulo __func__, iface->cac_started ? "yes" : "no", 8435b9c547cSRui Paulo hostapd_csa_in_progress(iface) ? "yes" : "no"); 8445b9c547cSRui Paulo 8455b9c547cSRui Paulo /* Check if CSA in progress */ 8465b9c547cSRui Paulo if (hostapd_csa_in_progress(iface)) 8475b9c547cSRui Paulo return 0; 8485b9c547cSRui Paulo 8495b9c547cSRui Paulo /* Check if active CAC */ 8505b9c547cSRui Paulo if (iface->cac_started) 8515b9c547cSRui Paulo return hostapd_dfs_start_channel_switch_cac(iface); 8525b9c547cSRui Paulo 8535b9c547cSRui Paulo /* Perform channel switch/CSA */ 8545b9c547cSRui Paulo channel = dfs_get_valid_channel(iface, &secondary_channel, 8555b9c547cSRui Paulo &vht_oper_centr_freq_seg0_idx, 8565b9c547cSRui Paulo &vht_oper_centr_freq_seg1_idx, 8575b9c547cSRui Paulo skip_radar); 8585b9c547cSRui Paulo 8595b9c547cSRui Paulo if (!channel) { 8605b9c547cSRui Paulo /* 8615b9c547cSRui Paulo * If there is no channel to switch immediately to, check if 8625b9c547cSRui Paulo * there is another channel where we can switch even if it 8635b9c547cSRui Paulo * requires to perform a CAC first. 8645b9c547cSRui Paulo */ 8655b9c547cSRui Paulo skip_radar = 0; 8665b9c547cSRui Paulo channel = dfs_get_valid_channel(iface, &secondary_channel, 8675b9c547cSRui Paulo &vht_oper_centr_freq_seg0_idx, 8685b9c547cSRui Paulo &vht_oper_centr_freq_seg1_idx, 8695b9c547cSRui Paulo skip_radar); 8705b9c547cSRui Paulo if (!channel) { 8715b9c547cSRui Paulo /* FIXME: Wait for channel(s) to become available */ 8725b9c547cSRui Paulo hostapd_disable_iface(iface); 8735b9c547cSRui Paulo return err; 8745b9c547cSRui Paulo } 8755b9c547cSRui Paulo 8765b9c547cSRui Paulo iface->freq = channel->freq; 8775b9c547cSRui Paulo iface->conf->channel = channel->chan; 8785b9c547cSRui Paulo iface->conf->secondary_channel = secondary_channel; 8795b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx = 8805b9c547cSRui Paulo vht_oper_centr_freq_seg0_idx; 8815b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx = 8825b9c547cSRui Paulo vht_oper_centr_freq_seg1_idx; 8835b9c547cSRui Paulo 8845b9c547cSRui Paulo hostapd_disable_iface(iface); 8855b9c547cSRui Paulo hostapd_enable_iface(iface); 8865b9c547cSRui Paulo return 0; 8875b9c547cSRui Paulo } 8885b9c547cSRui Paulo 8895b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", 8905b9c547cSRui Paulo channel->chan); 8915b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL 8925b9c547cSRui Paulo "freq=%d chan=%d sec_chan=%d", channel->freq, 8935b9c547cSRui Paulo channel->chan, secondary_channel); 8945b9c547cSRui Paulo 8955b9c547cSRui Paulo /* Setup CSA request */ 8965b9c547cSRui Paulo os_memset(&csa_settings, 0, sizeof(csa_settings)); 8975b9c547cSRui Paulo csa_settings.cs_count = 5; 8985b9c547cSRui Paulo csa_settings.block_tx = 1; 8995b9c547cSRui Paulo err = hostapd_set_freq_params(&csa_settings.freq_params, 9005b9c547cSRui Paulo iface->conf->hw_mode, 9015b9c547cSRui Paulo channel->freq, 9025b9c547cSRui Paulo channel->chan, 9035b9c547cSRui Paulo iface->conf->ieee80211n, 9045b9c547cSRui Paulo iface->conf->ieee80211ac, 9055b9c547cSRui Paulo secondary_channel, 9065b9c547cSRui Paulo iface->conf->vht_oper_chwidth, 9075b9c547cSRui Paulo vht_oper_centr_freq_seg0_idx, 9085b9c547cSRui Paulo vht_oper_centr_freq_seg1_idx, 9095b9c547cSRui Paulo iface->current_mode->vht_capab); 9105b9c547cSRui Paulo 9115b9c547cSRui Paulo if (err) { 9125b9c547cSRui Paulo wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); 9135b9c547cSRui Paulo hostapd_disable_iface(iface); 9145b9c547cSRui Paulo return err; 9155b9c547cSRui Paulo } 9165b9c547cSRui Paulo 9175b9c547cSRui Paulo for (i = 0; i < iface->num_bss; i++) { 9185b9c547cSRui Paulo err = hostapd_switch_channel(iface->bss[i], &csa_settings); 9195b9c547cSRui Paulo if (err) 9205b9c547cSRui Paulo break; 9215b9c547cSRui Paulo } 9225b9c547cSRui Paulo 9235b9c547cSRui Paulo if (err) { 9245b9c547cSRui Paulo wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", 9255b9c547cSRui Paulo err); 9265b9c547cSRui Paulo iface->freq = channel->freq; 9275b9c547cSRui Paulo iface->conf->channel = channel->chan; 9285b9c547cSRui Paulo iface->conf->secondary_channel = secondary_channel; 9295b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg0_idx = 9305b9c547cSRui Paulo vht_oper_centr_freq_seg0_idx; 9315b9c547cSRui Paulo iface->conf->vht_oper_centr_freq_seg1_idx = 9325b9c547cSRui Paulo vht_oper_centr_freq_seg1_idx; 9335b9c547cSRui Paulo 9345b9c547cSRui Paulo hostapd_disable_iface(iface); 9355b9c547cSRui Paulo hostapd_enable_iface(iface); 9365b9c547cSRui Paulo return 0; 9375b9c547cSRui Paulo } 9385b9c547cSRui Paulo 9395b9c547cSRui Paulo /* Channel configuration will be updated once CSA completes and 9405b9c547cSRui Paulo * ch_switch_notify event is received */ 9415b9c547cSRui Paulo 9425b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); 9435b9c547cSRui Paulo return 0; 9445b9c547cSRui Paulo } 9455b9c547cSRui Paulo 9465b9c547cSRui Paulo 9475b9c547cSRui Paulo int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, 9485b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 9495b9c547cSRui Paulo int cf1, int cf2) 9505b9c547cSRui Paulo { 9515b9c547cSRui Paulo int res; 9525b9c547cSRui Paulo 9535b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED 9545b9c547cSRui Paulo "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 9555b9c547cSRui Paulo freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 9565b9c547cSRui Paulo 9575b9c547cSRui Paulo /* Proceed only if DFS is not offloaded to the driver */ 9585b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 9595b9c547cSRui Paulo return 0; 9605b9c547cSRui Paulo 9615b9c547cSRui Paulo if (!iface->conf->ieee80211h) 9625b9c547cSRui Paulo return 0; 9635b9c547cSRui Paulo 9645b9c547cSRui Paulo /* mark radar frequency as invalid */ 9655b9c547cSRui Paulo set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 9665b9c547cSRui Paulo cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE); 9675b9c547cSRui Paulo 9685b9c547cSRui Paulo /* Skip if reported radar event not overlapped our channels */ 9695b9c547cSRui Paulo res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); 9705b9c547cSRui Paulo if (!res) 9715b9c547cSRui Paulo return 0; 9725b9c547cSRui Paulo 9735b9c547cSRui Paulo /* radar detected while operating, switch the channel. */ 9745b9c547cSRui Paulo res = hostapd_dfs_start_channel_switch(iface); 9755b9c547cSRui Paulo 9765b9c547cSRui Paulo return res; 9775b9c547cSRui Paulo } 9785b9c547cSRui Paulo 9795b9c547cSRui Paulo 9805b9c547cSRui Paulo int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, 9815b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 9825b9c547cSRui Paulo int cf1, int cf2) 9835b9c547cSRui Paulo { 9845b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED 9855b9c547cSRui Paulo "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 9865b9c547cSRui Paulo freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 9875b9c547cSRui Paulo 9885b9c547cSRui Paulo /* Proceed only if DFS is not offloaded to the driver */ 9895b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 9905b9c547cSRui Paulo return 0; 9915b9c547cSRui Paulo 9925b9c547cSRui Paulo /* TODO add correct implementation here */ 9935b9c547cSRui Paulo set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 9945b9c547cSRui Paulo cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); 9955b9c547cSRui Paulo return 0; 9965b9c547cSRui Paulo } 9975b9c547cSRui Paulo 9985b9c547cSRui Paulo 9995b9c547cSRui Paulo int hostapd_is_dfs_required(struct hostapd_iface *iface) 10005b9c547cSRui Paulo { 10015b9c547cSRui Paulo int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res; 10025b9c547cSRui Paulo 10035b9c547cSRui Paulo if (!iface->conf->ieee80211h || !iface->current_mode || 10045b9c547cSRui Paulo iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) 10055b9c547cSRui Paulo return 0; 10065b9c547cSRui Paulo 10075b9c547cSRui Paulo /* Get start (first) channel for current configuration */ 10085b9c547cSRui Paulo start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 10095b9c547cSRui Paulo if (start_chan_idx == -1) 10105b9c547cSRui Paulo return -1; 10115b9c547cSRui Paulo 10125b9c547cSRui Paulo /* Get number of used channels, depend on width */ 10135b9c547cSRui Paulo n_chans = dfs_get_used_n_chans(iface, &n_chans1); 10145b9c547cSRui Paulo 10155b9c547cSRui Paulo /* Check if any of configured channels require DFS */ 10165b9c547cSRui Paulo res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); 10175b9c547cSRui Paulo if (res) 10185b9c547cSRui Paulo return res; 10195b9c547cSRui Paulo if (start_chan_idx1 >= 0 && n_chans1 > 0) 10205b9c547cSRui Paulo res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1); 10215b9c547cSRui Paulo return res; 10225b9c547cSRui Paulo } 10235b9c547cSRui Paulo 10245b9c547cSRui Paulo 10255b9c547cSRui Paulo int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, 10265b9c547cSRui Paulo int ht_enabled, int chan_offset, int chan_width, 10275b9c547cSRui Paulo int cf1, int cf2) 10285b9c547cSRui Paulo { 10295b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START 10305b9c547cSRui Paulo "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " 10315b9c547cSRui Paulo "seg1=%d cac_time=%ds", 10325b9c547cSRui Paulo freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60); 10335b9c547cSRui Paulo iface->cac_started = 1; 10345b9c547cSRui Paulo return 0; 10355b9c547cSRui Paulo } 10365b9c547cSRui Paulo 10375b9c547cSRui Paulo 10385b9c547cSRui Paulo /* 10395b9c547cSRui Paulo * Main DFS handler for offloaded case. 10405b9c547cSRui Paulo * 2 - continue channel/AP setup for non-DFS channel 10415b9c547cSRui Paulo * 1 - continue channel/AP setup for DFS channel 10425b9c547cSRui Paulo * 0 - channel/AP setup will be continued after CAC 10435b9c547cSRui Paulo * -1 - hit critical error 10445b9c547cSRui Paulo */ 10455b9c547cSRui Paulo int hostapd_handle_dfs_offload(struct hostapd_iface *iface) 10465b9c547cSRui Paulo { 10475b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", 10485b9c547cSRui Paulo __func__, iface->cac_started); 10495b9c547cSRui Paulo 10505b9c547cSRui Paulo /* 10515b9c547cSRui Paulo * If DFS has already been started, then we are being called from a 10525b9c547cSRui Paulo * callback to continue AP/channel setup. Reset the CAC start flag and 10535b9c547cSRui Paulo * return. 10545b9c547cSRui Paulo */ 10555b9c547cSRui Paulo if (iface->cac_started) { 10565b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", 10575b9c547cSRui Paulo __func__, iface->cac_started); 10585b9c547cSRui Paulo iface->cac_started = 0; 10595b9c547cSRui Paulo return 1; 10605b9c547cSRui Paulo } 10615b9c547cSRui Paulo 10625b9c547cSRui Paulo if (ieee80211_is_dfs(iface->freq)) { 10635b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS", 10645b9c547cSRui Paulo __func__, iface->freq); 10655b9c547cSRui Paulo return 0; 10665b9c547cSRui Paulo } 10675b9c547cSRui Paulo 10685b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 10695b9c547cSRui Paulo "%s: freq %d MHz does not require DFS. Continue channel/AP setup", 10705b9c547cSRui Paulo __func__, iface->freq); 10715b9c547cSRui Paulo return 2; 10725b9c547cSRui Paulo } 1073