1*5b9c547cSRui Paulo /* 2*5b9c547cSRui Paulo * Common hostapd/wpa_supplicant HW features 3*5b9c547cSRui Paulo * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 4*5b9c547cSRui Paulo * Copyright (c) 2015, Qualcomm Atheros, Inc. 5*5b9c547cSRui Paulo * 6*5b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 7*5b9c547cSRui Paulo * See README for more details. 8*5b9c547cSRui Paulo */ 9*5b9c547cSRui Paulo 10*5b9c547cSRui Paulo #include "includes.h" 11*5b9c547cSRui Paulo 12*5b9c547cSRui Paulo #include "common.h" 13*5b9c547cSRui Paulo #include "defs.h" 14*5b9c547cSRui Paulo #include "ieee802_11_defs.h" 15*5b9c547cSRui Paulo #include "ieee802_11_common.h" 16*5b9c547cSRui Paulo #include "hw_features_common.h" 17*5b9c547cSRui Paulo 18*5b9c547cSRui Paulo 19*5b9c547cSRui Paulo struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, 20*5b9c547cSRui Paulo int chan, int *freq) 21*5b9c547cSRui Paulo { 22*5b9c547cSRui Paulo int i; 23*5b9c547cSRui Paulo 24*5b9c547cSRui Paulo if (freq) 25*5b9c547cSRui Paulo *freq = 0; 26*5b9c547cSRui Paulo 27*5b9c547cSRui Paulo if (!mode) 28*5b9c547cSRui Paulo return NULL; 29*5b9c547cSRui Paulo 30*5b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 31*5b9c547cSRui Paulo struct hostapd_channel_data *ch = &mode->channels[i]; 32*5b9c547cSRui Paulo if (ch->chan == chan) { 33*5b9c547cSRui Paulo if (freq) 34*5b9c547cSRui Paulo *freq = ch->freq; 35*5b9c547cSRui Paulo return ch; 36*5b9c547cSRui Paulo } 37*5b9c547cSRui Paulo } 38*5b9c547cSRui Paulo 39*5b9c547cSRui Paulo return NULL; 40*5b9c547cSRui Paulo } 41*5b9c547cSRui Paulo 42*5b9c547cSRui Paulo 43*5b9c547cSRui Paulo struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode, 44*5b9c547cSRui Paulo int freq, int *chan) 45*5b9c547cSRui Paulo { 46*5b9c547cSRui Paulo int i; 47*5b9c547cSRui Paulo 48*5b9c547cSRui Paulo if (chan) 49*5b9c547cSRui Paulo *chan = 0; 50*5b9c547cSRui Paulo 51*5b9c547cSRui Paulo if (!mode) 52*5b9c547cSRui Paulo return NULL; 53*5b9c547cSRui Paulo 54*5b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 55*5b9c547cSRui Paulo struct hostapd_channel_data *ch = &mode->channels[i]; 56*5b9c547cSRui Paulo if (ch->freq == freq) { 57*5b9c547cSRui Paulo if (chan) 58*5b9c547cSRui Paulo *chan = ch->chan; 59*5b9c547cSRui Paulo return ch; 60*5b9c547cSRui Paulo } 61*5b9c547cSRui Paulo } 62*5b9c547cSRui Paulo 63*5b9c547cSRui Paulo return NULL; 64*5b9c547cSRui Paulo } 65*5b9c547cSRui Paulo 66*5b9c547cSRui Paulo 67*5b9c547cSRui Paulo int hw_get_freq(struct hostapd_hw_modes *mode, int chan) 68*5b9c547cSRui Paulo { 69*5b9c547cSRui Paulo int freq; 70*5b9c547cSRui Paulo 71*5b9c547cSRui Paulo hw_get_channel_chan(mode, chan, &freq); 72*5b9c547cSRui Paulo 73*5b9c547cSRui Paulo return freq; 74*5b9c547cSRui Paulo } 75*5b9c547cSRui Paulo 76*5b9c547cSRui Paulo 77*5b9c547cSRui Paulo int hw_get_chan(struct hostapd_hw_modes *mode, int freq) 78*5b9c547cSRui Paulo { 79*5b9c547cSRui Paulo int chan; 80*5b9c547cSRui Paulo 81*5b9c547cSRui Paulo hw_get_channel_freq(mode, freq, &chan); 82*5b9c547cSRui Paulo 83*5b9c547cSRui Paulo return chan; 84*5b9c547cSRui Paulo } 85*5b9c547cSRui Paulo 86*5b9c547cSRui Paulo 87*5b9c547cSRui Paulo int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, 88*5b9c547cSRui Paulo int sec_chan) 89*5b9c547cSRui Paulo { 90*5b9c547cSRui Paulo int ok, j, first; 91*5b9c547cSRui Paulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 92*5b9c547cSRui Paulo 184, 192 }; 93*5b9c547cSRui Paulo size_t k; 94*5b9c547cSRui Paulo 95*5b9c547cSRui Paulo if (pri_chan == sec_chan || !sec_chan) 96*5b9c547cSRui Paulo return 1; /* HT40 not used */ 97*5b9c547cSRui Paulo 98*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 99*5b9c547cSRui Paulo "HT40: control channel: %d secondary channel: %d", 100*5b9c547cSRui Paulo pri_chan, sec_chan); 101*5b9c547cSRui Paulo 102*5b9c547cSRui Paulo /* Verify that HT40 secondary channel is an allowed 20 MHz 103*5b9c547cSRui Paulo * channel */ 104*5b9c547cSRui Paulo ok = 0; 105*5b9c547cSRui Paulo for (j = 0; j < mode->num_channels; j++) { 106*5b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[j]; 107*5b9c547cSRui Paulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 108*5b9c547cSRui Paulo chan->chan == sec_chan) { 109*5b9c547cSRui Paulo ok = 1; 110*5b9c547cSRui Paulo break; 111*5b9c547cSRui Paulo } 112*5b9c547cSRui Paulo } 113*5b9c547cSRui Paulo if (!ok) { 114*5b9c547cSRui Paulo wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 115*5b9c547cSRui Paulo sec_chan); 116*5b9c547cSRui Paulo return 0; 117*5b9c547cSRui Paulo } 118*5b9c547cSRui Paulo 119*5b9c547cSRui Paulo /* 120*5b9c547cSRui Paulo * Verify that HT40 primary,secondary channel pair is allowed per 121*5b9c547cSRui Paulo * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 122*5b9c547cSRui Paulo * 2.4 GHz rules allow all cases where the secondary channel fits into 123*5b9c547cSRui Paulo * the list of allowed channels (already checked above). 124*5b9c547cSRui Paulo */ 125*5b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) 126*5b9c547cSRui Paulo return 1; 127*5b9c547cSRui Paulo 128*5b9c547cSRui Paulo first = pri_chan < sec_chan ? pri_chan : sec_chan; 129*5b9c547cSRui Paulo 130*5b9c547cSRui Paulo ok = 0; 131*5b9c547cSRui Paulo for (k = 0; k < ARRAY_SIZE(allowed); k++) { 132*5b9c547cSRui Paulo if (first == allowed[k]) { 133*5b9c547cSRui Paulo ok = 1; 134*5b9c547cSRui Paulo break; 135*5b9c547cSRui Paulo } 136*5b9c547cSRui Paulo } 137*5b9c547cSRui Paulo if (!ok) { 138*5b9c547cSRui Paulo wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 139*5b9c547cSRui Paulo pri_chan, sec_chan); 140*5b9c547cSRui Paulo return 0; 141*5b9c547cSRui Paulo } 142*5b9c547cSRui Paulo 143*5b9c547cSRui Paulo return 1; 144*5b9c547cSRui Paulo } 145*5b9c547cSRui Paulo 146*5b9c547cSRui Paulo 147*5b9c547cSRui Paulo void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) 148*5b9c547cSRui Paulo { 149*5b9c547cSRui Paulo struct ieee80211_ht_operation *oper; 150*5b9c547cSRui Paulo struct ieee802_11_elems elems; 151*5b9c547cSRui Paulo 152*5b9c547cSRui Paulo *pri_chan = *sec_chan = 0; 153*5b9c547cSRui Paulo 154*5b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 155*5b9c547cSRui Paulo if (elems.ht_operation && 156*5b9c547cSRui Paulo elems.ht_operation_len >= sizeof(*oper)) { 157*5b9c547cSRui Paulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 158*5b9c547cSRui Paulo *pri_chan = oper->primary_chan; 159*5b9c547cSRui Paulo if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { 160*5b9c547cSRui Paulo int sec = oper->ht_param & 161*5b9c547cSRui Paulo HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; 162*5b9c547cSRui Paulo if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 163*5b9c547cSRui Paulo *sec_chan = *pri_chan + 4; 164*5b9c547cSRui Paulo else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 165*5b9c547cSRui Paulo *sec_chan = *pri_chan - 4; 166*5b9c547cSRui Paulo } 167*5b9c547cSRui Paulo } 168*5b9c547cSRui Paulo } 169*5b9c547cSRui Paulo 170*5b9c547cSRui Paulo 171*5b9c547cSRui Paulo int check_40mhz_5g(struct hostapd_hw_modes *mode, 172*5b9c547cSRui Paulo struct wpa_scan_results *scan_res, int pri_chan, 173*5b9c547cSRui Paulo int sec_chan) 174*5b9c547cSRui Paulo { 175*5b9c547cSRui Paulo int pri_freq, sec_freq, pri_bss, sec_bss; 176*5b9c547cSRui Paulo int bss_pri_chan, bss_sec_chan; 177*5b9c547cSRui Paulo size_t i; 178*5b9c547cSRui Paulo int match; 179*5b9c547cSRui Paulo 180*5b9c547cSRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan) 181*5b9c547cSRui Paulo return 0; 182*5b9c547cSRui Paulo 183*5b9c547cSRui Paulo if (pri_chan == sec_chan) 184*5b9c547cSRui Paulo return 0; 185*5b9c547cSRui Paulo 186*5b9c547cSRui Paulo pri_freq = hw_get_freq(mode, pri_chan); 187*5b9c547cSRui Paulo sec_freq = hw_get_freq(mode, sec_chan); 188*5b9c547cSRui Paulo 189*5b9c547cSRui Paulo /* 190*5b9c547cSRui Paulo * Switch PRI/SEC channels if Beacons were detected on selected SEC 191*5b9c547cSRui Paulo * channel, but not on selected PRI channel. 192*5b9c547cSRui Paulo */ 193*5b9c547cSRui Paulo pri_bss = sec_bss = 0; 194*5b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 195*5b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 196*5b9c547cSRui Paulo if (bss->freq == pri_freq) 197*5b9c547cSRui Paulo pri_bss++; 198*5b9c547cSRui Paulo else if (bss->freq == sec_freq) 199*5b9c547cSRui Paulo sec_bss++; 200*5b9c547cSRui Paulo } 201*5b9c547cSRui Paulo if (sec_bss && !pri_bss) { 202*5b9c547cSRui Paulo wpa_printf(MSG_INFO, 203*5b9c547cSRui Paulo "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes"); 204*5b9c547cSRui Paulo return 2; 205*5b9c547cSRui Paulo } 206*5b9c547cSRui Paulo 207*5b9c547cSRui Paulo /* 208*5b9c547cSRui Paulo * Match PRI/SEC channel with any existing HT40 BSS on the same 209*5b9c547cSRui Paulo * channels that we are about to use (if already mixed order in 210*5b9c547cSRui Paulo * existing BSSes, use own preference). 211*5b9c547cSRui Paulo */ 212*5b9c547cSRui Paulo match = 0; 213*5b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 214*5b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 215*5b9c547cSRui Paulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 216*5b9c547cSRui Paulo if (pri_chan == bss_pri_chan && 217*5b9c547cSRui Paulo sec_chan == bss_sec_chan) { 218*5b9c547cSRui Paulo match = 1; 219*5b9c547cSRui Paulo break; 220*5b9c547cSRui Paulo } 221*5b9c547cSRui Paulo } 222*5b9c547cSRui Paulo if (!match) { 223*5b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 224*5b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 225*5b9c547cSRui Paulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 226*5b9c547cSRui Paulo if (pri_chan == bss_sec_chan && 227*5b9c547cSRui Paulo sec_chan == bss_pri_chan) { 228*5b9c547cSRui Paulo wpa_printf(MSG_INFO, "Switch own primary and " 229*5b9c547cSRui Paulo "secondary channel due to BSS " 230*5b9c547cSRui Paulo "overlap with " MACSTR, 231*5b9c547cSRui Paulo MAC2STR(bss->bssid)); 232*5b9c547cSRui Paulo return 2; 233*5b9c547cSRui Paulo } 234*5b9c547cSRui Paulo } 235*5b9c547cSRui Paulo } 236*5b9c547cSRui Paulo 237*5b9c547cSRui Paulo return 1; 238*5b9c547cSRui Paulo } 239*5b9c547cSRui Paulo 240*5b9c547cSRui Paulo 241*5b9c547cSRui Paulo int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end) 242*5b9c547cSRui Paulo { 243*5b9c547cSRui Paulo struct ieee802_11_elems elems; 244*5b9c547cSRui Paulo struct ieee80211_ht_operation *oper; 245*5b9c547cSRui Paulo 246*5b9c547cSRui Paulo if (bss->freq < start || bss->freq > end || bss->freq == pri_freq) 247*5b9c547cSRui Paulo return 0; 248*5b9c547cSRui Paulo 249*5b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 250*5b9c547cSRui Paulo if (!elems.ht_capabilities) { 251*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: " 252*5b9c547cSRui Paulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 253*5b9c547cSRui Paulo return 1; 254*5b9c547cSRui Paulo } 255*5b9c547cSRui Paulo 256*5b9c547cSRui Paulo if (elems.ht_operation && 257*5b9c547cSRui Paulo elems.ht_operation_len >= sizeof(*oper)) { 258*5b9c547cSRui Paulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 259*5b9c547cSRui Paulo if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) 260*5b9c547cSRui Paulo return 0; 261*5b9c547cSRui Paulo 262*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: " 263*5b9c547cSRui Paulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 264*5b9c547cSRui Paulo return 1; 265*5b9c547cSRui Paulo } 266*5b9c547cSRui Paulo return 0; 267*5b9c547cSRui Paulo } 268*5b9c547cSRui Paulo 269*5b9c547cSRui Paulo 270*5b9c547cSRui Paulo int check_40mhz_2g4(struct hostapd_hw_modes *mode, 271*5b9c547cSRui Paulo struct wpa_scan_results *scan_res, int pri_chan, 272*5b9c547cSRui Paulo int sec_chan) 273*5b9c547cSRui Paulo { 274*5b9c547cSRui Paulo int pri_freq, sec_freq; 275*5b9c547cSRui Paulo int affected_start, affected_end; 276*5b9c547cSRui Paulo size_t i; 277*5b9c547cSRui Paulo 278*5b9c547cSRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan) 279*5b9c547cSRui Paulo return 0; 280*5b9c547cSRui Paulo 281*5b9c547cSRui Paulo if (pri_chan == sec_chan) 282*5b9c547cSRui Paulo return 0; 283*5b9c547cSRui Paulo 284*5b9c547cSRui Paulo pri_freq = hw_get_freq(mode, pri_chan); 285*5b9c547cSRui Paulo sec_freq = hw_get_freq(mode, sec_chan); 286*5b9c547cSRui Paulo 287*5b9c547cSRui Paulo affected_start = (pri_freq + sec_freq) / 2 - 25; 288*5b9c547cSRui Paulo affected_end = (pri_freq + sec_freq) / 2 + 25; 289*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 290*5b9c547cSRui Paulo affected_start, affected_end); 291*5b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 292*5b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 293*5b9c547cSRui Paulo int pri = bss->freq; 294*5b9c547cSRui Paulo int sec = pri; 295*5b9c547cSRui Paulo struct ieee802_11_elems elems; 296*5b9c547cSRui Paulo 297*5b9c547cSRui Paulo /* Check for overlapping 20 MHz BSS */ 298*5b9c547cSRui Paulo if (check_20mhz_bss(bss, pri_freq, affected_start, 299*5b9c547cSRui Paulo affected_end)) { 300*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 301*5b9c547cSRui Paulo "Overlapping 20 MHz BSS is found"); 302*5b9c547cSRui Paulo return 0; 303*5b9c547cSRui Paulo } 304*5b9c547cSRui Paulo 305*5b9c547cSRui Paulo get_pri_sec_chan(bss, &pri_chan, &sec_chan); 306*5b9c547cSRui Paulo 307*5b9c547cSRui Paulo if (sec_chan) { 308*5b9c547cSRui Paulo if (sec_chan < pri_chan) 309*5b9c547cSRui Paulo sec = pri - 20; 310*5b9c547cSRui Paulo else 311*5b9c547cSRui Paulo sec = pri + 20; 312*5b9c547cSRui Paulo } 313*5b9c547cSRui Paulo 314*5b9c547cSRui Paulo if ((pri < affected_start || pri > affected_end) && 315*5b9c547cSRui Paulo (sec < affected_start || sec > affected_end)) 316*5b9c547cSRui Paulo continue; /* not within affected channel range */ 317*5b9c547cSRui Paulo 318*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 319*5b9c547cSRui Paulo " freq=%d pri=%d sec=%d", 320*5b9c547cSRui Paulo MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 321*5b9c547cSRui Paulo 322*5b9c547cSRui Paulo if (sec_chan) { 323*5b9c547cSRui Paulo if (pri_freq != pri || sec_freq != sec) { 324*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 325*5b9c547cSRui Paulo "40 MHz pri/sec mismatch with BSS " 326*5b9c547cSRui Paulo MACSTR 327*5b9c547cSRui Paulo " <%d,%d> (chan=%d%c) vs. <%d,%d>", 328*5b9c547cSRui Paulo MAC2STR(bss->bssid), 329*5b9c547cSRui Paulo pri, sec, pri_chan, 330*5b9c547cSRui Paulo sec > pri ? '+' : '-', 331*5b9c547cSRui Paulo pri_freq, sec_freq); 332*5b9c547cSRui Paulo return 0; 333*5b9c547cSRui Paulo } 334*5b9c547cSRui Paulo } 335*5b9c547cSRui Paulo 336*5b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 337*5b9c547cSRui Paulo 0); 338*5b9c547cSRui Paulo if (elems.ht_capabilities && 339*5b9c547cSRui Paulo elems.ht_capabilities_len >= 340*5b9c547cSRui Paulo sizeof(struct ieee80211_ht_capabilities)) { 341*5b9c547cSRui Paulo struct ieee80211_ht_capabilities *ht_cap = 342*5b9c547cSRui Paulo (struct ieee80211_ht_capabilities *) 343*5b9c547cSRui Paulo elems.ht_capabilities; 344*5b9c547cSRui Paulo 345*5b9c547cSRui Paulo if (le_to_host16(ht_cap->ht_capabilities_info) & 346*5b9c547cSRui Paulo HT_CAP_INFO_40MHZ_INTOLERANT) { 347*5b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 348*5b9c547cSRui Paulo "40 MHz Intolerant is set on channel %d in BSS " 349*5b9c547cSRui Paulo MACSTR, pri, MAC2STR(bss->bssid)); 350*5b9c547cSRui Paulo return 0; 351*5b9c547cSRui Paulo } 352*5b9c547cSRui Paulo } 353*5b9c547cSRui Paulo } 354*5b9c547cSRui Paulo 355*5b9c547cSRui Paulo return 1; 356*5b9c547cSRui Paulo } 357*5b9c547cSRui Paulo 358*5b9c547cSRui Paulo 359*5b9c547cSRui Paulo int hostapd_set_freq_params(struct hostapd_freq_params *data, 360*5b9c547cSRui Paulo enum hostapd_hw_mode mode, 361*5b9c547cSRui Paulo int freq, int channel, int ht_enabled, 362*5b9c547cSRui Paulo int vht_enabled, int sec_channel_offset, 363*5b9c547cSRui Paulo int vht_oper_chwidth, int center_segment0, 364*5b9c547cSRui Paulo int center_segment1, u32 vht_caps) 365*5b9c547cSRui Paulo { 366*5b9c547cSRui Paulo int tmp; 367*5b9c547cSRui Paulo 368*5b9c547cSRui Paulo os_memset(data, 0, sizeof(*data)); 369*5b9c547cSRui Paulo data->mode = mode; 370*5b9c547cSRui Paulo data->freq = freq; 371*5b9c547cSRui Paulo data->channel = channel; 372*5b9c547cSRui Paulo data->ht_enabled = ht_enabled; 373*5b9c547cSRui Paulo data->vht_enabled = vht_enabled; 374*5b9c547cSRui Paulo data->sec_channel_offset = sec_channel_offset; 375*5b9c547cSRui Paulo data->center_freq1 = freq + sec_channel_offset * 10; 376*5b9c547cSRui Paulo data->center_freq2 = 0; 377*5b9c547cSRui Paulo data->bandwidth = sec_channel_offset ? 40 : 20; 378*5b9c547cSRui Paulo 379*5b9c547cSRui Paulo if (data->vht_enabled) switch (vht_oper_chwidth) { 380*5b9c547cSRui Paulo case VHT_CHANWIDTH_USE_HT: 381*5b9c547cSRui Paulo if (center_segment1) 382*5b9c547cSRui Paulo return -1; 383*5b9c547cSRui Paulo if (center_segment0 != 0 && 384*5b9c547cSRui Paulo 5000 + center_segment0 * 5 != data->center_freq1 && 385*5b9c547cSRui Paulo 2407 + center_segment0 * 5 != data->center_freq1) 386*5b9c547cSRui Paulo return -1; 387*5b9c547cSRui Paulo break; 388*5b9c547cSRui Paulo case VHT_CHANWIDTH_80P80MHZ: 389*5b9c547cSRui Paulo if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { 390*5b9c547cSRui Paulo wpa_printf(MSG_ERROR, 391*5b9c547cSRui Paulo "80+80 channel width is not supported!"); 392*5b9c547cSRui Paulo return -1; 393*5b9c547cSRui Paulo } 394*5b9c547cSRui Paulo if (center_segment1 == center_segment0 + 4 || 395*5b9c547cSRui Paulo center_segment1 == center_segment0 - 4) 396*5b9c547cSRui Paulo return -1; 397*5b9c547cSRui Paulo data->center_freq2 = 5000 + center_segment1 * 5; 398*5b9c547cSRui Paulo /* fall through */ 399*5b9c547cSRui Paulo case VHT_CHANWIDTH_80MHZ: 400*5b9c547cSRui Paulo data->bandwidth = 80; 401*5b9c547cSRui Paulo if (vht_oper_chwidth == 1 && center_segment1) 402*5b9c547cSRui Paulo return -1; 403*5b9c547cSRui Paulo if (vht_oper_chwidth == 3 && !center_segment1) 404*5b9c547cSRui Paulo return -1; 405*5b9c547cSRui Paulo if (!sec_channel_offset) 406*5b9c547cSRui Paulo return -1; 407*5b9c547cSRui Paulo /* primary 40 part must match the HT configuration */ 408*5b9c547cSRui Paulo tmp = (30 + freq - 5000 - center_segment0 * 5) / 20; 409*5b9c547cSRui Paulo tmp /= 2; 410*5b9c547cSRui Paulo if (data->center_freq1 != 5000 + 411*5b9c547cSRui Paulo center_segment0 * 5 - 20 + 40 * tmp) 412*5b9c547cSRui Paulo return -1; 413*5b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 414*5b9c547cSRui Paulo break; 415*5b9c547cSRui Paulo case VHT_CHANWIDTH_160MHZ: 416*5b9c547cSRui Paulo data->bandwidth = 160; 417*5b9c547cSRui Paulo if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 418*5b9c547cSRui Paulo VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { 419*5b9c547cSRui Paulo wpa_printf(MSG_ERROR, 420*5b9c547cSRui Paulo "160MHZ channel width is not supported!"); 421*5b9c547cSRui Paulo return -1; 422*5b9c547cSRui Paulo } 423*5b9c547cSRui Paulo if (center_segment1) 424*5b9c547cSRui Paulo return -1; 425*5b9c547cSRui Paulo if (!sec_channel_offset) 426*5b9c547cSRui Paulo return -1; 427*5b9c547cSRui Paulo /* primary 40 part must match the HT configuration */ 428*5b9c547cSRui Paulo tmp = (70 + freq - 5000 - center_segment0 * 5) / 20; 429*5b9c547cSRui Paulo tmp /= 2; 430*5b9c547cSRui Paulo if (data->center_freq1 != 5000 + 431*5b9c547cSRui Paulo center_segment0 * 5 - 60 + 40 * tmp) 432*5b9c547cSRui Paulo return -1; 433*5b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 434*5b9c547cSRui Paulo break; 435*5b9c547cSRui Paulo } 436*5b9c547cSRui Paulo 437*5b9c547cSRui Paulo return 0; 438*5b9c547cSRui Paulo } 439