1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * DFS - Dynamic Frequency Selection 3e5b75505Sopenharmony_ci * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 4e5b75505Sopenharmony_ci * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. 5e5b75505Sopenharmony_ci * 6e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 7e5b75505Sopenharmony_ci * See README for more details. 8e5b75505Sopenharmony_ci */ 9e5b75505Sopenharmony_ci 10e5b75505Sopenharmony_ci#include "utils/includes.h" 11e5b75505Sopenharmony_ci 12e5b75505Sopenharmony_ci#include "utils/common.h" 13e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h" 14e5b75505Sopenharmony_ci#include "common/hw_features_common.h" 15e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h" 16e5b75505Sopenharmony_ci#include "hostapd.h" 17e5b75505Sopenharmony_ci#include "ap_drv_ops.h" 18e5b75505Sopenharmony_ci#include "drivers/driver.h" 19e5b75505Sopenharmony_ci#include "dfs.h" 20e5b75505Sopenharmony_ci 21e5b75505Sopenharmony_ci 22e5b75505Sopenharmony_cistatic int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) 23e5b75505Sopenharmony_ci{ 24e5b75505Sopenharmony_ci int n_chans = 1; 25e5b75505Sopenharmony_ci 26e5b75505Sopenharmony_ci *seg1 = 0; 27e5b75505Sopenharmony_ci 28e5b75505Sopenharmony_ci if (iface->conf->ieee80211n && iface->conf->secondary_channel) 29e5b75505Sopenharmony_ci n_chans = 2; 30e5b75505Sopenharmony_ci 31e5b75505Sopenharmony_ci if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { 32e5b75505Sopenharmony_ci switch (hostapd_get_oper_chwidth(iface->conf)) { 33e5b75505Sopenharmony_ci case CHANWIDTH_USE_HT: 34e5b75505Sopenharmony_ci break; 35e5b75505Sopenharmony_ci case CHANWIDTH_80MHZ: 36e5b75505Sopenharmony_ci n_chans = 4; 37e5b75505Sopenharmony_ci break; 38e5b75505Sopenharmony_ci case CHANWIDTH_160MHZ: 39e5b75505Sopenharmony_ci#ifdef CONFIG_P2P_160M 40e5b75505Sopenharmony_ci/*only need to check CH36,40,44,48 the 4 channels for P2P 160M*/ 41e5b75505Sopenharmony_ci n_chans = 4; 42e5b75505Sopenharmony_ci#else 43e5b75505Sopenharmony_ci n_chans = 8; 44e5b75505Sopenharmony_ci#endif 45e5b75505Sopenharmony_ci break; 46e5b75505Sopenharmony_ci case CHANWIDTH_80P80MHZ: 47e5b75505Sopenharmony_ci n_chans = 4; 48e5b75505Sopenharmony_ci *seg1 = 4; 49e5b75505Sopenharmony_ci break; 50e5b75505Sopenharmony_ci default: 51e5b75505Sopenharmony_ci break; 52e5b75505Sopenharmony_ci } 53e5b75505Sopenharmony_ci } 54e5b75505Sopenharmony_ci 55e5b75505Sopenharmony_ci return n_chans; 56e5b75505Sopenharmony_ci} 57e5b75505Sopenharmony_ci 58e5b75505Sopenharmony_ci 59e5b75505Sopenharmony_cistatic int dfs_channel_available(struct hostapd_channel_data *chan, 60e5b75505Sopenharmony_ci int skip_radar) 61e5b75505Sopenharmony_ci{ 62e5b75505Sopenharmony_ci /* 63e5b75505Sopenharmony_ci * When radar detection happens, CSA is performed. However, there's no 64e5b75505Sopenharmony_ci * time for CAC, so radar channels must be skipped when finding a new 65e5b75505Sopenharmony_ci * channel for CSA, unless they are available for immediate use. 66e5b75505Sopenharmony_ci */ 67e5b75505Sopenharmony_ci if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) && 68e5b75505Sopenharmony_ci ((chan->flag & HOSTAPD_CHAN_DFS_MASK) != 69e5b75505Sopenharmony_ci HOSTAPD_CHAN_DFS_AVAILABLE)) 70e5b75505Sopenharmony_ci return 0; 71e5b75505Sopenharmony_ci 72e5b75505Sopenharmony_ci if (chan->flag & HOSTAPD_CHAN_DISABLED) 73e5b75505Sopenharmony_ci return 0; 74e5b75505Sopenharmony_ci if ((chan->flag & HOSTAPD_CHAN_RADAR) && 75e5b75505Sopenharmony_ci ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == 76e5b75505Sopenharmony_ci HOSTAPD_CHAN_DFS_UNAVAILABLE)) 77e5b75505Sopenharmony_ci return 0; 78e5b75505Sopenharmony_ci return 1; 79e5b75505Sopenharmony_ci} 80e5b75505Sopenharmony_ci 81e5b75505Sopenharmony_ci 82e5b75505Sopenharmony_cistatic int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) 83e5b75505Sopenharmony_ci{ 84e5b75505Sopenharmony_ci /* 85e5b75505Sopenharmony_ci * The tables contain first valid channel number based on channel width. 86e5b75505Sopenharmony_ci * We will also choose this first channel as the control one. 87e5b75505Sopenharmony_ci */ 88e5b75505Sopenharmony_ci int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 89e5b75505Sopenharmony_ci 165, 173, 184, 192 }; 90e5b75505Sopenharmony_ci /* 91e5b75505Sopenharmony_ci * VHT80, valid channels based on center frequency: 92e5b75505Sopenharmony_ci * 42, 58, 106, 122, 138, 155, 171 93e5b75505Sopenharmony_ci */ 94e5b75505Sopenharmony_ci int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 }; 95e5b75505Sopenharmony_ci /* 96e5b75505Sopenharmony_ci * VHT160 valid channels based on center frequency: 97e5b75505Sopenharmony_ci * 50, 114, 163 98e5b75505Sopenharmony_ci */ 99e5b75505Sopenharmony_ci int allowed_160[] = { 36, 100, 149 }; 100e5b75505Sopenharmony_ci int *allowed = allowed_40; 101e5b75505Sopenharmony_ci unsigned int i, allowed_no = 0; 102e5b75505Sopenharmony_ci 103e5b75505Sopenharmony_ci switch (n_chans) { 104e5b75505Sopenharmony_ci case 2: 105e5b75505Sopenharmony_ci allowed = allowed_40; 106e5b75505Sopenharmony_ci allowed_no = ARRAY_SIZE(allowed_40); 107e5b75505Sopenharmony_ci break; 108e5b75505Sopenharmony_ci case 4: 109e5b75505Sopenharmony_ci allowed = allowed_80; 110e5b75505Sopenharmony_ci allowed_no = ARRAY_SIZE(allowed_80); 111e5b75505Sopenharmony_ci break; 112e5b75505Sopenharmony_ci case 8: 113e5b75505Sopenharmony_ci allowed = allowed_160; 114e5b75505Sopenharmony_ci allowed_no = ARRAY_SIZE(allowed_160); 115e5b75505Sopenharmony_ci break; 116e5b75505Sopenharmony_ci default: 117e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); 118e5b75505Sopenharmony_ci break; 119e5b75505Sopenharmony_ci } 120e5b75505Sopenharmony_ci 121e5b75505Sopenharmony_ci for (i = 0; i < allowed_no; i++) { 122e5b75505Sopenharmony_ci if (chan->chan == allowed[i]) 123e5b75505Sopenharmony_ci return 1; 124e5b75505Sopenharmony_ci } 125e5b75505Sopenharmony_ci 126e5b75505Sopenharmony_ci return 0; 127e5b75505Sopenharmony_ci} 128e5b75505Sopenharmony_ci 129e5b75505Sopenharmony_ci 130e5b75505Sopenharmony_cistatic struct hostapd_channel_data * 131e5b75505Sopenharmony_cidfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) 132e5b75505Sopenharmony_ci{ 133e5b75505Sopenharmony_ci int i; 134e5b75505Sopenharmony_ci 135e5b75505Sopenharmony_ci for (i = first_chan_idx; i < mode->num_channels; i++) { 136e5b75505Sopenharmony_ci if (mode->channels[i].freq == freq) 137e5b75505Sopenharmony_ci return &mode->channels[i]; 138e5b75505Sopenharmony_ci } 139e5b75505Sopenharmony_ci 140e5b75505Sopenharmony_ci return NULL; 141e5b75505Sopenharmony_ci} 142e5b75505Sopenharmony_ci 143e5b75505Sopenharmony_ci 144e5b75505Sopenharmony_cistatic int dfs_chan_range_available(struct hostapd_hw_modes *mode, 145e5b75505Sopenharmony_ci int first_chan_idx, int num_chans, 146e5b75505Sopenharmony_ci int skip_radar) 147e5b75505Sopenharmony_ci{ 148e5b75505Sopenharmony_ci struct hostapd_channel_data *first_chan, *chan; 149e5b75505Sopenharmony_ci int i; 150e5b75505Sopenharmony_ci u32 bw = num_chan_to_bw(num_chans); 151e5b75505Sopenharmony_ci 152e5b75505Sopenharmony_ci if (first_chan_idx + num_chans > mode->num_channels) { 153e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 154e5b75505Sopenharmony_ci "DFS: some channels in range not defined"); 155e5b75505Sopenharmony_ci return 0; 156e5b75505Sopenharmony_ci } 157e5b75505Sopenharmony_ci 158e5b75505Sopenharmony_ci first_chan = &mode->channels[first_chan_idx]; 159e5b75505Sopenharmony_ci 160e5b75505Sopenharmony_ci /* hostapd DFS implementation assumes the first channel as primary. 161e5b75505Sopenharmony_ci * If it's not allowed to use the first channel as primary, decline the 162e5b75505Sopenharmony_ci * whole channel range. */ 163e5b75505Sopenharmony_ci if (!chan_pri_allowed(first_chan)) { 164e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed"); 165e5b75505Sopenharmony_ci return 0; 166e5b75505Sopenharmony_ci } 167e5b75505Sopenharmony_ci 168e5b75505Sopenharmony_ci for (i = 0; i < num_chans; i++) { 169e5b75505Sopenharmony_ci chan = dfs_get_chan_data(mode, first_chan->freq + i * 20, 170e5b75505Sopenharmony_ci first_chan_idx); 171e5b75505Sopenharmony_ci if (!chan) { 172e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: no channel data for %d", 173e5b75505Sopenharmony_ci first_chan->freq + i * 20); 174e5b75505Sopenharmony_ci return 0; 175e5b75505Sopenharmony_ci } 176e5b75505Sopenharmony_ci 177e5b75505Sopenharmony_ci /* HT 40 MHz secondary channel availability checked only for 178e5b75505Sopenharmony_ci * primary channel */ 179e5b75505Sopenharmony_ci if (!chan_bw_allowed(chan, bw, 1, !i)) { 180e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: bw now allowed for %d", 181e5b75505Sopenharmony_ci first_chan->freq + i * 20); 182e5b75505Sopenharmony_ci return 0; 183e5b75505Sopenharmony_ci } 184e5b75505Sopenharmony_ci 185e5b75505Sopenharmony_ci if (!dfs_channel_available(chan, skip_radar)) { 186e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: channel not available %d", 187e5b75505Sopenharmony_ci first_chan->freq + i * 20); 188e5b75505Sopenharmony_ci return 0; 189e5b75505Sopenharmony_ci } 190e5b75505Sopenharmony_ci } 191e5b75505Sopenharmony_ci 192e5b75505Sopenharmony_ci return 1; 193e5b75505Sopenharmony_ci} 194e5b75505Sopenharmony_ci 195e5b75505Sopenharmony_ci 196e5b75505Sopenharmony_cistatic int is_in_chanlist(struct hostapd_iface *iface, 197e5b75505Sopenharmony_ci struct hostapd_channel_data *chan) 198e5b75505Sopenharmony_ci{ 199e5b75505Sopenharmony_ci if (!iface->conf->acs_ch_list.num) 200e5b75505Sopenharmony_ci return 1; 201e5b75505Sopenharmony_ci 202e5b75505Sopenharmony_ci return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); 203e5b75505Sopenharmony_ci} 204e5b75505Sopenharmony_ci 205e5b75505Sopenharmony_ci 206e5b75505Sopenharmony_ci/* 207e5b75505Sopenharmony_ci * The function assumes HT40+ operation. 208e5b75505Sopenharmony_ci * Make sure to adjust the following variables after calling this: 209e5b75505Sopenharmony_ci * - hapd->secondary_channel 210e5b75505Sopenharmony_ci * - hapd->vht/he_oper_centr_freq_seg0_idx 211e5b75505Sopenharmony_ci * - hapd->vht/he_oper_centr_freq_seg1_idx 212e5b75505Sopenharmony_ci */ 213e5b75505Sopenharmony_cistatic int dfs_find_channel(struct hostapd_iface *iface, 214e5b75505Sopenharmony_ci struct hostapd_channel_data **ret_chan, 215e5b75505Sopenharmony_ci int idx, int skip_radar) 216e5b75505Sopenharmony_ci{ 217e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 218e5b75505Sopenharmony_ci struct hostapd_channel_data *chan; 219e5b75505Sopenharmony_ci int i, channel_idx = 0, n_chans, n_chans1; 220e5b75505Sopenharmony_ci 221e5b75505Sopenharmony_ci mode = iface->current_mode; 222e5b75505Sopenharmony_ci n_chans = dfs_get_used_n_chans(iface, &n_chans1); 223e5b75505Sopenharmony_ci 224e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); 225e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 226e5b75505Sopenharmony_ci chan = &mode->channels[i]; 227e5b75505Sopenharmony_ci 228e5b75505Sopenharmony_ci /* Skip HT40/VHT incompatible channels */ 229e5b75505Sopenharmony_ci if (iface->conf->ieee80211n && 230e5b75505Sopenharmony_ci iface->conf->secondary_channel && 231e5b75505Sopenharmony_ci (!dfs_is_chan_allowed(chan, n_chans) || 232e5b75505Sopenharmony_ci !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) { 233e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 234e5b75505Sopenharmony_ci "DFS: channel %d (%d) is incompatible", 235e5b75505Sopenharmony_ci chan->freq, chan->chan); 236e5b75505Sopenharmony_ci continue; 237e5b75505Sopenharmony_ci } 238e5b75505Sopenharmony_ci 239e5b75505Sopenharmony_ci /* Skip incompatible chandefs */ 240e5b75505Sopenharmony_ci if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) { 241e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 242e5b75505Sopenharmony_ci "DFS: range not available for %d (%d)", 243e5b75505Sopenharmony_ci chan->freq, chan->chan); 244e5b75505Sopenharmony_ci continue; 245e5b75505Sopenharmony_ci } 246e5b75505Sopenharmony_ci 247e5b75505Sopenharmony_ci if (!is_in_chanlist(iface, chan)) { 248e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 249e5b75505Sopenharmony_ci "DFS: channel %d (%d) not in chanlist", 250e5b75505Sopenharmony_ci chan->freq, chan->chan); 251e5b75505Sopenharmony_ci continue; 252e5b75505Sopenharmony_ci } 253e5b75505Sopenharmony_ci 254e5b75505Sopenharmony_ci if (chan->max_tx_power < iface->conf->min_tx_power) 255e5b75505Sopenharmony_ci continue; 256e5b75505Sopenharmony_ci 257e5b75505Sopenharmony_ci if (ret_chan && idx == channel_idx) { 258e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Selected channel %d (%d)", 259e5b75505Sopenharmony_ci chan->freq, chan->chan); 260e5b75505Sopenharmony_ci *ret_chan = chan; 261e5b75505Sopenharmony_ci return idx; 262e5b75505Sopenharmony_ci } 263e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Adding channel %d (%d)", 264e5b75505Sopenharmony_ci chan->freq, chan->chan); 265e5b75505Sopenharmony_ci channel_idx++; 266e5b75505Sopenharmony_ci } 267e5b75505Sopenharmony_ci return channel_idx; 268e5b75505Sopenharmony_ci} 269e5b75505Sopenharmony_ci 270e5b75505Sopenharmony_ci 271e5b75505Sopenharmony_cistatic void dfs_adjust_center_freq(struct hostapd_iface *iface, 272e5b75505Sopenharmony_ci struct hostapd_channel_data *chan, 273e5b75505Sopenharmony_ci int secondary_channel, 274e5b75505Sopenharmony_ci int sec_chan_idx_80p80, 275e5b75505Sopenharmony_ci u8 *oper_centr_freq_seg0_idx, 276e5b75505Sopenharmony_ci u8 *oper_centr_freq_seg1_idx) 277e5b75505Sopenharmony_ci{ 278e5b75505Sopenharmony_ci if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax) 279e5b75505Sopenharmony_ci return; 280e5b75505Sopenharmony_ci 281e5b75505Sopenharmony_ci if (!chan) 282e5b75505Sopenharmony_ci return; 283e5b75505Sopenharmony_ci 284e5b75505Sopenharmony_ci *oper_centr_freq_seg1_idx = 0; 285e5b75505Sopenharmony_ci 286e5b75505Sopenharmony_ci switch (hostapd_get_oper_chwidth(iface->conf)) { 287e5b75505Sopenharmony_ci case CHANWIDTH_USE_HT: 288e5b75505Sopenharmony_ci if (secondary_channel == 1) 289e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = chan->chan + 2; 290e5b75505Sopenharmony_ci else if (secondary_channel == -1) 291e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = chan->chan - 2; 292e5b75505Sopenharmony_ci else 293e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = chan->chan; 294e5b75505Sopenharmony_ci break; 295e5b75505Sopenharmony_ci case CHANWIDTH_80MHZ: 296e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = chan->chan + 6; 297e5b75505Sopenharmony_ci break; 298e5b75505Sopenharmony_ci case CHANWIDTH_160MHZ: 299e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = chan->chan + 14; 300e5b75505Sopenharmony_ci break; 301e5b75505Sopenharmony_ci case CHANWIDTH_80P80MHZ: 302e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = chan->chan + 6; 303e5b75505Sopenharmony_ci *oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6; 304e5b75505Sopenharmony_ci break; 305e5b75505Sopenharmony_ci 306e5b75505Sopenharmony_ci default: 307e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 308e5b75505Sopenharmony_ci "DFS: Unsupported channel width configuration"); 309e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = 0; 310e5b75505Sopenharmony_ci break; 311e5b75505Sopenharmony_ci } 312e5b75505Sopenharmony_ci 313e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", 314e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx, 315e5b75505Sopenharmony_ci *oper_centr_freq_seg1_idx); 316e5b75505Sopenharmony_ci} 317e5b75505Sopenharmony_ci 318e5b75505Sopenharmony_ci 319e5b75505Sopenharmony_ci/* Return start channel idx we will use for mode->channels[idx] */ 320e5b75505Sopenharmony_cistatic int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start) 321e5b75505Sopenharmony_ci{ 322e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 323e5b75505Sopenharmony_ci struct hostapd_channel_data *chan; 324e5b75505Sopenharmony_ci int channel_no = iface->conf->channel; 325e5b75505Sopenharmony_ci int res = -1, i; 326e5b75505Sopenharmony_ci int chan_seg1 = -1; 327e5b75505Sopenharmony_ci 328e5b75505Sopenharmony_ci *seg1_start = -1; 329e5b75505Sopenharmony_ci 330e5b75505Sopenharmony_ci /* HT40- */ 331e5b75505Sopenharmony_ci if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) 332e5b75505Sopenharmony_ci channel_no -= 4; 333e5b75505Sopenharmony_ci 334e5b75505Sopenharmony_ci /* VHT/HE */ 335e5b75505Sopenharmony_ci if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) { 336e5b75505Sopenharmony_ci switch (hostapd_get_oper_chwidth(iface->conf)) { 337e5b75505Sopenharmony_ci case CHANWIDTH_USE_HT: 338e5b75505Sopenharmony_ci break; 339e5b75505Sopenharmony_ci case CHANWIDTH_80MHZ: 340e5b75505Sopenharmony_ci channel_no = hostapd_get_oper_centr_freq_seg0_idx( 341e5b75505Sopenharmony_ci iface->conf) - 6; 342e5b75505Sopenharmony_ci break; 343e5b75505Sopenharmony_ci case CHANWIDTH_160MHZ: 344e5b75505Sopenharmony_ci channel_no = hostapd_get_oper_centr_freq_seg0_idx( 345e5b75505Sopenharmony_ci iface->conf) - 14; 346e5b75505Sopenharmony_ci break; 347e5b75505Sopenharmony_ci case CHANWIDTH_80P80MHZ: 348e5b75505Sopenharmony_ci channel_no = hostapd_get_oper_centr_freq_seg0_idx( 349e5b75505Sopenharmony_ci iface->conf) - 6; 350e5b75505Sopenharmony_ci chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx( 351e5b75505Sopenharmony_ci iface->conf) - 6; 352e5b75505Sopenharmony_ci break; 353e5b75505Sopenharmony_ci default: 354e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 355e5b75505Sopenharmony_ci "DFS only VHT20/40/80/160/80+80 is supported now"); 356e5b75505Sopenharmony_ci channel_no = -1; 357e5b75505Sopenharmony_ci break; 358e5b75505Sopenharmony_ci } 359e5b75505Sopenharmony_ci } 360e5b75505Sopenharmony_ci 361e5b75505Sopenharmony_ci /* Get idx */ 362e5b75505Sopenharmony_ci mode = iface->current_mode; 363e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 364e5b75505Sopenharmony_ci chan = &mode->channels[i]; 365e5b75505Sopenharmony_ci if (chan->chan == channel_no) { 366e5b75505Sopenharmony_ci res = i; 367e5b75505Sopenharmony_ci break; 368e5b75505Sopenharmony_ci } 369e5b75505Sopenharmony_ci } 370e5b75505Sopenharmony_ci 371e5b75505Sopenharmony_ci if (res != -1 && chan_seg1 > -1) { 372e5b75505Sopenharmony_ci int found = 0; 373e5b75505Sopenharmony_ci 374e5b75505Sopenharmony_ci /* Get idx for seg1 */ 375e5b75505Sopenharmony_ci mode = iface->current_mode; 376e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 377e5b75505Sopenharmony_ci chan = &mode->channels[i]; 378e5b75505Sopenharmony_ci if (chan->chan == chan_seg1) { 379e5b75505Sopenharmony_ci *seg1_start = i; 380e5b75505Sopenharmony_ci found = 1; 381e5b75505Sopenharmony_ci break; 382e5b75505Sopenharmony_ci } 383e5b75505Sopenharmony_ci } 384e5b75505Sopenharmony_ci if (!found) 385e5b75505Sopenharmony_ci res = -1; 386e5b75505Sopenharmony_ci } 387e5b75505Sopenharmony_ci 388e5b75505Sopenharmony_ci if (res == -1) { 389e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 390e5b75505Sopenharmony_ci "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d", 391e5b75505Sopenharmony_ci mode->num_channels, channel_no, iface->conf->channel, 392e5b75505Sopenharmony_ci iface->conf->ieee80211n, 393e5b75505Sopenharmony_ci iface->conf->secondary_channel, 394e5b75505Sopenharmony_ci hostapd_get_oper_chwidth(iface->conf)); 395e5b75505Sopenharmony_ci 396e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 397e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Available channel: %d", 398e5b75505Sopenharmony_ci mode->channels[i].chan); 399e5b75505Sopenharmony_ci } 400e5b75505Sopenharmony_ci } 401e5b75505Sopenharmony_ci 402e5b75505Sopenharmony_ci return res; 403e5b75505Sopenharmony_ci} 404e5b75505Sopenharmony_ci 405e5b75505Sopenharmony_ci 406e5b75505Sopenharmony_ci/* At least one channel have radar flag */ 407e5b75505Sopenharmony_cistatic int dfs_check_chans_radar(struct hostapd_iface *iface, 408e5b75505Sopenharmony_ci int start_chan_idx, int n_chans) 409e5b75505Sopenharmony_ci{ 410e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 411e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 412e5b75505Sopenharmony_ci int i, res = 0; 413e5b75505Sopenharmony_ci 414e5b75505Sopenharmony_ci mode = iface->current_mode; 415e5b75505Sopenharmony_ci 416e5b75505Sopenharmony_ci for (i = 0; i < n_chans; i++) { 417e5b75505Sopenharmony_ci channel = &mode->channels[start_chan_idx + i]; 418e5b75505Sopenharmony_ci if (channel->flag & HOSTAPD_CHAN_RADAR) 419e5b75505Sopenharmony_ci res++; 420e5b75505Sopenharmony_ci } 421e5b75505Sopenharmony_ci 422e5b75505Sopenharmony_ci return res; 423e5b75505Sopenharmony_ci} 424e5b75505Sopenharmony_ci 425e5b75505Sopenharmony_ci 426e5b75505Sopenharmony_ci/* All channels available */ 427e5b75505Sopenharmony_cistatic int dfs_check_chans_available(struct hostapd_iface *iface, 428e5b75505Sopenharmony_ci int start_chan_idx, int n_chans) 429e5b75505Sopenharmony_ci{ 430e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 431e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 432e5b75505Sopenharmony_ci int i; 433e5b75505Sopenharmony_ci 434e5b75505Sopenharmony_ci mode = iface->current_mode; 435e5b75505Sopenharmony_ci 436e5b75505Sopenharmony_ci for (i = 0; i < n_chans; i++) { 437e5b75505Sopenharmony_ci channel = &mode->channels[start_chan_idx + i]; 438e5b75505Sopenharmony_ci 439e5b75505Sopenharmony_ci if (channel->flag & HOSTAPD_CHAN_DISABLED) 440e5b75505Sopenharmony_ci break; 441e5b75505Sopenharmony_ci 442e5b75505Sopenharmony_ci if (!(channel->flag & HOSTAPD_CHAN_RADAR)) 443e5b75505Sopenharmony_ci continue; 444e5b75505Sopenharmony_ci 445e5b75505Sopenharmony_ci if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != 446e5b75505Sopenharmony_ci HOSTAPD_CHAN_DFS_AVAILABLE) 447e5b75505Sopenharmony_ci break; 448e5b75505Sopenharmony_ci } 449e5b75505Sopenharmony_ci 450e5b75505Sopenharmony_ci return i == n_chans; 451e5b75505Sopenharmony_ci} 452e5b75505Sopenharmony_ci 453e5b75505Sopenharmony_ci 454e5b75505Sopenharmony_ci/* At least one channel unavailable */ 455e5b75505Sopenharmony_cistatic int dfs_check_chans_unavailable(struct hostapd_iface *iface, 456e5b75505Sopenharmony_ci int start_chan_idx, 457e5b75505Sopenharmony_ci int n_chans) 458e5b75505Sopenharmony_ci{ 459e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 460e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 461e5b75505Sopenharmony_ci int i, res = 0; 462e5b75505Sopenharmony_ci 463e5b75505Sopenharmony_ci mode = iface->current_mode; 464e5b75505Sopenharmony_ci 465e5b75505Sopenharmony_ci for (i = 0; i < n_chans; i++) { 466e5b75505Sopenharmony_ci channel = &mode->channels[start_chan_idx + i]; 467e5b75505Sopenharmony_ci if (channel->flag & HOSTAPD_CHAN_DISABLED) 468e5b75505Sopenharmony_ci res++; 469e5b75505Sopenharmony_ci if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == 470e5b75505Sopenharmony_ci HOSTAPD_CHAN_DFS_UNAVAILABLE) 471e5b75505Sopenharmony_ci res++; 472e5b75505Sopenharmony_ci } 473e5b75505Sopenharmony_ci 474e5b75505Sopenharmony_ci return res; 475e5b75505Sopenharmony_ci} 476e5b75505Sopenharmony_ci 477e5b75505Sopenharmony_ci 478e5b75505Sopenharmony_cistatic struct hostapd_channel_data * 479e5b75505Sopenharmony_cidfs_get_valid_channel(struct hostapd_iface *iface, 480e5b75505Sopenharmony_ci int *secondary_channel, 481e5b75505Sopenharmony_ci u8 *oper_centr_freq_seg0_idx, 482e5b75505Sopenharmony_ci u8 *oper_centr_freq_seg1_idx, 483e5b75505Sopenharmony_ci int skip_radar) 484e5b75505Sopenharmony_ci{ 485e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 486e5b75505Sopenharmony_ci struct hostapd_channel_data *chan = NULL; 487e5b75505Sopenharmony_ci struct hostapd_channel_data *chan2 = NULL; 488e5b75505Sopenharmony_ci int num_available_chandefs; 489e5b75505Sopenharmony_ci int chan_idx, chan_idx2; 490e5b75505Sopenharmony_ci int sec_chan_idx_80p80 = -1; 491e5b75505Sopenharmony_ci int i; 492e5b75505Sopenharmony_ci u32 _rand; 493e5b75505Sopenharmony_ci 494e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); 495e5b75505Sopenharmony_ci *secondary_channel = 0; 496e5b75505Sopenharmony_ci *oper_centr_freq_seg0_idx = 0; 497e5b75505Sopenharmony_ci *oper_centr_freq_seg1_idx = 0; 498e5b75505Sopenharmony_ci 499e5b75505Sopenharmony_ci if (iface->current_mode == NULL) 500e5b75505Sopenharmony_ci return NULL; 501e5b75505Sopenharmony_ci 502e5b75505Sopenharmony_ci mode = iface->current_mode; 503e5b75505Sopenharmony_ci if (mode->mode != HOSTAPD_MODE_IEEE80211A) 504e5b75505Sopenharmony_ci return NULL; 505e5b75505Sopenharmony_ci 506e5b75505Sopenharmony_ci /* Get the count first */ 507e5b75505Sopenharmony_ci num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); 508e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d", 509e5b75505Sopenharmony_ci num_available_chandefs); 510e5b75505Sopenharmony_ci if (num_available_chandefs == 0) 511e5b75505Sopenharmony_ci return NULL; 512e5b75505Sopenharmony_ci 513e5b75505Sopenharmony_ci if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) 514e5b75505Sopenharmony_ci return NULL; 515e5b75505Sopenharmony_ci chan_idx = _rand % num_available_chandefs; 516e5b75505Sopenharmony_ci dfs_find_channel(iface, &chan, chan_idx, skip_radar); 517e5b75505Sopenharmony_ci if (!chan) { 518e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: no random channel found"); 519e5b75505Sopenharmony_ci return NULL; 520e5b75505Sopenharmony_ci } 521e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: got random channel %d (%d)", 522e5b75505Sopenharmony_ci chan->freq, chan->chan); 523e5b75505Sopenharmony_ci 524e5b75505Sopenharmony_ci /* dfs_find_channel() calculations assume HT40+ */ 525e5b75505Sopenharmony_ci if (iface->conf->secondary_channel) 526e5b75505Sopenharmony_ci *secondary_channel = 1; 527e5b75505Sopenharmony_ci else 528e5b75505Sopenharmony_ci *secondary_channel = 0; 529e5b75505Sopenharmony_ci 530e5b75505Sopenharmony_ci /* Get secondary channel for HT80P80 */ 531e5b75505Sopenharmony_ci if (hostapd_get_oper_chwidth(iface->conf) == CHANWIDTH_80P80MHZ) { 532e5b75505Sopenharmony_ci if (num_available_chandefs <= 1) { 533e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 534e5b75505Sopenharmony_ci "only 1 valid chan, can't support 80+80"); 535e5b75505Sopenharmony_ci return NULL; 536e5b75505Sopenharmony_ci } 537e5b75505Sopenharmony_ci 538e5b75505Sopenharmony_ci /* 539e5b75505Sopenharmony_ci * Loop all channels except channel1 to find a valid channel2 540e5b75505Sopenharmony_ci * that is not adjacent to channel1. 541e5b75505Sopenharmony_ci */ 542e5b75505Sopenharmony_ci for (i = 0; i < num_available_chandefs - 1; i++) { 543e5b75505Sopenharmony_ci /* start from chan_idx + 1, end when chan_idx - 1 */ 544e5b75505Sopenharmony_ci chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs; 545e5b75505Sopenharmony_ci dfs_find_channel(iface, &chan2, chan_idx2, skip_radar); 546e5b75505Sopenharmony_ci if (chan2 && abs(chan2->chan - chan->chan) > 12) { 547e5b75505Sopenharmony_ci /* two channels are not adjacent */ 548e5b75505Sopenharmony_ci sec_chan_idx_80p80 = chan2->chan; 549e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 550e5b75505Sopenharmony_ci "DFS: got second chan: %d (%d)", 551e5b75505Sopenharmony_ci chan2->freq, chan2->chan); 552e5b75505Sopenharmony_ci break; 553e5b75505Sopenharmony_ci } 554e5b75505Sopenharmony_ci } 555e5b75505Sopenharmony_ci 556e5b75505Sopenharmony_ci /* Check if we got a valid secondary channel which is not 557e5b75505Sopenharmony_ci * adjacent to the first channel. 558e5b75505Sopenharmony_ci */ 559e5b75505Sopenharmony_ci if (sec_chan_idx_80p80 == -1) { 560e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 561e5b75505Sopenharmony_ci "DFS: failed to get chan2 for 80+80"); 562e5b75505Sopenharmony_ci return NULL; 563e5b75505Sopenharmony_ci } 564e5b75505Sopenharmony_ci } 565e5b75505Sopenharmony_ci 566e5b75505Sopenharmony_ci dfs_adjust_center_freq(iface, chan, 567e5b75505Sopenharmony_ci *secondary_channel, 568e5b75505Sopenharmony_ci sec_chan_idx_80p80, 569e5b75505Sopenharmony_ci oper_centr_freq_seg0_idx, 570e5b75505Sopenharmony_ci oper_centr_freq_seg1_idx); 571e5b75505Sopenharmony_ci 572e5b75505Sopenharmony_ci return chan; 573e5b75505Sopenharmony_ci} 574e5b75505Sopenharmony_ci 575e5b75505Sopenharmony_ci 576e5b75505Sopenharmony_cistatic int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) 577e5b75505Sopenharmony_ci{ 578e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 579e5b75505Sopenharmony_ci struct hostapd_channel_data *chan = NULL; 580e5b75505Sopenharmony_ci int i; 581e5b75505Sopenharmony_ci 582e5b75505Sopenharmony_ci mode = iface->current_mode; 583e5b75505Sopenharmony_ci if (mode == NULL) 584e5b75505Sopenharmony_ci return 0; 585e5b75505Sopenharmony_ci 586e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); 587e5b75505Sopenharmony_ci for (i = 0; i < iface->current_mode->num_channels; i++) { 588e5b75505Sopenharmony_ci chan = &iface->current_mode->channels[i]; 589e5b75505Sopenharmony_ci if (chan->freq == freq) { 590e5b75505Sopenharmony_ci if (chan->flag & HOSTAPD_CHAN_RADAR) { 591e5b75505Sopenharmony_ci chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; 592e5b75505Sopenharmony_ci chan->flag |= state; 593e5b75505Sopenharmony_ci return 1; /* Channel found */ 594e5b75505Sopenharmony_ci } 595e5b75505Sopenharmony_ci } 596e5b75505Sopenharmony_ci } 597e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); 598e5b75505Sopenharmony_ci return 0; 599e5b75505Sopenharmony_ci} 600e5b75505Sopenharmony_ci 601e5b75505Sopenharmony_ci 602e5b75505Sopenharmony_cistatic int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, 603e5b75505Sopenharmony_ci int chan_offset, int chan_width, int cf1, 604e5b75505Sopenharmony_ci int cf2, u32 state) 605e5b75505Sopenharmony_ci{ 606e5b75505Sopenharmony_ci int n_chans = 1, i; 607e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 608e5b75505Sopenharmony_ci int frequency = freq; 609e5b75505Sopenharmony_ci int frequency2 = 0; 610e5b75505Sopenharmony_ci int ret = 0; 611e5b75505Sopenharmony_ci 612e5b75505Sopenharmony_ci mode = iface->current_mode; 613e5b75505Sopenharmony_ci if (mode == NULL) 614e5b75505Sopenharmony_ci return 0; 615e5b75505Sopenharmony_ci 616e5b75505Sopenharmony_ci if (mode->mode != HOSTAPD_MODE_IEEE80211A) { 617e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); 618e5b75505Sopenharmony_ci return 0; 619e5b75505Sopenharmony_ci } 620e5b75505Sopenharmony_ci 621e5b75505Sopenharmony_ci /* Seems cf1 and chan_width is enough here */ 622e5b75505Sopenharmony_ci switch (chan_width) { 623e5b75505Sopenharmony_ci case CHAN_WIDTH_20_NOHT: 624e5b75505Sopenharmony_ci case CHAN_WIDTH_20: 625e5b75505Sopenharmony_ci n_chans = 1; 626e5b75505Sopenharmony_ci if (frequency == 0) 627e5b75505Sopenharmony_ci frequency = cf1; 628e5b75505Sopenharmony_ci break; 629e5b75505Sopenharmony_ci case CHAN_WIDTH_40: 630e5b75505Sopenharmony_ci n_chans = 2; 631e5b75505Sopenharmony_ci frequency = cf1 - 10; 632e5b75505Sopenharmony_ci break; 633e5b75505Sopenharmony_ci case CHAN_WIDTH_80: 634e5b75505Sopenharmony_ci n_chans = 4; 635e5b75505Sopenharmony_ci frequency = cf1 - 30; 636e5b75505Sopenharmony_ci break; 637e5b75505Sopenharmony_ci case CHAN_WIDTH_80P80: 638e5b75505Sopenharmony_ci n_chans = 4; 639e5b75505Sopenharmony_ci frequency = cf1 - 30; 640e5b75505Sopenharmony_ci frequency2 = cf2 - 30; 641e5b75505Sopenharmony_ci break; 642e5b75505Sopenharmony_ci case CHAN_WIDTH_160: 643e5b75505Sopenharmony_ci n_chans = 8; 644e5b75505Sopenharmony_ci frequency = cf1 - 70; 645e5b75505Sopenharmony_ci break; 646e5b75505Sopenharmony_ci default: 647e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DFS chan_width %d not supported", 648e5b75505Sopenharmony_ci chan_width); 649e5b75505Sopenharmony_ci break; 650e5b75505Sopenharmony_ci } 651e5b75505Sopenharmony_ci 652e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, 653e5b75505Sopenharmony_ci n_chans); 654e5b75505Sopenharmony_ci for (i = 0; i < n_chans; i++) { 655e5b75505Sopenharmony_ci ret += set_dfs_state_freq(iface, frequency, state); 656e5b75505Sopenharmony_ci frequency = frequency + 20; 657e5b75505Sopenharmony_ci 658e5b75505Sopenharmony_ci if (chan_width == CHAN_WIDTH_80P80) { 659e5b75505Sopenharmony_ci ret += set_dfs_state_freq(iface, frequency2, state); 660e5b75505Sopenharmony_ci frequency2 = frequency2 + 20; 661e5b75505Sopenharmony_ci } 662e5b75505Sopenharmony_ci } 663e5b75505Sopenharmony_ci 664e5b75505Sopenharmony_ci return ret; 665e5b75505Sopenharmony_ci} 666e5b75505Sopenharmony_ci 667e5b75505Sopenharmony_ci 668e5b75505Sopenharmony_cistatic int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, 669e5b75505Sopenharmony_ci int chan_width, int cf1, int cf2) 670e5b75505Sopenharmony_ci{ 671e5b75505Sopenharmony_ci int start_chan_idx, start_chan_idx1; 672e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 673e5b75505Sopenharmony_ci struct hostapd_channel_data *chan; 674e5b75505Sopenharmony_ci int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1; 675e5b75505Sopenharmony_ci u8 radar_chan; 676e5b75505Sopenharmony_ci int res = 0; 677e5b75505Sopenharmony_ci 678e5b75505Sopenharmony_ci /* Our configuration */ 679e5b75505Sopenharmony_ci mode = iface->current_mode; 680e5b75505Sopenharmony_ci start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 681e5b75505Sopenharmony_ci n_chans = dfs_get_used_n_chans(iface, &n_chans1); 682e5b75505Sopenharmony_ci 683e5b75505Sopenharmony_ci /* Check we are on DFS channel(s) */ 684e5b75505Sopenharmony_ci if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) 685e5b75505Sopenharmony_ci return 0; 686e5b75505Sopenharmony_ci 687e5b75505Sopenharmony_ci /* Reported via radar event */ 688e5b75505Sopenharmony_ci switch (chan_width) { 689e5b75505Sopenharmony_ci case CHAN_WIDTH_20_NOHT: 690e5b75505Sopenharmony_ci case CHAN_WIDTH_20: 691e5b75505Sopenharmony_ci radar_n_chans = 1; 692e5b75505Sopenharmony_ci if (frequency == 0) 693e5b75505Sopenharmony_ci frequency = cf1; 694e5b75505Sopenharmony_ci break; 695e5b75505Sopenharmony_ci case CHAN_WIDTH_40: 696e5b75505Sopenharmony_ci radar_n_chans = 2; 697e5b75505Sopenharmony_ci frequency = cf1 - 10; 698e5b75505Sopenharmony_ci break; 699e5b75505Sopenharmony_ci case CHAN_WIDTH_80: 700e5b75505Sopenharmony_ci radar_n_chans = 4; 701e5b75505Sopenharmony_ci frequency = cf1 - 30; 702e5b75505Sopenharmony_ci break; 703e5b75505Sopenharmony_ci case CHAN_WIDTH_160: 704e5b75505Sopenharmony_ci radar_n_chans = 8; 705e5b75505Sopenharmony_ci frequency = cf1 - 70; 706e5b75505Sopenharmony_ci break; 707e5b75505Sopenharmony_ci default: 708e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DFS chan_width %d not supported", 709e5b75505Sopenharmony_ci chan_width); 710e5b75505Sopenharmony_ci break; 711e5b75505Sopenharmony_ci } 712e5b75505Sopenharmony_ci 713e5b75505Sopenharmony_ci ieee80211_freq_to_chan(frequency, &radar_chan); 714e5b75505Sopenharmony_ci 715e5b75505Sopenharmony_ci for (i = 0; i < n_chans; i++) { 716e5b75505Sopenharmony_ci chan = &mode->channels[start_chan_idx + i]; 717e5b75505Sopenharmony_ci if (!(chan->flag & HOSTAPD_CHAN_RADAR)) 718e5b75505Sopenharmony_ci continue; 719e5b75505Sopenharmony_ci for (j = 0; j < radar_n_chans; j++) { 720e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", 721e5b75505Sopenharmony_ci chan->chan, radar_chan + j * 4); 722e5b75505Sopenharmony_ci if (chan->chan == radar_chan + j * 4) 723e5b75505Sopenharmony_ci res++; 724e5b75505Sopenharmony_ci } 725e5b75505Sopenharmony_ci } 726e5b75505Sopenharmony_ci 727e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "overlapped: %d", res); 728e5b75505Sopenharmony_ci 729e5b75505Sopenharmony_ci return res; 730e5b75505Sopenharmony_ci} 731e5b75505Sopenharmony_ci 732e5b75505Sopenharmony_ci 733e5b75505Sopenharmony_cistatic unsigned int dfs_get_cac_time(struct hostapd_iface *iface, 734e5b75505Sopenharmony_ci int start_chan_idx, int n_chans) 735e5b75505Sopenharmony_ci{ 736e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 737e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 738e5b75505Sopenharmony_ci int i; 739e5b75505Sopenharmony_ci unsigned int cac_time_ms = 0; 740e5b75505Sopenharmony_ci 741e5b75505Sopenharmony_ci mode = iface->current_mode; 742e5b75505Sopenharmony_ci 743e5b75505Sopenharmony_ci for (i = 0; i < n_chans; i++) { 744e5b75505Sopenharmony_ci channel = &mode->channels[start_chan_idx + i]; 745e5b75505Sopenharmony_ci if (!(channel->flag & HOSTAPD_CHAN_RADAR)) 746e5b75505Sopenharmony_ci continue; 747e5b75505Sopenharmony_ci if (channel->dfs_cac_ms > cac_time_ms) 748e5b75505Sopenharmony_ci cac_time_ms = channel->dfs_cac_ms; 749e5b75505Sopenharmony_ci } 750e5b75505Sopenharmony_ci 751e5b75505Sopenharmony_ci return cac_time_ms; 752e5b75505Sopenharmony_ci} 753e5b75505Sopenharmony_ci 754e5b75505Sopenharmony_ci 755e5b75505Sopenharmony_ci/* 756e5b75505Sopenharmony_ci * Main DFS handler 757e5b75505Sopenharmony_ci * 1 - continue channel/ap setup 758e5b75505Sopenharmony_ci * 0 - channel/ap setup will be continued after CAC 759e5b75505Sopenharmony_ci * -1 - hit critical error 760e5b75505Sopenharmony_ci */ 761e5b75505Sopenharmony_ciint hostapd_handle_dfs(struct hostapd_iface *iface) 762e5b75505Sopenharmony_ci{ 763e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 764e5b75505Sopenharmony_ci int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; 765e5b75505Sopenharmony_ci int skip_radar = 0; 766e5b75505Sopenharmony_ci 767e5b75505Sopenharmony_ci if (is_6ghz_freq(iface->freq)) 768e5b75505Sopenharmony_ci return 1; 769e5b75505Sopenharmony_ci 770e5b75505Sopenharmony_ci if (!iface->current_mode) { 771e5b75505Sopenharmony_ci /* 772e5b75505Sopenharmony_ci * This can happen with drivers that do not provide mode 773e5b75505Sopenharmony_ci * information and as such, cannot really use hostapd for DFS. 774e5b75505Sopenharmony_ci */ 775e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 776e5b75505Sopenharmony_ci "DFS: No current_mode information - assume no need to perform DFS operations by hostapd"); 777e5b75505Sopenharmony_ci return 1; 778e5b75505Sopenharmony_ci } 779e5b75505Sopenharmony_ci 780e5b75505Sopenharmony_ci iface->cac_started = 0; 781e5b75505Sopenharmony_ci 782e5b75505Sopenharmony_ci do { 783e5b75505Sopenharmony_ci /* Get start (first) channel for current configuration */ 784e5b75505Sopenharmony_ci start_chan_idx = dfs_get_start_chan_idx(iface, 785e5b75505Sopenharmony_ci &start_chan_idx1); 786e5b75505Sopenharmony_ci if (start_chan_idx == -1) 787e5b75505Sopenharmony_ci return -1; 788e5b75505Sopenharmony_ci 789e5b75505Sopenharmony_ci /* Get number of used channels, depend on width */ 790e5b75505Sopenharmony_ci n_chans = dfs_get_used_n_chans(iface, &n_chans1); 791e5b75505Sopenharmony_ci 792e5b75505Sopenharmony_ci /* Setup CAC time */ 793e5b75505Sopenharmony_ci iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx, 794e5b75505Sopenharmony_ci n_chans); 795e5b75505Sopenharmony_ci 796e5b75505Sopenharmony_ci /* Check if any of configured channels require DFS */ 797e5b75505Sopenharmony_ci res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); 798e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 799e5b75505Sopenharmony_ci "DFS %d channels required radar detection", 800e5b75505Sopenharmony_ci res); 801e5b75505Sopenharmony_ci if (!res) 802e5b75505Sopenharmony_ci return 1; 803e5b75505Sopenharmony_ci 804e5b75505Sopenharmony_ci /* Check if all channels are DFS available */ 805e5b75505Sopenharmony_ci res = dfs_check_chans_available(iface, start_chan_idx, n_chans); 806e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 807e5b75505Sopenharmony_ci "DFS all channels available, (SKIP CAC): %s", 808e5b75505Sopenharmony_ci res ? "yes" : "no"); 809e5b75505Sopenharmony_ci if (res) 810e5b75505Sopenharmony_ci return 1; 811e5b75505Sopenharmony_ci 812e5b75505Sopenharmony_ci /* Check if any of configured channels is unavailable */ 813e5b75505Sopenharmony_ci res = dfs_check_chans_unavailable(iface, start_chan_idx, 814e5b75505Sopenharmony_ci n_chans); 815e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", 816e5b75505Sopenharmony_ci res, res ? "yes": "no"); 817e5b75505Sopenharmony_ci if (res) { 818e5b75505Sopenharmony_ci int sec = 0; 819e5b75505Sopenharmony_ci u8 cf1 = 0, cf2 = 0; 820e5b75505Sopenharmony_ci 821e5b75505Sopenharmony_ci channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 822e5b75505Sopenharmony_ci skip_radar); 823e5b75505Sopenharmony_ci if (!channel) { 824e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "could not get valid channel"); 825e5b75505Sopenharmony_ci hostapd_set_state(iface, HAPD_IFACE_DFS); 826e5b75505Sopenharmony_ci return 0; 827e5b75505Sopenharmony_ci } 828e5b75505Sopenharmony_ci 829e5b75505Sopenharmony_ci iface->freq = channel->freq; 830e5b75505Sopenharmony_ci iface->conf->channel = channel->chan; 831e5b75505Sopenharmony_ci iface->conf->secondary_channel = sec; 832e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1); 833e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2); 834e5b75505Sopenharmony_ci } 835e5b75505Sopenharmony_ci } while (res); 836e5b75505Sopenharmony_ci 837e5b75505Sopenharmony_ci /* Finally start CAC */ 838e5b75505Sopenharmony_ci hostapd_set_state(iface, HAPD_IFACE_DFS); 839e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); 840e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START 841e5b75505Sopenharmony_ci "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", 842e5b75505Sopenharmony_ci iface->freq, 843e5b75505Sopenharmony_ci iface->conf->channel, iface->conf->secondary_channel, 844e5b75505Sopenharmony_ci hostapd_get_oper_chwidth(iface->conf), 845e5b75505Sopenharmony_ci hostapd_get_oper_centr_freq_seg0_idx(iface->conf), 846e5b75505Sopenharmony_ci hostapd_get_oper_centr_freq_seg1_idx(iface->conf), 847e5b75505Sopenharmony_ci iface->dfs_cac_ms / 1000); 848e5b75505Sopenharmony_ci 849e5b75505Sopenharmony_ci res = hostapd_start_dfs_cac( 850e5b75505Sopenharmony_ci iface, iface->conf->hw_mode, iface->freq, iface->conf->channel, 851e5b75505Sopenharmony_ci iface->conf->ieee80211n, iface->conf->ieee80211ac, 852e5b75505Sopenharmony_ci iface->conf->ieee80211ax, 853e5b75505Sopenharmony_ci iface->conf->secondary_channel, 854e5b75505Sopenharmony_ci hostapd_get_oper_chwidth(iface->conf), 855e5b75505Sopenharmony_ci hostapd_get_oper_centr_freq_seg0_idx(iface->conf), 856e5b75505Sopenharmony_ci hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); 857e5b75505Sopenharmony_ci 858e5b75505Sopenharmony_ci if (res) { 859e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); 860e5b75505Sopenharmony_ci return -1; 861e5b75505Sopenharmony_ci } 862e5b75505Sopenharmony_ci 863e5b75505Sopenharmony_ci return 0; 864e5b75505Sopenharmony_ci} 865e5b75505Sopenharmony_ci 866e5b75505Sopenharmony_ci 867e5b75505Sopenharmony_ciint hostapd_is_dfs_chan_available(struct hostapd_iface *iface) 868e5b75505Sopenharmony_ci{ 869e5b75505Sopenharmony_ci int n_chans, n_chans1, start_chan_idx, start_chan_idx1; 870e5b75505Sopenharmony_ci 871e5b75505Sopenharmony_ci /* Get the start (first) channel for current configuration */ 872e5b75505Sopenharmony_ci start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 873e5b75505Sopenharmony_ci if (start_chan_idx < 0) 874e5b75505Sopenharmony_ci return 0; 875e5b75505Sopenharmony_ci 876e5b75505Sopenharmony_ci /* Get the number of used channels, depending on width */ 877e5b75505Sopenharmony_ci n_chans = dfs_get_used_n_chans(iface, &n_chans1); 878e5b75505Sopenharmony_ci 879e5b75505Sopenharmony_ci /* Check if all channels are DFS available */ 880e5b75505Sopenharmony_ci return dfs_check_chans_available(iface, start_chan_idx, n_chans); 881e5b75505Sopenharmony_ci} 882e5b75505Sopenharmony_ci 883e5b75505Sopenharmony_ci 884e5b75505Sopenharmony_ciint hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, 885e5b75505Sopenharmony_ci int ht_enabled, int chan_offset, int chan_width, 886e5b75505Sopenharmony_ci int cf1, int cf2) 887e5b75505Sopenharmony_ci{ 888e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED 889e5b75505Sopenharmony_ci "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 890e5b75505Sopenharmony_ci success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 891e5b75505Sopenharmony_ci 892e5b75505Sopenharmony_ci if (success) { 893e5b75505Sopenharmony_ci /* Complete iface/ap configuration */ 894e5b75505Sopenharmony_ci if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) { 895e5b75505Sopenharmony_ci /* Complete AP configuration for the first bring up. */ 896e5b75505Sopenharmony_ci if (iface->state != HAPD_IFACE_ENABLED) 897e5b75505Sopenharmony_ci hostapd_setup_interface_complete(iface, 0); 898e5b75505Sopenharmony_ci else 899e5b75505Sopenharmony_ci iface->cac_started = 0; 900e5b75505Sopenharmony_ci } else { 901e5b75505Sopenharmony_ci set_dfs_state(iface, freq, ht_enabled, chan_offset, 902e5b75505Sopenharmony_ci chan_width, cf1, cf2, 903e5b75505Sopenharmony_ci HOSTAPD_CHAN_DFS_AVAILABLE); 904e5b75505Sopenharmony_ci /* 905e5b75505Sopenharmony_ci * Just mark the channel available when CAC completion 906e5b75505Sopenharmony_ci * event is received in enabled state. CAC result could 907e5b75505Sopenharmony_ci * have been propagated from another radio having the 908e5b75505Sopenharmony_ci * same regulatory configuration. When CAC completion is 909e5b75505Sopenharmony_ci * received during non-HAPD_IFACE_ENABLED state, make 910e5b75505Sopenharmony_ci * sure the configured channel is available because this 911e5b75505Sopenharmony_ci * CAC completion event could have been propagated from 912e5b75505Sopenharmony_ci * another radio. 913e5b75505Sopenharmony_ci */ 914e5b75505Sopenharmony_ci if (iface->state != HAPD_IFACE_ENABLED && 915e5b75505Sopenharmony_ci hostapd_is_dfs_chan_available(iface)) { 916e5b75505Sopenharmony_ci hostapd_setup_interface_complete(iface, 0); 917e5b75505Sopenharmony_ci iface->cac_started = 0; 918e5b75505Sopenharmony_ci } 919e5b75505Sopenharmony_ci } 920e5b75505Sopenharmony_ci } 921e5b75505Sopenharmony_ci 922e5b75505Sopenharmony_ci return 0; 923e5b75505Sopenharmony_ci} 924e5b75505Sopenharmony_ci 925e5b75505Sopenharmony_ci 926e5b75505Sopenharmony_ciint hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, 927e5b75505Sopenharmony_ci int ht_enabled, int chan_offset, int chan_width, 928e5b75505Sopenharmony_ci int cf1, int cf2) 929e5b75505Sopenharmony_ci{ 930e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED 931e5b75505Sopenharmony_ci "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 932e5b75505Sopenharmony_ci freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 933e5b75505Sopenharmony_ci 934e5b75505Sopenharmony_ci /* Proceed only if DFS is not offloaded to the driver */ 935e5b75505Sopenharmony_ci if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 936e5b75505Sopenharmony_ci return 0; 937e5b75505Sopenharmony_ci 938e5b75505Sopenharmony_ci set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 939e5b75505Sopenharmony_ci cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); 940e5b75505Sopenharmony_ci 941e5b75505Sopenharmony_ci return 0; 942e5b75505Sopenharmony_ci} 943e5b75505Sopenharmony_ci 944e5b75505Sopenharmony_ci 945e5b75505Sopenharmony_cistatic struct hostapd_channel_data * 946e5b75505Sopenharmony_cidfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel, 947e5b75505Sopenharmony_ci u8 *oper_centr_freq_seg0_idx, 948e5b75505Sopenharmony_ci u8 *oper_centr_freq_seg1_idx, int *skip_radar) 949e5b75505Sopenharmony_ci{ 950e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 951e5b75505Sopenharmony_ci 952e5b75505Sopenharmony_ci for (;;) { 953e5b75505Sopenharmony_ci channel = dfs_get_valid_channel(iface, secondary_channel, 954e5b75505Sopenharmony_ci oper_centr_freq_seg0_idx, 955e5b75505Sopenharmony_ci oper_centr_freq_seg1_idx, 956e5b75505Sopenharmony_ci *skip_radar); 957e5b75505Sopenharmony_ci if (channel) { 958e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d", 959e5b75505Sopenharmony_ci channel->chan); 960e5b75505Sopenharmony_ci return channel; 961e5b75505Sopenharmony_ci } 962e5b75505Sopenharmony_ci 963e5b75505Sopenharmony_ci if (*skip_radar) { 964e5b75505Sopenharmony_ci *skip_radar = 0; 965e5b75505Sopenharmony_ci } else { 966e5b75505Sopenharmony_ci int oper_chwidth; 967e5b75505Sopenharmony_ci 968e5b75505Sopenharmony_ci oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 969e5b75505Sopenharmony_ci if (oper_chwidth == CHANWIDTH_USE_HT) 970e5b75505Sopenharmony_ci break; 971e5b75505Sopenharmony_ci *skip_radar = 1; 972e5b75505Sopenharmony_ci hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1); 973e5b75505Sopenharmony_ci } 974e5b75505Sopenharmony_ci } 975e5b75505Sopenharmony_ci 976e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 977e5b75505Sopenharmony_ci "%s: no DFS channels left, waiting for NOP to finish", 978e5b75505Sopenharmony_ci __func__); 979e5b75505Sopenharmony_ci return NULL; 980e5b75505Sopenharmony_ci} 981e5b75505Sopenharmony_ci 982e5b75505Sopenharmony_ci 983e5b75505Sopenharmony_cistatic int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) 984e5b75505Sopenharmony_ci{ 985e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 986e5b75505Sopenharmony_ci int secondary_channel; 987e5b75505Sopenharmony_ci u8 oper_centr_freq_seg0_idx = 0; 988e5b75505Sopenharmony_ci u8 oper_centr_freq_seg1_idx = 0; 989e5b75505Sopenharmony_ci int skip_radar = 0; 990e5b75505Sopenharmony_ci int err = 1; 991e5b75505Sopenharmony_ci 992e5b75505Sopenharmony_ci /* Radar detected during active CAC */ 993e5b75505Sopenharmony_ci iface->cac_started = 0; 994e5b75505Sopenharmony_ci channel = dfs_get_valid_channel(iface, &secondary_channel, 995e5b75505Sopenharmony_ci &oper_centr_freq_seg0_idx, 996e5b75505Sopenharmony_ci &oper_centr_freq_seg1_idx, 997e5b75505Sopenharmony_ci skip_radar); 998e5b75505Sopenharmony_ci 999e5b75505Sopenharmony_ci if (!channel) { 1000e5b75505Sopenharmony_ci channel = dfs_downgrade_bandwidth(iface, &secondary_channel, 1001e5b75505Sopenharmony_ci &oper_centr_freq_seg0_idx, 1002e5b75505Sopenharmony_ci &oper_centr_freq_seg1_idx, 1003e5b75505Sopenharmony_ci &skip_radar); 1004e5b75505Sopenharmony_ci if (!channel) { 1005e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "No valid channel available"); 1006e5b75505Sopenharmony_ci return err; 1007e5b75505Sopenharmony_ci } 1008e5b75505Sopenharmony_ci } 1009e5b75505Sopenharmony_ci 1010e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", 1011e5b75505Sopenharmony_ci channel->chan); 1012e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL 1013e5b75505Sopenharmony_ci "freq=%d chan=%d sec_chan=%d", channel->freq, 1014e5b75505Sopenharmony_ci channel->chan, secondary_channel); 1015e5b75505Sopenharmony_ci 1016e5b75505Sopenharmony_ci iface->freq = channel->freq; 1017e5b75505Sopenharmony_ci iface->conf->channel = channel->chan; 1018e5b75505Sopenharmony_ci iface->conf->secondary_channel = secondary_channel; 1019e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 1020e5b75505Sopenharmony_ci oper_centr_freq_seg0_idx); 1021e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 1022e5b75505Sopenharmony_ci oper_centr_freq_seg1_idx); 1023e5b75505Sopenharmony_ci err = 0; 1024e5b75505Sopenharmony_ci 1025e5b75505Sopenharmony_ci hostapd_setup_interface_complete(iface, err); 1026e5b75505Sopenharmony_ci return err; 1027e5b75505Sopenharmony_ci} 1028e5b75505Sopenharmony_ci 1029e5b75505Sopenharmony_ci 1030e5b75505Sopenharmony_cistatic int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) 1031e5b75505Sopenharmony_ci{ 1032e5b75505Sopenharmony_ci struct hostapd_channel_data *channel; 1033e5b75505Sopenharmony_ci int secondary_channel; 1034e5b75505Sopenharmony_ci u8 oper_centr_freq_seg0_idx; 1035e5b75505Sopenharmony_ci u8 oper_centr_freq_seg1_idx; 1036e5b75505Sopenharmony_ci u8 new_vht_oper_chwidth; 1037e5b75505Sopenharmony_ci int skip_radar = 1; 1038e5b75505Sopenharmony_ci struct csa_settings csa_settings; 1039e5b75505Sopenharmony_ci unsigned int i; 1040e5b75505Sopenharmony_ci int err = 1; 1041e5b75505Sopenharmony_ci struct hostapd_hw_modes *cmode = iface->current_mode; 1042e5b75505Sopenharmony_ci u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 1043e5b75505Sopenharmony_ci int ieee80211_mode = IEEE80211_MODE_AP; 1044e5b75505Sopenharmony_ci 1045e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", 1046e5b75505Sopenharmony_ci __func__, iface->cac_started ? "yes" : "no", 1047e5b75505Sopenharmony_ci hostapd_csa_in_progress(iface) ? "yes" : "no"); 1048e5b75505Sopenharmony_ci 1049e5b75505Sopenharmony_ci /* Check if CSA in progress */ 1050e5b75505Sopenharmony_ci if (hostapd_csa_in_progress(iface)) 1051e5b75505Sopenharmony_ci return 0; 1052e5b75505Sopenharmony_ci 1053e5b75505Sopenharmony_ci /* Check if active CAC */ 1054e5b75505Sopenharmony_ci if (iface->cac_started) 1055e5b75505Sopenharmony_ci return hostapd_dfs_start_channel_switch_cac(iface); 1056e5b75505Sopenharmony_ci 1057e5b75505Sopenharmony_ci /* 1058e5b75505Sopenharmony_ci * Allow selection of DFS channel in ETSI to comply with 1059e5b75505Sopenharmony_ci * uniform spreading. 1060e5b75505Sopenharmony_ci */ 1061e5b75505Sopenharmony_ci if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) 1062e5b75505Sopenharmony_ci skip_radar = 0; 1063e5b75505Sopenharmony_ci 1064e5b75505Sopenharmony_ci /* Perform channel switch/CSA */ 1065e5b75505Sopenharmony_ci channel = dfs_get_valid_channel(iface, &secondary_channel, 1066e5b75505Sopenharmony_ci &oper_centr_freq_seg0_idx, 1067e5b75505Sopenharmony_ci &oper_centr_freq_seg1_idx, 1068e5b75505Sopenharmony_ci skip_radar); 1069e5b75505Sopenharmony_ci 1070e5b75505Sopenharmony_ci if (!channel) { 1071e5b75505Sopenharmony_ci /* 1072e5b75505Sopenharmony_ci * If there is no channel to switch immediately to, check if 1073e5b75505Sopenharmony_ci * there is another channel where we can switch even if it 1074e5b75505Sopenharmony_ci * requires to perform a CAC first. 1075e5b75505Sopenharmony_ci */ 1076e5b75505Sopenharmony_ci skip_radar = 0; 1077e5b75505Sopenharmony_ci channel = dfs_downgrade_bandwidth(iface, &secondary_channel, 1078e5b75505Sopenharmony_ci &oper_centr_freq_seg0_idx, 1079e5b75505Sopenharmony_ci &oper_centr_freq_seg1_idx, 1080e5b75505Sopenharmony_ci &skip_radar); 1081e5b75505Sopenharmony_ci if (!channel) { 1082e5b75505Sopenharmony_ci /* 1083e5b75505Sopenharmony_ci * Toggle interface state to enter DFS state 1084e5b75505Sopenharmony_ci * until NOP is finished. 1085e5b75505Sopenharmony_ci */ 1086e5b75505Sopenharmony_ci hostapd_disable_iface(iface); 1087e5b75505Sopenharmony_ci hostapd_enable_iface(iface); 1088e5b75505Sopenharmony_ci return 0; 1089e5b75505Sopenharmony_ci } 1090e5b75505Sopenharmony_ci 1091e5b75505Sopenharmony_ci if (!skip_radar) { 1092e5b75505Sopenharmony_ci iface->freq = channel->freq; 1093e5b75505Sopenharmony_ci iface->conf->channel = channel->chan; 1094e5b75505Sopenharmony_ci iface->conf->secondary_channel = secondary_channel; 1095e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg0_idx( 1096e5b75505Sopenharmony_ci iface->conf, oper_centr_freq_seg0_idx); 1097e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg1_idx( 1098e5b75505Sopenharmony_ci iface->conf, oper_centr_freq_seg1_idx); 1099e5b75505Sopenharmony_ci 1100e5b75505Sopenharmony_ci hostapd_disable_iface(iface); 1101e5b75505Sopenharmony_ci hostapd_enable_iface(iface); 1102e5b75505Sopenharmony_ci return 0; 1103e5b75505Sopenharmony_ci } 1104e5b75505Sopenharmony_ci } 1105e5b75505Sopenharmony_ci 1106e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", 1107e5b75505Sopenharmony_ci channel->chan); 1108e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL 1109e5b75505Sopenharmony_ci "freq=%d chan=%d sec_chan=%d", channel->freq, 1110e5b75505Sopenharmony_ci channel->chan, secondary_channel); 1111e5b75505Sopenharmony_ci 1112e5b75505Sopenharmony_ci new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); 1113e5b75505Sopenharmony_ci hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth); 1114e5b75505Sopenharmony_ci 1115e5b75505Sopenharmony_ci /* Setup CSA request */ 1116e5b75505Sopenharmony_ci os_memset(&csa_settings, 0, sizeof(csa_settings)); 1117e5b75505Sopenharmony_ci csa_settings.cs_count = 5; 1118e5b75505Sopenharmony_ci csa_settings.block_tx = 1; 1119e5b75505Sopenharmony_ci#ifdef CONFIG_MESH 1120e5b75505Sopenharmony_ci if (iface->mconf) 1121e5b75505Sopenharmony_ci ieee80211_mode = IEEE80211_MODE_MESH; 1122e5b75505Sopenharmony_ci#endif /* CONFIG_MESH */ 1123e5b75505Sopenharmony_ci err = hostapd_set_freq_params(&csa_settings.freq_params, 1124e5b75505Sopenharmony_ci iface->conf->hw_mode, 1125e5b75505Sopenharmony_ci channel->freq, 1126e5b75505Sopenharmony_ci channel->chan, 1127e5b75505Sopenharmony_ci iface->conf->enable_edmg, 1128e5b75505Sopenharmony_ci iface->conf->edmg_channel, 1129e5b75505Sopenharmony_ci iface->conf->ieee80211n, 1130e5b75505Sopenharmony_ci iface->conf->ieee80211ac, 1131e5b75505Sopenharmony_ci iface->conf->ieee80211ax, 1132e5b75505Sopenharmony_ci secondary_channel, 1133e5b75505Sopenharmony_ci new_vht_oper_chwidth, 1134e5b75505Sopenharmony_ci oper_centr_freq_seg0_idx, 1135e5b75505Sopenharmony_ci oper_centr_freq_seg1_idx, 1136e5b75505Sopenharmony_ci cmode->vht_capab, 1137e5b75505Sopenharmony_ci &cmode->he_capab[ieee80211_mode]); 1138e5b75505Sopenharmony_ci 1139e5b75505Sopenharmony_ci if (err) { 1140e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); 1141e5b75505Sopenharmony_ci hostapd_disable_iface(iface); 1142e5b75505Sopenharmony_ci return err; 1143e5b75505Sopenharmony_ci } 1144e5b75505Sopenharmony_ci 1145e5b75505Sopenharmony_ci for (i = 0; i < iface->num_bss; i++) { 1146e5b75505Sopenharmony_ci err = hostapd_switch_channel(iface->bss[i], &csa_settings); 1147e5b75505Sopenharmony_ci if (err) 1148e5b75505Sopenharmony_ci break; 1149e5b75505Sopenharmony_ci } 1150e5b75505Sopenharmony_ci 1151e5b75505Sopenharmony_ci if (err) { 1152e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", 1153e5b75505Sopenharmony_ci err); 1154e5b75505Sopenharmony_ci iface->freq = channel->freq; 1155e5b75505Sopenharmony_ci iface->conf->channel = channel->chan; 1156e5b75505Sopenharmony_ci iface->conf->secondary_channel = secondary_channel; 1157e5b75505Sopenharmony_ci hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth); 1158e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 1159e5b75505Sopenharmony_ci oper_centr_freq_seg0_idx); 1160e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 1161e5b75505Sopenharmony_ci oper_centr_freq_seg1_idx); 1162e5b75505Sopenharmony_ci 1163e5b75505Sopenharmony_ci hostapd_disable_iface(iface); 1164e5b75505Sopenharmony_ci hostapd_enable_iface(iface); 1165e5b75505Sopenharmony_ci return 0; 1166e5b75505Sopenharmony_ci } 1167e5b75505Sopenharmony_ci 1168e5b75505Sopenharmony_ci /* Channel configuration will be updated once CSA completes and 1169e5b75505Sopenharmony_ci * ch_switch_notify event is received */ 1170e5b75505Sopenharmony_ci 1171e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); 1172e5b75505Sopenharmony_ci return 0; 1173e5b75505Sopenharmony_ci} 1174e5b75505Sopenharmony_ci 1175e5b75505Sopenharmony_ci 1176e5b75505Sopenharmony_ciint hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, 1177e5b75505Sopenharmony_ci int ht_enabled, int chan_offset, int chan_width, 1178e5b75505Sopenharmony_ci int cf1, int cf2) 1179e5b75505Sopenharmony_ci{ 1180e5b75505Sopenharmony_ci int res; 1181e5b75505Sopenharmony_ci 1182e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED 1183e5b75505Sopenharmony_ci "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 1184e5b75505Sopenharmony_ci freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 1185e5b75505Sopenharmony_ci 1186e5b75505Sopenharmony_ci /* Proceed only if DFS is not offloaded to the driver */ 1187e5b75505Sopenharmony_ci if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 1188e5b75505Sopenharmony_ci return 0; 1189e5b75505Sopenharmony_ci 1190e5b75505Sopenharmony_ci if (!iface->conf->ieee80211h) 1191e5b75505Sopenharmony_ci return 0; 1192e5b75505Sopenharmony_ci 1193e5b75505Sopenharmony_ci /* mark radar frequency as invalid */ 1194e5b75505Sopenharmony_ci res = set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 1195e5b75505Sopenharmony_ci cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE); 1196e5b75505Sopenharmony_ci if (!res) 1197e5b75505Sopenharmony_ci return 0; 1198e5b75505Sopenharmony_ci 1199e5b75505Sopenharmony_ci /* Skip if reported radar event not overlapped our channels */ 1200e5b75505Sopenharmony_ci res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); 1201e5b75505Sopenharmony_ci if (!res) 1202e5b75505Sopenharmony_ci return 0; 1203e5b75505Sopenharmony_ci 1204e5b75505Sopenharmony_ci /* radar detected while operating, switch the channel. */ 1205e5b75505Sopenharmony_ci res = hostapd_dfs_start_channel_switch(iface); 1206e5b75505Sopenharmony_ci 1207e5b75505Sopenharmony_ci return res; 1208e5b75505Sopenharmony_ci} 1209e5b75505Sopenharmony_ci 1210e5b75505Sopenharmony_ci 1211e5b75505Sopenharmony_ciint hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, 1212e5b75505Sopenharmony_ci int ht_enabled, int chan_offset, int chan_width, 1213e5b75505Sopenharmony_ci int cf1, int cf2) 1214e5b75505Sopenharmony_ci{ 1215e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED 1216e5b75505Sopenharmony_ci "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", 1217e5b75505Sopenharmony_ci freq, ht_enabled, chan_offset, chan_width, cf1, cf2); 1218e5b75505Sopenharmony_ci 1219e5b75505Sopenharmony_ci /* Proceed only if DFS is not offloaded to the driver */ 1220e5b75505Sopenharmony_ci if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) 1221e5b75505Sopenharmony_ci return 0; 1222e5b75505Sopenharmony_ci 1223e5b75505Sopenharmony_ci /* TODO add correct implementation here */ 1224e5b75505Sopenharmony_ci set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, 1225e5b75505Sopenharmony_ci cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); 1226e5b75505Sopenharmony_ci 1227e5b75505Sopenharmony_ci /* Handle cases where all channels were initially unavailable */ 1228e5b75505Sopenharmony_ci if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) 1229e5b75505Sopenharmony_ci hostapd_handle_dfs(iface); 1230e5b75505Sopenharmony_ci 1231e5b75505Sopenharmony_ci return 0; 1232e5b75505Sopenharmony_ci} 1233e5b75505Sopenharmony_ci 1234e5b75505Sopenharmony_ci 1235e5b75505Sopenharmony_ciint hostapd_is_dfs_required(struct hostapd_iface *iface) 1236e5b75505Sopenharmony_ci{ 1237e5b75505Sopenharmony_ci int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res; 1238e5b75505Sopenharmony_ci 1239e5b75505Sopenharmony_ci if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && 1240e5b75505Sopenharmony_ci !iface->conf->ieee80211h) || 1241e5b75505Sopenharmony_ci !iface->current_mode || 1242e5b75505Sopenharmony_ci iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) 1243e5b75505Sopenharmony_ci return 0; 1244e5b75505Sopenharmony_ci 1245e5b75505Sopenharmony_ci /* Get start (first) channel for current configuration */ 1246e5b75505Sopenharmony_ci start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); 1247e5b75505Sopenharmony_ci if (start_chan_idx == -1) 1248e5b75505Sopenharmony_ci return -1; 1249e5b75505Sopenharmony_ci 1250e5b75505Sopenharmony_ci /* Get number of used channels, depend on width */ 1251e5b75505Sopenharmony_ci n_chans = dfs_get_used_n_chans(iface, &n_chans1); 1252e5b75505Sopenharmony_ci 1253e5b75505Sopenharmony_ci /* Check if any of configured channels require DFS */ 1254e5b75505Sopenharmony_ci res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); 1255e5b75505Sopenharmony_ci if (res) 1256e5b75505Sopenharmony_ci return res; 1257e5b75505Sopenharmony_ci if (start_chan_idx1 >= 0 && n_chans1 > 0) 1258e5b75505Sopenharmony_ci res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1); 1259e5b75505Sopenharmony_ci return res; 1260e5b75505Sopenharmony_ci} 1261e5b75505Sopenharmony_ci 1262e5b75505Sopenharmony_ci 1263e5b75505Sopenharmony_ciint hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, 1264e5b75505Sopenharmony_ci int ht_enabled, int chan_offset, int chan_width, 1265e5b75505Sopenharmony_ci int cf1, int cf2) 1266e5b75505Sopenharmony_ci{ 1267e5b75505Sopenharmony_ci /* This is called when the driver indicates that an offloaded DFS has 1268e5b75505Sopenharmony_ci * started CAC. */ 1269e5b75505Sopenharmony_ci hostapd_set_state(iface, HAPD_IFACE_DFS); 1270e5b75505Sopenharmony_ci /* TODO: How to check CAC time for ETSI weather channels? */ 1271e5b75505Sopenharmony_ci iface->dfs_cac_ms = 60000; 1272e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START 1273e5b75505Sopenharmony_ci "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " 1274e5b75505Sopenharmony_ci "seg1=%d cac_time=%ds", 1275e5b75505Sopenharmony_ci freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 1276e5b75505Sopenharmony_ci iface->dfs_cac_ms / 1000); 1277e5b75505Sopenharmony_ci iface->cac_started = 1; 1278e5b75505Sopenharmony_ci os_get_reltime(&iface->dfs_cac_start); 1279e5b75505Sopenharmony_ci return 0; 1280e5b75505Sopenharmony_ci} 1281e5b75505Sopenharmony_ci 1282e5b75505Sopenharmony_ci 1283e5b75505Sopenharmony_ci/* 1284e5b75505Sopenharmony_ci * Main DFS handler for offloaded case. 1285e5b75505Sopenharmony_ci * 2 - continue channel/AP setup for non-DFS channel 1286e5b75505Sopenharmony_ci * 1 - continue channel/AP setup for DFS channel 1287e5b75505Sopenharmony_ci * 0 - channel/AP setup will be continued after CAC 1288e5b75505Sopenharmony_ci * -1 - hit critical error 1289e5b75505Sopenharmony_ci */ 1290e5b75505Sopenharmony_ciint hostapd_handle_dfs_offload(struct hostapd_iface *iface) 1291e5b75505Sopenharmony_ci{ 1292e5b75505Sopenharmony_ci int dfs_res; 1293e5b75505Sopenharmony_ci 1294e5b75505Sopenharmony_ci wpa_printf(MSG_EXCESSIVE, "%s: iface->cac_started: %d", 1295e5b75505Sopenharmony_ci __func__, iface->cac_started); 1296e5b75505Sopenharmony_ci 1297e5b75505Sopenharmony_ci /* 1298e5b75505Sopenharmony_ci * If DFS has already been started, then we are being called from a 1299e5b75505Sopenharmony_ci * callback to continue AP/channel setup. Reset the CAC start flag and 1300e5b75505Sopenharmony_ci * return. 1301e5b75505Sopenharmony_ci */ 1302e5b75505Sopenharmony_ci if (iface->cac_started) { 1303e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d", 1304e5b75505Sopenharmony_ci __func__, iface->cac_started); 1305e5b75505Sopenharmony_ci iface->cac_started = 0; 1306e5b75505Sopenharmony_ci return 1; 1307e5b75505Sopenharmony_ci } 1308e5b75505Sopenharmony_ci 1309e5b75505Sopenharmony_ci dfs_res = hostapd_is_dfs_required(iface); 1310e5b75505Sopenharmony_ci if (dfs_res > 0) { 1311e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1312e5b75505Sopenharmony_ci "%s: freq %d MHz requires DFS for %d chans", 1313e5b75505Sopenharmony_ci __func__, iface->freq, dfs_res); 1314e5b75505Sopenharmony_ci#ifdef CONFIG_P2P_160M 1315e5b75505Sopenharmony_ci/* if P2P 160M return 2 -continue channel/AP setup for non-DFS channel*/ 1316e5b75505Sopenharmony_ci return 2; 1317e5b75505Sopenharmony_ci#else 1318e5b75505Sopenharmony_ci return 0; 1319e5b75505Sopenharmony_ci#endif 1320e5b75505Sopenharmony_ci } 1321e5b75505Sopenharmony_ci 1322e5b75505Sopenharmony_ci wpa_printf(MSG_EXCESSIVE, 1323e5b75505Sopenharmony_ci "%s: freq %d MHz does not require DFS. Continue channel/AP setup", 1324e5b75505Sopenharmony_ci __func__, iface->freq); 1325e5b75505Sopenharmony_ci return 2; 1326e5b75505Sopenharmony_ci} 1327e5b75505Sopenharmony_ci 1328e5b75505Sopenharmony_ci 1329e5b75505Sopenharmony_ciint hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width, 1330e5b75505Sopenharmony_ci int center_freq) 1331e5b75505Sopenharmony_ci{ 1332e5b75505Sopenharmony_ci struct hostapd_channel_data *chan; 1333e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode = iface->current_mode; 1334e5b75505Sopenharmony_ci int half_width; 1335e5b75505Sopenharmony_ci int res = 0; 1336e5b75505Sopenharmony_ci int i; 1337e5b75505Sopenharmony_ci 1338e5b75505Sopenharmony_ci if (!iface->conf->ieee80211h || !mode || 1339e5b75505Sopenharmony_ci mode->mode != HOSTAPD_MODE_IEEE80211A) 1340e5b75505Sopenharmony_ci return 0; 1341e5b75505Sopenharmony_ci 1342e5b75505Sopenharmony_ci switch (width) { 1343e5b75505Sopenharmony_ci case CHAN_WIDTH_20_NOHT: 1344e5b75505Sopenharmony_ci case CHAN_WIDTH_20: 1345e5b75505Sopenharmony_ci half_width = 10; 1346e5b75505Sopenharmony_ci break; 1347e5b75505Sopenharmony_ci case CHAN_WIDTH_40: 1348e5b75505Sopenharmony_ci half_width = 20; 1349e5b75505Sopenharmony_ci break; 1350e5b75505Sopenharmony_ci case CHAN_WIDTH_80: 1351e5b75505Sopenharmony_ci case CHAN_WIDTH_80P80: 1352e5b75505Sopenharmony_ci half_width = 40; 1353e5b75505Sopenharmony_ci break; 1354e5b75505Sopenharmony_ci case CHAN_WIDTH_160: 1355e5b75505Sopenharmony_ci half_width = 80; 1356e5b75505Sopenharmony_ci break; 1357e5b75505Sopenharmony_ci default: 1358e5b75505Sopenharmony_ci wpa_printf(MSG_WARNING, "DFS chanwidth %d not supported", 1359e5b75505Sopenharmony_ci width); 1360e5b75505Sopenharmony_ci return 0; 1361e5b75505Sopenharmony_ci } 1362e5b75505Sopenharmony_ci 1363e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 1364e5b75505Sopenharmony_ci chan = &mode->channels[i]; 1365e5b75505Sopenharmony_ci 1366e5b75505Sopenharmony_ci if (!(chan->flag & HOSTAPD_CHAN_RADAR)) 1367e5b75505Sopenharmony_ci continue; 1368e5b75505Sopenharmony_ci 1369e5b75505Sopenharmony_ci if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == 1370e5b75505Sopenharmony_ci HOSTAPD_CHAN_DFS_AVAILABLE) 1371e5b75505Sopenharmony_ci continue; 1372e5b75505Sopenharmony_ci 1373e5b75505Sopenharmony_ci if (center_freq - chan->freq < half_width && 1374e5b75505Sopenharmony_ci chan->freq - center_freq < half_width) 1375e5b75505Sopenharmony_ci res++; 1376e5b75505Sopenharmony_ci } 1377e5b75505Sopenharmony_ci 1378e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s", 1379e5b75505Sopenharmony_ci center_freq - half_width, center_freq + half_width, 1380e5b75505Sopenharmony_ci res ? "yes" : "no"); 1381e5b75505Sopenharmony_ci 1382e5b75505Sopenharmony_ci return res; 1383e5b75505Sopenharmony_ci} 1384