1214501Srpaulo/* 2214501Srpaulo * hostapd / Hardware feature query and different modes 3214501Srpaulo * Copyright 2002-2003, Instant802 Networks, Inc. 4214501Srpaulo * Copyright 2005-2006, Devicescape Software, Inc. 5214501Srpaulo * Copyright (c) 2008-2009, 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 104214501Srpaulostatic int hostapd_prepare_rates(struct hostapd_data *hapd, 105214501Srpaulo 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 113214501Srpaulo if (hapd->iconf->basic_rates) 114214501Srpaulo basic_rates = hapd->iconf->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; 125214501Srpaulo default: 126214501Srpaulo return -1; 127214501Srpaulo } 128214501Srpaulo 129214501Srpaulo if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, 130214501Srpaulo basic_rates, mode->mode)) { 131214501Srpaulo wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " 132214501Srpaulo "module"); 133214501Srpaulo } 134214501Srpaulo 135214501Srpaulo os_free(hapd->iface->current_rates); 136214501Srpaulo hapd->iface->num_rates = 0; 137214501Srpaulo 138214501Srpaulo hapd->iface->current_rates = 139214501Srpaulo os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data)); 140214501Srpaulo if (!hapd->iface->current_rates) { 141214501Srpaulo wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " 142214501Srpaulo "table."); 143214501Srpaulo return -1; 144214501Srpaulo } 145214501Srpaulo 146214501Srpaulo for (i = 0; i < mode->num_rates; i++) { 147214501Srpaulo struct hostapd_rate_data *rate; 148214501Srpaulo 149214501Srpaulo if (hapd->iconf->supported_rates && 150214501Srpaulo !hostapd_rate_found(hapd->iconf->supported_rates, 151214501Srpaulo mode->rates[i])) 152214501Srpaulo continue; 153214501Srpaulo 154214501Srpaulo rate = &hapd->iface->current_rates[hapd->iface->num_rates]; 155214501Srpaulo rate->rate = mode->rates[i]; 156214501Srpaulo if (hostapd_rate_found(basic_rates, rate->rate)) { 157214501Srpaulo rate->flags |= HOSTAPD_RATE_BASIC; 158214501Srpaulo num_basic_rates++; 159214501Srpaulo } 160214501Srpaulo wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", 161214501Srpaulo hapd->iface->num_rates, rate->rate, rate->flags); 162214501Srpaulo hapd->iface->num_rates++; 163214501Srpaulo } 164214501Srpaulo 165214501Srpaulo if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { 166214501Srpaulo wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " 167214501Srpaulo "rate sets (%d,%d).", 168214501Srpaulo hapd->iface->num_rates, num_basic_rates); 169214501Srpaulo return -1; 170214501Srpaulo } 171214501Srpaulo 172214501Srpaulo return 0; 173214501Srpaulo} 174214501Srpaulo 175214501Srpaulo 176214501Srpaulo#ifdef CONFIG_IEEE80211N 177214501Srpaulostatic int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) 178214501Srpaulo{ 179214501Srpaulo int sec_chan, ok, j, first; 180214501Srpaulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 181214501Srpaulo 184, 192 }; 182214501Srpaulo size_t k; 183214501Srpaulo 184214501Srpaulo if (!iface->conf->secondary_channel) 185214501Srpaulo return 1; /* HT40 not used */ 186214501Srpaulo 187214501Srpaulo sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; 188214501Srpaulo wpa_printf(MSG_DEBUG, "HT40: control channel: %d " 189214501Srpaulo "secondary channel: %d", 190214501Srpaulo iface->conf->channel, sec_chan); 191214501Srpaulo 192214501Srpaulo /* Verify that HT40 secondary channel is an allowed 20 MHz 193214501Srpaulo * channel */ 194214501Srpaulo ok = 0; 195214501Srpaulo for (j = 0; j < iface->current_mode->num_channels; j++) { 196214501Srpaulo struct hostapd_channel_data *chan = 197214501Srpaulo &iface->current_mode->channels[j]; 198214501Srpaulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 199214501Srpaulo chan->chan == sec_chan) { 200214501Srpaulo ok = 1; 201214501Srpaulo break; 202214501Srpaulo } 203214501Srpaulo } 204214501Srpaulo if (!ok) { 205214501Srpaulo wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 206214501Srpaulo sec_chan); 207214501Srpaulo return 0; 208214501Srpaulo } 209214501Srpaulo 210214501Srpaulo /* 211214501Srpaulo * Verify that HT40 primary,secondary channel pair is allowed per 212214501Srpaulo * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 213214501Srpaulo * 2.4 GHz rules allow all cases where the secondary channel fits into 214214501Srpaulo * the list of allowed channels (already checked above). 215214501Srpaulo */ 216214501Srpaulo if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) 217214501Srpaulo return 1; 218214501Srpaulo 219214501Srpaulo if (iface->conf->secondary_channel > 0) 220214501Srpaulo first = iface->conf->channel; 221214501Srpaulo else 222214501Srpaulo first = sec_chan; 223214501Srpaulo 224214501Srpaulo ok = 0; 225214501Srpaulo for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { 226214501Srpaulo if (first == allowed[k]) { 227214501Srpaulo ok = 1; 228214501Srpaulo break; 229214501Srpaulo } 230214501Srpaulo } 231214501Srpaulo if (!ok) { 232214501Srpaulo wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 233214501Srpaulo iface->conf->channel, 234214501Srpaulo iface->conf->secondary_channel); 235214501Srpaulo return 0; 236214501Srpaulo } 237214501Srpaulo 238214501Srpaulo return 1; 239214501Srpaulo} 240214501Srpaulo 241214501Srpaulo 242214501Srpaulostatic void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) 243214501Srpaulo{ 244214501Srpaulo if (iface->conf->secondary_channel > 0) { 245214501Srpaulo iface->conf->channel += 4; 246214501Srpaulo iface->conf->secondary_channel = -1; 247214501Srpaulo } else { 248214501Srpaulo iface->conf->channel -= 4; 249214501Srpaulo iface->conf->secondary_channel = 1; 250214501Srpaulo } 251214501Srpaulo} 252214501Srpaulo 253214501Srpaulo 254214501Srpaulostatic void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, 255214501Srpaulo int *pri_chan, int *sec_chan) 256214501Srpaulo{ 257214501Srpaulo struct ieee80211_ht_operation *oper; 258214501Srpaulo struct ieee802_11_elems elems; 259214501Srpaulo 260214501Srpaulo *pri_chan = *sec_chan = 0; 261214501Srpaulo 262214501Srpaulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 263214501Srpaulo if (elems.ht_operation && 264214501Srpaulo elems.ht_operation_len >= sizeof(*oper)) { 265214501Srpaulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 266214501Srpaulo *pri_chan = oper->control_chan; 267214501Srpaulo if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { 268214501Srpaulo if (oper->ht_param & 269214501Srpaulo HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 270214501Srpaulo *sec_chan = *pri_chan + 4; 271214501Srpaulo else if (oper->ht_param & 272214501Srpaulo HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 273214501Srpaulo *sec_chan = *pri_chan - 4; 274214501Srpaulo } 275214501Srpaulo } 276214501Srpaulo} 277214501Srpaulo 278214501Srpaulo 279214501Srpaulostatic int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, 280214501Srpaulo struct wpa_scan_results *scan_res) 281214501Srpaulo{ 282214501Srpaulo int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss; 283214501Srpaulo int bss_pri_chan, bss_sec_chan; 284214501Srpaulo size_t i; 285214501Srpaulo int match; 286214501Srpaulo 287214501Srpaulo pri_chan = iface->conf->channel; 288214501Srpaulo sec_chan = iface->conf->secondary_channel * 4; 289214501Srpaulo pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan); 290214501Srpaulo if (iface->conf->secondary_channel > 0) 291214501Srpaulo sec_freq = pri_freq + 20; 292214501Srpaulo else 293214501Srpaulo sec_freq = pri_freq - 20; 294214501Srpaulo 295214501Srpaulo /* 296214501Srpaulo * Switch PRI/SEC channels if Beacons were detected on selected SEC 297214501Srpaulo * channel, but not on selected PRI channel. 298214501Srpaulo */ 299214501Srpaulo pri_bss = sec_bss = 0; 300214501Srpaulo for (i = 0; i < scan_res->num; i++) { 301214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 302214501Srpaulo if (bss->freq == pri_freq) 303214501Srpaulo pri_bss++; 304214501Srpaulo else if (bss->freq == sec_freq) 305214501Srpaulo sec_bss++; 306214501Srpaulo } 307214501Srpaulo if (sec_bss && !pri_bss) { 308214501Srpaulo wpa_printf(MSG_INFO, "Switch own primary and secondary " 309214501Srpaulo "channel to get secondary channel with no Beacons " 310214501Srpaulo "from other BSSes"); 311214501Srpaulo ieee80211n_switch_pri_sec(iface); 312214501Srpaulo } 313214501Srpaulo 314214501Srpaulo /* 315214501Srpaulo * Match PRI/SEC channel with any existing HT40 BSS on the same 316214501Srpaulo * channels that we are about to use (if already mixed order in 317214501Srpaulo * existing BSSes, use own preference). 318214501Srpaulo */ 319214501Srpaulo match = 0; 320214501Srpaulo for (i = 0; i < scan_res->num; i++) { 321214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 322214501Srpaulo ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 323214501Srpaulo if (pri_chan == bss_pri_chan && 324214501Srpaulo sec_chan == bss_sec_chan) { 325214501Srpaulo match = 1; 326214501Srpaulo break; 327214501Srpaulo } 328214501Srpaulo } 329214501Srpaulo if (!match) { 330214501Srpaulo for (i = 0; i < scan_res->num; i++) { 331214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 332214501Srpaulo ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, 333214501Srpaulo &bss_sec_chan); 334214501Srpaulo if (pri_chan == bss_sec_chan && 335214501Srpaulo sec_chan == bss_pri_chan) { 336214501Srpaulo wpa_printf(MSG_INFO, "Switch own primary and " 337214501Srpaulo "secondary channel due to BSS " 338214501Srpaulo "overlap with " MACSTR, 339214501Srpaulo MAC2STR(bss->bssid)); 340214501Srpaulo ieee80211n_switch_pri_sec(iface); 341214501Srpaulo break; 342214501Srpaulo } 343214501Srpaulo } 344214501Srpaulo } 345214501Srpaulo 346214501Srpaulo return 1; 347214501Srpaulo} 348214501Srpaulo 349214501Srpaulo 350214501Srpaulostatic int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, 351214501Srpaulo struct wpa_scan_results *scan_res) 352214501Srpaulo{ 353214501Srpaulo int pri_freq, sec_freq; 354214501Srpaulo int affected_start, affected_end; 355214501Srpaulo size_t i; 356214501Srpaulo 357214501Srpaulo pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 358214501Srpaulo if (iface->conf->secondary_channel > 0) 359214501Srpaulo sec_freq = pri_freq + 20; 360214501Srpaulo else 361214501Srpaulo sec_freq = pri_freq - 20; 362214501Srpaulo affected_start = (pri_freq + sec_freq) / 2 - 25; 363214501Srpaulo affected_end = (pri_freq + sec_freq) / 2 + 25; 364214501Srpaulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 365214501Srpaulo affected_start, affected_end); 366214501Srpaulo for (i = 0; i < scan_res->num; i++) { 367214501Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 368214501Srpaulo int pri = bss->freq; 369214501Srpaulo int sec = pri; 370214501Srpaulo int sec_chan, pri_chan; 371214501Srpaulo 372214501Srpaulo ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan); 373214501Srpaulo 374214501Srpaulo if (sec_chan) { 375214501Srpaulo if (sec_chan < pri_chan) 376214501Srpaulo sec = pri - 20; 377214501Srpaulo else 378214501Srpaulo sec = pri + 20; 379214501Srpaulo } 380214501Srpaulo 381214501Srpaulo if ((pri < affected_start || pri > affected_end) && 382214501Srpaulo (sec < affected_start || sec > affected_end)) 383214501Srpaulo continue; /* not within affected channel range */ 384214501Srpaulo 385214501Srpaulo wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 386214501Srpaulo " freq=%d pri=%d sec=%d", 387214501Srpaulo MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 388214501Srpaulo 389214501Srpaulo if (sec_chan) { 390214501Srpaulo if (pri_freq != pri || sec_freq != sec) { 391214501Srpaulo wpa_printf(MSG_DEBUG, "40 MHz pri/sec " 392214501Srpaulo "mismatch with BSS " MACSTR 393214501Srpaulo " <%d,%d> (chan=%d%c) vs. <%d,%d>", 394214501Srpaulo MAC2STR(bss->bssid), 395214501Srpaulo pri, sec, pri_chan, 396214501Srpaulo sec > pri ? '+' : '-', 397214501Srpaulo pri_freq, sec_freq); 398214501Srpaulo return 0; 399214501Srpaulo } 400214501Srpaulo } 401214501Srpaulo 402214501Srpaulo /* TODO: 40 MHz intolerant */ 403214501Srpaulo } 404214501Srpaulo 405214501Srpaulo return 1; 406214501Srpaulo} 407214501Srpaulo 408214501Srpaulo 409214501Srpaulostatic void wpa_scan_results_free(struct wpa_scan_results *res) 410214501Srpaulo{ 411214501Srpaulo size_t i; 412214501Srpaulo 413214501Srpaulo if (res == NULL) 414214501Srpaulo return; 415214501Srpaulo 416214501Srpaulo for (i = 0; i < res->num; i++) 417214501Srpaulo os_free(res->res[i]); 418214501Srpaulo os_free(res->res); 419214501Srpaulo os_free(res); 420214501Srpaulo} 421214501Srpaulo 422214501Srpaulo 423214501Srpaulostatic void ieee80211n_check_scan(struct hostapd_iface *iface) 424214501Srpaulo{ 425214501Srpaulo struct wpa_scan_results *scan_res; 426214501Srpaulo int oper40; 427214501Srpaulo 428214501Srpaulo /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is 429214501Srpaulo * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ 430214501Srpaulo 431214501Srpaulo iface->scan_cb = NULL; 432214501Srpaulo 433214501Srpaulo scan_res = hostapd_driver_get_scan_results(iface->bss[0]); 434214501Srpaulo if (scan_res == NULL) { 435214501Srpaulo hostapd_setup_interface_complete(iface, 1); 436214501Srpaulo return; 437214501Srpaulo } 438214501Srpaulo 439214501Srpaulo if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) 440214501Srpaulo oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); 441214501Srpaulo else 442214501Srpaulo oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); 443214501Srpaulo wpa_scan_results_free(scan_res); 444214501Srpaulo 445214501Srpaulo if (!oper40) { 446214501Srpaulo wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " 447214501Srpaulo "channel pri=%d sec=%d based on overlapping BSSes", 448214501Srpaulo iface->conf->channel, 449214501Srpaulo iface->conf->channel + 450214501Srpaulo iface->conf->secondary_channel * 4); 451214501Srpaulo iface->conf->secondary_channel = 0; 452214501Srpaulo iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; 453214501Srpaulo } 454214501Srpaulo 455214501Srpaulo hostapd_setup_interface_complete(iface, 0); 456214501Srpaulo} 457214501Srpaulo 458214501Srpaulo 459214501Srpaulostatic int ieee80211n_check_40mhz(struct hostapd_iface *iface) 460214501Srpaulo{ 461214501Srpaulo struct wpa_driver_scan_params params; 462214501Srpaulo 463214501Srpaulo if (!iface->conf->secondary_channel) 464214501Srpaulo return 0; /* HT40 not used */ 465214501Srpaulo 466214501Srpaulo wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " 467214501Srpaulo "40 MHz channel"); 468214501Srpaulo os_memset(¶ms, 0, sizeof(params)); 469214501Srpaulo /* TODO: scan only the needed frequency */ 470214501Srpaulo if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { 471214501Srpaulo wpa_printf(MSG_ERROR, "Failed to request a scan of " 472214501Srpaulo "neighboring BSSes"); 473214501Srpaulo return -1; 474214501Srpaulo } 475214501Srpaulo 476214501Srpaulo iface->scan_cb = ieee80211n_check_scan; 477214501Srpaulo return 1; 478214501Srpaulo} 479214501Srpaulo 480214501Srpaulo 481214501Srpaulostatic int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) 482214501Srpaulo{ 483214501Srpaulo u16 hw = iface->current_mode->ht_capab; 484214501Srpaulo u16 conf = iface->conf->ht_capab; 485214501Srpaulo 486214501Srpaulo if (!iface->conf->ieee80211n) 487214501Srpaulo return 1; 488214501Srpaulo 489214501Srpaulo if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && 490214501Srpaulo !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { 491214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 492214501Srpaulo "HT capability [LDPC]"); 493214501Srpaulo return 0; 494214501Srpaulo } 495214501Srpaulo 496214501Srpaulo if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 497214501Srpaulo !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { 498214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 499214501Srpaulo "HT capability [HT40*]"); 500214501Srpaulo return 0; 501214501Srpaulo } 502214501Srpaulo 503214501Srpaulo if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && 504214501Srpaulo (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { 505214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 506214501Srpaulo "HT capability [SMPS-*]"); 507214501Srpaulo return 0; 508214501Srpaulo } 509214501Srpaulo 510214501Srpaulo if ((conf & HT_CAP_INFO_GREEN_FIELD) && 511214501Srpaulo !(hw & HT_CAP_INFO_GREEN_FIELD)) { 512214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 513214501Srpaulo "HT capability [GF]"); 514214501Srpaulo return 0; 515214501Srpaulo } 516214501Srpaulo 517214501Srpaulo if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && 518214501Srpaulo !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { 519214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 520214501Srpaulo "HT capability [SHORT-GI-20]"); 521214501Srpaulo return 0; 522214501Srpaulo } 523214501Srpaulo 524214501Srpaulo if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && 525214501Srpaulo !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { 526214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 527214501Srpaulo "HT capability [SHORT-GI-40]"); 528214501Srpaulo return 0; 529214501Srpaulo } 530214501Srpaulo 531214501Srpaulo if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { 532214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 533214501Srpaulo "HT capability [TX-STBC]"); 534214501Srpaulo return 0; 535214501Srpaulo } 536214501Srpaulo 537214501Srpaulo if ((conf & HT_CAP_INFO_RX_STBC_MASK) > 538214501Srpaulo (hw & HT_CAP_INFO_RX_STBC_MASK)) { 539214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 540214501Srpaulo "HT capability [RX-STBC*]"); 541214501Srpaulo return 0; 542214501Srpaulo } 543214501Srpaulo 544214501Srpaulo if ((conf & HT_CAP_INFO_DELAYED_BA) && 545214501Srpaulo !(hw & HT_CAP_INFO_DELAYED_BA)) { 546214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 547214501Srpaulo "HT capability [DELAYED-BA]"); 548214501Srpaulo return 0; 549214501Srpaulo } 550214501Srpaulo 551214501Srpaulo if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && 552214501Srpaulo !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { 553214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 554214501Srpaulo "HT capability [MAX-AMSDU-7935]"); 555214501Srpaulo return 0; 556214501Srpaulo } 557214501Srpaulo 558214501Srpaulo if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && 559214501Srpaulo !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { 560214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 561214501Srpaulo "HT capability [DSSS_CCK-40]"); 562214501Srpaulo return 0; 563214501Srpaulo } 564214501Srpaulo 565214501Srpaulo if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { 566214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 567214501Srpaulo "HT capability [PSMP]"); 568214501Srpaulo return 0; 569214501Srpaulo } 570214501Srpaulo 571214501Srpaulo if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && 572214501Srpaulo !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { 573214501Srpaulo wpa_printf(MSG_ERROR, "Driver does not support configured " 574214501Srpaulo "HT capability [LSIG-TXOP-PROT]"); 575214501Srpaulo return 0; 576214501Srpaulo } 577214501Srpaulo 578214501Srpaulo return 1; 579214501Srpaulo} 580214501Srpaulo 581214501Srpaulo#endif /* CONFIG_IEEE80211N */ 582214501Srpaulo 583214501Srpaulo 584214501Srpauloint hostapd_check_ht_capab(struct hostapd_iface *iface) 585214501Srpaulo{ 586214501Srpaulo#ifdef CONFIG_IEEE80211N 587214501Srpaulo int ret; 588214501Srpaulo ret = ieee80211n_check_40mhz(iface); 589214501Srpaulo if (ret) 590214501Srpaulo return ret; 591214501Srpaulo if (!ieee80211n_allowed_ht40_channel_pair(iface)) 592214501Srpaulo return -1; 593214501Srpaulo if (!ieee80211n_supported_ht_capab(iface)) 594214501Srpaulo return -1; 595214501Srpaulo#endif /* CONFIG_IEEE80211N */ 596214501Srpaulo 597214501Srpaulo return 0; 598214501Srpaulo} 599214501Srpaulo 600214501Srpaulo 601214501Srpaulo/** 602214501Srpaulo * hostapd_select_hw_mode - Select the hardware mode 603214501Srpaulo * @iface: Pointer to interface data. 604214501Srpaulo * Returns: 0 on success, -1 on failure 605214501Srpaulo * 606214501Srpaulo * Sets up the hardware mode, channel, rates, and passive scanning 607214501Srpaulo * based on the configuration. 608214501Srpaulo */ 609214501Srpauloint hostapd_select_hw_mode(struct hostapd_iface *iface) 610214501Srpaulo{ 611214501Srpaulo int i, j, ok; 612214501Srpaulo 613214501Srpaulo if (iface->num_hw_features < 1) 614214501Srpaulo return -1; 615214501Srpaulo 616214501Srpaulo iface->current_mode = NULL; 617214501Srpaulo for (i = 0; i < iface->num_hw_features; i++) { 618214501Srpaulo struct hostapd_hw_modes *mode = &iface->hw_features[i]; 619214501Srpaulo if (mode->mode == iface->conf->hw_mode) { 620214501Srpaulo iface->current_mode = mode; 621214501Srpaulo break; 622214501Srpaulo } 623214501Srpaulo } 624214501Srpaulo 625214501Srpaulo if (iface->current_mode == NULL) { 626214501Srpaulo wpa_printf(MSG_ERROR, "Hardware does not support configured " 627214501Srpaulo "mode"); 628214501Srpaulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 629214501Srpaulo HOSTAPD_LEVEL_WARNING, 630214501Srpaulo "Hardware does not support configured mode " 631214501Srpaulo "(%d)", (int) iface->conf->hw_mode); 632214501Srpaulo return -1; 633214501Srpaulo } 634214501Srpaulo 635214501Srpaulo ok = 0; 636214501Srpaulo for (j = 0; j < iface->current_mode->num_channels; j++) { 637214501Srpaulo struct hostapd_channel_data *chan = 638214501Srpaulo &iface->current_mode->channels[j]; 639214501Srpaulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 640214501Srpaulo (chan->chan == iface->conf->channel)) { 641214501Srpaulo ok = 1; 642214501Srpaulo break; 643214501Srpaulo } 644214501Srpaulo } 645214501Srpaulo if (iface->conf->channel == 0) { 646214501Srpaulo /* TODO: could request a scan of neighboring BSSes and select 647214501Srpaulo * the channel automatically */ 648214501Srpaulo wpa_printf(MSG_ERROR, "Channel not configured " 649214501Srpaulo "(hw_mode/channel in hostapd.conf)"); 650214501Srpaulo return -1; 651214501Srpaulo } 652214501Srpaulo if (ok == 0 && iface->conf->channel != 0) { 653214501Srpaulo hostapd_logger(iface->bss[0], NULL, 654214501Srpaulo HOSTAPD_MODULE_IEEE80211, 655214501Srpaulo HOSTAPD_LEVEL_WARNING, 656214501Srpaulo "Configured channel (%d) not found from the " 657214501Srpaulo "channel list of current mode (%d) %s", 658214501Srpaulo iface->conf->channel, 659214501Srpaulo iface->current_mode->mode, 660214501Srpaulo hostapd_hw_mode_txt(iface->current_mode->mode)); 661214501Srpaulo iface->current_mode = NULL; 662214501Srpaulo } 663214501Srpaulo 664214501Srpaulo if (iface->current_mode == NULL) { 665214501Srpaulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 666214501Srpaulo HOSTAPD_LEVEL_WARNING, 667214501Srpaulo "Hardware does not support configured channel"); 668214501Srpaulo return -1; 669214501Srpaulo } 670214501Srpaulo 671214501Srpaulo if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { 672214501Srpaulo wpa_printf(MSG_ERROR, "Failed to prepare rates table."); 673214501Srpaulo hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, 674214501Srpaulo HOSTAPD_LEVEL_WARNING, 675214501Srpaulo "Failed to prepare rates table."); 676214501Srpaulo return -1; 677214501Srpaulo } 678214501Srpaulo 679214501Srpaulo return 0; 680214501Srpaulo} 681214501Srpaulo 682214501Srpaulo 683214501Srpauloconst char * hostapd_hw_mode_txt(int mode) 684214501Srpaulo{ 685214501Srpaulo switch (mode) { 686214501Srpaulo case HOSTAPD_MODE_IEEE80211A: 687214501Srpaulo return "IEEE 802.11a"; 688214501Srpaulo case HOSTAPD_MODE_IEEE80211B: 689214501Srpaulo return "IEEE 802.11b"; 690214501Srpaulo case HOSTAPD_MODE_IEEE80211G: 691214501Srpaulo return "IEEE 802.11g"; 692214501Srpaulo default: 693214501Srpaulo return "UNKNOWN"; 694214501Srpaulo } 695214501Srpaulo} 696214501Srpaulo 697214501Srpaulo 698214501Srpauloint hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) 699214501Srpaulo{ 700214501Srpaulo int i; 701214501Srpaulo 702214501Srpaulo if (!hapd->iface->current_mode) 703214501Srpaulo return 0; 704214501Srpaulo 705214501Srpaulo for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { 706214501Srpaulo struct hostapd_channel_data *ch = 707214501Srpaulo &hapd->iface->current_mode->channels[i]; 708214501Srpaulo if (ch->chan == chan) 709214501Srpaulo return ch->freq; 710214501Srpaulo } 711214501Srpaulo 712214501Srpaulo return 0; 713214501Srpaulo} 714214501Srpaulo 715214501Srpaulo 716214501Srpauloint hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) 717214501Srpaulo{ 718214501Srpaulo int i; 719214501Srpaulo 720214501Srpaulo if (!hapd->iface->current_mode) 721214501Srpaulo return 0; 722214501Srpaulo 723214501Srpaulo for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { 724214501Srpaulo struct hostapd_channel_data *ch = 725214501Srpaulo &hapd->iface->current_mode->channels[i]; 726214501Srpaulo if (ch->freq == freq) 727214501Srpaulo return ch->chan; 728214501Srpaulo } 729214501Srpaulo 730214501Srpaulo return 0; 731214501Srpaulo} 732