1214501Srpaulo/* 2214501Srpaulo * hostapd / Hardware feature query and different modes 3214501Srpaulo * Copyright 2002-2003, Instant802 Networks, Inc. 4214501Srpaulo * Copyright 2005-2006, Devicescape Software, Inc. 5252726Srpaulo * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> 6214501Srpaulo * 7214501Srpaulo * This program is free software; you can redistribute it and/or modify 8214501Srpaulo * it under the terms of the GNU General Public License version 2 as 9214501Srpaulo * published by the Free Software Foundation. 10214501Srpaulo * 11214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD 12214501Srpaulo * license. 13214501Srpaulo * 14214501Srpaulo * See README and COPYING for more details. 15214501Srpaulo */ 16214501Srpaulo 17214501Srpaulo#include "utils/includes.h" 18214501Srpaulo 19214501Srpaulo#include "utils/common.h" 20214501Srpaulo#include "utils/eloop.h" 21214501Srpaulo#include "common/ieee802_11_defs.h" 22214501Srpaulo#include "common/ieee802_11_common.h" 23214501Srpaulo#include "drivers/driver.h" 24214501Srpaulo#include "hostapd.h" 25214501Srpaulo#include "ap_config.h" 26214501Srpaulo#include "ap_drv_ops.h" 27214501Srpaulo#include "hw_features.h" 28214501Srpaulo 29214501Srpaulo 30214501Srpaulovoid hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, 31214501Srpaulo size_t num_hw_features) 32214501Srpaulo{ 33214501Srpaulo size_t i; 34214501Srpaulo 35214501Srpaulo if (hw_features == NULL) 36214501Srpaulo return; 37214501Srpaulo 38214501Srpaulo for (i = 0; i < num_hw_features; i++) { 39214501Srpaulo os_free(hw_features[i].channels); 40214501Srpaulo os_free(hw_features[i].rates); 41214501Srpaulo } 42214501Srpaulo 43214501Srpaulo os_free(hw_features); 44214501Srpaulo} 45214501Srpaulo 46214501Srpaulo 47214501Srpauloint hostapd_get_hw_features(struct hostapd_iface *iface) 48214501Srpaulo{ 49214501Srpaulo struct hostapd_data *hapd = iface->bss[0]; 50214501Srpaulo int ret = 0, i, j; 51214501Srpaulo u16 num_modes, flags; 52214501Srpaulo struct hostapd_hw_modes *modes; 53214501Srpaulo 54214501Srpaulo if (hostapd_drv_none(hapd)) 55214501Srpaulo return -1; 56214501Srpaulo modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); 57214501Srpaulo if (modes == NULL) { 58214501Srpaulo hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, 59214501Srpaulo HOSTAPD_LEVEL_DEBUG, 60214501Srpaulo "Fetching hardware channel/rate support not " 61214501Srpaulo "supported."); 62214501Srpaulo return -1; 63214501Srpaulo } 64214501Srpaulo 65214501Srpaulo iface->hw_flags = flags; 66214501Srpaulo 67214501Srpaulo hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); 68214501Srpaulo iface->hw_features = modes; 69214501Srpaulo iface->num_hw_features = num_modes; 70214501Srpaulo 71214501Srpaulo for (i = 0; i < num_modes; i++) { 72214501Srpaulo struct hostapd_hw_modes *feature = &modes[i]; 73214501Srpaulo /* set flag for channels we can use in current regulatory 74214501Srpaulo * domain */ 75214501Srpaulo for (j = 0; j < feature->num_channels; j++) { 76214501Srpaulo /* 77214501Srpaulo * Disable all channels that are marked not to allow 78214501Srpaulo * IBSS operation or active scanning. In addition, 79214501Srpaulo * disable all channels that require radar detection, 80214501Srpaulo * since that (in addition to full DFS) is not yet 81214501Srpaulo * supported. 82214501Srpaulo */ 83214501Srpaulo if (feature->channels[j].flag & 84214501Srpaulo (HOSTAPD_CHAN_NO_IBSS | 85214501Srpaulo HOSTAPD_CHAN_PASSIVE_SCAN | 86214501Srpaulo HOSTAPD_CHAN_RADAR)) 87214501Srpaulo feature->channels[j].flag |= 88214501Srpaulo HOSTAPD_CHAN_DISABLED; 89214501Srpaulo if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) 90214501Srpaulo continue; 91214501Srpaulo wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " 92214501Srpaulo "chan=%d freq=%d MHz max_tx_power=%d dBm", 93214501Srpaulo feature->mode, 94214501Srpaulo feature->channels[j].chan, 95214501Srpaulo feature->channels[j].freq, 96214501Srpaulo feature->channels[j].max_tx_power); 97214501Srpaulo } 98214501Srpaulo } 99214501Srpaulo 100214501Srpaulo return ret; 101214501Srpaulo} 102214501Srpaulo 103214501Srpaulo 104252726Srpauloint hostapd_prepare_rates(struct hostapd_iface *iface, 105252726Srpaulo struct hostapd_hw_modes *mode) 106214501Srpaulo{ 107214501Srpaulo int i, num_basic_rates = 0; 108214501Srpaulo int basic_rates_a[] = { 60, 120, 240, -1 }; 109214501Srpaulo int basic_rates_b[] = { 10, 20, -1 }; 110214501Srpaulo int basic_rates_g[] = { 10, 20, 55, 110, -1 }; 111214501Srpaulo int *basic_rates; 112214501Srpaulo 113252726Srpaulo if (iface->conf->basic_rates) 114252726Srpaulo basic_rates = iface->conf->basic_rates; 115214501Srpaulo else switch (mode->mode) { 116214501Srpaulo case HOSTAPD_MODE_IEEE80211A: 117214501Srpaulo basic_rates = basic_rates_a; 118214501Srpaulo break; 119214501Srpaulo case HOSTAPD_MODE_IEEE80211B: 120214501Srpaulo basic_rates = basic_rates_b; 121214501Srpaulo break; 122214501Srpaulo case HOSTAPD_MODE_IEEE80211G: 123214501Srpaulo basic_rates = basic_rates_g; 124214501Srpaulo break; 125252726Srpaulo case HOSTAPD_MODE_IEEE80211AD: 126252726Srpaulo return 0; /* No basic rates for 11ad */ 127214501Srpaulo default: 128214501Srpaulo return -1; 129214501Srpaulo } 130214501Srpaulo 131252726Srpaulo i = 0; 132252726Srpaulo while (basic_rates[i] >= 0) 133252726Srpaulo i++; 134252726Srpaulo if (i) 135252726Srpaulo i++; /* -1 termination */ 136252726Srpaulo os_free(iface->basic_rates); 137252726Srpaulo iface->basic_rates = os_malloc(i * sizeof(int)); 138252726Srpaulo if (iface->basic_rates) 139252726Srpaulo os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); 140214501Srpaulo 141252726Srpaulo os_free(iface->current_rates); 142252726Srpaulo iface->num_rates = 0; 143214501Srpaulo 144252726Srpaulo iface->current_rates = 145252726Srpaulo os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); 146252726Srpaulo if (!iface->current_rates) { 147214501Srpaulo wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " 148214501Srpaulo "table."); 149214501Srpaulo return -1; 150214501Srpaulo } 151214501Srpaulo 152214501Srpaulo for (i = 0; i < mode->num_rates; i++) { 153214501Srpaulo struct hostapd_rate_data *rate; 154214501Srpaulo 155252726Srpaulo if (iface->conf->supported_rates && 156252726Srpaulo !hostapd_rate_found(iface->conf->supported_rates, 157214501Srpaulo mode->rates[i])) 158214501Srpaulo continue; 159214501Srpaulo 160252726Srpaulo rate = &iface->current_rates[iface->num_rates]; 161214501Srpaulo rate->rate = mode->rates[i]; 162214501Srpaulo if (hostapd_rate_found(basic_rates, rate->rate)) { 163214501Srpaulo rate->flags |= HOSTAPD_RATE_BASIC; 164214501Srpaulo num_basic_rates++; 165214501Srpaulo } 166214501Srpaulo wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", 167252726Srpaulo iface->num_rates, rate->rate, rate->flags); 168252726Srpaulo iface->num_rates++; 169214501Srpaulo } 170214501Srpaulo 171252726Srpaulo if ((iface->num_rates == 0 || num_basic_rates == 0) && 172252726Srpaulo (!iface->conf->ieee80211n || !iface->conf->require_ht)) { 173214501Srpaulo wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " 174214501Srpaulo "rate sets (%d,%d).", 175252726Srpaulo iface->num_rates, num_basic_rates); 176214501Srpaulo return -1; 177214501Srpaulo } 178214501Srpaulo 179214501Srpaulo return 0; 180214501Srpaulo} 181214501Srpaulo 182214501Srpaulo 183214501Srpaulo#ifdef CONFIG_IEEE80211N 184214501Srpaulostatic int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) 185214501Srpaulo{ 186214501Srpaulo int sec_chan, ok, j, first; 187214501Srpaulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 188214501Srpaulo 184, 192 }; 189214501Srpaulo size_t k; 190214501Srpaulo 191214501Srpaulo if (!iface->conf->secondary_channel) 192214501Srpaulo return 1; /* HT40 not used */ 193214501Srpaulo 194214501Srpaulo sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; 195214501Srpaulo wpa_printf(MSG_DEBUG, "HT40: control channel: %d " 196214501Srpaulo "secondary channel: %d", 197214501Srpaulo iface->conf->channel, sec_chan); 198214501Srpaulo 199214501Srpaulo /* Verify that HT40 secondary channel is an allowed 20 MHz 200214501Srpaulo * channel */ 201214501Srpaulo ok = 0; 202214501Srpaulo for (j = 0; j < iface->current_mode->num_channels; j++) { 203214501Srpaulo struct hostapd_channel_data *chan = 204214501Srpaulo &iface->current_mode->channels[j]; 205214501Srpaulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 206214501Srpaulo chan->chan == sec_chan) { 207214501Srpaulo ok = 1; 208214501Srpaulo break; 209214501Srpaulo } 210214501Srpaulo } 211214501Srpaulo if (!ok) { 212214501Srpaulo wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 213214501Srpaulo sec_chan); 214214501Srpaulo return 0; 215214501Srpaulo } 216214501Srpaulo 217214501Srpaulo /* 218214501Srpaulo * Verify that HT40 primary,secondary channel pair is allowed per 219214501Srpaulo * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 220214501Srpaulo * 2.4 GHz rules allow all cases where the secondary channel fits into 221214501Srpaulo * the list of allowed channels (already checked above). 222214501Srpaulo */ 223214501Srpaulo if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) 224214501Srpaulo return 1; 225214501Srpaulo 226214501Srpaulo if (iface->conf->secondary_channel > 0) 227214501Srpaulo first = iface->conf->channel; 228214501Srpaulo else 229214501Srpaulo first = sec_chan; 230214501Srpaulo 231214501Srpaulo ok = 0; 232214501Srpaulo for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { 233214501Srpaulo if (first == allowed[k]) { 234214501Srpaulo ok = 1; 235214501Srpaulo break; 236214501Srpaulo } 237214501Srpaulo } 238214501Srpaulo if (!ok) { 239214501Srpaulo wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 240214501Srpaulo iface->conf->channel, 241214501Srpaulo iface->conf->secondary_channel); 242214501Srpaulo return 0; 243214501Srpaulo } 244214501Srpaulo 245214501Srpaulo return 1; 246214501Srpaulo} 247214501Srpaulo 248214501Srpaulo 249214501Srpaulostatic void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) 250214501Srpaulo{ 251214501Srpaulo if (iface->conf->secondary_channel > 0) { 252214501Srpaulo iface->conf->channel += 4; 253214501Srpaulo iface->conf->secondary_channel = -1; 254214501Srpaulo } else { 255214501Srpaulo iface->conf->channel -= 4; 256214501Srpaulo iface->conf->secondary_channel = 1; 257214501Srpaulo } 258214501Srpaulo} 259214501Srpaulo 260214501Srpaulo 261214501Srpaulostatic void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, 262214501Srpaulo int *pri_chan, int *sec_chan) 263214501Srpaulo{ 264214501Srpaulo struct ieee80211_ht_operation *oper; 265214501Srpaulo struct ieee802_11_elems elems; 266214501Srpaulo 267214501Srpaulo *pri_chan = *sec_chan = 0; 268214501Srpaulo 269214501Srpaulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 270214501Srpaulo if (elems.ht_operation && 271214501Srpaulo elems.ht_operation_len >= sizeof(*oper)) { 272214501Srpaulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 273214501Srpaulo *pri_chan = oper->control_chan; 274214501Srpaulo if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { 275252726Srpaulo int sec = oper->ht_param & 276252726Srpaulo HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; 277252726Srpaulo if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 278214501Srpaulo *sec_chan = *pri_chan + 4; 279252726Srpaulo else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 280214501Srpaulo *sec_chan = *pri_chan - 4; 281214501Srpaulo } 282214501Srpaulo } 283214501Srpaulo} 284214501Srpaulo 285214501Srpaulo 286214501Srpaulostatic int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, 287214501Srpaulo struct wpa_scan_results *scan_res) 288214501Srpaulo{ 289214501Srpaulo int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss; 290214501Srpaulo int bss_pri_chan, bss_sec_chan; 291214501Srpaulo size_t i; 292214501Srpaulo int match; 293214501Srpaulo 294214501Srpaulo pri_chan = iface->conf->channel; 295214501Srpaulo sec_chan = iface->conf->secondary_channel * 4; 296214501Srpaulo pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan); 297214501Srpaulo if (iface->conf->secondary_channel > 0) 298214501Srpaulo sec_freq = pri_freq + 20; 299214501Srpaulo else 300214501Srpaulo sec_freq = pri_freq - 20; 301214501Srpaulo 302214501Srpaulo /* 303214501Srpaulo * Switch PRI/SEC channels if Beacons were detected on selected SEC 304214501Srpaulo * channel, but not on selected PRI channel. 305214501Srpaulo */ 306214501Srpaulo pri_bss = sec_bss = 0; 307214501Srpaulo for (i = 0; i < scan_res->num; i++) { 308214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 309214501Srpaulo if (bss->freq == pri_freq) 310214501Srpaulo pri_bss++; 311214501Srpaulo else if (bss->freq == sec_freq) 312214501Srpaulo sec_bss++; 313214501Srpaulo } 314214501Srpaulo if (sec_bss && !pri_bss) { 315214501Srpaulo wpa_printf(MSG_INFO, "Switch own primary and secondary " 316214501Srpaulo "channel to get secondary channel with no Beacons " 317214501Srpaulo "from other BSSes"); 318214501Srpaulo ieee80211n_switch_pri_sec(iface); 319214501Srpaulo } 320214501Srpaulo 321214501Srpaulo /* 322214501Srpaulo * Match PRI/SEC channel with any existing HT40 BSS on the same 323214501Srpaulo * channels that we are about to use (if already mixed order in 324214501Srpaulo * existing BSSes, use own preference). 325214501Srpaulo */ 326214501Srpaulo match = 0; 327214501Srpaulo for (i = 0; i < scan_res->num; i++) { 328214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 329214501Srpaulo ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 330214501Srpaulo if (pri_chan == bss_pri_chan && 331214501Srpaulo sec_chan == bss_sec_chan) { 332214501Srpaulo match = 1; 333214501Srpaulo break; 334214501Srpaulo } 335214501Srpaulo } 336214501Srpaulo if (!match) { 337214501Srpaulo for (i = 0; i < scan_res->num; i++) { 338214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 339214501Srpaulo ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, 340214501Srpaulo &bss_sec_chan); 341214501Srpaulo if (pri_chan == bss_sec_chan && 342214501Srpaulo sec_chan == bss_pri_chan) { 343214501Srpaulo wpa_printf(MSG_INFO, "Switch own primary and " 344214501Srpaulo "secondary channel due to BSS " 345214501Srpaulo "overlap with " MACSTR, 346214501Srpaulo MAC2STR(bss->bssid)); 347214501Srpaulo ieee80211n_switch_pri_sec(iface); 348214501Srpaulo break; 349214501Srpaulo } 350214501Srpaulo } 351214501Srpaulo } 352214501Srpaulo 353214501Srpaulo return 1; 354214501Srpaulo} 355214501Srpaulo 356214501Srpaulo 357214501Srpaulostatic int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, 358214501Srpaulo struct wpa_scan_results *scan_res) 359214501Srpaulo{ 360214501Srpaulo int pri_freq, sec_freq; 361214501Srpaulo int affected_start, affected_end; 362214501Srpaulo size_t i; 363214501Srpaulo 364214501Srpaulo pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 365214501Srpaulo if (iface->conf->secondary_channel > 0) 366214501Srpaulo sec_freq = pri_freq + 20; 367214501Srpaulo else 368214501Srpaulo sec_freq = pri_freq - 20; 369214501Srpaulo affected_start = (pri_freq + sec_freq) / 2 - 25; 370214501Srpaulo affected_end = (pri_freq + sec_freq) / 2 + 25; 371214501Srpaulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 372214501Srpaulo affected_start, affected_end); 373214501Srpaulo for (i = 0; i < scan_res->num; i++) { 374214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 375214501Srpaulo int pri = bss->freq; 376214501Srpaulo int sec = pri; 377214501Srpaulo int sec_chan, pri_chan; 378214501Srpaulo 379214501Srpaulo ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan); 380214501Srpaulo 381214501Srpaulo if (sec_chan) { 382214501Srpaulo if (sec_chan < pri_chan) 383214501Srpaulo sec = pri - 20; 384214501Srpaulo else 385214501Srpaulo sec = pri + 20; 386214501Srpaulo } 387214501Srpaulo 388214501Srpaulo if ((pri < affected_start || pri > affected_end) && 389214501Srpaulo (sec < affected_start || sec > affected_end)) 390214501Srpaulo continue; /* not within affected channel range */ 391214501Srpaulo 392214501Srpaulo wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 393214501Srpaulo " freq=%d pri=%d sec=%d", 394214501Srpaulo MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 395214501Srpaulo 396214501Srpaulo if (sec_chan) { 397214501Srpaulo if (pri_freq != pri || sec_freq != sec) { 398214501Srpaulo wpa_printf(MSG_DEBUG, "40 MHz pri/sec " 399214501Srpaulo "mismatch with BSS " MACSTR 400214501Srpaulo " <%d,%d> (chan=%d%c) vs. <%d,%d>", 401214501Srpaulo MAC2STR(bss->bssid), 402214501Srpaulo pri, sec, pri_chan, 403214501Srpaulo sec > pri ? '+' : '-', 404214501Srpaulo pri_freq, sec_freq); 405214501Srpaulo return 0; 406214501Srpaulo } 407214501Srpaulo } 408214501Srpaulo 409214501Srpaulo /* TODO: 40 MHz intolerant */ 410214501Srpaulo } 411214501Srpaulo 412214501Srpaulo return 1; 413214501Srpaulo} 414214501Srpaulo 415214501Srpaulo 416214501Srpaulostatic void ieee80211n_check_scan(struct hostapd_iface *iface) 417214501Srpaulo{ 418214501Srpaulo struct wpa_scan_results *scan_res; 419214501Srpaulo int oper40; 420252726Srpaulo int res; 421214501Srpaulo 422214501Srpaulo /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is 423252726Srpaulo * allowed per IEEE Std 802.11-2012, 10.15.3.2 */ 424214501Srpaulo 425214501Srpaulo iface->scan_cb = NULL; 426214501Srpaulo 427214501Srpaulo scan_res = hostapd_driver_get_scan_results(iface->bss[0]); 428214501Srpaulo if (scan_res == NULL) { 429214501Srpaulo hostapd_setup_interface_complete(iface, 1); 430214501Srpaulo return; 431214501Srpaulo } 432214501Srpaulo 433214501Srpaulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) 434214501Srpaulo oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); 435214501Srpaulo else 436214501Srpaulo oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); 437214501Srpaulo wpa_scan_results_free(scan_res); 438214501Srpaulo 439214501Srpaulo if (!oper40) { 440214501Srpaulo wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " 441214501Srpaulo "channel pri=%d sec=%d based on overlapping BSSes", 442214501Srpaulo iface->conf->channel, 443214501Srpaulo iface->conf->channel + 444214501Srpaulo iface->conf->secondary_channel * 4); 445214501Srpaulo iface->conf->secondary_channel = 0; 446214501Srpaulo iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; 447214501Srpaulo } 448214501Srpaulo 449252726Srpaulo res = ieee80211n_allowed_ht40_channel_pair(iface); 450252726Srpaulo hostapd_setup_interface_complete(iface, !res); 451214501Srpaulo} 452214501Srpaulo 453214501Srpaulo 454252726Srpaulostatic void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, 455252726Srpaulo struct wpa_driver_scan_params *params) 456252726Srpaulo{ 457252726Srpaulo /* Scan only the affected frequency range */ 458252726Srpaulo int pri_freq, sec_freq; 459252726Srpaulo int affected_start, affected_end; 460252726Srpaulo int i, pos; 461252726Srpaulo struct hostapd_hw_modes *mode; 462252726Srpaulo 463252726Srpaulo if (iface->current_mode == NULL) 464252726Srpaulo return; 465252726Srpaulo 466252726Srpaulo pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 467252726Srpaulo if (iface->conf->secondary_channel > 0) 468252726Srpaulo sec_freq = pri_freq + 20; 469252726Srpaulo else 470252726Srpaulo sec_freq = pri_freq - 20; 471252726Srpaulo affected_start = (pri_freq + sec_freq) / 2 - 25; 472252726Srpaulo affected_end = (pri_freq + sec_freq) / 2 + 25; 473252726Srpaulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 474252726Srpaulo affected_start, affected_end); 475252726Srpaulo 476252726Srpaulo mode = iface->current_mode; 477252726Srpaulo params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); 478252726Srpaulo if (params->freqs == NULL) 479252726Srpaulo return; 480252726Srpaulo pos = 0; 481252726Srpaulo 482252726Srpaulo for (i = 0; i < mode->num_channels; i++) { 483252726Srpaulo struct hostapd_channel_data *chan = &mode->channels[i]; 484252726Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) 485252726Srpaulo continue; 486252726Srpaulo if (chan->freq < affected_start || 487252726Srpaulo chan->freq > affected_end) 488252726Srpaulo continue; 489252726Srpaulo params->freqs[pos++] = chan->freq; 490252726Srpaulo } 491252726Srpaulo} 492252726Srpaulo 493252726Srpaulo 494214501Srpaulostatic int ieee80211n_check_40mhz(struct hostapd_iface *iface) 495214501Srpaulo{ 496214501Srpaulo struct wpa_driver_scan_params params; 497214501Srpaulo 498214501Srpaulo if (!iface->conf->secondary_channel) 499214501Srpaulo return 0; /* HT40 not used */ 500214501Srpaulo 501214501Srpaulo wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " 502214501Srpaulo "40 MHz channel"); 503214501Srpaulo os_memset(¶ms, 0, sizeof(params)); 504252726Srpaulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) 505252726Srpaulo ieee80211n_scan_channels_2g4(iface, ¶ms); 506214501Srpaulo if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { 507214501Srpaulo wpa_printf(MSG_ERROR, "Failed to request a scan of " 508214501Srpaulo "neighboring BSSes"); 509252726Srpaulo os_free(params.freqs); 510214501Srpaulo return -1; 511214501Srpaulo } 512252726Srpaulo os_free(params.freqs); 513214501Srpaulo 514214501Srpaulo iface->scan_cb = ieee80211n_check_scan; 515214501Srpaulo return 1; 516214501Srpaulo} 517214501Srpaulo 518214501Srpaulo 519214501Srpaulostatic int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) 520214501Srpaulo{ 521214501Srpaulo u16 hw = iface->current_mode->ht_capab; 522214501Srpaulo u16 conf = iface->conf->ht_capab; 523214501Srpaulo 524214501Srpaulo if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && 525214501Srpaulo !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { 526214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 527214501Srpaulo "HT capability [LDPC]"); 528214501Srpaulo return 0; 529214501Srpaulo } 530214501Srpaulo 531214501Srpaulo if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 532214501Srpaulo !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { 533214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 534214501Srpaulo "HT capability [HT40*]"); 535214501Srpaulo return 0; 536214501Srpaulo } 537214501Srpaulo 538214501Srpaulo if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && 539214501Srpaulo (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { 540214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 541214501Srpaulo "HT capability [SMPS-*]"); 542214501Srpaulo return 0; 543214501Srpaulo } 544214501Srpaulo 545214501Srpaulo if ((conf & HT_CAP_INFO_GREEN_FIELD) && 546214501Srpaulo !(hw & HT_CAP_INFO_GREEN_FIELD)) { 547214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 548214501Srpaulo "HT capability [GF]"); 549214501Srpaulo return 0; 550214501Srpaulo } 551214501Srpaulo 552214501Srpaulo if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && 553214501Srpaulo !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { 554214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 555214501Srpaulo "HT capability [SHORT-GI-20]"); 556214501Srpaulo return 0; 557214501Srpaulo } 558214501Srpaulo 559214501Srpaulo if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && 560214501Srpaulo !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { 561214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 562214501Srpaulo "HT capability [SHORT-GI-40]"); 563214501Srpaulo return 0; 564214501Srpaulo } 565214501Srpaulo 566214501Srpaulo if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { 567214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 568214501Srpaulo "HT capability [TX-STBC]"); 569214501Srpaulo return 0; 570214501Srpaulo } 571214501Srpaulo 572214501Srpaulo if ((conf & HT_CAP_INFO_RX_STBC_MASK) > 573214501Srpaulo (hw & HT_CAP_INFO_RX_STBC_MASK)) { 574214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 575214501Srpaulo "HT capability [RX-STBC*]"); 576214501Srpaulo return 0; 577214501Srpaulo } 578214501Srpaulo 579214501Srpaulo if ((conf & HT_CAP_INFO_DELAYED_BA) && 580214501Srpaulo !(hw & HT_CAP_INFO_DELAYED_BA)) { 581214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 582214501Srpaulo "HT capability [DELAYED-BA]"); 583214501Srpaulo return 0; 584214501Srpaulo } 585214501Srpaulo 586214501Srpaulo if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && 587214501Srpaulo !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { 588214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 589214501Srpaulo "HT capability [MAX-AMSDU-7935]"); 590214501Srpaulo return 0; 591214501Srpaulo } 592214501Srpaulo 593214501Srpaulo if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && 594214501Srpaulo !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { 595214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 596214501Srpaulo "HT capability [DSSS_CCK-40]"); 597214501Srpaulo return 0; 598214501Srpaulo } 599214501Srpaulo 600214501Srpaulo if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { 601214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 602214501Srpaulo "HT capability [PSMP]"); 603214501Srpaulo return 0; 604214501Srpaulo } 605214501Srpaulo 606214501Srpaulo if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && 607214501Srpaulo !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { 608214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 609214501Srpaulo "HT capability [LSIG-TXOP-PROT]"); 610214501Srpaulo return 0; 611214501Srpaulo } 612214501Srpaulo 613214501Srpaulo return 1; 614214501Srpaulo} 615214501Srpaulo 616214501Srpaulo#endif /* CONFIG_IEEE80211N */ 617214501Srpaulo 618214501Srpaulo 619214501Srpauloint hostapd_check_ht_capab(struct hostapd_iface *iface) 620214501Srpaulo{ 621214501Srpaulo#ifdef CONFIG_IEEE80211N 622214501Srpaulo int ret; 623252726Srpaulo if (!iface->conf->ieee80211n) 624252726Srpaulo return 0; 625252726Srpaulo if (!ieee80211n_supported_ht_capab(iface)) 626252726Srpaulo return -1; 627214501Srpaulo ret = ieee80211n_check_40mhz(iface); 628214501Srpaulo if (ret) 629214501Srpaulo return ret; 630214501Srpaulo if (!ieee80211n_allowed_ht40_channel_pair(iface)) 631214501Srpaulo return -1; 632214501Srpaulo#endif /* CONFIG_IEEE80211N */ 633214501Srpaulo 634214501Srpaulo return 0; 635214501Srpaulo} 636214501Srpaulo 637214501Srpaulo 638214501Srpaulo/** 639214501Srpaulo * hostapd_select_hw_mode - Select the hardware mode 640214501Srpaulo * @iface: Pointer to interface data. 641252726Srpaulo * Returns: 0 on success, < 0 on failure 642214501Srpaulo * 643214501Srpaulo * Sets up the hardware mode, channel, rates, and passive scanning 644214501Srpaulo * based on the configuration. 645214501Srpaulo */ 646214501Srpauloint hostapd_select_hw_mode(struct hostapd_iface *iface) 647214501Srpaulo{ 648214501Srpaulo int i, j, ok; 649214501Srpaulo 650214501Srpaulo if (iface->num_hw_features < 1) 651214501Srpaulo return -1; 652214501Srpaulo 653214501Srpaulo iface->current_mode = NULL; 654214501Srpaulo for (i = 0; i < iface->num_hw_features; i++) { 655214501Srpaulo struct hostapd_hw_modes *mode = &iface->hw_features[i]; 656214501Srpaulo if (mode->mode == iface->conf->hw_mode) { 657214501Srpaulo iface->current_mode = mode; 658214501Srpaulo break; 659214501Srpaulo } 660214501Srpaulo } 661214501Srpaulo 662214501Srpaulo if (iface->current_mode == NULL) { 663214501Srpaulo wpa_printf(MSG_ERROR, "Hardware does not support configured " 664214501Srpaulo "mode"); 665214501Srpaulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 666214501Srpaulo HOSTAPD_LEVEL_WARNING, 667214501Srpaulo "Hardware does not support configured mode " 668252726Srpaulo "(%d) (hw_mode in hostapd.conf)", 669252726Srpaulo (int) iface->conf->hw_mode); 670252726Srpaulo return -2; 671214501Srpaulo } 672214501Srpaulo 673214501Srpaulo ok = 0; 674214501Srpaulo for (j = 0; j < iface->current_mode->num_channels; j++) { 675214501Srpaulo struct hostapd_channel_data *chan = 676214501Srpaulo &iface->current_mode->channels[j]; 677252726Srpaulo if (chan->chan == iface->conf->channel) { 678252726Srpaulo if (chan->flag & HOSTAPD_CHAN_DISABLED) { 679252726Srpaulo wpa_printf(MSG_ERROR, 680252726Srpaulo "channel [%i] (%i) is disabled for " 681252726Srpaulo "use in AP mode, flags: 0x%x%s%s%s", 682252726Srpaulo j, chan->chan, chan->flag, 683252726Srpaulo chan->flag & HOSTAPD_CHAN_NO_IBSS ? 684252726Srpaulo " NO-IBSS" : "", 685252726Srpaulo chan->flag & 686252726Srpaulo HOSTAPD_CHAN_PASSIVE_SCAN ? 687252726Srpaulo " PASSIVE-SCAN" : "", 688252726Srpaulo chan->flag & HOSTAPD_CHAN_RADAR ? 689252726Srpaulo " RADAR" : ""); 690252726Srpaulo } else { 691252726Srpaulo ok = 1; 692252726Srpaulo break; 693252726Srpaulo } 694214501Srpaulo } 695214501Srpaulo } 696252726Srpaulo if (ok && iface->conf->secondary_channel) { 697252726Srpaulo int sec_ok = 0; 698252726Srpaulo int sec_chan = iface->conf->channel + 699252726Srpaulo iface->conf->secondary_channel * 4; 700252726Srpaulo for (j = 0; j < iface->current_mode->num_channels; j++) { 701252726Srpaulo struct hostapd_channel_data *chan = 702252726Srpaulo &iface->current_mode->channels[j]; 703252726Srpaulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 704252726Srpaulo (chan->chan == sec_chan)) { 705252726Srpaulo sec_ok = 1; 706252726Srpaulo break; 707252726Srpaulo } 708252726Srpaulo } 709252726Srpaulo if (!sec_ok) { 710252726Srpaulo hostapd_logger(iface->bss[0], NULL, 711252726Srpaulo HOSTAPD_MODULE_IEEE80211, 712252726Srpaulo HOSTAPD_LEVEL_WARNING, 713252726Srpaulo "Configured HT40 secondary channel " 714252726Srpaulo "(%d) not found from the channel list " 715252726Srpaulo "of current mode (%d) %s", 716252726Srpaulo sec_chan, iface->current_mode->mode, 717252726Srpaulo hostapd_hw_mode_txt( 718252726Srpaulo iface->current_mode->mode)); 719252726Srpaulo ok = 0; 720252726Srpaulo } 721252726Srpaulo } 722214501Srpaulo if (iface->conf->channel == 0) { 723214501Srpaulo /* TODO: could request a scan of neighboring BSSes and select 724214501Srpaulo * the channel automatically */ 725214501Srpaulo wpa_printf(MSG_ERROR, "Channel not configured " 726214501Srpaulo "(hw_mode/channel in hostapd.conf)"); 727252726Srpaulo return -3; 728214501Srpaulo } 729214501Srpaulo if (ok == 0 && iface->conf->channel != 0) { 730214501Srpaulo hostapd_logger(iface->bss[0], NULL, 731214501Srpaulo HOSTAPD_MODULE_IEEE80211, 732214501Srpaulo HOSTAPD_LEVEL_WARNING, 733214501Srpaulo "Configured channel (%d) not found from the " 734214501Srpaulo "channel list of current mode (%d) %s", 735214501Srpaulo iface->conf->channel, 736214501Srpaulo iface->current_mode->mode, 737214501Srpaulo hostapd_hw_mode_txt(iface->current_mode->mode)); 738214501Srpaulo iface->current_mode = NULL; 739214501Srpaulo } 740214501Srpaulo 741214501Srpaulo if (iface->current_mode == NULL) { 742214501Srpaulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 743214501Srpaulo HOSTAPD_LEVEL_WARNING, 744214501Srpaulo "Hardware does not support configured channel"); 745252726Srpaulo return -4; 746214501Srpaulo } 747214501Srpaulo 748214501Srpaulo return 0; 749214501Srpaulo} 750214501Srpaulo 751214501Srpaulo 752214501Srpauloconst char * hostapd_hw_mode_txt(int mode) 753214501Srpaulo{ 754214501Srpaulo switch (mode) { 755214501Srpaulo case HOSTAPD_MODE_IEEE80211A: 756214501Srpaulo return "IEEE 802.11a"; 757214501Srpaulo case HOSTAPD_MODE_IEEE80211B: 758214501Srpaulo return "IEEE 802.11b"; 759214501Srpaulo case HOSTAPD_MODE_IEEE80211G: 760214501Srpaulo return "IEEE 802.11g"; 761252726Srpaulo case HOSTAPD_MODE_IEEE80211AD: 762252726Srpaulo return "IEEE 802.11ad"; 763214501Srpaulo default: 764214501Srpaulo return "UNKNOWN"; 765214501Srpaulo } 766214501Srpaulo} 767214501Srpaulo 768214501Srpaulo 769214501Srpauloint hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) 770214501Srpaulo{ 771214501Srpaulo int i; 772214501Srpaulo 773214501Srpaulo if (!hapd->iface->current_mode) 774214501Srpaulo return 0; 775214501Srpaulo 776214501Srpaulo for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { 777214501Srpaulo struct hostapd_channel_data *ch = 778214501Srpaulo &hapd->iface->current_mode->channels[i]; 779214501Srpaulo if (ch->chan == chan) 780214501Srpaulo return ch->freq; 781214501Srpaulo } 782214501Srpaulo 783214501Srpaulo return 0; 784214501Srpaulo} 785214501Srpaulo 786214501Srpaulo 787214501Srpauloint hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) 788214501Srpaulo{ 789214501Srpaulo int i; 790214501Srpaulo 791214501Srpaulo if (!hapd->iface->current_mode) 792214501Srpaulo return 0; 793214501Srpaulo 794214501Srpaulo for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { 795214501Srpaulo struct hostapd_channel_data *ch = 796214501Srpaulo &hapd->iface->current_mode->channels[i]; 797214501Srpaulo if (ch->freq == freq) 798214501Srpaulo return ch->chan; 799214501Srpaulo } 800214501Srpaulo 801214501Srpaulo return 0; 802214501Srpaulo} 803