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