1e28a4053SRui Paulo /* 2e28a4053SRui Paulo * hostapd / Hardware feature query and different modes 3e28a4053SRui Paulo * Copyright 2002-2003, Instant802 Networks, Inc. 4e28a4053SRui Paulo * Copyright 2005-2006, Devicescape Software, Inc. 5f05cddf9SRui Paulo * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> 6e28a4053SRui Paulo * 75b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 85b9c547cSRui Paulo * See README for more details. 9e28a4053SRui Paulo */ 10e28a4053SRui Paulo 11e28a4053SRui Paulo #include "utils/includes.h" 12e28a4053SRui Paulo 13e28a4053SRui Paulo #include "utils/common.h" 14e28a4053SRui Paulo #include "utils/eloop.h" 15e28a4053SRui Paulo #include "common/ieee802_11_defs.h" 16e28a4053SRui Paulo #include "common/ieee802_11_common.h" 175b9c547cSRui Paulo #include "common/wpa_ctrl.h" 185b9c547cSRui Paulo #include "common/hw_features_common.h" 19e28a4053SRui Paulo #include "hostapd.h" 20e28a4053SRui Paulo #include "ap_config.h" 21e28a4053SRui Paulo #include "ap_drv_ops.h" 225b9c547cSRui Paulo #include "acs.h" 235b9c547cSRui Paulo #include "ieee802_11.h" 245b9c547cSRui Paulo #include "beacon.h" 25e28a4053SRui Paulo #include "hw_features.h" 26e28a4053SRui Paulo 27e28a4053SRui Paulo 28e28a4053SRui Paulo void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, 29e28a4053SRui Paulo size_t num_hw_features) 30e28a4053SRui Paulo { 31e28a4053SRui Paulo size_t i; 32e28a4053SRui Paulo 33e28a4053SRui Paulo if (hw_features == NULL) 34e28a4053SRui Paulo return; 35e28a4053SRui Paulo 36e28a4053SRui Paulo for (i = 0; i < num_hw_features; i++) { 37e28a4053SRui Paulo os_free(hw_features[i].channels); 38e28a4053SRui Paulo os_free(hw_features[i].rates); 39e28a4053SRui Paulo } 40e28a4053SRui Paulo 41e28a4053SRui Paulo os_free(hw_features); 42e28a4053SRui Paulo } 43e28a4053SRui Paulo 44e28a4053SRui Paulo 455b9c547cSRui Paulo #ifndef CONFIG_NO_STDOUT_DEBUG 465b9c547cSRui Paulo static char * dfs_info(struct hostapd_channel_data *chan) 475b9c547cSRui Paulo { 485b9c547cSRui Paulo static char info[256]; 495b9c547cSRui Paulo char *state; 505b9c547cSRui Paulo 515b9c547cSRui Paulo switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { 525b9c547cSRui Paulo case HOSTAPD_CHAN_DFS_UNKNOWN: 535b9c547cSRui Paulo state = "unknown"; 545b9c547cSRui Paulo break; 555b9c547cSRui Paulo case HOSTAPD_CHAN_DFS_USABLE: 565b9c547cSRui Paulo state = "usable"; 575b9c547cSRui Paulo break; 585b9c547cSRui Paulo case HOSTAPD_CHAN_DFS_UNAVAILABLE: 595b9c547cSRui Paulo state = "unavailable"; 605b9c547cSRui Paulo break; 615b9c547cSRui Paulo case HOSTAPD_CHAN_DFS_AVAILABLE: 625b9c547cSRui Paulo state = "available"; 635b9c547cSRui Paulo break; 645b9c547cSRui Paulo default: 655b9c547cSRui Paulo return ""; 665b9c547cSRui Paulo } 675b9c547cSRui Paulo os_snprintf(info, sizeof(info), " (DFS state = %s)", state); 685b9c547cSRui Paulo info[sizeof(info) - 1] = '\0'; 695b9c547cSRui Paulo 705b9c547cSRui Paulo return info; 715b9c547cSRui Paulo } 725b9c547cSRui Paulo #endif /* CONFIG_NO_STDOUT_DEBUG */ 735b9c547cSRui Paulo 745b9c547cSRui Paulo 75e28a4053SRui Paulo int hostapd_get_hw_features(struct hostapd_iface *iface) 76e28a4053SRui Paulo { 77e28a4053SRui Paulo struct hostapd_data *hapd = iface->bss[0]; 785b9c547cSRui Paulo int i, j; 79e28a4053SRui Paulo u16 num_modes, flags; 80e28a4053SRui Paulo struct hostapd_hw_modes *modes; 8185732ac8SCy Schubert u8 dfs_domain; 82*a90b9d01SCy Schubert enum hostapd_hw_mode mode = HOSTAPD_MODE_IEEE80211ANY; 83*a90b9d01SCy Schubert bool is_6ghz = false; 84*a90b9d01SCy Schubert bool orig_mode_valid = false; 85e28a4053SRui Paulo 86e28a4053SRui Paulo if (hostapd_drv_none(hapd)) 87e28a4053SRui Paulo return -1; 8885732ac8SCy Schubert modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags, 8985732ac8SCy Schubert &dfs_domain); 90e28a4053SRui Paulo if (modes == NULL) { 91e28a4053SRui Paulo hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, 92e28a4053SRui Paulo HOSTAPD_LEVEL_DEBUG, 93e28a4053SRui Paulo "Fetching hardware channel/rate support not " 94e28a4053SRui Paulo "supported."); 95e28a4053SRui Paulo return -1; 96e28a4053SRui Paulo } 97e28a4053SRui Paulo 98e28a4053SRui Paulo iface->hw_flags = flags; 9985732ac8SCy Schubert iface->dfs_domain = dfs_domain; 100e28a4053SRui Paulo 101*a90b9d01SCy Schubert if (iface->current_mode) { 102*a90b9d01SCy Schubert /* 103*a90b9d01SCy Schubert * Received driver event CHANNEL_LIST_CHANGED when the current 104*a90b9d01SCy Schubert * hw mode is valid. Clear iface->current_mode temporarily as 105*a90b9d01SCy Schubert * the mode instance will be replaced with a new instance and 106*a90b9d01SCy Schubert * the current pointer would be pointing to freed memory. 107*a90b9d01SCy Schubert */ 108*a90b9d01SCy Schubert orig_mode_valid = true; 109*a90b9d01SCy Schubert mode = iface->current_mode->mode; 110*a90b9d01SCy Schubert is_6ghz = iface->current_mode->is_6ghz; 111*a90b9d01SCy Schubert iface->current_mode = NULL; 112*a90b9d01SCy Schubert } 113e28a4053SRui Paulo hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); 114e28a4053SRui Paulo iface->hw_features = modes; 115e28a4053SRui Paulo iface->num_hw_features = num_modes; 116e28a4053SRui Paulo 117e28a4053SRui Paulo for (i = 0; i < num_modes; i++) { 118e28a4053SRui Paulo struct hostapd_hw_modes *feature = &modes[i]; 1195b9c547cSRui Paulo int dfs_enabled = hapd->iconf->ieee80211h && 1205b9c547cSRui Paulo (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); 1215b9c547cSRui Paulo 122*a90b9d01SCy Schubert /* Restore orignal mode if possible */ 123*a90b9d01SCy Schubert if (orig_mode_valid && feature->mode == mode && 124*a90b9d01SCy Schubert feature->num_channels > 0 && 125*a90b9d01SCy Schubert is_6ghz == is_6ghz_freq(feature->channels[0].freq)) 126*a90b9d01SCy Schubert iface->current_mode = feature; 127*a90b9d01SCy Schubert 128e28a4053SRui Paulo /* set flag for channels we can use in current regulatory 129e28a4053SRui Paulo * domain */ 130e28a4053SRui Paulo for (j = 0; j < feature->num_channels; j++) { 1315b9c547cSRui Paulo int dfs = 0; 1325b9c547cSRui Paulo 133e28a4053SRui Paulo /* 134e28a4053SRui Paulo * Disable all channels that are marked not to allow 1355b9c547cSRui Paulo * to initiate radiation (a.k.a. passive scan and no 1365b9c547cSRui Paulo * IBSS). 1375b9c547cSRui Paulo * Use radar channels only if the driver supports DFS. 138e28a4053SRui Paulo */ 1395b9c547cSRui Paulo if ((feature->channels[j].flag & 1405b9c547cSRui Paulo HOSTAPD_CHAN_RADAR) && dfs_enabled) { 1415b9c547cSRui Paulo dfs = 1; 1425b9c547cSRui Paulo } else if (((feature->channels[j].flag & 1435b9c547cSRui Paulo HOSTAPD_CHAN_RADAR) && 1445b9c547cSRui Paulo !(iface->drv_flags & 1455b9c547cSRui Paulo WPA_DRIVER_FLAGS_DFS_OFFLOAD)) || 1465b9c547cSRui Paulo (feature->channels[j].flag & 1475b9c547cSRui Paulo HOSTAPD_CHAN_NO_IR)) { 148e28a4053SRui Paulo feature->channels[j].flag |= 149e28a4053SRui Paulo HOSTAPD_CHAN_DISABLED; 1505b9c547cSRui Paulo } 1515b9c547cSRui Paulo 152e28a4053SRui Paulo if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) 153e28a4053SRui Paulo continue; 1545b9c547cSRui Paulo 155e28a4053SRui Paulo wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " 1565b9c547cSRui Paulo "chan=%d freq=%d MHz max_tx_power=%d dBm%s", 157e28a4053SRui Paulo feature->mode, 158e28a4053SRui Paulo feature->channels[j].chan, 159e28a4053SRui Paulo feature->channels[j].freq, 1605b9c547cSRui Paulo feature->channels[j].max_tx_power, 1615b9c547cSRui Paulo dfs ? dfs_info(&feature->channels[j]) : ""); 162e28a4053SRui Paulo } 163e28a4053SRui Paulo } 164e28a4053SRui Paulo 165*a90b9d01SCy Schubert if (orig_mode_valid && !iface->current_mode) { 166*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 167*a90b9d01SCy Schubert "%s: Could not update iface->current_mode", 168*a90b9d01SCy Schubert __func__); 169*a90b9d01SCy Schubert } 170*a90b9d01SCy Schubert 1715b9c547cSRui Paulo return 0; 172e28a4053SRui Paulo } 173e28a4053SRui Paulo 174e28a4053SRui Paulo 175f05cddf9SRui Paulo int hostapd_prepare_rates(struct hostapd_iface *iface, 176e28a4053SRui Paulo struct hostapd_hw_modes *mode) 177e28a4053SRui Paulo { 178e28a4053SRui Paulo int i, num_basic_rates = 0; 179e28a4053SRui Paulo int basic_rates_a[] = { 60, 120, 240, -1 }; 180e28a4053SRui Paulo int basic_rates_b[] = { 10, 20, -1 }; 181e28a4053SRui Paulo int basic_rates_g[] = { 10, 20, 55, 110, -1 }; 182e28a4053SRui Paulo int *basic_rates; 183e28a4053SRui Paulo 184f05cddf9SRui Paulo if (iface->conf->basic_rates) 185f05cddf9SRui Paulo basic_rates = iface->conf->basic_rates; 186e28a4053SRui Paulo else switch (mode->mode) { 187e28a4053SRui Paulo case HOSTAPD_MODE_IEEE80211A: 188e28a4053SRui Paulo basic_rates = basic_rates_a; 189e28a4053SRui Paulo break; 190e28a4053SRui Paulo case HOSTAPD_MODE_IEEE80211B: 191e28a4053SRui Paulo basic_rates = basic_rates_b; 192e28a4053SRui Paulo break; 193e28a4053SRui Paulo case HOSTAPD_MODE_IEEE80211G: 194e28a4053SRui Paulo basic_rates = basic_rates_g; 195e28a4053SRui Paulo break; 196f05cddf9SRui Paulo case HOSTAPD_MODE_IEEE80211AD: 197f05cddf9SRui Paulo return 0; /* No basic rates for 11ad */ 198e28a4053SRui Paulo default: 199e28a4053SRui Paulo return -1; 200e28a4053SRui Paulo } 201e28a4053SRui Paulo 202f05cddf9SRui Paulo i = 0; 203f05cddf9SRui Paulo while (basic_rates[i] >= 0) 204f05cddf9SRui Paulo i++; 205f05cddf9SRui Paulo if (i) 206f05cddf9SRui Paulo i++; /* -1 termination */ 207f05cddf9SRui Paulo os_free(iface->basic_rates); 208f05cddf9SRui Paulo iface->basic_rates = os_malloc(i * sizeof(int)); 209f05cddf9SRui Paulo if (iface->basic_rates) 210f05cddf9SRui Paulo os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); 211e28a4053SRui Paulo 212f05cddf9SRui Paulo os_free(iface->current_rates); 213f05cddf9SRui Paulo iface->num_rates = 0; 214e28a4053SRui Paulo 215f05cddf9SRui Paulo iface->current_rates = 216f05cddf9SRui Paulo os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); 217f05cddf9SRui Paulo if (!iface->current_rates) { 218e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " 219e28a4053SRui Paulo "table."); 220e28a4053SRui Paulo return -1; 221e28a4053SRui Paulo } 222e28a4053SRui Paulo 223e28a4053SRui Paulo for (i = 0; i < mode->num_rates; i++) { 224e28a4053SRui Paulo struct hostapd_rate_data *rate; 225e28a4053SRui Paulo 226f05cddf9SRui Paulo if (iface->conf->supported_rates && 227f05cddf9SRui Paulo !hostapd_rate_found(iface->conf->supported_rates, 228e28a4053SRui Paulo mode->rates[i])) 229e28a4053SRui Paulo continue; 230e28a4053SRui Paulo 231f05cddf9SRui Paulo rate = &iface->current_rates[iface->num_rates]; 232e28a4053SRui Paulo rate->rate = mode->rates[i]; 233e28a4053SRui Paulo if (hostapd_rate_found(basic_rates, rate->rate)) { 234e28a4053SRui Paulo rate->flags |= HOSTAPD_RATE_BASIC; 235e28a4053SRui Paulo num_basic_rates++; 236e28a4053SRui Paulo } 237e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", 238f05cddf9SRui Paulo iface->num_rates, rate->rate, rate->flags); 239f05cddf9SRui Paulo iface->num_rates++; 240e28a4053SRui Paulo } 241e28a4053SRui Paulo 242f05cddf9SRui Paulo if ((iface->num_rates == 0 || num_basic_rates == 0) && 243f05cddf9SRui Paulo (!iface->conf->ieee80211n || !iface->conf->require_ht)) { 244e28a4053SRui Paulo wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " 245e28a4053SRui Paulo "rate sets (%d,%d).", 246f05cddf9SRui Paulo iface->num_rates, num_basic_rates); 247e28a4053SRui Paulo return -1; 248e28a4053SRui Paulo } 249e28a4053SRui Paulo 250e28a4053SRui Paulo return 0; 251e28a4053SRui Paulo } 252e28a4053SRui Paulo 253e28a4053SRui Paulo 254e28a4053SRui Paulo static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) 255e28a4053SRui Paulo { 256c1d255d3SCy Schubert int pri_freq, sec_freq; 257c1d255d3SCy Schubert struct hostapd_channel_data *p_chan, *s_chan; 258e28a4053SRui Paulo 259c1d255d3SCy Schubert pri_freq = iface->freq; 260c1d255d3SCy Schubert sec_freq = pri_freq + iface->conf->secondary_channel * 20; 261e28a4053SRui Paulo 262c1d255d3SCy Schubert if (!iface->current_mode) 263c1d255d3SCy Schubert return 0; 264c1d255d3SCy Schubert 265c1d255d3SCy Schubert p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL, 266c1d255d3SCy Schubert iface->hw_features, 267c1d255d3SCy Schubert iface->num_hw_features); 268c1d255d3SCy Schubert 269c1d255d3SCy Schubert s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL, 270c1d255d3SCy Schubert iface->hw_features, 271c1d255d3SCy Schubert iface->num_hw_features); 272c1d255d3SCy Schubert 273c1d255d3SCy Schubert return allowed_ht40_channel_pair(iface->current_mode->mode, 274c1d255d3SCy Schubert p_chan, s_chan); 275e28a4053SRui Paulo } 276e28a4053SRui Paulo 277e28a4053SRui Paulo 278e28a4053SRui Paulo static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) 279e28a4053SRui Paulo { 280e28a4053SRui Paulo if (iface->conf->secondary_channel > 0) { 281e28a4053SRui Paulo iface->conf->channel += 4; 282c1d255d3SCy Schubert iface->freq += 20; 283e28a4053SRui Paulo iface->conf->secondary_channel = -1; 284e28a4053SRui Paulo } else { 285e28a4053SRui Paulo iface->conf->channel -= 4; 286c1d255d3SCy Schubert iface->freq -= 20; 287e28a4053SRui Paulo iface->conf->secondary_channel = 1; 288e28a4053SRui Paulo } 289e28a4053SRui Paulo } 290e28a4053SRui Paulo 291e28a4053SRui Paulo 292e28a4053SRui Paulo static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, 293e28a4053SRui Paulo struct wpa_scan_results *scan_res) 294e28a4053SRui Paulo { 295c1d255d3SCy Schubert unsigned int pri_freq, sec_freq; 2965b9c547cSRui Paulo int res; 297c1d255d3SCy Schubert struct hostapd_channel_data *pri_chan, *sec_chan; 298e28a4053SRui Paulo 299c1d255d3SCy Schubert pri_freq = iface->freq; 300c1d255d3SCy Schubert sec_freq = pri_freq + iface->conf->secondary_channel * 20; 301e28a4053SRui Paulo 302c1d255d3SCy Schubert if (!iface->current_mode) 303c1d255d3SCy Schubert return 0; 304c1d255d3SCy Schubert pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, 305c1d255d3SCy Schubert NULL, iface->hw_features, 306c1d255d3SCy Schubert iface->num_hw_features); 307c1d255d3SCy Schubert sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, 308c1d255d3SCy Schubert NULL, iface->hw_features, 309c1d255d3SCy Schubert iface->num_hw_features); 310c1d255d3SCy Schubert 311c1d255d3SCy Schubert res = check_40mhz_5g(scan_res, pri_chan, sec_chan); 3125b9c547cSRui Paulo 313325151a3SRui Paulo if (res == 2) { 314325151a3SRui Paulo if (iface->conf->no_pri_sec_switch) { 315325151a3SRui Paulo wpa_printf(MSG_DEBUG, 316325151a3SRui Paulo "Cannot switch PRI/SEC channels due to local constraint"); 317325151a3SRui Paulo } else { 318e28a4053SRui Paulo ieee80211n_switch_pri_sec(iface); 319325151a3SRui Paulo } 320325151a3SRui Paulo } 321e28a4053SRui Paulo 3225b9c547cSRui Paulo return !!res; 323e28a4053SRui Paulo } 324e28a4053SRui Paulo 325e28a4053SRui Paulo 326e28a4053SRui Paulo static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, 327e28a4053SRui Paulo struct wpa_scan_results *scan_res) 328e28a4053SRui Paulo { 3295b9c547cSRui Paulo int pri_chan, sec_chan; 330e28a4053SRui Paulo 3315b9c547cSRui Paulo pri_chan = iface->conf->channel; 3325b9c547cSRui Paulo sec_chan = pri_chan + iface->conf->secondary_channel * 4; 333e28a4053SRui Paulo 3345b9c547cSRui Paulo return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan, 3355b9c547cSRui Paulo sec_chan); 336e28a4053SRui Paulo } 337e28a4053SRui Paulo 338e28a4053SRui Paulo 339e28a4053SRui Paulo static void ieee80211n_check_scan(struct hostapd_iface *iface) 340e28a4053SRui Paulo { 341e28a4053SRui Paulo struct wpa_scan_results *scan_res; 342e28a4053SRui Paulo int oper40; 343c1d255d3SCy Schubert int res = 0; 344e28a4053SRui Paulo 345e28a4053SRui Paulo /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is 346f05cddf9SRui Paulo * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ 347e28a4053SRui Paulo 348e28a4053SRui Paulo iface->scan_cb = NULL; 349e28a4053SRui Paulo 350e28a4053SRui Paulo scan_res = hostapd_driver_get_scan_results(iface->bss[0]); 351e28a4053SRui Paulo if (scan_res == NULL) { 352e28a4053SRui Paulo hostapd_setup_interface_complete(iface, 1); 353e28a4053SRui Paulo return; 354e28a4053SRui Paulo } 355e28a4053SRui Paulo 356e28a4053SRui Paulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) 357e28a4053SRui Paulo oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); 358e28a4053SRui Paulo else 359e28a4053SRui Paulo oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); 360e28a4053SRui Paulo wpa_scan_results_free(scan_res); 361e28a4053SRui Paulo 3625b9c547cSRui Paulo iface->secondary_ch = iface->conf->secondary_channel; 363e28a4053SRui Paulo if (!oper40) { 364e28a4053SRui Paulo wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " 365e28a4053SRui Paulo "channel pri=%d sec=%d based on overlapping BSSes", 366e28a4053SRui Paulo iface->conf->channel, 367e28a4053SRui Paulo iface->conf->channel + 368e28a4053SRui Paulo iface->conf->secondary_channel * 4); 369e28a4053SRui Paulo iface->conf->secondary_channel = 0; 3705b9c547cSRui Paulo if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) { 3715b9c547cSRui Paulo /* 3725b9c547cSRui Paulo * TODO: Could consider scheduling another scan to check 3735b9c547cSRui Paulo * if channel width can be changed if no coex reports 3745b9c547cSRui Paulo * are received from associating stations. 3755b9c547cSRui Paulo */ 3765b9c547cSRui Paulo } 377e28a4053SRui Paulo } 378e28a4053SRui Paulo 379c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX 380c1d255d3SCy Schubert if (iface->conf->secondary_channel && 381c1d255d3SCy Schubert iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && 382c1d255d3SCy Schubert iface->conf->ieee80211ax) { 383c1d255d3SCy Schubert struct he_capabilities *he_cap; 384c1d255d3SCy Schubert 385c1d255d3SCy Schubert he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP]; 386c1d255d3SCy Schubert if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & 387c1d255d3SCy Schubert HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) { 388c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 389c1d255d3SCy Schubert "HE: 40 MHz channel width is not supported in 2.4 GHz; clear secondary channel configuration"); 390c1d255d3SCy Schubert iface->conf->secondary_channel = 0; 391c1d255d3SCy Schubert } 392c1d255d3SCy Schubert } 393c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */ 394c1d255d3SCy Schubert 395c1d255d3SCy Schubert if (iface->conf->secondary_channel) 396f05cddf9SRui Paulo res = ieee80211n_allowed_ht40_channel_pair(iface); 3975b9c547cSRui Paulo if (!res) { 3985b9c547cSRui Paulo iface->conf->secondary_channel = 0; 399206b73d0SCy Schubert hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0); 400206b73d0SCy Schubert hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0); 401*a90b9d01SCy Schubert hostapd_set_oper_chwidth(iface->conf, CONF_OPER_CHWIDTH_USE_HT); 402780fb4a2SCy Schubert res = 1; 4035b9c547cSRui Paulo wpa_printf(MSG_INFO, "Fallback to 20 MHz"); 4045b9c547cSRui Paulo } 4055b9c547cSRui Paulo 406f05cddf9SRui Paulo hostapd_setup_interface_complete(iface, !res); 407f05cddf9SRui Paulo } 408f05cddf9SRui Paulo 409f05cddf9SRui Paulo 410f05cddf9SRui Paulo static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, 411f05cddf9SRui Paulo struct wpa_driver_scan_params *params) 412f05cddf9SRui Paulo { 413f05cddf9SRui Paulo /* Scan only the affected frequency range */ 414f05cddf9SRui Paulo int pri_freq, sec_freq; 415f05cddf9SRui Paulo int affected_start, affected_end; 416f05cddf9SRui Paulo int i, pos; 417f05cddf9SRui Paulo struct hostapd_hw_modes *mode; 418f05cddf9SRui Paulo 419f05cddf9SRui Paulo if (iface->current_mode == NULL) 420f05cddf9SRui Paulo return; 421f05cddf9SRui Paulo 422c1d255d3SCy Schubert pri_freq = iface->freq; 423f05cddf9SRui Paulo if (iface->conf->secondary_channel > 0) 424f05cddf9SRui Paulo sec_freq = pri_freq + 20; 425f05cddf9SRui Paulo else 426f05cddf9SRui Paulo sec_freq = pri_freq - 20; 427325151a3SRui Paulo /* 428325151a3SRui Paulo * Note: Need to find the PRI channel also in cases where the affected 429325151a3SRui Paulo * channel is the SEC channel of a 40 MHz BSS, so need to include the 430325151a3SRui Paulo * scanning coverage here to be 40 MHz from the center frequency. 431325151a3SRui Paulo */ 432325151a3SRui Paulo affected_start = (pri_freq + sec_freq) / 2 - 40; 433325151a3SRui Paulo affected_end = (pri_freq + sec_freq) / 2 + 40; 434f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 435f05cddf9SRui Paulo affected_start, affected_end); 436f05cddf9SRui Paulo 437f05cddf9SRui Paulo mode = iface->current_mode; 438f05cddf9SRui Paulo params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); 439f05cddf9SRui Paulo if (params->freqs == NULL) 440f05cddf9SRui Paulo return; 441f05cddf9SRui Paulo pos = 0; 442f05cddf9SRui Paulo 443f05cddf9SRui Paulo for (i = 0; i < mode->num_channels; i++) { 444f05cddf9SRui Paulo struct hostapd_channel_data *chan = &mode->channels[i]; 445f05cddf9SRui Paulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 446f05cddf9SRui Paulo continue; 447f05cddf9SRui Paulo if (chan->freq < affected_start || 448f05cddf9SRui Paulo chan->freq > affected_end) 449f05cddf9SRui Paulo continue; 450f05cddf9SRui Paulo params->freqs[pos++] = chan->freq; 451f05cddf9SRui Paulo } 452e28a4053SRui Paulo } 453e28a4053SRui Paulo 454e28a4053SRui Paulo 4555b9c547cSRui Paulo static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, 4565b9c547cSRui Paulo struct wpa_driver_scan_params *params) 4575b9c547cSRui Paulo { 4585b9c547cSRui Paulo /* Scan only the affected frequency range */ 4595b9c547cSRui Paulo int pri_freq; 4605b9c547cSRui Paulo int affected_start, affected_end; 4615b9c547cSRui Paulo int i, pos; 4625b9c547cSRui Paulo struct hostapd_hw_modes *mode; 4635b9c547cSRui Paulo 4645b9c547cSRui Paulo if (iface->current_mode == NULL) 4655b9c547cSRui Paulo return; 4665b9c547cSRui Paulo 467c1d255d3SCy Schubert pri_freq = iface->freq; 4685b9c547cSRui Paulo if (iface->conf->secondary_channel > 0) { 4695b9c547cSRui Paulo affected_start = pri_freq - 10; 4705b9c547cSRui Paulo affected_end = pri_freq + 30; 4715b9c547cSRui Paulo } else { 4725b9c547cSRui Paulo affected_start = pri_freq - 30; 4735b9c547cSRui Paulo affected_end = pri_freq + 10; 4745b9c547cSRui Paulo } 4755b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 4765b9c547cSRui Paulo affected_start, affected_end); 4775b9c547cSRui Paulo 4785b9c547cSRui Paulo mode = iface->current_mode; 4795b9c547cSRui Paulo params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); 4805b9c547cSRui Paulo if (params->freqs == NULL) 4815b9c547cSRui Paulo return; 4825b9c547cSRui Paulo pos = 0; 4835b9c547cSRui Paulo 4845b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 4855b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[i]; 4865b9c547cSRui Paulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 4875b9c547cSRui Paulo continue; 4885b9c547cSRui Paulo if (chan->freq < affected_start || 4895b9c547cSRui Paulo chan->freq > affected_end) 4905b9c547cSRui Paulo continue; 4915b9c547cSRui Paulo params->freqs[pos++] = chan->freq; 4925b9c547cSRui Paulo } 4935b9c547cSRui Paulo } 4945b9c547cSRui Paulo 4955b9c547cSRui Paulo 4965b9c547cSRui Paulo static void ap_ht40_scan_retry(void *eloop_data, void *user_data) 4975b9c547cSRui Paulo { 4985b9c547cSRui Paulo #define HT2040_COEX_SCAN_RETRY 15 4995b9c547cSRui Paulo struct hostapd_iface *iface = eloop_data; 5005b9c547cSRui Paulo struct wpa_driver_scan_params params; 5015b9c547cSRui Paulo int ret; 5025b9c547cSRui Paulo 5035b9c547cSRui Paulo os_memset(¶ms, 0, sizeof(params)); 5045b9c547cSRui Paulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) 5055b9c547cSRui Paulo ieee80211n_scan_channels_2g4(iface, ¶ms); 5065b9c547cSRui Paulo else 5075b9c547cSRui Paulo ieee80211n_scan_channels_5g(iface, ¶ms); 5085b9c547cSRui Paulo 509*a90b9d01SCy Schubert params.link_id = -1; 510*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE 511*a90b9d01SCy Schubert if (iface->bss[0]->conf->mld_ap) 512*a90b9d01SCy Schubert params.link_id = iface->bss[0]->mld_link_id; 513*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */ 514*a90b9d01SCy Schubert 5155b9c547cSRui Paulo ret = hostapd_driver_scan(iface->bss[0], ¶ms); 5165b9c547cSRui Paulo iface->num_ht40_scan_tries++; 5175b9c547cSRui Paulo os_free(params.freqs); 5185b9c547cSRui Paulo 5195b9c547cSRui Paulo if (ret == -EBUSY && 5205b9c547cSRui Paulo iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) { 5215b9c547cSRui Paulo wpa_printf(MSG_ERROR, 5225b9c547cSRui Paulo "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)", 5235b9c547cSRui Paulo ret, strerror(-ret), iface->num_ht40_scan_tries); 5245b9c547cSRui Paulo eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); 5255b9c547cSRui Paulo return; 5265b9c547cSRui Paulo } 5275b9c547cSRui Paulo 5285b9c547cSRui Paulo if (ret == 0) { 5295b9c547cSRui Paulo iface->scan_cb = ieee80211n_check_scan; 530*a90b9d01SCy Schubert iface->bss[0]->scan_cookie = params.scan_cookie; 5315b9c547cSRui Paulo return; 5325b9c547cSRui Paulo } 5335b9c547cSRui Paulo 5345b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 5355b9c547cSRui Paulo "Failed to request a scan in device, bringing up in HT20 mode"); 5365b9c547cSRui Paulo iface->conf->secondary_channel = 0; 5375b9c547cSRui Paulo iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; 5385b9c547cSRui Paulo hostapd_setup_interface_complete(iface, 0); 5395b9c547cSRui Paulo } 5405b9c547cSRui Paulo 5415b9c547cSRui Paulo 5425b9c547cSRui Paulo void hostapd_stop_setup_timers(struct hostapd_iface *iface) 5435b9c547cSRui Paulo { 5445b9c547cSRui Paulo eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); 5455b9c547cSRui Paulo } 5465b9c547cSRui Paulo 5475b9c547cSRui Paulo 548e28a4053SRui Paulo static int ieee80211n_check_40mhz(struct hostapd_iface *iface) 549e28a4053SRui Paulo { 550e28a4053SRui Paulo struct wpa_driver_scan_params params; 5515b9c547cSRui Paulo int ret; 552e28a4053SRui Paulo 553780fb4a2SCy Schubert /* Check that HT40 is used and PRI / SEC switch is allowed */ 554780fb4a2SCy Schubert if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch) 555780fb4a2SCy Schubert return 0; 556e28a4053SRui Paulo 5575b9c547cSRui Paulo hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); 558e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " 559e28a4053SRui Paulo "40 MHz channel"); 560e28a4053SRui Paulo os_memset(¶ms, 0, sizeof(params)); 561f05cddf9SRui Paulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) 562f05cddf9SRui Paulo ieee80211n_scan_channels_2g4(iface, ¶ms); 5635b9c547cSRui Paulo else 5645b9c547cSRui Paulo ieee80211n_scan_channels_5g(iface, ¶ms); 5655b9c547cSRui Paulo 566*a90b9d01SCy Schubert params.link_id = -1; 567*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE 568*a90b9d01SCy Schubert if (iface->bss[0]->conf->mld_ap) 569*a90b9d01SCy Schubert params.link_id = iface->bss[0]->mld_link_id; 570*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */ 5715b9c547cSRui Paulo ret = hostapd_driver_scan(iface->bss[0], ¶ms); 572f05cddf9SRui Paulo os_free(params.freqs); 5735b9c547cSRui Paulo 5745b9c547cSRui Paulo if (ret == -EBUSY) { 5755b9c547cSRui Paulo wpa_printf(MSG_ERROR, 5765b9c547cSRui Paulo "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again", 5775b9c547cSRui Paulo ret, strerror(-ret)); 5785b9c547cSRui Paulo iface->num_ht40_scan_tries = 1; 5795b9c547cSRui Paulo eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL); 5805b9c547cSRui Paulo eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL); 5815b9c547cSRui Paulo return 1; 5825b9c547cSRui Paulo } 5835b9c547cSRui Paulo 5845b9c547cSRui Paulo if (ret < 0) { 5855b9c547cSRui Paulo wpa_printf(MSG_ERROR, 5865b9c547cSRui Paulo "Failed to request a scan of neighboring BSSes ret=%d (%s)", 5875b9c547cSRui Paulo ret, strerror(-ret)); 588e28a4053SRui Paulo return -1; 589e28a4053SRui Paulo } 590e28a4053SRui Paulo 591e28a4053SRui Paulo iface->scan_cb = ieee80211n_check_scan; 592*a90b9d01SCy Schubert iface->bss[0]->scan_cookie = params.scan_cookie; 593e28a4053SRui Paulo return 1; 594e28a4053SRui Paulo } 595e28a4053SRui Paulo 596e28a4053SRui Paulo 597e28a4053SRui Paulo static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) 598e28a4053SRui Paulo { 599e28a4053SRui Paulo u16 hw = iface->current_mode->ht_capab; 600e28a4053SRui Paulo u16 conf = iface->conf->ht_capab; 601e28a4053SRui Paulo 602e28a4053SRui Paulo if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && 603e28a4053SRui Paulo !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { 604e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 605e28a4053SRui Paulo "HT capability [LDPC]"); 606e28a4053SRui Paulo return 0; 607e28a4053SRui Paulo } 608e28a4053SRui Paulo 609325151a3SRui Paulo /* 610325151a3SRui Paulo * Driver ACS chosen channel may not be HT40 due to internal driver 611325151a3SRui Paulo * restrictions. 612325151a3SRui Paulo */ 613325151a3SRui Paulo if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 614e28a4053SRui Paulo !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { 615e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 616e28a4053SRui Paulo "HT capability [HT40*]"); 617e28a4053SRui Paulo return 0; 618e28a4053SRui Paulo } 619e28a4053SRui Paulo 620e28a4053SRui Paulo if ((conf & HT_CAP_INFO_GREEN_FIELD) && 621e28a4053SRui Paulo !(hw & HT_CAP_INFO_GREEN_FIELD)) { 622e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 623e28a4053SRui Paulo "HT capability [GF]"); 624e28a4053SRui Paulo return 0; 625e28a4053SRui Paulo } 626e28a4053SRui Paulo 627e28a4053SRui Paulo if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && 628e28a4053SRui Paulo !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { 629e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 630e28a4053SRui Paulo "HT capability [SHORT-GI-20]"); 631e28a4053SRui Paulo return 0; 632e28a4053SRui Paulo } 633e28a4053SRui Paulo 634e28a4053SRui Paulo if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && 635e28a4053SRui Paulo !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { 636e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 637e28a4053SRui Paulo "HT capability [SHORT-GI-40]"); 638e28a4053SRui Paulo return 0; 639e28a4053SRui Paulo } 640e28a4053SRui Paulo 641e28a4053SRui Paulo if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { 642e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 643e28a4053SRui Paulo "HT capability [TX-STBC]"); 644e28a4053SRui Paulo return 0; 645e28a4053SRui Paulo } 646e28a4053SRui Paulo 647e28a4053SRui Paulo if ((conf & HT_CAP_INFO_RX_STBC_MASK) > 648e28a4053SRui Paulo (hw & HT_CAP_INFO_RX_STBC_MASK)) { 649e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 650e28a4053SRui Paulo "HT capability [RX-STBC*]"); 651e28a4053SRui Paulo return 0; 652e28a4053SRui Paulo } 653e28a4053SRui Paulo 654e28a4053SRui Paulo if ((conf & HT_CAP_INFO_DELAYED_BA) && 655e28a4053SRui Paulo !(hw & HT_CAP_INFO_DELAYED_BA)) { 656e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 657e28a4053SRui Paulo "HT capability [DELAYED-BA]"); 658e28a4053SRui Paulo return 0; 659e28a4053SRui Paulo } 660e28a4053SRui Paulo 661e28a4053SRui Paulo if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && 662e28a4053SRui Paulo !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { 663e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 664e28a4053SRui Paulo "HT capability [MAX-AMSDU-7935]"); 665e28a4053SRui Paulo return 0; 666e28a4053SRui Paulo } 667e28a4053SRui Paulo 668e28a4053SRui Paulo if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && 669e28a4053SRui Paulo !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { 670e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 671e28a4053SRui Paulo "HT capability [DSSS_CCK-40]"); 672e28a4053SRui Paulo return 0; 673e28a4053SRui Paulo } 674e28a4053SRui Paulo 675e28a4053SRui Paulo if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && 676e28a4053SRui Paulo !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { 677e28a4053SRui Paulo wpa_printf(MSG_ERROR, "Driver does not support configured " 678e28a4053SRui Paulo "HT capability [LSIG-TXOP-PROT]"); 679e28a4053SRui Paulo return 0; 680e28a4053SRui Paulo } 681e28a4053SRui Paulo 682e28a4053SRui Paulo return 1; 683e28a4053SRui Paulo } 684e28a4053SRui Paulo 6855b9c547cSRui Paulo 6865b9c547cSRui Paulo #ifdef CONFIG_IEEE80211AC 6875b9c547cSRui Paulo static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) 6885b9c547cSRui Paulo { 6895b9c547cSRui Paulo struct hostapd_hw_modes *mode = iface->current_mode; 6905b9c547cSRui Paulo u32 hw = mode->vht_capab; 6915b9c547cSRui Paulo u32 conf = iface->conf->vht_capab; 6925b9c547cSRui Paulo 6935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", 6945b9c547cSRui Paulo hw, conf); 6955b9c547cSRui Paulo 6965b9c547cSRui Paulo if (mode->mode == HOSTAPD_MODE_IEEE80211G && 6975b9c547cSRui Paulo iface->conf->bss[0]->vendor_vht && 6985b9c547cSRui Paulo mode->vht_capab == 0 && iface->hw_features) { 6995b9c547cSRui Paulo int i; 7005b9c547cSRui Paulo 7015b9c547cSRui Paulo for (i = 0; i < iface->num_hw_features; i++) { 7025b9c547cSRui Paulo if (iface->hw_features[i].mode == 7035b9c547cSRui Paulo HOSTAPD_MODE_IEEE80211A) { 7045b9c547cSRui Paulo mode = &iface->hw_features[i]; 7055b9c547cSRui Paulo hw = mode->vht_capab; 7065b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 7075b9c547cSRui Paulo "update hw vht capab based on 5 GHz band: 0x%x", 7085b9c547cSRui Paulo hw); 7095b9c547cSRui Paulo break; 7105b9c547cSRui Paulo } 7115b9c547cSRui Paulo } 7125b9c547cSRui Paulo } 7135b9c547cSRui Paulo 71485732ac8SCy Schubert return ieee80211ac_cap_check(hw, conf); 7155b9c547cSRui Paulo } 7165b9c547cSRui Paulo #endif /* CONFIG_IEEE80211AC */ 7175b9c547cSRui Paulo 718206b73d0SCy Schubert 719206b73d0SCy Schubert #ifdef CONFIG_IEEE80211AX 720206b73d0SCy Schubert static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface) 721206b73d0SCy Schubert { 722206b73d0SCy Schubert return 1; 723206b73d0SCy Schubert } 724206b73d0SCy Schubert #endif /* CONFIG_IEEE80211AX */ 725206b73d0SCy Schubert 726e28a4053SRui Paulo 727e28a4053SRui Paulo int hostapd_check_ht_capab(struct hostapd_iface *iface) 728e28a4053SRui Paulo { 729e28a4053SRui Paulo int ret; 730c1d255d3SCy Schubert 731c1d255d3SCy Schubert if (is_6ghz_freq(iface->freq)) 732c1d255d3SCy Schubert return 0; 733f05cddf9SRui Paulo if (!iface->conf->ieee80211n) 734f05cddf9SRui Paulo return 0; 735325151a3SRui Paulo 736325151a3SRui Paulo if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B && 737325151a3SRui Paulo iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G && 738325151a3SRui Paulo (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) { 739325151a3SRui Paulo wpa_printf(MSG_DEBUG, 740325151a3SRui Paulo "Disable HT capability [DSSS_CCK-40] on 5 GHz band"); 741325151a3SRui Paulo iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ; 742325151a3SRui Paulo } 743325151a3SRui Paulo 744f05cddf9SRui Paulo if (!ieee80211n_supported_ht_capab(iface)) 745f05cddf9SRui Paulo return -1; 746206b73d0SCy Schubert #ifdef CONFIG_IEEE80211AX 747206b73d0SCy Schubert if (iface->conf->ieee80211ax && 748206b73d0SCy Schubert !ieee80211ax_supported_he_capab(iface)) 749206b73d0SCy Schubert return -1; 750206b73d0SCy Schubert #endif /* CONFIG_IEEE80211AX */ 7515b9c547cSRui Paulo #ifdef CONFIG_IEEE80211AC 75285732ac8SCy Schubert if (iface->conf->ieee80211ac && 75385732ac8SCy Schubert !ieee80211ac_supported_vht_capab(iface)) 7545b9c547cSRui Paulo return -1; 7555b9c547cSRui Paulo #endif /* CONFIG_IEEE80211AC */ 756e28a4053SRui Paulo ret = ieee80211n_check_40mhz(iface); 757e28a4053SRui Paulo if (ret) 758e28a4053SRui Paulo return ret; 759e28a4053SRui Paulo if (!ieee80211n_allowed_ht40_channel_pair(iface)) 760e28a4053SRui Paulo return -1; 761e28a4053SRui Paulo 762e28a4053SRui Paulo return 0; 763e28a4053SRui Paulo } 764e28a4053SRui Paulo 765e28a4053SRui Paulo 766c1d255d3SCy Schubert int hostapd_check_edmg_capab(struct hostapd_iface *iface) 767c1d255d3SCy Schubert { 768c1d255d3SCy Schubert struct hostapd_hw_modes *mode = iface->hw_features; 769c1d255d3SCy Schubert struct ieee80211_edmg_config edmg; 770c1d255d3SCy Schubert 771c1d255d3SCy Schubert if (!iface->conf->enable_edmg) 772c1d255d3SCy Schubert return 0; 773c1d255d3SCy Schubert 774c1d255d3SCy Schubert hostapd_encode_edmg_chan(iface->conf->enable_edmg, 775c1d255d3SCy Schubert iface->conf->edmg_channel, 776c1d255d3SCy Schubert iface->conf->channel, 777c1d255d3SCy Schubert &edmg); 778c1d255d3SCy Schubert 779c1d255d3SCy Schubert if (mode->edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg)) 780c1d255d3SCy Schubert return 0; 781c1d255d3SCy Schubert 782c1d255d3SCy Schubert wpa_printf(MSG_WARNING, "Requested EDMG configuration is not valid"); 783c1d255d3SCy Schubert wpa_printf(MSG_INFO, "EDMG capab: channels 0x%x, bw_config %d", 784c1d255d3SCy Schubert mode->edmg.channels, mode->edmg.bw_config); 785c1d255d3SCy Schubert wpa_printf(MSG_INFO, 786c1d255d3SCy Schubert "Requested EDMG configuration: channels 0x%x, bw_config %d", 787c1d255d3SCy Schubert edmg.channels, edmg.bw_config); 788c1d255d3SCy Schubert return -1; 789c1d255d3SCy Schubert } 790c1d255d3SCy Schubert 791c1d255d3SCy Schubert 792c1d255d3SCy Schubert int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface) 793c1d255d3SCy Schubert { 794c1d255d3SCy Schubert #ifdef CONFIG_IEEE80211AX 795c1d255d3SCy Schubert struct he_capabilities *he_cap; 796c1d255d3SCy Schubert u16 hw; 797c1d255d3SCy Schubert 798c1d255d3SCy Schubert if (!iface->current_mode || !is_6ghz_freq(iface->freq)) 799c1d255d3SCy Schubert return 0; 800c1d255d3SCy Schubert 801c1d255d3SCy Schubert he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP]; 802c1d255d3SCy Schubert hw = he_cap->he_6ghz_capa; 803c1d255d3SCy Schubert if (iface->conf->he_6ghz_max_mpdu > 804c1d255d3SCy Schubert ((hw & HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK) >> 805c1d255d3SCy Schubert HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT)) { 806c1d255d3SCy Schubert wpa_printf(MSG_ERROR, 807c1d255d3SCy Schubert "The driver does not support the configured HE 6 GHz Max MPDU length"); 808c1d255d3SCy Schubert return -1; 809c1d255d3SCy Schubert } 810c1d255d3SCy Schubert 811c1d255d3SCy Schubert if (iface->conf->he_6ghz_max_ampdu_len_exp > 812c1d255d3SCy Schubert ((hw & HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK) >> 813c1d255d3SCy Schubert HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT)) { 814c1d255d3SCy Schubert wpa_printf(MSG_ERROR, 815c1d255d3SCy Schubert "The driver does not support the configured HE 6 GHz Max AMPDU Length Exponent"); 816c1d255d3SCy Schubert return -1; 817c1d255d3SCy Schubert } 818c1d255d3SCy Schubert 819c1d255d3SCy Schubert if (iface->conf->he_6ghz_rx_ant_pat && 820c1d255d3SCy Schubert !(hw & HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS)) { 821c1d255d3SCy Schubert wpa_printf(MSG_ERROR, 822c1d255d3SCy Schubert "The driver does not support the configured HE 6 GHz Rx Antenna Pattern"); 823c1d255d3SCy Schubert return -1; 824c1d255d3SCy Schubert } 825c1d255d3SCy Schubert 826c1d255d3SCy Schubert if (iface->conf->he_6ghz_tx_ant_pat && 827c1d255d3SCy Schubert !(hw & HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS)) { 828c1d255d3SCy Schubert wpa_printf(MSG_ERROR, 829c1d255d3SCy Schubert "The driver does not support the configured HE 6 GHz Tx Antenna Pattern"); 830c1d255d3SCy Schubert return -1; 831c1d255d3SCy Schubert } 832c1d255d3SCy Schubert #endif /* CONFIG_IEEE80211AX */ 833c1d255d3SCy Schubert return 0; 834c1d255d3SCy Schubert } 835c1d255d3SCy Schubert 836c1d255d3SCy Schubert 837*a90b9d01SCy Schubert /* Returns: 838*a90b9d01SCy Schubert * 1 = usable 839*a90b9d01SCy Schubert * 0 = not usable 840*a90b9d01SCy Schubert * -1 = not currently usable due to 6 GHz NO-IR 841*a90b9d01SCy Schubert */ 8425b9c547cSRui Paulo static int hostapd_is_usable_chan(struct hostapd_iface *iface, 843c1d255d3SCy Schubert int frequency, int primary) 8445b9c547cSRui Paulo { 8455b9c547cSRui Paulo struct hostapd_channel_data *chan; 8465b9c547cSRui Paulo 847325151a3SRui Paulo if (!iface->current_mode) 848325151a3SRui Paulo return 0; 849325151a3SRui Paulo 850c1d255d3SCy Schubert chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL, 851c1d255d3SCy Schubert iface->hw_features, iface->num_hw_features); 8524bc52338SCy Schubert if (!chan) 8534bc52338SCy Schubert return 0; 8545b9c547cSRui Paulo 8554bc52338SCy Schubert if ((primary && chan_pri_allowed(chan)) || 8564bc52338SCy Schubert (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED))) 8575b9c547cSRui Paulo return 1; 8585b9c547cSRui Paulo 8594bc52338SCy Schubert wpa_printf(MSG_INFO, 860c1d255d3SCy Schubert "Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s", 861c1d255d3SCy Schubert frequency, primary ? "primary" : "secondary", 8624bc52338SCy Schubert chan->flag, 8635b9c547cSRui Paulo chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", 8645b9c547cSRui Paulo chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); 865*a90b9d01SCy Schubert 866*a90b9d01SCy Schubert if (is_6ghz_freq(chan->freq) && (chan->flag & HOSTAPD_CHAN_NO_IR)) 867*a90b9d01SCy Schubert return -1; 868*a90b9d01SCy Schubert 8695b9c547cSRui Paulo return 0; 8705b9c547cSRui Paulo } 8715b9c547cSRui Paulo 8725b9c547cSRui Paulo 873c1d255d3SCy Schubert static int hostapd_is_usable_edmg(struct hostapd_iface *iface) 8745b9c547cSRui Paulo { 875c1d255d3SCy Schubert int i, contiguous = 0; 876c1d255d3SCy Schubert int num_of_enabled = 0; 877c1d255d3SCy Schubert int max_contiguous = 0; 878*a90b9d01SCy Schubert int err; 879c1d255d3SCy Schubert struct ieee80211_edmg_config edmg; 8804bc52338SCy Schubert struct hostapd_channel_data *pri_chan; 8814bc52338SCy Schubert 882c1d255d3SCy Schubert if (!iface->conf->enable_edmg) 883c1d255d3SCy Schubert return 1; 884c1d255d3SCy Schubert 885c1d255d3SCy Schubert if (!iface->current_mode) 886c1d255d3SCy Schubert return 0; 887c1d255d3SCy Schubert pri_chan = hw_get_channel_freq(iface->current_mode->mode, 888c1d255d3SCy Schubert iface->freq, NULL, 889c1d255d3SCy Schubert iface->hw_features, 890c1d255d3SCy Schubert iface->num_hw_features); 8914b72b91aSCy Schubert if (!pri_chan) 8924b72b91aSCy Schubert return 0; 893c1d255d3SCy Schubert hostapd_encode_edmg_chan(iface->conf->enable_edmg, 894c1d255d3SCy Schubert iface->conf->edmg_channel, 895c1d255d3SCy Schubert pri_chan->chan, 896c1d255d3SCy Schubert &edmg); 897c1d255d3SCy Schubert if (!(edmg.channels & BIT(pri_chan->chan - 1))) 8984bc52338SCy Schubert return 0; 89985732ac8SCy Schubert 900c1d255d3SCy Schubert /* 60 GHz channels 1..6 */ 901c1d255d3SCy Schubert for (i = 0; i < 6; i++) { 902c1d255d3SCy Schubert int freq = 56160 + 2160 * (i + 1); 903c1d255d3SCy Schubert 904c1d255d3SCy Schubert if (edmg.channels & BIT(i)) { 905c1d255d3SCy Schubert contiguous++; 906c1d255d3SCy Schubert num_of_enabled++; 907c1d255d3SCy Schubert } else { 908c1d255d3SCy Schubert contiguous = 0; 909c1d255d3SCy Schubert continue; 910c1d255d3SCy Schubert } 911c1d255d3SCy Schubert 912c1d255d3SCy Schubert /* P802.11ay defines that the total number of subfields 913c1d255d3SCy Schubert * set to one does not exceed 4. 914c1d255d3SCy Schubert */ 915c1d255d3SCy Schubert if (num_of_enabled > 4) 916c1d255d3SCy Schubert return 0; 917c1d255d3SCy Schubert 918*a90b9d01SCy Schubert err = hostapd_is_usable_chan(iface, freq, 1); 919*a90b9d01SCy Schubert if (err <= 0) 920*a90b9d01SCy Schubert return err; 921c1d255d3SCy Schubert 922c1d255d3SCy Schubert if (contiguous > max_contiguous) 923c1d255d3SCy Schubert max_contiguous = contiguous; 924c1d255d3SCy Schubert } 925c1d255d3SCy Schubert 926c1d255d3SCy Schubert /* Check if the EDMG configuration is valid under the limitations 927c1d255d3SCy Schubert * of P802.11ay. 928c1d255d3SCy Schubert */ 929c1d255d3SCy Schubert /* check bw_config against contiguous EDMG channels */ 930c1d255d3SCy Schubert switch (edmg.bw_config) { 931c1d255d3SCy Schubert case EDMG_BW_CONFIG_4: 932c1d255d3SCy Schubert if (!max_contiguous) 933c1d255d3SCy Schubert return 0; 934c1d255d3SCy Schubert break; 935c1d255d3SCy Schubert case EDMG_BW_CONFIG_5: 936c1d255d3SCy Schubert if (max_contiguous < 2) 937c1d255d3SCy Schubert return 0; 938c1d255d3SCy Schubert break; 939c1d255d3SCy Schubert default: 940c1d255d3SCy Schubert return 0; 941c1d255d3SCy Schubert } 942c1d255d3SCy Schubert 943c1d255d3SCy Schubert return 1; 944c1d255d3SCy Schubert } 945c1d255d3SCy Schubert 946c1d255d3SCy Schubert 947*a90b9d01SCy Schubert static bool hostapd_is_usable_punct_bitmap(struct hostapd_iface *iface) 948*a90b9d01SCy Schubert { 949*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE 950*a90b9d01SCy Schubert struct hostapd_config *conf = iface->conf; 951*a90b9d01SCy Schubert u16 bw; 952*a90b9d01SCy Schubert u8 start_chan; 953*a90b9d01SCy Schubert 954*a90b9d01SCy Schubert if (!conf->punct_bitmap) 955*a90b9d01SCy Schubert return true; 956*a90b9d01SCy Schubert 957*a90b9d01SCy Schubert if (!conf->ieee80211be) { 958*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 959*a90b9d01SCy Schubert "Currently RU puncturing is supported only if ieee80211be is enabled"); 960*a90b9d01SCy Schubert return false; 961*a90b9d01SCy Schubert } 962*a90b9d01SCy Schubert 963*a90b9d01SCy Schubert if (iface->freq >= 2412 && iface->freq <= 2484) { 964*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 965*a90b9d01SCy Schubert "RU puncturing not supported in 2.4 GHz"); 966*a90b9d01SCy Schubert return false; 967*a90b9d01SCy Schubert } 968*a90b9d01SCy Schubert 969*a90b9d01SCy Schubert /* 970*a90b9d01SCy Schubert * In the 6 GHz band, eht_oper_chwidth is ignored. Use operating class 971*a90b9d01SCy Schubert * to determine channel width. 972*a90b9d01SCy Schubert */ 973*a90b9d01SCy Schubert if (conf->op_class == 137) { 974*a90b9d01SCy Schubert bw = 320; 975*a90b9d01SCy Schubert start_chan = conf->eht_oper_centr_freq_seg0_idx - 30; 976*a90b9d01SCy Schubert } else { 977*a90b9d01SCy Schubert switch (conf->eht_oper_chwidth) { 978*a90b9d01SCy Schubert case 0: 979*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, 980*a90b9d01SCy Schubert "RU puncturing is supported only in 80 MHz and 160 MHz"); 981*a90b9d01SCy Schubert return false; 982*a90b9d01SCy Schubert case 1: 983*a90b9d01SCy Schubert bw = 80; 984*a90b9d01SCy Schubert start_chan = conf->eht_oper_centr_freq_seg0_idx - 6; 985*a90b9d01SCy Schubert break; 986*a90b9d01SCy Schubert case 2: 987*a90b9d01SCy Schubert bw = 160; 988*a90b9d01SCy Schubert start_chan = conf->eht_oper_centr_freq_seg0_idx - 14; 989*a90b9d01SCy Schubert break; 990*a90b9d01SCy Schubert default: 991*a90b9d01SCy Schubert return false; 992*a90b9d01SCy Schubert } 993*a90b9d01SCy Schubert } 994*a90b9d01SCy Schubert 995*a90b9d01SCy Schubert if (!is_punct_bitmap_valid(bw, (conf->channel - start_chan) / 4, 996*a90b9d01SCy Schubert conf->punct_bitmap)) { 997*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "Invalid puncturing bitmap"); 998*a90b9d01SCy Schubert return false; 999*a90b9d01SCy Schubert } 1000*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */ 1001*a90b9d01SCy Schubert 1002*a90b9d01SCy Schubert return true; 1003*a90b9d01SCy Schubert } 1004*a90b9d01SCy Schubert 1005*a90b9d01SCy Schubert 1006*a90b9d01SCy Schubert /* Returns: 1007*a90b9d01SCy Schubert * 1 = usable 1008*a90b9d01SCy Schubert * 0 = not usable 1009*a90b9d01SCy Schubert * -1 = not currently usable due to 6 GHz NO-IR 1010*a90b9d01SCy Schubert */ 1011c1d255d3SCy Schubert static int hostapd_is_usable_chans(struct hostapd_iface *iface) 1012c1d255d3SCy Schubert { 1013c1d255d3SCy Schubert int secondary_freq; 1014c1d255d3SCy Schubert struct hostapd_channel_data *pri_chan; 1015*a90b9d01SCy Schubert int err, err2; 1016c1d255d3SCy Schubert 1017c1d255d3SCy Schubert if (!iface->current_mode) 1018c1d255d3SCy Schubert return 0; 1019c1d255d3SCy Schubert pri_chan = hw_get_channel_freq(iface->current_mode->mode, 1020c1d255d3SCy Schubert iface->freq, NULL, 1021c1d255d3SCy Schubert iface->hw_features, 1022c1d255d3SCy Schubert iface->num_hw_features); 1023c1d255d3SCy Schubert if (!pri_chan) { 1024c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "Primary frequency not present"); 1025c1d255d3SCy Schubert return 0; 1026c1d255d3SCy Schubert } 1027*a90b9d01SCy Schubert 1028*a90b9d01SCy Schubert err = hostapd_is_usable_chan(iface, pri_chan->freq, 1); 1029*a90b9d01SCy Schubert if (err <= 0) { 1030c1d255d3SCy Schubert wpa_printf(MSG_ERROR, "Primary frequency not allowed"); 1031*a90b9d01SCy Schubert return err; 1032c1d255d3SCy Schubert } 1033*a90b9d01SCy Schubert err = hostapd_is_usable_edmg(iface); 1034*a90b9d01SCy Schubert if (err <= 0) 1035*a90b9d01SCy Schubert return err; 1036*a90b9d01SCy Schubert 1037*a90b9d01SCy Schubert if (!hostapd_is_usable_punct_bitmap(iface)) 10385b9c547cSRui Paulo return 0; 10395b9c547cSRui Paulo 10405b9c547cSRui Paulo if (!iface->conf->secondary_channel) 10415b9c547cSRui Paulo return 1; 10425b9c547cSRui Paulo 1043*a90b9d01SCy Schubert err = hostapd_is_usable_chan(iface, iface->freq + 1044*a90b9d01SCy Schubert iface->conf->secondary_channel * 20, 0); 1045*a90b9d01SCy Schubert if (err > 0) { 1046c1d255d3SCy Schubert if (iface->conf->secondary_channel == 1 && 1047c1d255d3SCy Schubert (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) 1048c1d255d3SCy Schubert return 1; 1049c1d255d3SCy Schubert if (iface->conf->secondary_channel == -1 && 1050c1d255d3SCy Schubert (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) 1051c1d255d3SCy Schubert return 1; 1052c1d255d3SCy Schubert } 105385732ac8SCy Schubert if (!iface->conf->ht40_plus_minus_allowed) 1054*a90b9d01SCy Schubert return err; 105585732ac8SCy Schubert 105685732ac8SCy Schubert /* Both HT40+ and HT40- are set, pick a valid secondary channel */ 1057c1d255d3SCy Schubert secondary_freq = iface->freq + 20; 1058*a90b9d01SCy Schubert err2 = hostapd_is_usable_chan(iface, secondary_freq, 0); 1059*a90b9d01SCy Schubert if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) { 106085732ac8SCy Schubert iface->conf->secondary_channel = 1; 106185732ac8SCy Schubert return 1; 106285732ac8SCy Schubert } 106385732ac8SCy Schubert 1064c1d255d3SCy Schubert secondary_freq = iface->freq - 20; 1065*a90b9d01SCy Schubert err2 = hostapd_is_usable_chan(iface, secondary_freq, 0); 1066*a90b9d01SCy Schubert if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) { 106785732ac8SCy Schubert iface->conf->secondary_channel = -1; 106885732ac8SCy Schubert return 1; 106985732ac8SCy Schubert } 107085732ac8SCy Schubert 1071*a90b9d01SCy Schubert return err; 10725b9c547cSRui Paulo } 10735b9c547cSRui Paulo 10745b9c547cSRui Paulo 1075*a90b9d01SCy Schubert static bool skip_mode(struct hostapd_iface *iface, 1076*a90b9d01SCy Schubert struct hostapd_hw_modes *mode) 1077*a90b9d01SCy Schubert { 1078*a90b9d01SCy Schubert int chan; 1079*a90b9d01SCy Schubert 1080*a90b9d01SCy Schubert if (iface->freq > 0 && !hw_mode_get_channel(mode, iface->freq, &chan)) 1081*a90b9d01SCy Schubert return true; 1082*a90b9d01SCy Schubert 1083*a90b9d01SCy Schubert if (is_6ghz_op_class(iface->conf->op_class) && iface->freq == 0 && 1084*a90b9d01SCy Schubert !mode->is_6ghz) 1085*a90b9d01SCy Schubert return true; 1086*a90b9d01SCy Schubert 1087*a90b9d01SCy Schubert return false; 1088*a90b9d01SCy Schubert } 1089*a90b9d01SCy Schubert 1090*a90b9d01SCy Schubert 1091*a90b9d01SCy Schubert int hostapd_determine_mode(struct hostapd_iface *iface) 1092c1d255d3SCy Schubert { 1093c1d255d3SCy Schubert int i; 1094c1d255d3SCy Schubert enum hostapd_hw_mode target_mode; 1095c1d255d3SCy Schubert 1096c1d255d3SCy Schubert if (iface->current_mode || 1097c1d255d3SCy Schubert iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY) 1098*a90b9d01SCy Schubert return 0; 1099c1d255d3SCy Schubert 1100c1d255d3SCy Schubert if (iface->freq < 4000) 1101c1d255d3SCy Schubert target_mode = HOSTAPD_MODE_IEEE80211G; 1102c1d255d3SCy Schubert else if (iface->freq > 50000) 1103c1d255d3SCy Schubert target_mode = HOSTAPD_MODE_IEEE80211AD; 1104c1d255d3SCy Schubert else 1105c1d255d3SCy Schubert target_mode = HOSTAPD_MODE_IEEE80211A; 1106c1d255d3SCy Schubert 1107c1d255d3SCy Schubert for (i = 0; i < iface->num_hw_features; i++) { 1108c1d255d3SCy Schubert struct hostapd_hw_modes *mode; 1109c1d255d3SCy Schubert 1110c1d255d3SCy Schubert mode = &iface->hw_features[i]; 1111c1d255d3SCy Schubert if (mode->mode == target_mode) { 1112*a90b9d01SCy Schubert if (skip_mode(iface, mode)) 1113*a90b9d01SCy Schubert continue; 1114*a90b9d01SCy Schubert 1115c1d255d3SCy Schubert iface->current_mode = mode; 1116c1d255d3SCy Schubert iface->conf->hw_mode = mode->mode; 1117c1d255d3SCy Schubert break; 1118c1d255d3SCy Schubert } 1119c1d255d3SCy Schubert } 1120c1d255d3SCy Schubert 1121*a90b9d01SCy Schubert if (!iface->current_mode) { 1122*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "ACS/CSA: Cannot decide mode"); 1123*a90b9d01SCy Schubert return -1; 1124*a90b9d01SCy Schubert } 1125*a90b9d01SCy Schubert return 0; 1126c1d255d3SCy Schubert } 1127c1d255d3SCy Schubert 1128c1d255d3SCy Schubert 11295b9c547cSRui Paulo static enum hostapd_chan_status 11305b9c547cSRui Paulo hostapd_check_chans(struct hostapd_iface *iface) 11315b9c547cSRui Paulo { 1132c1d255d3SCy Schubert if (iface->freq) { 1133*a90b9d01SCy Schubert int err; 1134*a90b9d01SCy Schubert 1135c1d255d3SCy Schubert hostapd_determine_mode(iface); 1136*a90b9d01SCy Schubert 1137*a90b9d01SCy Schubert err = hostapd_is_usable_chans(iface); 1138*a90b9d01SCy Schubert if (err <= 0) { 1139*a90b9d01SCy Schubert if (!err) 11405b9c547cSRui Paulo return HOSTAPD_CHAN_INVALID; 1141*a90b9d01SCy Schubert return HOSTAPD_CHAN_INVALID_NO_IR; 1142*a90b9d01SCy Schubert } 1143*a90b9d01SCy Schubert return HOSTAPD_CHAN_VALID; 11445b9c547cSRui Paulo } 11455b9c547cSRui Paulo 11465b9c547cSRui Paulo /* 11475b9c547cSRui Paulo * The user set channel=0 or channel=acs_survey 11485b9c547cSRui Paulo * which is used to trigger ACS. 11495b9c547cSRui Paulo */ 11505b9c547cSRui Paulo 11515b9c547cSRui Paulo switch (acs_init(iface)) { 11525b9c547cSRui Paulo case HOSTAPD_CHAN_ACS: 11535b9c547cSRui Paulo return HOSTAPD_CHAN_ACS; 1154*a90b9d01SCy Schubert case HOSTAPD_CHAN_INVALID_NO_IR: 1155*a90b9d01SCy Schubert return HOSTAPD_CHAN_INVALID_NO_IR; 11565b9c547cSRui Paulo case HOSTAPD_CHAN_VALID: 11575b9c547cSRui Paulo case HOSTAPD_CHAN_INVALID: 11585b9c547cSRui Paulo default: 11595b9c547cSRui Paulo return HOSTAPD_CHAN_INVALID; 11605b9c547cSRui Paulo } 11615b9c547cSRui Paulo } 11625b9c547cSRui Paulo 11635b9c547cSRui Paulo 11645b9c547cSRui Paulo static void hostapd_notify_bad_chans(struct hostapd_iface *iface) 11655b9c547cSRui Paulo { 1166325151a3SRui Paulo if (!iface->current_mode) { 1167325151a3SRui Paulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 1168325151a3SRui Paulo HOSTAPD_LEVEL_WARNING, 1169325151a3SRui Paulo "Hardware does not support configured mode"); 1170325151a3SRui Paulo return; 1171325151a3SRui Paulo } 11725b9c547cSRui Paulo hostapd_logger(iface->bss[0], NULL, 11735b9c547cSRui Paulo HOSTAPD_MODULE_IEEE80211, 11745b9c547cSRui Paulo HOSTAPD_LEVEL_WARNING, 1175c1d255d3SCy Schubert "Configured channel (%d) or frequency (%d) (secondary_channel=%d) not found from the channel list of the current mode (%d) %s", 11765b9c547cSRui Paulo iface->conf->channel, 1177c1d255d3SCy Schubert iface->freq, iface->conf->secondary_channel, 11785b9c547cSRui Paulo iface->current_mode->mode, 11795b9c547cSRui Paulo hostapd_hw_mode_txt(iface->current_mode->mode)); 11805b9c547cSRui Paulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 11815b9c547cSRui Paulo HOSTAPD_LEVEL_WARNING, 11825b9c547cSRui Paulo "Hardware does not support configured channel"); 11835b9c547cSRui Paulo } 11845b9c547cSRui Paulo 11855b9c547cSRui Paulo 11865b9c547cSRui Paulo int hostapd_acs_completed(struct hostapd_iface *iface, int err) 11875b9c547cSRui Paulo { 11885b9c547cSRui Paulo int ret = -1; 11895b9c547cSRui Paulo 11905b9c547cSRui Paulo if (err) 11915b9c547cSRui Paulo goto out; 11925b9c547cSRui Paulo 11935b9c547cSRui Paulo switch (hostapd_check_chans(iface)) { 11945b9c547cSRui Paulo case HOSTAPD_CHAN_VALID: 1195*a90b9d01SCy Schubert iface->is_no_ir = false; 11965b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, 11975b9c547cSRui Paulo ACS_EVENT_COMPLETED "freq=%d channel=%d", 1198c1d255d3SCy Schubert iface->freq, iface->conf->channel); 11995b9c547cSRui Paulo break; 12005b9c547cSRui Paulo case HOSTAPD_CHAN_ACS: 12015b9c547cSRui Paulo wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); 12025b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); 12035b9c547cSRui Paulo hostapd_notify_bad_chans(iface); 12045b9c547cSRui Paulo goto out; 1205*a90b9d01SCy Schubert case HOSTAPD_CHAN_INVALID_NO_IR: 1206*a90b9d01SCy Schubert iface->is_no_ir = true; 1207*a90b9d01SCy Schubert /* fall through */ 12085b9c547cSRui Paulo case HOSTAPD_CHAN_INVALID: 12095b9c547cSRui Paulo default: 12105b9c547cSRui Paulo wpa_printf(MSG_ERROR, "ACS picked unusable channels"); 12115b9c547cSRui Paulo wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); 12125b9c547cSRui Paulo hostapd_notify_bad_chans(iface); 12135b9c547cSRui Paulo goto out; 12145b9c547cSRui Paulo } 12155b9c547cSRui Paulo 12165b9c547cSRui Paulo ret = hostapd_check_ht_capab(iface); 12175b9c547cSRui Paulo if (ret < 0) 12185b9c547cSRui Paulo goto out; 12195b9c547cSRui Paulo if (ret == 1) { 12205b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback"); 12215b9c547cSRui Paulo return 0; 12225b9c547cSRui Paulo } 12235b9c547cSRui Paulo 12245b9c547cSRui Paulo ret = 0; 12255b9c547cSRui Paulo out: 12265b9c547cSRui Paulo return hostapd_setup_interface_complete(iface, ret); 12275b9c547cSRui Paulo } 12285b9c547cSRui Paulo 12295b9c547cSRui Paulo 1230e28a4053SRui Paulo /** 1231*a90b9d01SCy Schubert * hostapd_csa_update_hwmode - Update hardware mode 1232*a90b9d01SCy Schubert * @iface: Pointer to interface data. 1233*a90b9d01SCy Schubert * Returns: 0 on success, < 0 on failure 1234*a90b9d01SCy Schubert * 1235*a90b9d01SCy Schubert * Update hardware mode when the operating channel changed because of CSA. 1236*a90b9d01SCy Schubert */ 1237*a90b9d01SCy Schubert int hostapd_csa_update_hwmode(struct hostapd_iface *iface) 1238*a90b9d01SCy Schubert { 1239*a90b9d01SCy Schubert if (!iface || !iface->conf) 1240*a90b9d01SCy Schubert return -1; 1241*a90b9d01SCy Schubert 1242*a90b9d01SCy Schubert iface->current_mode = NULL; 1243*a90b9d01SCy Schubert iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY; 1244*a90b9d01SCy Schubert 1245*a90b9d01SCy Schubert return hostapd_determine_mode(iface); 1246*a90b9d01SCy Schubert } 1247*a90b9d01SCy Schubert 1248*a90b9d01SCy Schubert 1249*a90b9d01SCy Schubert /** 1250e28a4053SRui Paulo * hostapd_select_hw_mode - Select the hardware mode 1251e28a4053SRui Paulo * @iface: Pointer to interface data. 1252f05cddf9SRui Paulo * Returns: 0 on success, < 0 on failure 1253e28a4053SRui Paulo * 1254e28a4053SRui Paulo * Sets up the hardware mode, channel, rates, and passive scanning 1255e28a4053SRui Paulo * based on the configuration. 1256e28a4053SRui Paulo */ 1257e28a4053SRui Paulo int hostapd_select_hw_mode(struct hostapd_iface *iface) 1258e28a4053SRui Paulo { 12595b9c547cSRui Paulo int i; 1260e28a4053SRui Paulo 1261e28a4053SRui Paulo if (iface->num_hw_features < 1) 1262e28a4053SRui Paulo return -1; 1263e28a4053SRui Paulo 12645b9c547cSRui Paulo if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G || 1265206b73d0SCy Schubert iface->conf->ieee80211n || iface->conf->ieee80211ac || 1266*a90b9d01SCy Schubert iface->conf->ieee80211ax || iface->conf->ieee80211be) && 12675b9c547cSRui Paulo iface->conf->channel == 14) { 1268*a90b9d01SCy Schubert wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE/EHT on channel 14"); 12695b9c547cSRui Paulo iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B; 12705b9c547cSRui Paulo iface->conf->ieee80211n = 0; 12715b9c547cSRui Paulo iface->conf->ieee80211ac = 0; 1272206b73d0SCy Schubert iface->conf->ieee80211ax = 0; 1273*a90b9d01SCy Schubert iface->conf->ieee80211be = 0; 12745b9c547cSRui Paulo } 12755b9c547cSRui Paulo 1276e28a4053SRui Paulo iface->current_mode = NULL; 1277e28a4053SRui Paulo for (i = 0; i < iface->num_hw_features; i++) { 1278e28a4053SRui Paulo struct hostapd_hw_modes *mode = &iface->hw_features[i]; 1279c1d255d3SCy Schubert 1280e28a4053SRui Paulo if (mode->mode == iface->conf->hw_mode) { 1281*a90b9d01SCy Schubert if (skip_mode(iface, mode)) 1282c1d255d3SCy Schubert continue; 1283c1d255d3SCy Schubert 1284e28a4053SRui Paulo iface->current_mode = mode; 1285e28a4053SRui Paulo break; 1286e28a4053SRui Paulo } 1287e28a4053SRui Paulo } 1288e28a4053SRui Paulo 1289e28a4053SRui Paulo if (iface->current_mode == NULL) { 1290c1d255d3SCy Schubert if ((iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && 1291c1d255d3SCy Schubert (iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) { 1292c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1293c1d255d3SCy Schubert "Using offloaded hw_mode=any ACS"); 1294c1d255d3SCy Schubert } else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && 1295c1d255d3SCy Schubert iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) { 1296c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 1297c1d255d3SCy Schubert "Using internal ACS for hw_mode=any"); 1298c1d255d3SCy Schubert } else { 1299325151a3SRui Paulo wpa_printf(MSG_ERROR, 1300325151a3SRui Paulo "Hardware does not support configured mode"); 1301325151a3SRui Paulo hostapd_logger(iface->bss[0], NULL, 1302325151a3SRui Paulo HOSTAPD_MODULE_IEEE80211, 1303e28a4053SRui Paulo HOSTAPD_LEVEL_WARNING, 1304325151a3SRui Paulo "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)", 1305f05cddf9SRui Paulo (int) iface->conf->hw_mode); 1306f05cddf9SRui Paulo return -2; 1307e28a4053SRui Paulo } 1308325151a3SRui Paulo } 1309e28a4053SRui Paulo 13105b9c547cSRui Paulo switch (hostapd_check_chans(iface)) { 13115b9c547cSRui Paulo case HOSTAPD_CHAN_VALID: 1312*a90b9d01SCy Schubert iface->is_no_ir = false; 13135b9c547cSRui Paulo return 0; 13145b9c547cSRui Paulo case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */ 13155b9c547cSRui Paulo return 1; 1316*a90b9d01SCy Schubert case HOSTAPD_CHAN_INVALID_NO_IR: 1317*a90b9d01SCy Schubert iface->is_no_ir = true; 1318*a90b9d01SCy Schubert /* fall through */ 13195b9c547cSRui Paulo case HOSTAPD_CHAN_INVALID: 13205b9c547cSRui Paulo default: 13215b9c547cSRui Paulo hostapd_notify_bad_chans(iface); 1322f05cddf9SRui Paulo return -3; 1323e28a4053SRui Paulo } 1324e28a4053SRui Paulo } 1325e28a4053SRui Paulo 1326e28a4053SRui Paulo 1327e28a4053SRui Paulo const char * hostapd_hw_mode_txt(int mode) 1328e28a4053SRui Paulo { 1329e28a4053SRui Paulo switch (mode) { 1330e28a4053SRui Paulo case HOSTAPD_MODE_IEEE80211A: 1331e28a4053SRui Paulo return "IEEE 802.11a"; 1332e28a4053SRui Paulo case HOSTAPD_MODE_IEEE80211B: 1333e28a4053SRui Paulo return "IEEE 802.11b"; 1334e28a4053SRui Paulo case HOSTAPD_MODE_IEEE80211G: 1335e28a4053SRui Paulo return "IEEE 802.11g"; 1336f05cddf9SRui Paulo case HOSTAPD_MODE_IEEE80211AD: 1337f05cddf9SRui Paulo return "IEEE 802.11ad"; 1338e28a4053SRui Paulo default: 1339e28a4053SRui Paulo return "UNKNOWN"; 1340e28a4053SRui Paulo } 1341e28a4053SRui Paulo } 1342e28a4053SRui Paulo 1343e28a4053SRui Paulo 1344e28a4053SRui Paulo int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) 1345e28a4053SRui Paulo { 13465b9c547cSRui Paulo return hw_get_freq(hapd->iface->current_mode, chan); 1347e28a4053SRui Paulo } 1348e28a4053SRui Paulo 1349e28a4053SRui Paulo 1350e28a4053SRui Paulo int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) 1351e28a4053SRui Paulo { 135285732ac8SCy Schubert int i, channel; 135385732ac8SCy Schubert struct hostapd_hw_modes *mode; 135485732ac8SCy Schubert 1355206b73d0SCy Schubert if (hapd->iface->current_mode) { 1356c1d255d3SCy Schubert channel = hw_get_chan(hapd->iface->current_mode->mode, freq, 1357c1d255d3SCy Schubert hapd->iface->hw_features, 1358c1d255d3SCy Schubert hapd->iface->num_hw_features); 135985732ac8SCy Schubert if (channel) 136085732ac8SCy Schubert return channel; 1361206b73d0SCy Schubert } 1362206b73d0SCy Schubert 136385732ac8SCy Schubert /* Check other available modes since the channel list for the current 136485732ac8SCy Schubert * mode did not include the specified frequency. */ 1365206b73d0SCy Schubert if (!hapd->iface->hw_features) 1366206b73d0SCy Schubert return 0; 136785732ac8SCy Schubert for (i = 0; i < hapd->iface->num_hw_features; i++) { 136885732ac8SCy Schubert mode = &hapd->iface->hw_features[i]; 1369c1d255d3SCy Schubert channel = hw_get_chan(mode->mode, freq, 1370c1d255d3SCy Schubert hapd->iface->hw_features, 1371c1d255d3SCy Schubert hapd->iface->num_hw_features); 137285732ac8SCy Schubert if (channel) 137385732ac8SCy Schubert return channel; 137485732ac8SCy Schubert } 137585732ac8SCy Schubert return 0; 1376e28a4053SRui Paulo } 1377c1d255d3SCy Schubert 1378c1d255d3SCy Schubert 1379c1d255d3SCy Schubert int hostapd_hw_skip_mode(struct hostapd_iface *iface, 1380c1d255d3SCy Schubert struct hostapd_hw_modes *mode) 1381c1d255d3SCy Schubert { 1382c1d255d3SCy Schubert int i; 1383c1d255d3SCy Schubert 1384c1d255d3SCy Schubert if (iface->current_mode) 1385c1d255d3SCy Schubert return mode != iface->current_mode; 1386c1d255d3SCy Schubert if (mode->mode != HOSTAPD_MODE_IEEE80211B) 1387c1d255d3SCy Schubert return 0; 1388c1d255d3SCy Schubert for (i = 0; i < iface->num_hw_features; i++) { 1389c1d255d3SCy Schubert if (iface->hw_features[i].mode == HOSTAPD_MODE_IEEE80211G) 1390c1d255d3SCy Schubert return 1; 1391c1d255d3SCy Schubert } 1392c1d255d3SCy Schubert return 0; 1393c1d255d3SCy Schubert } 1394