1281681Srpaulo/* 2281681Srpaulo * Common hostapd/wpa_supplicant HW features 3281681Srpaulo * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 4281681Srpaulo * Copyright (c) 2015, Qualcomm Atheros, Inc. 5281681Srpaulo * 6281681Srpaulo * This software may be distributed under the terms of the BSD license. 7281681Srpaulo * See README for more details. 8281681Srpaulo */ 9281681Srpaulo 10281681Srpaulo#include "includes.h" 11281681Srpaulo 12281681Srpaulo#include "common.h" 13281681Srpaulo#include "defs.h" 14281681Srpaulo#include "ieee802_11_defs.h" 15281681Srpaulo#include "ieee802_11_common.h" 16281681Srpaulo#include "hw_features_common.h" 17281681Srpaulo 18281681Srpaulo 19281681Srpaulostruct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, 20281681Srpaulo int chan, int *freq) 21281681Srpaulo{ 22281681Srpaulo int i; 23281681Srpaulo 24281681Srpaulo if (freq) 25281681Srpaulo *freq = 0; 26281681Srpaulo 27281681Srpaulo if (!mode) 28281681Srpaulo return NULL; 29281681Srpaulo 30281681Srpaulo for (i = 0; i < mode->num_channels; i++) { 31281681Srpaulo struct hostapd_channel_data *ch = &mode->channels[i]; 32281681Srpaulo if (ch->chan == chan) { 33281681Srpaulo if (freq) 34281681Srpaulo *freq = ch->freq; 35281681Srpaulo return ch; 36281681Srpaulo } 37281681Srpaulo } 38281681Srpaulo 39281681Srpaulo return NULL; 40281681Srpaulo} 41281681Srpaulo 42281681Srpaulo 43281681Srpaulostruct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode, 44281681Srpaulo int freq, int *chan) 45281681Srpaulo{ 46281681Srpaulo int i; 47281681Srpaulo 48281681Srpaulo if (chan) 49281681Srpaulo *chan = 0; 50281681Srpaulo 51281681Srpaulo if (!mode) 52281681Srpaulo return NULL; 53281681Srpaulo 54281681Srpaulo for (i = 0; i < mode->num_channels; i++) { 55281681Srpaulo struct hostapd_channel_data *ch = &mode->channels[i]; 56281681Srpaulo if (ch->freq == freq) { 57281681Srpaulo if (chan) 58281681Srpaulo *chan = ch->chan; 59281681Srpaulo return ch; 60281681Srpaulo } 61281681Srpaulo } 62281681Srpaulo 63281681Srpaulo return NULL; 64281681Srpaulo} 65281681Srpaulo 66281681Srpaulo 67281681Srpauloint hw_get_freq(struct hostapd_hw_modes *mode, int chan) 68281681Srpaulo{ 69281681Srpaulo int freq; 70281681Srpaulo 71281681Srpaulo hw_get_channel_chan(mode, chan, &freq); 72281681Srpaulo 73281681Srpaulo return freq; 74281681Srpaulo} 75281681Srpaulo 76281681Srpaulo 77281681Srpauloint hw_get_chan(struct hostapd_hw_modes *mode, int freq) 78281681Srpaulo{ 79281681Srpaulo int chan; 80281681Srpaulo 81281681Srpaulo hw_get_channel_freq(mode, freq, &chan); 82281681Srpaulo 83281681Srpaulo return chan; 84281681Srpaulo} 85281681Srpaulo 86281681Srpaulo 87281681Srpauloint allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, 88281681Srpaulo int sec_chan) 89281681Srpaulo{ 90281681Srpaulo int ok, j, first; 91289549Srpaulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 92289549Srpaulo 149, 157, 184, 192 }; 93281681Srpaulo size_t k; 94281681Srpaulo 95281681Srpaulo if (pri_chan == sec_chan || !sec_chan) 96281681Srpaulo return 1; /* HT40 not used */ 97281681Srpaulo 98281681Srpaulo wpa_printf(MSG_DEBUG, 99281681Srpaulo "HT40: control channel: %d secondary channel: %d", 100281681Srpaulo pri_chan, sec_chan); 101281681Srpaulo 102281681Srpaulo /* Verify that HT40 secondary channel is an allowed 20 MHz 103281681Srpaulo * channel */ 104281681Srpaulo ok = 0; 105281681Srpaulo for (j = 0; j < mode->num_channels; j++) { 106281681Srpaulo struct hostapd_channel_data *chan = &mode->channels[j]; 107281681Srpaulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 108281681Srpaulo chan->chan == sec_chan) { 109281681Srpaulo ok = 1; 110281681Srpaulo break; 111281681Srpaulo } 112281681Srpaulo } 113281681Srpaulo if (!ok) { 114281681Srpaulo wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 115281681Srpaulo sec_chan); 116281681Srpaulo return 0; 117281681Srpaulo } 118281681Srpaulo 119281681Srpaulo /* 120281681Srpaulo * Verify that HT40 primary,secondary channel pair is allowed per 121281681Srpaulo * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 122281681Srpaulo * 2.4 GHz rules allow all cases where the secondary channel fits into 123281681Srpaulo * the list of allowed channels (already checked above). 124281681Srpaulo */ 125281681Srpaulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) 126281681Srpaulo return 1; 127281681Srpaulo 128281681Srpaulo first = pri_chan < sec_chan ? pri_chan : sec_chan; 129281681Srpaulo 130281681Srpaulo ok = 0; 131281681Srpaulo for (k = 0; k < ARRAY_SIZE(allowed); k++) { 132281681Srpaulo if (first == allowed[k]) { 133281681Srpaulo ok = 1; 134281681Srpaulo break; 135281681Srpaulo } 136281681Srpaulo } 137281681Srpaulo if (!ok) { 138281681Srpaulo wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 139281681Srpaulo pri_chan, sec_chan); 140281681Srpaulo return 0; 141281681Srpaulo } 142281681Srpaulo 143281681Srpaulo return 1; 144281681Srpaulo} 145281681Srpaulo 146281681Srpaulo 147281681Srpaulovoid get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) 148281681Srpaulo{ 149281681Srpaulo struct ieee80211_ht_operation *oper; 150281681Srpaulo struct ieee802_11_elems elems; 151281681Srpaulo 152281681Srpaulo *pri_chan = *sec_chan = 0; 153281681Srpaulo 154281681Srpaulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 155289549Srpaulo if (elems.ht_operation) { 156281681Srpaulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 157281681Srpaulo *pri_chan = oper->primary_chan; 158281681Srpaulo if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { 159281681Srpaulo int sec = oper->ht_param & 160281681Srpaulo HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; 161281681Srpaulo if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 162281681Srpaulo *sec_chan = *pri_chan + 4; 163281681Srpaulo else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 164281681Srpaulo *sec_chan = *pri_chan - 4; 165281681Srpaulo } 166281681Srpaulo } 167281681Srpaulo} 168281681Srpaulo 169281681Srpaulo 170281681Srpauloint check_40mhz_5g(struct hostapd_hw_modes *mode, 171281681Srpaulo struct wpa_scan_results *scan_res, int pri_chan, 172281681Srpaulo int sec_chan) 173281681Srpaulo{ 174281681Srpaulo int pri_freq, sec_freq, pri_bss, sec_bss; 175281681Srpaulo int bss_pri_chan, bss_sec_chan; 176281681Srpaulo size_t i; 177281681Srpaulo int match; 178281681Srpaulo 179289549Srpaulo if (!mode || !scan_res || !pri_chan || !sec_chan || 180289549Srpaulo pri_chan == sec_chan) 181281681Srpaulo return 0; 182281681Srpaulo 183281681Srpaulo pri_freq = hw_get_freq(mode, pri_chan); 184281681Srpaulo sec_freq = hw_get_freq(mode, sec_chan); 185281681Srpaulo 186281681Srpaulo /* 187281681Srpaulo * Switch PRI/SEC channels if Beacons were detected on selected SEC 188281681Srpaulo * channel, but not on selected PRI channel. 189281681Srpaulo */ 190281681Srpaulo pri_bss = sec_bss = 0; 191281681Srpaulo for (i = 0; i < scan_res->num; i++) { 192281681Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 193281681Srpaulo if (bss->freq == pri_freq) 194281681Srpaulo pri_bss++; 195281681Srpaulo else if (bss->freq == sec_freq) 196281681Srpaulo sec_bss++; 197281681Srpaulo } 198281681Srpaulo if (sec_bss && !pri_bss) { 199281681Srpaulo wpa_printf(MSG_INFO, 200281681Srpaulo "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes"); 201281681Srpaulo return 2; 202281681Srpaulo } 203281681Srpaulo 204281681Srpaulo /* 205281681Srpaulo * Match PRI/SEC channel with any existing HT40 BSS on the same 206281681Srpaulo * channels that we are about to use (if already mixed order in 207281681Srpaulo * existing BSSes, use own preference). 208281681Srpaulo */ 209281681Srpaulo match = 0; 210281681Srpaulo for (i = 0; i < scan_res->num; i++) { 211281681Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 212281681Srpaulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 213281681Srpaulo if (pri_chan == bss_pri_chan && 214281681Srpaulo sec_chan == bss_sec_chan) { 215281681Srpaulo match = 1; 216281681Srpaulo break; 217281681Srpaulo } 218281681Srpaulo } 219281681Srpaulo if (!match) { 220281681Srpaulo for (i = 0; i < scan_res->num; i++) { 221281681Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 222281681Srpaulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 223281681Srpaulo if (pri_chan == bss_sec_chan && 224281681Srpaulo sec_chan == bss_pri_chan) { 225281681Srpaulo wpa_printf(MSG_INFO, "Switch own primary and " 226281681Srpaulo "secondary channel due to BSS " 227281681Srpaulo "overlap with " MACSTR, 228281681Srpaulo MAC2STR(bss->bssid)); 229281681Srpaulo return 2; 230281681Srpaulo } 231281681Srpaulo } 232281681Srpaulo } 233281681Srpaulo 234281681Srpaulo return 1; 235281681Srpaulo} 236281681Srpaulo 237281681Srpaulo 238289549Srpaulostatic int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, 239289549Srpaulo int end) 240281681Srpaulo{ 241281681Srpaulo struct ieee802_11_elems elems; 242281681Srpaulo struct ieee80211_ht_operation *oper; 243281681Srpaulo 244281681Srpaulo if (bss->freq < start || bss->freq > end || bss->freq == pri_freq) 245281681Srpaulo return 0; 246281681Srpaulo 247281681Srpaulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 248281681Srpaulo if (!elems.ht_capabilities) { 249281681Srpaulo wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: " 250281681Srpaulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 251281681Srpaulo return 1; 252281681Srpaulo } 253281681Srpaulo 254289549Srpaulo if (elems.ht_operation) { 255281681Srpaulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 256281681Srpaulo if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) 257281681Srpaulo return 0; 258281681Srpaulo 259281681Srpaulo wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: " 260281681Srpaulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 261281681Srpaulo return 1; 262281681Srpaulo } 263281681Srpaulo return 0; 264281681Srpaulo} 265281681Srpaulo 266281681Srpaulo 267281681Srpauloint check_40mhz_2g4(struct hostapd_hw_modes *mode, 268281681Srpaulo struct wpa_scan_results *scan_res, int pri_chan, 269281681Srpaulo int sec_chan) 270281681Srpaulo{ 271281681Srpaulo int pri_freq, sec_freq; 272281681Srpaulo int affected_start, affected_end; 273281681Srpaulo size_t i; 274281681Srpaulo 275289549Srpaulo if (!mode || !scan_res || !pri_chan || !sec_chan || 276289549Srpaulo pri_chan == sec_chan) 277281681Srpaulo return 0; 278281681Srpaulo 279281681Srpaulo pri_freq = hw_get_freq(mode, pri_chan); 280281681Srpaulo sec_freq = hw_get_freq(mode, sec_chan); 281281681Srpaulo 282281681Srpaulo affected_start = (pri_freq + sec_freq) / 2 - 25; 283281681Srpaulo affected_end = (pri_freq + sec_freq) / 2 + 25; 284281681Srpaulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 285281681Srpaulo affected_start, affected_end); 286281681Srpaulo for (i = 0; i < scan_res->num; i++) { 287281681Srpaulo struct wpa_scan_res *bss = scan_res->res[i]; 288281681Srpaulo int pri = bss->freq; 289281681Srpaulo int sec = pri; 290281681Srpaulo struct ieee802_11_elems elems; 291281681Srpaulo 292281681Srpaulo /* Check for overlapping 20 MHz BSS */ 293281681Srpaulo if (check_20mhz_bss(bss, pri_freq, affected_start, 294281681Srpaulo affected_end)) { 295281681Srpaulo wpa_printf(MSG_DEBUG, 296281681Srpaulo "Overlapping 20 MHz BSS is found"); 297281681Srpaulo return 0; 298281681Srpaulo } 299281681Srpaulo 300281681Srpaulo get_pri_sec_chan(bss, &pri_chan, &sec_chan); 301281681Srpaulo 302281681Srpaulo if (sec_chan) { 303281681Srpaulo if (sec_chan < pri_chan) 304281681Srpaulo sec = pri - 20; 305281681Srpaulo else 306281681Srpaulo sec = pri + 20; 307281681Srpaulo } 308281681Srpaulo 309281681Srpaulo if ((pri < affected_start || pri > affected_end) && 310281681Srpaulo (sec < affected_start || sec > affected_end)) 311281681Srpaulo continue; /* not within affected channel range */ 312281681Srpaulo 313281681Srpaulo wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 314281681Srpaulo " freq=%d pri=%d sec=%d", 315281681Srpaulo MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 316281681Srpaulo 317281681Srpaulo if (sec_chan) { 318281681Srpaulo if (pri_freq != pri || sec_freq != sec) { 319281681Srpaulo wpa_printf(MSG_DEBUG, 320281681Srpaulo "40 MHz pri/sec mismatch with BSS " 321281681Srpaulo MACSTR 322281681Srpaulo " <%d,%d> (chan=%d%c) vs. <%d,%d>", 323281681Srpaulo MAC2STR(bss->bssid), 324281681Srpaulo pri, sec, pri_chan, 325281681Srpaulo sec > pri ? '+' : '-', 326281681Srpaulo pri_freq, sec_freq); 327281681Srpaulo return 0; 328281681Srpaulo } 329281681Srpaulo } 330281681Srpaulo 331281681Srpaulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 332281681Srpaulo 0); 333289549Srpaulo if (elems.ht_capabilities) { 334281681Srpaulo struct ieee80211_ht_capabilities *ht_cap = 335281681Srpaulo (struct ieee80211_ht_capabilities *) 336281681Srpaulo elems.ht_capabilities; 337281681Srpaulo 338281681Srpaulo if (le_to_host16(ht_cap->ht_capabilities_info) & 339281681Srpaulo HT_CAP_INFO_40MHZ_INTOLERANT) { 340281681Srpaulo wpa_printf(MSG_DEBUG, 341281681Srpaulo "40 MHz Intolerant is set on channel %d in BSS " 342281681Srpaulo MACSTR, pri, MAC2STR(bss->bssid)); 343281681Srpaulo return 0; 344281681Srpaulo } 345281681Srpaulo } 346281681Srpaulo } 347281681Srpaulo 348281681Srpaulo return 1; 349281681Srpaulo} 350281681Srpaulo 351281681Srpaulo 352281681Srpauloint hostapd_set_freq_params(struct hostapd_freq_params *data, 353281681Srpaulo enum hostapd_hw_mode mode, 354281681Srpaulo int freq, int channel, int ht_enabled, 355281681Srpaulo int vht_enabled, int sec_channel_offset, 356281681Srpaulo int vht_oper_chwidth, int center_segment0, 357281681Srpaulo int center_segment1, u32 vht_caps) 358281681Srpaulo{ 359281681Srpaulo os_memset(data, 0, sizeof(*data)); 360281681Srpaulo data->mode = mode; 361281681Srpaulo data->freq = freq; 362281681Srpaulo data->channel = channel; 363281681Srpaulo data->ht_enabled = ht_enabled; 364281681Srpaulo data->vht_enabled = vht_enabled; 365281681Srpaulo data->sec_channel_offset = sec_channel_offset; 366281681Srpaulo data->center_freq1 = freq + sec_channel_offset * 10; 367281681Srpaulo data->center_freq2 = 0; 368281681Srpaulo data->bandwidth = sec_channel_offset ? 40 : 20; 369281681Srpaulo 370281681Srpaulo if (data->vht_enabled) switch (vht_oper_chwidth) { 371281681Srpaulo case VHT_CHANWIDTH_USE_HT: 372289549Srpaulo if (center_segment1 || 373289549Srpaulo (center_segment0 != 0 && 374289549Srpaulo 5000 + center_segment0 * 5 != data->center_freq1 && 375289549Srpaulo 2407 + center_segment0 * 5 != data->center_freq1)) 376281681Srpaulo return -1; 377281681Srpaulo break; 378281681Srpaulo case VHT_CHANWIDTH_80P80MHZ: 379281681Srpaulo if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { 380281681Srpaulo wpa_printf(MSG_ERROR, 381281681Srpaulo "80+80 channel width is not supported!"); 382281681Srpaulo return -1; 383281681Srpaulo } 384281681Srpaulo if (center_segment1 == center_segment0 + 4 || 385281681Srpaulo center_segment1 == center_segment0 - 4) 386281681Srpaulo return -1; 387281681Srpaulo data->center_freq2 = 5000 + center_segment1 * 5; 388281681Srpaulo /* fall through */ 389281681Srpaulo case VHT_CHANWIDTH_80MHZ: 390281681Srpaulo data->bandwidth = 80; 391289549Srpaulo if ((vht_oper_chwidth == 1 && center_segment1) || 392289549Srpaulo (vht_oper_chwidth == 3 && !center_segment1) || 393289549Srpaulo !sec_channel_offset) 394281681Srpaulo return -1; 395289549Srpaulo if (!center_segment0) { 396289549Srpaulo if (channel <= 48) 397289549Srpaulo center_segment0 = 42; 398289549Srpaulo else if (channel <= 64) 399289549Srpaulo center_segment0 = 58; 400289549Srpaulo else if (channel <= 112) 401289549Srpaulo center_segment0 = 106; 402289549Srpaulo else if (channel <= 128) 403289549Srpaulo center_segment0 = 122; 404289549Srpaulo else if (channel <= 144) 405289549Srpaulo center_segment0 = 138; 406289549Srpaulo else if (channel <= 161) 407289549Srpaulo center_segment0 = 155; 408289549Srpaulo data->center_freq1 = 5000 + center_segment0 * 5; 409289549Srpaulo } else { 410289549Srpaulo /* 411289549Srpaulo * Note: HT/VHT config and params are coupled. Check if 412289549Srpaulo * HT40 channel band is in VHT80 Pri channel band 413289549Srpaulo * configuration. 414289549Srpaulo */ 415289549Srpaulo if (center_segment0 == channel + 6 || 416289549Srpaulo center_segment0 == channel + 2 || 417289549Srpaulo center_segment0 == channel - 2 || 418289549Srpaulo center_segment0 == channel - 6) 419289549Srpaulo data->center_freq1 = 5000 + center_segment0 * 5; 420289549Srpaulo else 421289549Srpaulo return -1; 422289549Srpaulo } 423281681Srpaulo break; 424281681Srpaulo case VHT_CHANWIDTH_160MHZ: 425281681Srpaulo data->bandwidth = 160; 426281681Srpaulo if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 427281681Srpaulo VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { 428281681Srpaulo wpa_printf(MSG_ERROR, 429281681Srpaulo "160MHZ channel width is not supported!"); 430281681Srpaulo return -1; 431281681Srpaulo } 432281681Srpaulo if (center_segment1) 433281681Srpaulo return -1; 434281681Srpaulo if (!sec_channel_offset) 435281681Srpaulo return -1; 436289549Srpaulo /* 437289549Srpaulo * Note: HT/VHT config and params are coupled. Check if 438289549Srpaulo * HT40 channel band is in VHT160 channel band configuration. 439289549Srpaulo */ 440289549Srpaulo if (center_segment0 == channel + 14 || 441289549Srpaulo center_segment0 == channel + 10 || 442289549Srpaulo center_segment0 == channel + 6 || 443289549Srpaulo center_segment0 == channel + 2 || 444289549Srpaulo center_segment0 == channel - 2 || 445289549Srpaulo center_segment0 == channel - 6 || 446289549Srpaulo center_segment0 == channel - 10 || 447289549Srpaulo center_segment0 == channel - 14) 448289549Srpaulo data->center_freq1 = 5000 + center_segment0 * 5; 449289549Srpaulo else 450281681Srpaulo return -1; 451281681Srpaulo break; 452281681Srpaulo } 453281681Srpaulo 454281681Srpaulo return 0; 455281681Srpaulo} 456