1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * hostapd / Hardware feature query and different modes 3e5b75505Sopenharmony_ci * Copyright 2002-2003, Instant802 Networks, Inc. 4e5b75505Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc. 5e5b75505Sopenharmony_ci * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> 6e5b75505Sopenharmony_ci * 7e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 8e5b75505Sopenharmony_ci * See README for more details. 9e5b75505Sopenharmony_ci */ 10e5b75505Sopenharmony_ci 11e5b75505Sopenharmony_ci#include "utils/includes.h" 12e5b75505Sopenharmony_ci 13e5b75505Sopenharmony_ci#include "utils/common.h" 14e5b75505Sopenharmony_ci#include "utils/eloop.h" 15e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h" 16e5b75505Sopenharmony_ci#include "common/ieee802_11_common.h" 17e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h" 18e5b75505Sopenharmony_ci#include "common/hw_features_common.h" 19e5b75505Sopenharmony_ci#include "hostapd.h" 20e5b75505Sopenharmony_ci#include "ap_config.h" 21e5b75505Sopenharmony_ci#include "ap_drv_ops.h" 22e5b75505Sopenharmony_ci#include "acs.h" 23e5b75505Sopenharmony_ci#include "ieee802_11.h" 24e5b75505Sopenharmony_ci#include "beacon.h" 25e5b75505Sopenharmony_ci#include "hw_features.h" 26e5b75505Sopenharmony_ci 27e5b75505Sopenharmony_ci 28e5b75505Sopenharmony_civoid hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, 29e5b75505Sopenharmony_ci size_t num_hw_features) 30e5b75505Sopenharmony_ci{ 31e5b75505Sopenharmony_ci size_t i; 32e5b75505Sopenharmony_ci 33e5b75505Sopenharmony_ci if (hw_features == NULL) 34e5b75505Sopenharmony_ci return; 35e5b75505Sopenharmony_ci 36e5b75505Sopenharmony_ci for (i = 0; i < num_hw_features; i++) { 37e5b75505Sopenharmony_ci os_free(hw_features[i].channels); 38e5b75505Sopenharmony_ci os_free(hw_features[i].rates); 39e5b75505Sopenharmony_ci } 40e5b75505Sopenharmony_ci 41e5b75505Sopenharmony_ci os_free(hw_features); 42e5b75505Sopenharmony_ci} 43e5b75505Sopenharmony_ci 44e5b75505Sopenharmony_ci 45e5b75505Sopenharmony_ci#ifndef CONFIG_NO_STDOUT_DEBUG 46e5b75505Sopenharmony_cistatic char * dfs_info(struct hostapd_channel_data *chan) 47e5b75505Sopenharmony_ci{ 48e5b75505Sopenharmony_ci static char info[256]; 49e5b75505Sopenharmony_ci char *state; 50e5b75505Sopenharmony_ci 51e5b75505Sopenharmony_ci switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { 52e5b75505Sopenharmony_ci case HOSTAPD_CHAN_DFS_UNKNOWN: 53e5b75505Sopenharmony_ci state = "unknown"; 54e5b75505Sopenharmony_ci break; 55e5b75505Sopenharmony_ci case HOSTAPD_CHAN_DFS_USABLE: 56e5b75505Sopenharmony_ci state = "usable"; 57e5b75505Sopenharmony_ci break; 58e5b75505Sopenharmony_ci case HOSTAPD_CHAN_DFS_UNAVAILABLE: 59e5b75505Sopenharmony_ci state = "unavailable"; 60e5b75505Sopenharmony_ci break; 61e5b75505Sopenharmony_ci case HOSTAPD_CHAN_DFS_AVAILABLE: 62e5b75505Sopenharmony_ci state = "available"; 63e5b75505Sopenharmony_ci break; 64e5b75505Sopenharmony_ci default: 65e5b75505Sopenharmony_ci return ""; 66e5b75505Sopenharmony_ci } 67e5b75505Sopenharmony_ci os_snprintf(info, sizeof(info), " (DFS state = %s)", state); 68e5b75505Sopenharmony_ci info[sizeof(info) - 1] = '\0'; 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_ci return info; 71e5b75505Sopenharmony_ci} 72e5b75505Sopenharmony_ci#endif /* CONFIG_NO_STDOUT_DEBUG */ 73e5b75505Sopenharmony_ci 74e5b75505Sopenharmony_ci 75e5b75505Sopenharmony_ciint hostapd_get_hw_features(struct hostapd_iface *iface) 76e5b75505Sopenharmony_ci{ 77e5b75505Sopenharmony_ci struct hostapd_data *hapd = iface->bss[0]; 78e5b75505Sopenharmony_ci int i, j; 79e5b75505Sopenharmony_ci u16 num_modes, flags; 80e5b75505Sopenharmony_ci struct hostapd_hw_modes *modes; 81e5b75505Sopenharmony_ci u8 dfs_domain; 82e5b75505Sopenharmony_ci 83e5b75505Sopenharmony_ci if (hostapd_drv_none(hapd)) 84e5b75505Sopenharmony_ci return -1; 85e5b75505Sopenharmony_ci modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags, 86e5b75505Sopenharmony_ci &dfs_domain); 87e5b75505Sopenharmony_ci if (modes == NULL) { 88e5b75505Sopenharmony_ci hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, 89e5b75505Sopenharmony_ci HOSTAPD_LEVEL_DEBUG, 90e5b75505Sopenharmony_ci "Fetching hardware channel/rate support not " 91e5b75505Sopenharmony_ci "supported."); 92e5b75505Sopenharmony_ci return -1; 93e5b75505Sopenharmony_ci } 94e5b75505Sopenharmony_ci 95e5b75505Sopenharmony_ci iface->hw_flags = flags; 96e5b75505Sopenharmony_ci iface->dfs_domain = dfs_domain; 97e5b75505Sopenharmony_ci 98e5b75505Sopenharmony_ci hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); 99e5b75505Sopenharmony_ci iface->hw_features = modes; 100e5b75505Sopenharmony_ci iface->num_hw_features = num_modes; 101e5b75505Sopenharmony_ci 102e5b75505Sopenharmony_ci for (i = 0; i < num_modes; i++) { 103e5b75505Sopenharmony_ci struct hostapd_hw_modes *feature = &modes[i]; 104e5b75505Sopenharmony_ci int dfs_enabled = hapd->iconf->ieee80211h && 105e5b75505Sopenharmony_ci (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); 106e5b75505Sopenharmony_ci 107e5b75505Sopenharmony_ci /* set flag for channels we can use in current regulatory 108e5b75505Sopenharmony_ci * domain */ 109e5b75505Sopenharmony_ci for (j = 0; j < feature->num_channels; j++) { 110e5b75505Sopenharmony_ci int dfs = 0; 111e5b75505Sopenharmony_ci 112e5b75505Sopenharmony_ci /* 113e5b75505Sopenharmony_ci * Disable all channels that are marked not to allow 114e5b75505Sopenharmony_ci * to initiate radiation (a.k.a. passive scan and no 115e5b75505Sopenharmony_ci * IBSS). 116e5b75505Sopenharmony_ci * Use radar channels only if the driver supports DFS. 117e5b75505Sopenharmony_ci */ 118e5b75505Sopenharmony_ci if ((feature->channels[j].flag & 119e5b75505Sopenharmony_ci HOSTAPD_CHAN_RADAR) && dfs_enabled) { 120e5b75505Sopenharmony_ci dfs = 1; 121e5b75505Sopenharmony_ci } else if (((feature->channels[j].flag & 122e5b75505Sopenharmony_ci HOSTAPD_CHAN_RADAR) && 123e5b75505Sopenharmony_ci !(iface->drv_flags & 124e5b75505Sopenharmony_ci WPA_DRIVER_FLAGS_DFS_OFFLOAD)) || 125e5b75505Sopenharmony_ci (feature->channels[j].flag & 126e5b75505Sopenharmony_ci HOSTAPD_CHAN_NO_IR)) { 127e5b75505Sopenharmony_ci feature->channels[j].flag |= 128e5b75505Sopenharmony_ci HOSTAPD_CHAN_DISABLED; 129e5b75505Sopenharmony_ci } 130e5b75505Sopenharmony_ci 131e5b75505Sopenharmony_ci if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) 132e5b75505Sopenharmony_ci continue; 133e5b75505Sopenharmony_ci 134e5b75505Sopenharmony_ci wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " 135e5b75505Sopenharmony_ci "chan=%d freq=%d MHz max_tx_power=%d dBm%s", 136e5b75505Sopenharmony_ci feature->mode, 137e5b75505Sopenharmony_ci feature->channels[j].chan, 138e5b75505Sopenharmony_ci feature->channels[j].freq, 139e5b75505Sopenharmony_ci feature->channels[j].max_tx_power, 140e5b75505Sopenharmony_ci dfs ? dfs_info(&feature->channels[j]) : ""); 141e5b75505Sopenharmony_ci } 142e5b75505Sopenharmony_ci } 143e5b75505Sopenharmony_ci 144e5b75505Sopenharmony_ci return 0; 145e5b75505Sopenharmony_ci} 146e5b75505Sopenharmony_ci 147e5b75505Sopenharmony_ci 148e5b75505Sopenharmony_ciint hostapd_prepare_rates(struct hostapd_iface *iface, 149e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode) 150e5b75505Sopenharmony_ci{ 151e5b75505Sopenharmony_ci int i, num_basic_rates = 0; 152e5b75505Sopenharmony_ci int basic_rates_a[] = { 60, 120, 240, -1 }; 153e5b75505Sopenharmony_ci int basic_rates_b[] = { 10, 20, -1 }; 154e5b75505Sopenharmony_ci int basic_rates_g[] = { 10, 20, 55, 110, -1 }; 155e5b75505Sopenharmony_ci int *basic_rates; 156e5b75505Sopenharmony_ci 157e5b75505Sopenharmony_ci if (iface->conf->basic_rates) 158e5b75505Sopenharmony_ci basic_rates = iface->conf->basic_rates; 159e5b75505Sopenharmony_ci else switch (mode->mode) { 160e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211A: 161e5b75505Sopenharmony_ci basic_rates = basic_rates_a; 162e5b75505Sopenharmony_ci break; 163e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211B: 164e5b75505Sopenharmony_ci basic_rates = basic_rates_b; 165e5b75505Sopenharmony_ci break; 166e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211G: 167e5b75505Sopenharmony_ci basic_rates = basic_rates_g; 168e5b75505Sopenharmony_ci break; 169e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211AD: 170e5b75505Sopenharmony_ci return 0; /* No basic rates for 11ad */ 171e5b75505Sopenharmony_ci default: 172e5b75505Sopenharmony_ci return -1; 173e5b75505Sopenharmony_ci } 174e5b75505Sopenharmony_ci 175e5b75505Sopenharmony_ci i = 0; 176e5b75505Sopenharmony_ci while (basic_rates[i] >= 0) 177e5b75505Sopenharmony_ci i++; 178e5b75505Sopenharmony_ci if (i) 179e5b75505Sopenharmony_ci i++; /* -1 termination */ 180e5b75505Sopenharmony_ci os_free(iface->basic_rates); 181e5b75505Sopenharmony_ci iface->basic_rates = os_malloc(i * sizeof(int)); 182e5b75505Sopenharmony_ci if (iface->basic_rates) 183e5b75505Sopenharmony_ci os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); 184e5b75505Sopenharmony_ci 185e5b75505Sopenharmony_ci os_free(iface->current_rates); 186e5b75505Sopenharmony_ci iface->num_rates = 0; 187e5b75505Sopenharmony_ci 188e5b75505Sopenharmony_ci iface->current_rates = 189e5b75505Sopenharmony_ci os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); 190e5b75505Sopenharmony_ci if (!iface->current_rates) { 191e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " 192e5b75505Sopenharmony_ci "table."); 193e5b75505Sopenharmony_ci return -1; 194e5b75505Sopenharmony_ci } 195e5b75505Sopenharmony_ci 196e5b75505Sopenharmony_ci for (i = 0; i < mode->num_rates; i++) { 197e5b75505Sopenharmony_ci struct hostapd_rate_data *rate; 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_ci if (iface->conf->supported_rates && 200e5b75505Sopenharmony_ci !hostapd_rate_found(iface->conf->supported_rates, 201e5b75505Sopenharmony_ci mode->rates[i])) 202e5b75505Sopenharmony_ci continue; 203e5b75505Sopenharmony_ci 204e5b75505Sopenharmony_ci rate = &iface->current_rates[iface->num_rates]; 205e5b75505Sopenharmony_ci rate->rate = mode->rates[i]; 206e5b75505Sopenharmony_ci if (hostapd_rate_found(basic_rates, rate->rate)) { 207e5b75505Sopenharmony_ci rate->flags |= HOSTAPD_RATE_BASIC; 208e5b75505Sopenharmony_ci num_basic_rates++; 209e5b75505Sopenharmony_ci } 210e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", 211e5b75505Sopenharmony_ci iface->num_rates, rate->rate, rate->flags); 212e5b75505Sopenharmony_ci iface->num_rates++; 213e5b75505Sopenharmony_ci } 214e5b75505Sopenharmony_ci 215e5b75505Sopenharmony_ci if ((iface->num_rates == 0 || num_basic_rates == 0) && 216e5b75505Sopenharmony_ci (!iface->conf->ieee80211n || !iface->conf->require_ht)) { 217e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " 218e5b75505Sopenharmony_ci "rate sets (%d,%d).", 219e5b75505Sopenharmony_ci iface->num_rates, num_basic_rates); 220e5b75505Sopenharmony_ci return -1; 221e5b75505Sopenharmony_ci } 222e5b75505Sopenharmony_ci 223e5b75505Sopenharmony_ci return 0; 224e5b75505Sopenharmony_ci} 225e5b75505Sopenharmony_ci 226e5b75505Sopenharmony_ci 227e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211N 228e5b75505Sopenharmony_cistatic int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) 229e5b75505Sopenharmony_ci{ 230e5b75505Sopenharmony_ci int pri_chan, sec_chan; 231e5b75505Sopenharmony_ci 232e5b75505Sopenharmony_ci#ifdef CONFIG_OHOS_P2P 233e5b75505Sopenharmony_ci if (!iface->conf->secondary_channel) { 234e5b75505Sopenharmony_ci return 1; // HT40 not used. 235e5b75505Sopenharmony_ci } 236e5b75505Sopenharmony_ci#endif 237e5b75505Sopenharmony_ci 238e5b75505Sopenharmony_ci pri_chan = iface->conf->channel; 239e5b75505Sopenharmony_ci sec_chan = pri_chan + iface->conf->secondary_channel * 4; 240e5b75505Sopenharmony_ci 241e5b75505Sopenharmony_ci return allowed_ht40_channel_pair(iface->current_mode, pri_chan, 242e5b75505Sopenharmony_ci sec_chan); 243e5b75505Sopenharmony_ci} 244e5b75505Sopenharmony_ci 245e5b75505Sopenharmony_ci 246e5b75505Sopenharmony_cistatic void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) 247e5b75505Sopenharmony_ci{ 248e5b75505Sopenharmony_ci if (iface->conf->secondary_channel > 0) { 249e5b75505Sopenharmony_ci iface->conf->channel += 4; 250e5b75505Sopenharmony_ci iface->conf->secondary_channel = -1; 251e5b75505Sopenharmony_ci } else { 252e5b75505Sopenharmony_ci iface->conf->channel -= 4; 253e5b75505Sopenharmony_ci iface->conf->secondary_channel = 1; 254e5b75505Sopenharmony_ci } 255e5b75505Sopenharmony_ci} 256e5b75505Sopenharmony_ci 257e5b75505Sopenharmony_ci 258e5b75505Sopenharmony_cistatic int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, 259e5b75505Sopenharmony_ci struct wpa_scan_results *scan_res) 260e5b75505Sopenharmony_ci{ 261e5b75505Sopenharmony_ci int pri_chan, sec_chan; 262e5b75505Sopenharmony_ci int res; 263e5b75505Sopenharmony_ci 264e5b75505Sopenharmony_ci pri_chan = iface->conf->channel; 265e5b75505Sopenharmony_ci sec_chan = pri_chan + iface->conf->secondary_channel * 4; 266e5b75505Sopenharmony_ci 267e5b75505Sopenharmony_ci res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan); 268e5b75505Sopenharmony_ci 269e5b75505Sopenharmony_ci if (res == 2) { 270e5b75505Sopenharmony_ci if (iface->conf->no_pri_sec_switch) { 271e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 272e5b75505Sopenharmony_ci "Cannot switch PRI/SEC channels due to local constraint"); 273e5b75505Sopenharmony_ci } else { 274e5b75505Sopenharmony_ci ieee80211n_switch_pri_sec(iface); 275e5b75505Sopenharmony_ci } 276e5b75505Sopenharmony_ci } 277e5b75505Sopenharmony_ci 278e5b75505Sopenharmony_ci return !!res; 279e5b75505Sopenharmony_ci} 280e5b75505Sopenharmony_ci 281e5b75505Sopenharmony_ci 282e5b75505Sopenharmony_cistatic int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, 283e5b75505Sopenharmony_ci struct wpa_scan_results *scan_res) 284e5b75505Sopenharmony_ci{ 285e5b75505Sopenharmony_ci int pri_chan, sec_chan; 286e5b75505Sopenharmony_ci 287e5b75505Sopenharmony_ci pri_chan = iface->conf->channel; 288e5b75505Sopenharmony_ci sec_chan = pri_chan + iface->conf->secondary_channel * 4; 289e5b75505Sopenharmony_ci 290e5b75505Sopenharmony_ci return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan, 291e5b75505Sopenharmony_ci sec_chan); 292e5b75505Sopenharmony_ci} 293e5b75505Sopenharmony_ci 294e5b75505Sopenharmony_ci 295e5b75505Sopenharmony_cistatic void ieee80211n_check_scan(struct hostapd_iface *iface) 296e5b75505Sopenharmony_ci{ 297e5b75505Sopenharmony_ci struct wpa_scan_results *scan_res; 298e5b75505Sopenharmony_ci int oper40; 299e5b75505Sopenharmony_ci int res; 300e5b75505Sopenharmony_ci 301e5b75505Sopenharmony_ci /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is 302e5b75505Sopenharmony_ci * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ 303e5b75505Sopenharmony_ci 304e5b75505Sopenharmony_ci iface->scan_cb = NULL; 305e5b75505Sopenharmony_ci 306e5b75505Sopenharmony_ci scan_res = hostapd_driver_get_scan_results(iface->bss[0]); 307e5b75505Sopenharmony_ci if (scan_res == NULL) { 308e5b75505Sopenharmony_ci hostapd_setup_interface_complete(iface, 1); 309e5b75505Sopenharmony_ci return; 310e5b75505Sopenharmony_ci } 311e5b75505Sopenharmony_ci 312e5b75505Sopenharmony_ci if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) 313e5b75505Sopenharmony_ci oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); 314e5b75505Sopenharmony_ci else 315e5b75505Sopenharmony_ci oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); 316e5b75505Sopenharmony_ci wpa_scan_results_free(scan_res); 317e5b75505Sopenharmony_ci 318e5b75505Sopenharmony_ci iface->secondary_ch = iface->conf->secondary_channel; 319e5b75505Sopenharmony_ci if (!oper40) { 320e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " 321e5b75505Sopenharmony_ci "channel pri=%d sec=%d based on overlapping BSSes", 322e5b75505Sopenharmony_ci iface->conf->channel, 323e5b75505Sopenharmony_ci iface->conf->channel + 324e5b75505Sopenharmony_ci iface->conf->secondary_channel * 4); 325e5b75505Sopenharmony_ci iface->conf->secondary_channel = 0; 326e5b75505Sopenharmony_ci if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) { 327e5b75505Sopenharmony_ci /* 328e5b75505Sopenharmony_ci * TODO: Could consider scheduling another scan to check 329e5b75505Sopenharmony_ci * if channel width can be changed if no coex reports 330e5b75505Sopenharmony_ci * are received from associating stations. 331e5b75505Sopenharmony_ci */ 332e5b75505Sopenharmony_ci } 333e5b75505Sopenharmony_ci } 334e5b75505Sopenharmony_ci 335e5b75505Sopenharmony_ci res = ieee80211n_allowed_ht40_channel_pair(iface); 336e5b75505Sopenharmony_ci if (!res) { 337e5b75505Sopenharmony_ci iface->conf->secondary_channel = 0; 338e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0); 339e5b75505Sopenharmony_ci hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0); 340e5b75505Sopenharmony_ci hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT); 341e5b75505Sopenharmony_ci res = 1; 342e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Fallback to 20 MHz"); 343e5b75505Sopenharmony_ci } 344e5b75505Sopenharmony_ci 345e5b75505Sopenharmony_ci hostapd_setup_interface_complete(iface, !res); 346e5b75505Sopenharmony_ci} 347e5b75505Sopenharmony_ci 348e5b75505Sopenharmony_ci 349e5b75505Sopenharmony_cistatic void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, 350e5b75505Sopenharmony_ci struct wpa_driver_scan_params *params) 351e5b75505Sopenharmony_ci{ 352e5b75505Sopenharmony_ci /* Scan only the affected frequency range */ 353e5b75505Sopenharmony_ci int pri_freq, sec_freq; 354e5b75505Sopenharmony_ci int affected_start, affected_end; 355e5b75505Sopenharmony_ci int i, pos; 356e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 357e5b75505Sopenharmony_ci 358e5b75505Sopenharmony_ci if (iface->current_mode == NULL) 359e5b75505Sopenharmony_ci return; 360e5b75505Sopenharmony_ci 361e5b75505Sopenharmony_ci pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 362e5b75505Sopenharmony_ci if (iface->conf->secondary_channel > 0) 363e5b75505Sopenharmony_ci sec_freq = pri_freq + 20; 364e5b75505Sopenharmony_ci else 365e5b75505Sopenharmony_ci sec_freq = pri_freq - 20; 366e5b75505Sopenharmony_ci /* 367e5b75505Sopenharmony_ci * Note: Need to find the PRI channel also in cases where the affected 368e5b75505Sopenharmony_ci * channel is the SEC channel of a 40 MHz BSS, so need to include the 369e5b75505Sopenharmony_ci * scanning coverage here to be 40 MHz from the center frequency. 370e5b75505Sopenharmony_ci */ 371e5b75505Sopenharmony_ci affected_start = (pri_freq + sec_freq) / 2 - 40; 372e5b75505Sopenharmony_ci affected_end = (pri_freq + sec_freq) / 2 + 40; 373e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 374e5b75505Sopenharmony_ci affected_start, affected_end); 375e5b75505Sopenharmony_ci 376e5b75505Sopenharmony_ci mode = iface->current_mode; 377e5b75505Sopenharmony_ci params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); 378e5b75505Sopenharmony_ci if (params->freqs == NULL) 379e5b75505Sopenharmony_ci return; 380e5b75505Sopenharmony_ci pos = 0; 381e5b75505Sopenharmony_ci 382e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 383e5b75505Sopenharmony_ci struct hostapd_channel_data *chan = &mode->channels[i]; 384e5b75505Sopenharmony_ci if (chan->flag & HOSTAPD_CHAN_DISABLED) 385e5b75505Sopenharmony_ci continue; 386e5b75505Sopenharmony_ci if (chan->freq < affected_start || 387e5b75505Sopenharmony_ci chan->freq > affected_end) 388e5b75505Sopenharmony_ci continue; 389e5b75505Sopenharmony_ci params->freqs[pos++] = chan->freq; 390e5b75505Sopenharmony_ci } 391e5b75505Sopenharmony_ci} 392e5b75505Sopenharmony_ci 393e5b75505Sopenharmony_ci 394e5b75505Sopenharmony_cistatic void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, 395e5b75505Sopenharmony_ci struct wpa_driver_scan_params *params) 396e5b75505Sopenharmony_ci{ 397e5b75505Sopenharmony_ci /* Scan only the affected frequency range */ 398e5b75505Sopenharmony_ci int pri_freq; 399e5b75505Sopenharmony_ci int affected_start, affected_end; 400e5b75505Sopenharmony_ci int i, pos; 401e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 402e5b75505Sopenharmony_ci 403e5b75505Sopenharmony_ci if (iface->current_mode == NULL) 404e5b75505Sopenharmony_ci return; 405e5b75505Sopenharmony_ci 406e5b75505Sopenharmony_ci pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 407e5b75505Sopenharmony_ci if (iface->conf->secondary_channel > 0) { 408e5b75505Sopenharmony_ci affected_start = pri_freq - 10; 409e5b75505Sopenharmony_ci affected_end = pri_freq + 30; 410e5b75505Sopenharmony_ci } else { 411e5b75505Sopenharmony_ci affected_start = pri_freq - 30; 412e5b75505Sopenharmony_ci affected_end = pri_freq + 10; 413e5b75505Sopenharmony_ci } 414e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 415e5b75505Sopenharmony_ci affected_start, affected_end); 416e5b75505Sopenharmony_ci 417e5b75505Sopenharmony_ci mode = iface->current_mode; 418e5b75505Sopenharmony_ci params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); 419e5b75505Sopenharmony_ci if (params->freqs == NULL) 420e5b75505Sopenharmony_ci return; 421e5b75505Sopenharmony_ci pos = 0; 422e5b75505Sopenharmony_ci 423e5b75505Sopenharmony_ci for (i = 0; i < mode->num_channels; i++) { 424e5b75505Sopenharmony_ci struct hostapd_channel_data *chan = &mode->channels[i]; 425e5b75505Sopenharmony_ci if (chan->flag & HOSTAPD_CHAN_DISABLED) 426e5b75505Sopenharmony_ci continue; 427e5b75505Sopenharmony_ci if (chan->freq < affected_start || 428e5b75505Sopenharmony_ci chan->freq > affected_end) 429e5b75505Sopenharmony_ci continue; 430e5b75505Sopenharmony_ci params->freqs[pos++] = chan->freq; 431e5b75505Sopenharmony_ci } 432e5b75505Sopenharmony_ci} 433e5b75505Sopenharmony_ci 434e5b75505Sopenharmony_ci 435e5b75505Sopenharmony_cistatic void ap_ht40_scan_retry(void *eloop_data, void *user_data) 436e5b75505Sopenharmony_ci{ 437e5b75505Sopenharmony_ci#define HT2040_COEX_SCAN_RETRY 15 438e5b75505Sopenharmony_ci struct hostapd_iface *iface = eloop_data; 439e5b75505Sopenharmony_ci struct wpa_driver_scan_params params; 440e5b75505Sopenharmony_ci int ret; 441e5b75505Sopenharmony_ci 442e5b75505Sopenharmony_ci os_memset(¶ms, 0, sizeof(params)); 443e5b75505Sopenharmony_ci if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) 444e5b75505Sopenharmony_ci ieee80211n_scan_channels_2g4(iface, ¶ms); 445e5b75505Sopenharmony_ci else 446e5b75505Sopenharmony_ci ieee80211n_scan_channels_5g(iface, ¶ms); 447e5b75505Sopenharmony_ci 448e5b75505Sopenharmony_ci ret = hostapd_driver_scan(iface->bss[0], ¶ms); 449e5b75505Sopenharmony_ci iface->num_ht40_scan_tries++; 450e5b75505Sopenharmony_ci os_free(params.freqs); 451e5b75505Sopenharmony_ci 452e5b75505Sopenharmony_ci if (ret == -EBUSY && 453e5b75505Sopenharmony_ci iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) { 454e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 455e5b75505Sopenharmony_ci "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)", 456e5b75505Sopenharmony_ci ret, strerror(-ret), iface->num_ht40_scan_tries); 457e5b75505Sopenharmony_ci eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); 458e5b75505Sopenharmony_ci return; 459e5b75505Sopenharmony_ci } 460e5b75505Sopenharmony_ci 461e5b75505Sopenharmony_ci if (ret == 0) { 462e5b75505Sopenharmony_ci iface->scan_cb = ieee80211n_check_scan; 463e5b75505Sopenharmony_ci return; 464e5b75505Sopenharmony_ci } 465e5b75505Sopenharmony_ci 466e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 467e5b75505Sopenharmony_ci "Failed to request a scan in device, bringing up in HT20 mode"); 468e5b75505Sopenharmony_ci iface->conf->secondary_channel = 0; 469e5b75505Sopenharmony_ci iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; 470e5b75505Sopenharmony_ci hostapd_setup_interface_complete(iface, 0); 471e5b75505Sopenharmony_ci} 472e5b75505Sopenharmony_ci 473e5b75505Sopenharmony_ci 474e5b75505Sopenharmony_civoid hostapd_stop_setup_timers(struct hostapd_iface *iface) 475e5b75505Sopenharmony_ci{ 476e5b75505Sopenharmony_ci eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); 477e5b75505Sopenharmony_ci} 478e5b75505Sopenharmony_ci 479e5b75505Sopenharmony_ci 480e5b75505Sopenharmony_cistatic int ieee80211n_check_40mhz(struct hostapd_iface *iface) 481e5b75505Sopenharmony_ci{ 482e5b75505Sopenharmony_ci struct wpa_driver_scan_params params; 483e5b75505Sopenharmony_ci int ret; 484e5b75505Sopenharmony_ci 485e5b75505Sopenharmony_ci /* Check that HT40 is used and PRI / SEC switch is allowed */ 486e5b75505Sopenharmony_ci if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch) 487e5b75505Sopenharmony_ci return 0; 488e5b75505Sopenharmony_ci 489e5b75505Sopenharmony_ci hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); 490e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " 491e5b75505Sopenharmony_ci "40 MHz channel"); 492e5b75505Sopenharmony_ci os_memset(¶ms, 0, sizeof(params)); 493e5b75505Sopenharmony_ci if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) 494e5b75505Sopenharmony_ci ieee80211n_scan_channels_2g4(iface, ¶ms); 495e5b75505Sopenharmony_ci else 496e5b75505Sopenharmony_ci ieee80211n_scan_channels_5g(iface, ¶ms); 497e5b75505Sopenharmony_ci 498e5b75505Sopenharmony_ci ret = hostapd_driver_scan(iface->bss[0], ¶ms); 499e5b75505Sopenharmony_ci os_free(params.freqs); 500e5b75505Sopenharmony_ci 501e5b75505Sopenharmony_ci if (ret == -EBUSY) { 502e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 503e5b75505Sopenharmony_ci "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again", 504e5b75505Sopenharmony_ci ret, strerror(-ret)); 505e5b75505Sopenharmony_ci iface->num_ht40_scan_tries = 1; 506e5b75505Sopenharmony_ci eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); 507e5b75505Sopenharmony_ci eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); 508e5b75505Sopenharmony_ci return 1; 509e5b75505Sopenharmony_ci } 510e5b75505Sopenharmony_ci 511e5b75505Sopenharmony_ci if (ret < 0) { 512e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 513e5b75505Sopenharmony_ci "Failed to request a scan of neighboring BSSes ret=%d (%s)", 514e5b75505Sopenharmony_ci ret, strerror(-ret)); 515e5b75505Sopenharmony_ci return -1; 516e5b75505Sopenharmony_ci } 517e5b75505Sopenharmony_ci 518e5b75505Sopenharmony_ci iface->scan_cb = ieee80211n_check_scan; 519e5b75505Sopenharmony_ci return 1; 520e5b75505Sopenharmony_ci} 521e5b75505Sopenharmony_ci 522e5b75505Sopenharmony_ci 523e5b75505Sopenharmony_cistatic int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) 524e5b75505Sopenharmony_ci{ 525e5b75505Sopenharmony_ci u16 hw = iface->current_mode->ht_capab; 526e5b75505Sopenharmony_ci u16 conf = iface->conf->ht_capab; 527e5b75505Sopenharmony_ci 528e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && 529e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { 530e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 531e5b75505Sopenharmony_ci "HT capability [LDPC]"); 532e5b75505Sopenharmony_ci return 0; 533e5b75505Sopenharmony_ci } 534e5b75505Sopenharmony_ci 535e5b75505Sopenharmony_ci /* 536e5b75505Sopenharmony_ci * Driver ACS chosen channel may not be HT40 due to internal driver 537e5b75505Sopenharmony_ci * restrictions. 538e5b75505Sopenharmony_ci */ 539e5b75505Sopenharmony_ci if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 540e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { 541e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 542e5b75505Sopenharmony_ci "HT capability [HT40*]"); 543e5b75505Sopenharmony_ci return 0; 544e5b75505Sopenharmony_ci } 545e5b75505Sopenharmony_ci 546e5b75505Sopenharmony_ci switch (conf & HT_CAP_INFO_SMPS_MASK) { 547e5b75505Sopenharmony_ci case HT_CAP_INFO_SMPS_STATIC: 548e5b75505Sopenharmony_ci if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) { 549e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 550e5b75505Sopenharmony_ci "Driver does not support configured HT capability [SMPS-STATIC]"); 551e5b75505Sopenharmony_ci return 0; 552e5b75505Sopenharmony_ci } 553e5b75505Sopenharmony_ci break; 554e5b75505Sopenharmony_ci case HT_CAP_INFO_SMPS_DYNAMIC: 555e5b75505Sopenharmony_ci if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) { 556e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 557e5b75505Sopenharmony_ci "Driver does not support configured HT capability [SMPS-DYNAMIC]"); 558e5b75505Sopenharmony_ci return 0; 559e5b75505Sopenharmony_ci } 560e5b75505Sopenharmony_ci break; 561e5b75505Sopenharmony_ci case HT_CAP_INFO_SMPS_DISABLED: 562e5b75505Sopenharmony_ci default: 563e5b75505Sopenharmony_ci break; 564e5b75505Sopenharmony_ci } 565e5b75505Sopenharmony_ci 566e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_GREEN_FIELD) && 567e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_GREEN_FIELD)) { 568e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 569e5b75505Sopenharmony_ci "HT capability [GF]"); 570e5b75505Sopenharmony_ci return 0; 571e5b75505Sopenharmony_ci } 572e5b75505Sopenharmony_ci 573e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && 574e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { 575e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 576e5b75505Sopenharmony_ci "HT capability [SHORT-GI-20]"); 577e5b75505Sopenharmony_ci return 0; 578e5b75505Sopenharmony_ci } 579e5b75505Sopenharmony_ci 580e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && 581e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { 582e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 583e5b75505Sopenharmony_ci "HT capability [SHORT-GI-40]"); 584e5b75505Sopenharmony_ci return 0; 585e5b75505Sopenharmony_ci } 586e5b75505Sopenharmony_ci 587e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { 588e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 589e5b75505Sopenharmony_ci "HT capability [TX-STBC]"); 590e5b75505Sopenharmony_ci return 0; 591e5b75505Sopenharmony_ci } 592e5b75505Sopenharmony_ci 593e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_RX_STBC_MASK) > 594e5b75505Sopenharmony_ci (hw & HT_CAP_INFO_RX_STBC_MASK)) { 595e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 596e5b75505Sopenharmony_ci "HT capability [RX-STBC*]"); 597e5b75505Sopenharmony_ci return 0; 598e5b75505Sopenharmony_ci } 599e5b75505Sopenharmony_ci 600e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_DELAYED_BA) && 601e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_DELAYED_BA)) { 602e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 603e5b75505Sopenharmony_ci "HT capability [DELAYED-BA]"); 604e5b75505Sopenharmony_ci return 0; 605e5b75505Sopenharmony_ci } 606e5b75505Sopenharmony_ci 607e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && 608e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { 609e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 610e5b75505Sopenharmony_ci "HT capability [MAX-AMSDU-7935]"); 611e5b75505Sopenharmony_ci return 0; 612e5b75505Sopenharmony_ci } 613e5b75505Sopenharmony_ci 614e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && 615e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { 616e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 617e5b75505Sopenharmony_ci "HT capability [DSSS_CCK-40]"); 618e5b75505Sopenharmony_ci return 0; 619e5b75505Sopenharmony_ci } 620e5b75505Sopenharmony_ci 621e5b75505Sopenharmony_ci if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && 622e5b75505Sopenharmony_ci !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { 623e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Driver does not support configured " 624e5b75505Sopenharmony_ci "HT capability [LSIG-TXOP-PROT]"); 625e5b75505Sopenharmony_ci return 0; 626e5b75505Sopenharmony_ci } 627e5b75505Sopenharmony_ci 628e5b75505Sopenharmony_ci return 1; 629e5b75505Sopenharmony_ci} 630e5b75505Sopenharmony_ci 631e5b75505Sopenharmony_ci 632e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211AC 633e5b75505Sopenharmony_cistatic int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) 634e5b75505Sopenharmony_ci{ 635e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode = iface->current_mode; 636e5b75505Sopenharmony_ci u32 hw = mode->vht_capab; 637e5b75505Sopenharmony_ci u32 conf = iface->conf->vht_capab; 638e5b75505Sopenharmony_ci 639e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", 640e5b75505Sopenharmony_ci hw, conf); 641e5b75505Sopenharmony_ci 642e5b75505Sopenharmony_ci if (mode->mode == HOSTAPD_MODE_IEEE80211G && 643e5b75505Sopenharmony_ci iface->conf->bss[0]->vendor_vht && 644e5b75505Sopenharmony_ci mode->vht_capab == 0 && iface->hw_features) { 645e5b75505Sopenharmony_ci int i; 646e5b75505Sopenharmony_ci 647e5b75505Sopenharmony_ci for (i = 0; i < iface->num_hw_features; i++) { 648e5b75505Sopenharmony_ci if (iface->hw_features[i].mode == 649e5b75505Sopenharmony_ci HOSTAPD_MODE_IEEE80211A) { 650e5b75505Sopenharmony_ci mode = &iface->hw_features[i]; 651e5b75505Sopenharmony_ci hw = mode->vht_capab; 652e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 653e5b75505Sopenharmony_ci "update hw vht capab based on 5 GHz band: 0x%x", 654e5b75505Sopenharmony_ci hw); 655e5b75505Sopenharmony_ci break; 656e5b75505Sopenharmony_ci } 657e5b75505Sopenharmony_ci } 658e5b75505Sopenharmony_ci } 659e5b75505Sopenharmony_ci 660e5b75505Sopenharmony_ci return ieee80211ac_cap_check(hw, conf); 661e5b75505Sopenharmony_ci} 662e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211AC */ 663e5b75505Sopenharmony_ci 664e5b75505Sopenharmony_ci 665e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211AX 666e5b75505Sopenharmony_cistatic int ieee80211ax_supported_he_capab(struct hostapd_iface *iface) 667e5b75505Sopenharmony_ci{ 668e5b75505Sopenharmony_ci return 1; 669e5b75505Sopenharmony_ci} 670e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211AX */ 671e5b75505Sopenharmony_ci 672e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211N */ 673e5b75505Sopenharmony_ci 674e5b75505Sopenharmony_ci 675e5b75505Sopenharmony_ciint hostapd_check_ht_capab(struct hostapd_iface *iface) 676e5b75505Sopenharmony_ci{ 677e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211N 678e5b75505Sopenharmony_ci int ret; 679e5b75505Sopenharmony_ci if (!iface->conf->ieee80211n) 680e5b75505Sopenharmony_ci return 0; 681e5b75505Sopenharmony_ci 682e5b75505Sopenharmony_ci if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B && 683e5b75505Sopenharmony_ci iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G && 684e5b75505Sopenharmony_ci (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) { 685e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 686e5b75505Sopenharmony_ci "Disable HT capability [DSSS_CCK-40] on 5 GHz band"); 687e5b75505Sopenharmony_ci iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ; 688e5b75505Sopenharmony_ci } 689e5b75505Sopenharmony_ci 690e5b75505Sopenharmony_ci if (!ieee80211n_supported_ht_capab(iface)) 691e5b75505Sopenharmony_ci return -1; 692e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211AX 693e5b75505Sopenharmony_ci if (iface->conf->ieee80211ax && 694e5b75505Sopenharmony_ci !ieee80211ax_supported_he_capab(iface)) 695e5b75505Sopenharmony_ci return -1; 696e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211AX */ 697e5b75505Sopenharmony_ci#ifdef CONFIG_IEEE80211AC 698e5b75505Sopenharmony_ci if (iface->conf->ieee80211ac && 699e5b75505Sopenharmony_ci !ieee80211ac_supported_vht_capab(iface)) 700e5b75505Sopenharmony_ci return -1; 701e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211AC */ 702e5b75505Sopenharmony_ci ret = ieee80211n_check_40mhz(iface); 703e5b75505Sopenharmony_ci if (ret) 704e5b75505Sopenharmony_ci return ret; 705e5b75505Sopenharmony_ci if (!ieee80211n_allowed_ht40_channel_pair(iface)) 706e5b75505Sopenharmony_ci return -1; 707e5b75505Sopenharmony_ci#endif /* CONFIG_IEEE80211N */ 708e5b75505Sopenharmony_ci 709e5b75505Sopenharmony_ci return 0; 710e5b75505Sopenharmony_ci} 711e5b75505Sopenharmony_ci 712e5b75505Sopenharmony_ci 713e5b75505Sopenharmony_cistatic int hostapd_is_usable_chan(struct hostapd_iface *iface, 714e5b75505Sopenharmony_ci int channel, int primary) 715e5b75505Sopenharmony_ci{ 716e5b75505Sopenharmony_ci struct hostapd_channel_data *chan; 717e5b75505Sopenharmony_ci 718e5b75505Sopenharmony_ci if (!iface->current_mode) 719e5b75505Sopenharmony_ci return 0; 720e5b75505Sopenharmony_ci 721e5b75505Sopenharmony_ci chan = hw_get_channel_chan(iface->current_mode, channel, NULL); 722e5b75505Sopenharmony_ci if (!chan) 723e5b75505Sopenharmony_ci return 0; 724e5b75505Sopenharmony_ci 725e5b75505Sopenharmony_ci if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) 726e5b75505Sopenharmony_ci return 1; 727e5b75505Sopenharmony_ci 728e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 729e5b75505Sopenharmony_ci "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s", 730e5b75505Sopenharmony_ci channel, primary ? "primary" : "secondary", 731e5b75505Sopenharmony_ci chan->flag, 732e5b75505Sopenharmony_ci chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", 733e5b75505Sopenharmony_ci chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); 734e5b75505Sopenharmony_ci return 0; 735e5b75505Sopenharmony_ci} 736e5b75505Sopenharmony_ci 737e5b75505Sopenharmony_ci 738e5b75505Sopenharmony_cistatic int hostapd_is_usable_chans(struct hostapd_iface *iface) 739e5b75505Sopenharmony_ci{ 740e5b75505Sopenharmony_ci int secondary_chan; 741e5b75505Sopenharmony_ci struct hostapd_channel_data *pri_chan; 742e5b75505Sopenharmony_ci 743e5b75505Sopenharmony_ci pri_chan = hw_get_channel_chan(iface->current_mode, 744e5b75505Sopenharmony_ci iface->conf->channel, NULL); 745e5b75505Sopenharmony_ci if (!pri_chan) 746e5b75505Sopenharmony_ci return 0; 747e5b75505Sopenharmony_ci 748e5b75505Sopenharmony_ci if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) 749e5b75505Sopenharmony_ci return 0; 750e5b75505Sopenharmony_ci 751e5b75505Sopenharmony_ci if (!iface->conf->secondary_channel) 752e5b75505Sopenharmony_ci return 1; 753e5b75505Sopenharmony_ci 754e5b75505Sopenharmony_ci if (!iface->conf->ht40_plus_minus_allowed) 755e5b75505Sopenharmony_ci return hostapd_is_usable_chan( 756e5b75505Sopenharmony_ci iface, iface->conf->channel + 757e5b75505Sopenharmony_ci iface->conf->secondary_channel * 4, 0); 758e5b75505Sopenharmony_ci 759e5b75505Sopenharmony_ci /* Both HT40+ and HT40- are set, pick a valid secondary channel */ 760e5b75505Sopenharmony_ci secondary_chan = iface->conf->channel + 4; 761e5b75505Sopenharmony_ci if (hostapd_is_usable_chan(iface, secondary_chan, 0) && 762e5b75505Sopenharmony_ci (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { 763e5b75505Sopenharmony_ci iface->conf->secondary_channel = 1; 764e5b75505Sopenharmony_ci return 1; 765e5b75505Sopenharmony_ci } 766e5b75505Sopenharmony_ci 767e5b75505Sopenharmony_ci secondary_chan = iface->conf->channel - 4; 768e5b75505Sopenharmony_ci if (hostapd_is_usable_chan(iface, secondary_chan, 0) && 769e5b75505Sopenharmony_ci (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { 770e5b75505Sopenharmony_ci iface->conf->secondary_channel = -1; 771e5b75505Sopenharmony_ci return 1; 772e5b75505Sopenharmony_ci } 773e5b75505Sopenharmony_ci 774e5b75505Sopenharmony_ci return 0; 775e5b75505Sopenharmony_ci} 776e5b75505Sopenharmony_ci 777e5b75505Sopenharmony_ci 778e5b75505Sopenharmony_cistatic enum hostapd_chan_status 779e5b75505Sopenharmony_cihostapd_check_chans(struct hostapd_iface *iface) 780e5b75505Sopenharmony_ci{ 781e5b75505Sopenharmony_ci if (iface->conf->channel) { 782e5b75505Sopenharmony_ci if (hostapd_is_usable_chans(iface)) 783e5b75505Sopenharmony_ci return HOSTAPD_CHAN_VALID; 784e5b75505Sopenharmony_ci else 785e5b75505Sopenharmony_ci return HOSTAPD_CHAN_INVALID; 786e5b75505Sopenharmony_ci } 787e5b75505Sopenharmony_ci 788e5b75505Sopenharmony_ci /* 789e5b75505Sopenharmony_ci * The user set channel=0 or channel=acs_survey 790e5b75505Sopenharmony_ci * which is used to trigger ACS. 791e5b75505Sopenharmony_ci */ 792e5b75505Sopenharmony_ci 793e5b75505Sopenharmony_ci switch (acs_init(iface)) { 794e5b75505Sopenharmony_ci case HOSTAPD_CHAN_ACS: 795e5b75505Sopenharmony_ci return HOSTAPD_CHAN_ACS; 796e5b75505Sopenharmony_ci case HOSTAPD_CHAN_VALID: 797e5b75505Sopenharmony_ci case HOSTAPD_CHAN_INVALID: 798e5b75505Sopenharmony_ci default: 799e5b75505Sopenharmony_ci return HOSTAPD_CHAN_INVALID; 800e5b75505Sopenharmony_ci } 801e5b75505Sopenharmony_ci} 802e5b75505Sopenharmony_ci 803e5b75505Sopenharmony_ci 804e5b75505Sopenharmony_cistatic void hostapd_notify_bad_chans(struct hostapd_iface *iface) 805e5b75505Sopenharmony_ci{ 806e5b75505Sopenharmony_ci if (!iface->current_mode) { 807e5b75505Sopenharmony_ci hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 808e5b75505Sopenharmony_ci HOSTAPD_LEVEL_WARNING, 809e5b75505Sopenharmony_ci "Hardware does not support configured mode"); 810e5b75505Sopenharmony_ci return; 811e5b75505Sopenharmony_ci } 812e5b75505Sopenharmony_ci hostapd_logger(iface->bss[0], NULL, 813e5b75505Sopenharmony_ci HOSTAPD_MODULE_IEEE80211, 814e5b75505Sopenharmony_ci HOSTAPD_LEVEL_WARNING, 815e5b75505Sopenharmony_ci "Configured channel (%d) not found from the " 816e5b75505Sopenharmony_ci "channel list of current mode (%d) %s", 817e5b75505Sopenharmony_ci iface->conf->channel, 818e5b75505Sopenharmony_ci iface->current_mode->mode, 819e5b75505Sopenharmony_ci hostapd_hw_mode_txt(iface->current_mode->mode)); 820e5b75505Sopenharmony_ci hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 821e5b75505Sopenharmony_ci HOSTAPD_LEVEL_WARNING, 822e5b75505Sopenharmony_ci "Hardware does not support configured channel"); 823e5b75505Sopenharmony_ci} 824e5b75505Sopenharmony_ci 825e5b75505Sopenharmony_ci 826e5b75505Sopenharmony_ciint hostapd_acs_completed(struct hostapd_iface *iface, int err) 827e5b75505Sopenharmony_ci{ 828e5b75505Sopenharmony_ci int ret = -1; 829e5b75505Sopenharmony_ci 830e5b75505Sopenharmony_ci if (err) 831e5b75505Sopenharmony_ci goto out; 832e5b75505Sopenharmony_ci 833e5b75505Sopenharmony_ci switch (hostapd_check_chans(iface)) { 834e5b75505Sopenharmony_ci case HOSTAPD_CHAN_VALID: 835e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, 836e5b75505Sopenharmony_ci ACS_EVENT_COMPLETED "freq=%d channel=%d", 837e5b75505Sopenharmony_ci hostapd_hw_get_freq(iface->bss[0], 838e5b75505Sopenharmony_ci iface->conf->channel), 839e5b75505Sopenharmony_ci iface->conf->channel); 840e5b75505Sopenharmony_ci break; 841e5b75505Sopenharmony_ci case HOSTAPD_CHAN_ACS: 842e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); 843e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); 844e5b75505Sopenharmony_ci hostapd_notify_bad_chans(iface); 845e5b75505Sopenharmony_ci goto out; 846e5b75505Sopenharmony_ci case HOSTAPD_CHAN_INVALID: 847e5b75505Sopenharmony_ci default: 848e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "ACS picked unusable channels"); 849e5b75505Sopenharmony_ci wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); 850e5b75505Sopenharmony_ci hostapd_notify_bad_chans(iface); 851e5b75505Sopenharmony_ci goto out; 852e5b75505Sopenharmony_ci } 853e5b75505Sopenharmony_ci 854e5b75505Sopenharmony_ci ret = hostapd_check_ht_capab(iface); 855e5b75505Sopenharmony_ci if (ret < 0) 856e5b75505Sopenharmony_ci goto out; 857e5b75505Sopenharmony_ci if (ret == 1) { 858e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback"); 859e5b75505Sopenharmony_ci return 0; 860e5b75505Sopenharmony_ci } 861e5b75505Sopenharmony_ci 862e5b75505Sopenharmony_ci ret = 0; 863e5b75505Sopenharmony_ciout: 864e5b75505Sopenharmony_ci return hostapd_setup_interface_complete(iface, ret); 865e5b75505Sopenharmony_ci} 866e5b75505Sopenharmony_ci 867e5b75505Sopenharmony_ci 868e5b75505Sopenharmony_ci/** 869e5b75505Sopenharmony_ci * hostapd_select_hw_mode - Select the hardware mode 870e5b75505Sopenharmony_ci * @iface: Pointer to interface data. 871e5b75505Sopenharmony_ci * Returns: 0 on success, < 0 on failure 872e5b75505Sopenharmony_ci * 873e5b75505Sopenharmony_ci * Sets up the hardware mode, channel, rates, and passive scanning 874e5b75505Sopenharmony_ci * based on the configuration. 875e5b75505Sopenharmony_ci */ 876e5b75505Sopenharmony_ciint hostapd_select_hw_mode(struct hostapd_iface *iface) 877e5b75505Sopenharmony_ci{ 878e5b75505Sopenharmony_ci int i; 879e5b75505Sopenharmony_ci 880e5b75505Sopenharmony_ci if (iface->num_hw_features < 1) 881e5b75505Sopenharmony_ci return -1; 882e5b75505Sopenharmony_ci 883e5b75505Sopenharmony_ci if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G || 884e5b75505Sopenharmony_ci iface->conf->ieee80211n || iface->conf->ieee80211ac || 885e5b75505Sopenharmony_ci iface->conf->ieee80211ax) && 886e5b75505Sopenharmony_ci iface->conf->channel == 14) { 887e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14"); 888e5b75505Sopenharmony_ci iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B; 889e5b75505Sopenharmony_ci iface->conf->ieee80211n = 0; 890e5b75505Sopenharmony_ci iface->conf->ieee80211ac = 0; 891e5b75505Sopenharmony_ci iface->conf->ieee80211ax = 0; 892e5b75505Sopenharmony_ci } 893e5b75505Sopenharmony_ci 894e5b75505Sopenharmony_ci iface->current_mode = NULL; 895e5b75505Sopenharmony_ci for (i = 0; i < iface->num_hw_features; i++) { 896e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode = &iface->hw_features[i]; 897e5b75505Sopenharmony_ci if (mode->mode == iface->conf->hw_mode) { 898e5b75505Sopenharmony_ci iface->current_mode = mode; 899e5b75505Sopenharmony_ci break; 900e5b75505Sopenharmony_ci } 901e5b75505Sopenharmony_ci } 902e5b75505Sopenharmony_ci 903e5b75505Sopenharmony_ci if (iface->current_mode == NULL) { 904e5b75505Sopenharmony_ci if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) || 905e5b75505Sopenharmony_ci !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) 906e5b75505Sopenharmony_ci { 907e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 908e5b75505Sopenharmony_ci "Hardware does not support configured mode"); 909e5b75505Sopenharmony_ci hostapd_logger(iface->bss[0], NULL, 910e5b75505Sopenharmony_ci HOSTAPD_MODULE_IEEE80211, 911e5b75505Sopenharmony_ci HOSTAPD_LEVEL_WARNING, 912e5b75505Sopenharmony_ci "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)", 913e5b75505Sopenharmony_ci (int) iface->conf->hw_mode); 914e5b75505Sopenharmony_ci return -2; 915e5b75505Sopenharmony_ci } 916e5b75505Sopenharmony_ci } 917e5b75505Sopenharmony_ci 918e5b75505Sopenharmony_ci switch (hostapd_check_chans(iface)) { 919e5b75505Sopenharmony_ci case HOSTAPD_CHAN_VALID: 920e5b75505Sopenharmony_ci return 0; 921e5b75505Sopenharmony_ci case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */ 922e5b75505Sopenharmony_ci return 1; 923e5b75505Sopenharmony_ci case HOSTAPD_CHAN_INVALID: 924e5b75505Sopenharmony_ci default: 925e5b75505Sopenharmony_ci hostapd_notify_bad_chans(iface); 926e5b75505Sopenharmony_ci return -3; 927e5b75505Sopenharmony_ci } 928e5b75505Sopenharmony_ci} 929e5b75505Sopenharmony_ci 930e5b75505Sopenharmony_ci 931e5b75505Sopenharmony_ciconst char * hostapd_hw_mode_txt(int mode) 932e5b75505Sopenharmony_ci{ 933e5b75505Sopenharmony_ci switch (mode) { 934e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211A: 935e5b75505Sopenharmony_ci return "IEEE 802.11a"; 936e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211B: 937e5b75505Sopenharmony_ci return "IEEE 802.11b"; 938e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211G: 939e5b75505Sopenharmony_ci return "IEEE 802.11g"; 940e5b75505Sopenharmony_ci case HOSTAPD_MODE_IEEE80211AD: 941e5b75505Sopenharmony_ci return "IEEE 802.11ad"; 942e5b75505Sopenharmony_ci default: 943e5b75505Sopenharmony_ci return "UNKNOWN"; 944e5b75505Sopenharmony_ci } 945e5b75505Sopenharmony_ci} 946e5b75505Sopenharmony_ci 947e5b75505Sopenharmony_ci 948e5b75505Sopenharmony_ciint hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) 949e5b75505Sopenharmony_ci{ 950e5b75505Sopenharmony_ci return hw_get_freq(hapd->iface->current_mode, chan); 951e5b75505Sopenharmony_ci} 952e5b75505Sopenharmony_ci 953e5b75505Sopenharmony_ci 954e5b75505Sopenharmony_ciint hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) 955e5b75505Sopenharmony_ci{ 956e5b75505Sopenharmony_ci int i, channel; 957e5b75505Sopenharmony_ci struct hostapd_hw_modes *mode; 958e5b75505Sopenharmony_ci 959e5b75505Sopenharmony_ci if (hapd->iface->current_mode) { 960e5b75505Sopenharmony_ci channel = hw_get_chan(hapd->iface->current_mode, freq); 961e5b75505Sopenharmony_ci if (channel) 962e5b75505Sopenharmony_ci return channel; 963e5b75505Sopenharmony_ci } 964e5b75505Sopenharmony_ci 965e5b75505Sopenharmony_ci /* Check other available modes since the channel list for the current 966e5b75505Sopenharmony_ci * mode did not include the specified frequency. */ 967e5b75505Sopenharmony_ci if (!hapd->iface->hw_features) 968e5b75505Sopenharmony_ci return 0; 969e5b75505Sopenharmony_ci for (i = 0; i < hapd->iface->num_hw_features; i++) { 970e5b75505Sopenharmony_ci mode = &hapd->iface->hw_features[i]; 971e5b75505Sopenharmony_ci channel = hw_get_chan(mode, freq); 972e5b75505Sopenharmony_ci if (channel) 973e5b75505Sopenharmony_ci return channel; 974e5b75505Sopenharmony_ci } 975e5b75505Sopenharmony_ci return 0; 976e5b75505Sopenharmony_ci} 977