15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Common hostapd/wpa_supplicant HW features 35b9c547cSRui Paulo * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 45b9c547cSRui Paulo * Copyright (c) 2015, Qualcomm Atheros, Inc. 55b9c547cSRui Paulo * 65b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 75b9c547cSRui Paulo * See README for more details. 85b9c547cSRui Paulo */ 95b9c547cSRui Paulo 105b9c547cSRui Paulo #include "includes.h" 115b9c547cSRui Paulo 125b9c547cSRui Paulo #include "common.h" 135b9c547cSRui Paulo #include "defs.h" 145b9c547cSRui Paulo #include "ieee802_11_defs.h" 155b9c547cSRui Paulo #include "ieee802_11_common.h" 165b9c547cSRui Paulo #include "hw_features_common.h" 175b9c547cSRui Paulo 185b9c547cSRui Paulo 195b9c547cSRui Paulo struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, 205b9c547cSRui Paulo int chan, int *freq) 215b9c547cSRui Paulo { 225b9c547cSRui Paulo int i; 235b9c547cSRui Paulo 245b9c547cSRui Paulo if (freq) 255b9c547cSRui Paulo *freq = 0; 265b9c547cSRui Paulo 275b9c547cSRui Paulo if (!mode) 285b9c547cSRui Paulo return NULL; 295b9c547cSRui Paulo 305b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 315b9c547cSRui Paulo struct hostapd_channel_data *ch = &mode->channels[i]; 325b9c547cSRui Paulo if (ch->chan == chan) { 335b9c547cSRui Paulo if (freq) 345b9c547cSRui Paulo *freq = ch->freq; 355b9c547cSRui Paulo return ch; 365b9c547cSRui Paulo } 375b9c547cSRui Paulo } 385b9c547cSRui Paulo 395b9c547cSRui Paulo return NULL; 405b9c547cSRui Paulo } 415b9c547cSRui Paulo 425b9c547cSRui Paulo 435b9c547cSRui Paulo struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode, 445b9c547cSRui Paulo int freq, int *chan) 455b9c547cSRui Paulo { 465b9c547cSRui Paulo int i; 475b9c547cSRui Paulo 485b9c547cSRui Paulo if (chan) 495b9c547cSRui Paulo *chan = 0; 505b9c547cSRui Paulo 515b9c547cSRui Paulo if (!mode) 525b9c547cSRui Paulo return NULL; 535b9c547cSRui Paulo 545b9c547cSRui Paulo for (i = 0; i < mode->num_channels; i++) { 555b9c547cSRui Paulo struct hostapd_channel_data *ch = &mode->channels[i]; 565b9c547cSRui Paulo if (ch->freq == freq) { 575b9c547cSRui Paulo if (chan) 585b9c547cSRui Paulo *chan = ch->chan; 595b9c547cSRui Paulo return ch; 605b9c547cSRui Paulo } 615b9c547cSRui Paulo } 625b9c547cSRui Paulo 635b9c547cSRui Paulo return NULL; 645b9c547cSRui Paulo } 655b9c547cSRui Paulo 665b9c547cSRui Paulo 675b9c547cSRui Paulo int hw_get_freq(struct hostapd_hw_modes *mode, int chan) 685b9c547cSRui Paulo { 695b9c547cSRui Paulo int freq; 705b9c547cSRui Paulo 715b9c547cSRui Paulo hw_get_channel_chan(mode, chan, &freq); 725b9c547cSRui Paulo 735b9c547cSRui Paulo return freq; 745b9c547cSRui Paulo } 755b9c547cSRui Paulo 765b9c547cSRui Paulo 775b9c547cSRui Paulo int hw_get_chan(struct hostapd_hw_modes *mode, int freq) 785b9c547cSRui Paulo { 795b9c547cSRui Paulo int chan; 805b9c547cSRui Paulo 815b9c547cSRui Paulo hw_get_channel_freq(mode, freq, &chan); 825b9c547cSRui Paulo 835b9c547cSRui Paulo return chan; 845b9c547cSRui Paulo } 855b9c547cSRui Paulo 865b9c547cSRui Paulo 875b9c547cSRui Paulo int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, 885b9c547cSRui Paulo int sec_chan) 895b9c547cSRui Paulo { 905b9c547cSRui Paulo int ok, j, first; 91*325151a3SRui Paulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 92*325151a3SRui Paulo 149, 157, 184, 192 }; 935b9c547cSRui Paulo size_t k; 945b9c547cSRui Paulo 955b9c547cSRui Paulo if (pri_chan == sec_chan || !sec_chan) 965b9c547cSRui Paulo return 1; /* HT40 not used */ 975b9c547cSRui Paulo 985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 995b9c547cSRui Paulo "HT40: control channel: %d secondary channel: %d", 1005b9c547cSRui Paulo pri_chan, sec_chan); 1015b9c547cSRui Paulo 1025b9c547cSRui Paulo /* Verify that HT40 secondary channel is an allowed 20 MHz 1035b9c547cSRui Paulo * channel */ 1045b9c547cSRui Paulo ok = 0; 1055b9c547cSRui Paulo for (j = 0; j < mode->num_channels; j++) { 1065b9c547cSRui Paulo struct hostapd_channel_data *chan = &mode->channels[j]; 1075b9c547cSRui Paulo if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && 1085b9c547cSRui Paulo chan->chan == sec_chan) { 1095b9c547cSRui Paulo ok = 1; 1105b9c547cSRui Paulo break; 1115b9c547cSRui Paulo } 1125b9c547cSRui Paulo } 1135b9c547cSRui Paulo if (!ok) { 1145b9c547cSRui Paulo wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 1155b9c547cSRui Paulo sec_chan); 1165b9c547cSRui Paulo return 0; 1175b9c547cSRui Paulo } 1185b9c547cSRui Paulo 1195b9c547cSRui Paulo /* 1205b9c547cSRui Paulo * Verify that HT40 primary,secondary channel pair is allowed per 1215b9c547cSRui Paulo * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 1225b9c547cSRui Paulo * 2.4 GHz rules allow all cases where the secondary channel fits into 1235b9c547cSRui Paulo * the list of allowed channels (already checked above). 1245b9c547cSRui Paulo */ 1255b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) 1265b9c547cSRui Paulo return 1; 1275b9c547cSRui Paulo 1285b9c547cSRui Paulo first = pri_chan < sec_chan ? pri_chan : sec_chan; 1295b9c547cSRui Paulo 1305b9c547cSRui Paulo ok = 0; 1315b9c547cSRui Paulo for (k = 0; k < ARRAY_SIZE(allowed); k++) { 1325b9c547cSRui Paulo if (first == allowed[k]) { 1335b9c547cSRui Paulo ok = 1; 1345b9c547cSRui Paulo break; 1355b9c547cSRui Paulo } 1365b9c547cSRui Paulo } 1375b9c547cSRui Paulo if (!ok) { 1385b9c547cSRui Paulo wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 1395b9c547cSRui Paulo pri_chan, sec_chan); 1405b9c547cSRui Paulo return 0; 1415b9c547cSRui Paulo } 1425b9c547cSRui Paulo 1435b9c547cSRui Paulo return 1; 1445b9c547cSRui Paulo } 1455b9c547cSRui Paulo 1465b9c547cSRui Paulo 1475b9c547cSRui Paulo void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) 1485b9c547cSRui Paulo { 1495b9c547cSRui Paulo struct ieee80211_ht_operation *oper; 1505b9c547cSRui Paulo struct ieee802_11_elems elems; 1515b9c547cSRui Paulo 1525b9c547cSRui Paulo *pri_chan = *sec_chan = 0; 1535b9c547cSRui Paulo 1545b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 155*325151a3SRui Paulo if (elems.ht_operation) { 1565b9c547cSRui Paulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 1575b9c547cSRui Paulo *pri_chan = oper->primary_chan; 1585b9c547cSRui Paulo if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { 1595b9c547cSRui Paulo int sec = oper->ht_param & 1605b9c547cSRui Paulo HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; 1615b9c547cSRui Paulo if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 1625b9c547cSRui Paulo *sec_chan = *pri_chan + 4; 1635b9c547cSRui Paulo else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 1645b9c547cSRui Paulo *sec_chan = *pri_chan - 4; 1655b9c547cSRui Paulo } 1665b9c547cSRui Paulo } 1675b9c547cSRui Paulo } 1685b9c547cSRui Paulo 1695b9c547cSRui Paulo 1705b9c547cSRui Paulo int check_40mhz_5g(struct hostapd_hw_modes *mode, 1715b9c547cSRui Paulo struct wpa_scan_results *scan_res, int pri_chan, 1725b9c547cSRui Paulo int sec_chan) 1735b9c547cSRui Paulo { 1745b9c547cSRui Paulo int pri_freq, sec_freq, pri_bss, sec_bss; 1755b9c547cSRui Paulo int bss_pri_chan, bss_sec_chan; 1765b9c547cSRui Paulo size_t i; 1775b9c547cSRui Paulo int match; 1785b9c547cSRui Paulo 179*325151a3SRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan || 180*325151a3SRui Paulo pri_chan == sec_chan) 1815b9c547cSRui Paulo return 0; 1825b9c547cSRui Paulo 1835b9c547cSRui Paulo pri_freq = hw_get_freq(mode, pri_chan); 1845b9c547cSRui Paulo sec_freq = hw_get_freq(mode, sec_chan); 1855b9c547cSRui Paulo 1865b9c547cSRui Paulo /* 1875b9c547cSRui Paulo * Switch PRI/SEC channels if Beacons were detected on selected SEC 1885b9c547cSRui Paulo * channel, but not on selected PRI channel. 1895b9c547cSRui Paulo */ 1905b9c547cSRui Paulo pri_bss = sec_bss = 0; 1915b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 1925b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 1935b9c547cSRui Paulo if (bss->freq == pri_freq) 1945b9c547cSRui Paulo pri_bss++; 1955b9c547cSRui Paulo else if (bss->freq == sec_freq) 1965b9c547cSRui Paulo sec_bss++; 1975b9c547cSRui Paulo } 1985b9c547cSRui Paulo if (sec_bss && !pri_bss) { 1995b9c547cSRui Paulo wpa_printf(MSG_INFO, 2005b9c547cSRui Paulo "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes"); 2015b9c547cSRui Paulo return 2; 2025b9c547cSRui Paulo } 2035b9c547cSRui Paulo 2045b9c547cSRui Paulo /* 2055b9c547cSRui Paulo * Match PRI/SEC channel with any existing HT40 BSS on the same 2065b9c547cSRui Paulo * channels that we are about to use (if already mixed order in 2075b9c547cSRui Paulo * existing BSSes, use own preference). 2085b9c547cSRui Paulo */ 2095b9c547cSRui Paulo match = 0; 2105b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2115b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2125b9c547cSRui Paulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 2135b9c547cSRui Paulo if (pri_chan == bss_pri_chan && 2145b9c547cSRui Paulo sec_chan == bss_sec_chan) { 2155b9c547cSRui Paulo match = 1; 2165b9c547cSRui Paulo break; 2175b9c547cSRui Paulo } 2185b9c547cSRui Paulo } 2195b9c547cSRui Paulo if (!match) { 2205b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2215b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2225b9c547cSRui Paulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 2235b9c547cSRui Paulo if (pri_chan == bss_sec_chan && 2245b9c547cSRui Paulo sec_chan == bss_pri_chan) { 2255b9c547cSRui Paulo wpa_printf(MSG_INFO, "Switch own primary and " 2265b9c547cSRui Paulo "secondary channel due to BSS " 2275b9c547cSRui Paulo "overlap with " MACSTR, 2285b9c547cSRui Paulo MAC2STR(bss->bssid)); 2295b9c547cSRui Paulo return 2; 2305b9c547cSRui Paulo } 2315b9c547cSRui Paulo } 2325b9c547cSRui Paulo } 2335b9c547cSRui Paulo 2345b9c547cSRui Paulo return 1; 2355b9c547cSRui Paulo } 2365b9c547cSRui Paulo 2375b9c547cSRui Paulo 238*325151a3SRui Paulo static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, 239*325151a3SRui Paulo int end) 2405b9c547cSRui Paulo { 2415b9c547cSRui Paulo struct ieee802_11_elems elems; 2425b9c547cSRui Paulo struct ieee80211_ht_operation *oper; 2435b9c547cSRui Paulo 2445b9c547cSRui Paulo if (bss->freq < start || bss->freq > end || bss->freq == pri_freq) 2455b9c547cSRui Paulo return 0; 2465b9c547cSRui Paulo 2475b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 2485b9c547cSRui Paulo if (!elems.ht_capabilities) { 2495b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: " 2505b9c547cSRui Paulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 2515b9c547cSRui Paulo return 1; 2525b9c547cSRui Paulo } 2535b9c547cSRui Paulo 254*325151a3SRui Paulo if (elems.ht_operation) { 2555b9c547cSRui Paulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 2565b9c547cSRui Paulo if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) 2575b9c547cSRui Paulo return 0; 2585b9c547cSRui Paulo 2595b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: " 2605b9c547cSRui Paulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 2615b9c547cSRui Paulo return 1; 2625b9c547cSRui Paulo } 2635b9c547cSRui Paulo return 0; 2645b9c547cSRui Paulo } 2655b9c547cSRui Paulo 2665b9c547cSRui Paulo 2675b9c547cSRui Paulo int check_40mhz_2g4(struct hostapd_hw_modes *mode, 2685b9c547cSRui Paulo struct wpa_scan_results *scan_res, int pri_chan, 2695b9c547cSRui Paulo int sec_chan) 2705b9c547cSRui Paulo { 2715b9c547cSRui Paulo int pri_freq, sec_freq; 2725b9c547cSRui Paulo int affected_start, affected_end; 2735b9c547cSRui Paulo size_t i; 2745b9c547cSRui Paulo 275*325151a3SRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan || 276*325151a3SRui Paulo pri_chan == sec_chan) 2775b9c547cSRui Paulo return 0; 2785b9c547cSRui Paulo 2795b9c547cSRui Paulo pri_freq = hw_get_freq(mode, pri_chan); 2805b9c547cSRui Paulo sec_freq = hw_get_freq(mode, sec_chan); 2815b9c547cSRui Paulo 2825b9c547cSRui Paulo affected_start = (pri_freq + sec_freq) / 2 - 25; 2835b9c547cSRui Paulo affected_end = (pri_freq + sec_freq) / 2 + 25; 2845b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 2855b9c547cSRui Paulo affected_start, affected_end); 2865b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2875b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2885b9c547cSRui Paulo int pri = bss->freq; 2895b9c547cSRui Paulo int sec = pri; 2905b9c547cSRui Paulo struct ieee802_11_elems elems; 2915b9c547cSRui Paulo 2925b9c547cSRui Paulo /* Check for overlapping 20 MHz BSS */ 2935b9c547cSRui Paulo if (check_20mhz_bss(bss, pri_freq, affected_start, 2945b9c547cSRui Paulo affected_end)) { 2955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 2965b9c547cSRui Paulo "Overlapping 20 MHz BSS is found"); 2975b9c547cSRui Paulo return 0; 2985b9c547cSRui Paulo } 2995b9c547cSRui Paulo 3005b9c547cSRui Paulo get_pri_sec_chan(bss, &pri_chan, &sec_chan); 3015b9c547cSRui Paulo 3025b9c547cSRui Paulo if (sec_chan) { 3035b9c547cSRui Paulo if (sec_chan < pri_chan) 3045b9c547cSRui Paulo sec = pri - 20; 3055b9c547cSRui Paulo else 3065b9c547cSRui Paulo sec = pri + 20; 3075b9c547cSRui Paulo } 3085b9c547cSRui Paulo 3095b9c547cSRui Paulo if ((pri < affected_start || pri > affected_end) && 3105b9c547cSRui Paulo (sec < affected_start || sec > affected_end)) 3115b9c547cSRui Paulo continue; /* not within affected channel range */ 3125b9c547cSRui Paulo 3135b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 3145b9c547cSRui Paulo " freq=%d pri=%d sec=%d", 3155b9c547cSRui Paulo MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 3165b9c547cSRui Paulo 3175b9c547cSRui Paulo if (sec_chan) { 3185b9c547cSRui Paulo if (pri_freq != pri || sec_freq != sec) { 3195b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 3205b9c547cSRui Paulo "40 MHz pri/sec mismatch with BSS " 3215b9c547cSRui Paulo MACSTR 3225b9c547cSRui Paulo " <%d,%d> (chan=%d%c) vs. <%d,%d>", 3235b9c547cSRui Paulo MAC2STR(bss->bssid), 3245b9c547cSRui Paulo pri, sec, pri_chan, 3255b9c547cSRui Paulo sec > pri ? '+' : '-', 3265b9c547cSRui Paulo pri_freq, sec_freq); 3275b9c547cSRui Paulo return 0; 3285b9c547cSRui Paulo } 3295b9c547cSRui Paulo } 3305b9c547cSRui Paulo 3315b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 3325b9c547cSRui Paulo 0); 333*325151a3SRui Paulo if (elems.ht_capabilities) { 3345b9c547cSRui Paulo struct ieee80211_ht_capabilities *ht_cap = 3355b9c547cSRui Paulo (struct ieee80211_ht_capabilities *) 3365b9c547cSRui Paulo elems.ht_capabilities; 3375b9c547cSRui Paulo 3385b9c547cSRui Paulo if (le_to_host16(ht_cap->ht_capabilities_info) & 3395b9c547cSRui Paulo HT_CAP_INFO_40MHZ_INTOLERANT) { 3405b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 3415b9c547cSRui Paulo "40 MHz Intolerant is set on channel %d in BSS " 3425b9c547cSRui Paulo MACSTR, pri, MAC2STR(bss->bssid)); 3435b9c547cSRui Paulo return 0; 3445b9c547cSRui Paulo } 3455b9c547cSRui Paulo } 3465b9c547cSRui Paulo } 3475b9c547cSRui Paulo 3485b9c547cSRui Paulo return 1; 3495b9c547cSRui Paulo } 3505b9c547cSRui Paulo 3515b9c547cSRui Paulo 3525b9c547cSRui Paulo int hostapd_set_freq_params(struct hostapd_freq_params *data, 3535b9c547cSRui Paulo enum hostapd_hw_mode mode, 3545b9c547cSRui Paulo int freq, int channel, int ht_enabled, 3555b9c547cSRui Paulo int vht_enabled, int sec_channel_offset, 3565b9c547cSRui Paulo int vht_oper_chwidth, int center_segment0, 3575b9c547cSRui Paulo int center_segment1, u32 vht_caps) 3585b9c547cSRui Paulo { 3595b9c547cSRui Paulo os_memset(data, 0, sizeof(*data)); 3605b9c547cSRui Paulo data->mode = mode; 3615b9c547cSRui Paulo data->freq = freq; 3625b9c547cSRui Paulo data->channel = channel; 3635b9c547cSRui Paulo data->ht_enabled = ht_enabled; 3645b9c547cSRui Paulo data->vht_enabled = vht_enabled; 3655b9c547cSRui Paulo data->sec_channel_offset = sec_channel_offset; 3665b9c547cSRui Paulo data->center_freq1 = freq + sec_channel_offset * 10; 3675b9c547cSRui Paulo data->center_freq2 = 0; 3685b9c547cSRui Paulo data->bandwidth = sec_channel_offset ? 40 : 20; 3695b9c547cSRui Paulo 3705b9c547cSRui Paulo if (data->vht_enabled) switch (vht_oper_chwidth) { 3715b9c547cSRui Paulo case VHT_CHANWIDTH_USE_HT: 372*325151a3SRui Paulo if (center_segment1 || 373*325151a3SRui Paulo (center_segment0 != 0 && 3745b9c547cSRui Paulo 5000 + center_segment0 * 5 != data->center_freq1 && 375*325151a3SRui Paulo 2407 + center_segment0 * 5 != data->center_freq1)) 3765b9c547cSRui Paulo return -1; 3775b9c547cSRui Paulo break; 3785b9c547cSRui Paulo case VHT_CHANWIDTH_80P80MHZ: 3795b9c547cSRui Paulo if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { 3805b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3815b9c547cSRui Paulo "80+80 channel width is not supported!"); 3825b9c547cSRui Paulo return -1; 3835b9c547cSRui Paulo } 3845b9c547cSRui Paulo if (center_segment1 == center_segment0 + 4 || 3855b9c547cSRui Paulo center_segment1 == center_segment0 - 4) 3865b9c547cSRui Paulo return -1; 3875b9c547cSRui Paulo data->center_freq2 = 5000 + center_segment1 * 5; 3885b9c547cSRui Paulo /* fall through */ 3895b9c547cSRui Paulo case VHT_CHANWIDTH_80MHZ: 3905b9c547cSRui Paulo data->bandwidth = 80; 391*325151a3SRui Paulo if ((vht_oper_chwidth == 1 && center_segment1) || 392*325151a3SRui Paulo (vht_oper_chwidth == 3 && !center_segment1) || 393*325151a3SRui Paulo !sec_channel_offset) 3945b9c547cSRui Paulo return -1; 395*325151a3SRui Paulo if (!center_segment0) { 396*325151a3SRui Paulo if (channel <= 48) 397*325151a3SRui Paulo center_segment0 = 42; 398*325151a3SRui Paulo else if (channel <= 64) 399*325151a3SRui Paulo center_segment0 = 58; 400*325151a3SRui Paulo else if (channel <= 112) 401*325151a3SRui Paulo center_segment0 = 106; 402*325151a3SRui Paulo else if (channel <= 128) 403*325151a3SRui Paulo center_segment0 = 122; 404*325151a3SRui Paulo else if (channel <= 144) 405*325151a3SRui Paulo center_segment0 = 138; 406*325151a3SRui Paulo else if (channel <= 161) 407*325151a3SRui Paulo center_segment0 = 155; 4085b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 409*325151a3SRui Paulo } else { 410*325151a3SRui Paulo /* 411*325151a3SRui Paulo * Note: HT/VHT config and params are coupled. Check if 412*325151a3SRui Paulo * HT40 channel band is in VHT80 Pri channel band 413*325151a3SRui Paulo * configuration. 414*325151a3SRui Paulo */ 415*325151a3SRui Paulo if (center_segment0 == channel + 6 || 416*325151a3SRui Paulo center_segment0 == channel + 2 || 417*325151a3SRui Paulo center_segment0 == channel - 2 || 418*325151a3SRui Paulo center_segment0 == channel - 6) 419*325151a3SRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 420*325151a3SRui Paulo else 421*325151a3SRui Paulo return -1; 422*325151a3SRui Paulo } 4235b9c547cSRui Paulo break; 4245b9c547cSRui Paulo case VHT_CHANWIDTH_160MHZ: 4255b9c547cSRui Paulo data->bandwidth = 160; 4265b9c547cSRui Paulo if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 4275b9c547cSRui Paulo VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { 4285b9c547cSRui Paulo wpa_printf(MSG_ERROR, 4295b9c547cSRui Paulo "160MHZ channel width is not supported!"); 4305b9c547cSRui Paulo return -1; 4315b9c547cSRui Paulo } 4325b9c547cSRui Paulo if (center_segment1) 4335b9c547cSRui Paulo return -1; 4345b9c547cSRui Paulo if (!sec_channel_offset) 4355b9c547cSRui Paulo return -1; 436*325151a3SRui Paulo /* 437*325151a3SRui Paulo * Note: HT/VHT config and params are coupled. Check if 438*325151a3SRui Paulo * HT40 channel band is in VHT160 channel band configuration. 439*325151a3SRui Paulo */ 440*325151a3SRui Paulo if (center_segment0 == channel + 14 || 441*325151a3SRui Paulo center_segment0 == channel + 10 || 442*325151a3SRui Paulo center_segment0 == channel + 6 || 443*325151a3SRui Paulo center_segment0 == channel + 2 || 444*325151a3SRui Paulo center_segment0 == channel - 2 || 445*325151a3SRui Paulo center_segment0 == channel - 6 || 446*325151a3SRui Paulo center_segment0 == channel - 10 || 447*325151a3SRui Paulo center_segment0 == channel - 14) 4485b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 449*325151a3SRui Paulo else 450*325151a3SRui Paulo return -1; 4515b9c547cSRui Paulo break; 4525b9c547cSRui Paulo } 4535b9c547cSRui Paulo 4545b9c547cSRui Paulo return 0; 4555b9c547cSRui Paulo } 456