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; 91325151a3SRui Paulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 92*85732ac8SCy Schubert 149, 157, 165, 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); 155325151a3SRui 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 179325151a3SRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan || 180325151a3SRui 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 238325151a3SRui Paulo static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, 239325151a3SRui 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 254325151a3SRui 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 275325151a3SRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan || 276325151a3SRui 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); 333325151a3SRui 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: 372325151a3SRui Paulo if (center_segment1 || 373325151a3SRui Paulo (center_segment0 != 0 && 3745b9c547cSRui Paulo 5000 + center_segment0 * 5 != data->center_freq1 && 375325151a3SRui 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*85732ac8SCy Schubert if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ && 392*85732ac8SCy Schubert center_segment1) || 393*85732ac8SCy Schubert (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ && 394*85732ac8SCy Schubert !center_segment1) || 395325151a3SRui Paulo !sec_channel_offset) 3965b9c547cSRui Paulo return -1; 397325151a3SRui Paulo if (!center_segment0) { 398325151a3SRui Paulo if (channel <= 48) 399325151a3SRui Paulo center_segment0 = 42; 400325151a3SRui Paulo else if (channel <= 64) 401325151a3SRui Paulo center_segment0 = 58; 402325151a3SRui Paulo else if (channel <= 112) 403325151a3SRui Paulo center_segment0 = 106; 404325151a3SRui Paulo else if (channel <= 128) 405325151a3SRui Paulo center_segment0 = 122; 406325151a3SRui Paulo else if (channel <= 144) 407325151a3SRui Paulo center_segment0 = 138; 408325151a3SRui Paulo else if (channel <= 161) 409325151a3SRui Paulo center_segment0 = 155; 4105b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 411325151a3SRui Paulo } else { 412325151a3SRui Paulo /* 413325151a3SRui Paulo * Note: HT/VHT config and params are coupled. Check if 414325151a3SRui Paulo * HT40 channel band is in VHT80 Pri channel band 415325151a3SRui Paulo * configuration. 416325151a3SRui Paulo */ 417325151a3SRui Paulo if (center_segment0 == channel + 6 || 418325151a3SRui Paulo center_segment0 == channel + 2 || 419325151a3SRui Paulo center_segment0 == channel - 2 || 420325151a3SRui Paulo center_segment0 == channel - 6) 421325151a3SRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 422325151a3SRui Paulo else 423325151a3SRui Paulo return -1; 424325151a3SRui Paulo } 4255b9c547cSRui Paulo break; 4265b9c547cSRui Paulo case VHT_CHANWIDTH_160MHZ: 4275b9c547cSRui Paulo data->bandwidth = 160; 4285b9c547cSRui Paulo if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 4295b9c547cSRui Paulo VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { 4305b9c547cSRui Paulo wpa_printf(MSG_ERROR, 4315b9c547cSRui Paulo "160MHZ channel width is not supported!"); 4325b9c547cSRui Paulo return -1; 4335b9c547cSRui Paulo } 4345b9c547cSRui Paulo if (center_segment1) 4355b9c547cSRui Paulo return -1; 4365b9c547cSRui Paulo if (!sec_channel_offset) 4375b9c547cSRui Paulo return -1; 438325151a3SRui Paulo /* 439325151a3SRui Paulo * Note: HT/VHT config and params are coupled. Check if 440325151a3SRui Paulo * HT40 channel band is in VHT160 channel band configuration. 441325151a3SRui Paulo */ 442325151a3SRui Paulo if (center_segment0 == channel + 14 || 443325151a3SRui Paulo center_segment0 == channel + 10 || 444325151a3SRui Paulo center_segment0 == channel + 6 || 445325151a3SRui Paulo center_segment0 == channel + 2 || 446325151a3SRui Paulo center_segment0 == channel - 2 || 447325151a3SRui Paulo center_segment0 == channel - 6 || 448325151a3SRui Paulo center_segment0 == channel - 10 || 449325151a3SRui Paulo center_segment0 == channel - 14) 4505b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 451325151a3SRui Paulo else 452325151a3SRui Paulo return -1; 4535b9c547cSRui Paulo break; 4545b9c547cSRui Paulo } 4555b9c547cSRui Paulo 4565b9c547cSRui Paulo return 0; 4575b9c547cSRui Paulo } 458*85732ac8SCy Schubert 459*85732ac8SCy Schubert 460*85732ac8SCy Schubert void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, 461*85732ac8SCy Schubert int disabled) 462*85732ac8SCy Schubert { 463*85732ac8SCy Schubert /* Masking these out disables HT40 */ 464*85732ac8SCy Schubert le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | 465*85732ac8SCy Schubert HT_CAP_INFO_SHORT_GI40MHZ); 466*85732ac8SCy Schubert 467*85732ac8SCy Schubert if (disabled) 468*85732ac8SCy Schubert htcaps->ht_capabilities_info &= ~msk; 469*85732ac8SCy Schubert else 470*85732ac8SCy Schubert htcaps->ht_capabilities_info |= msk; 471*85732ac8SCy Schubert } 472*85732ac8SCy Schubert 473*85732ac8SCy Schubert 474*85732ac8SCy Schubert #ifdef CONFIG_IEEE80211AC 475*85732ac8SCy Schubert 476*85732ac8SCy Schubert static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, 477*85732ac8SCy Schubert const char *name) 478*85732ac8SCy Schubert { 479*85732ac8SCy Schubert u32 req_cap = conf & cap; 480*85732ac8SCy Schubert 481*85732ac8SCy Schubert /* 482*85732ac8SCy Schubert * Make sure we support all requested capabilities. 483*85732ac8SCy Schubert * NOTE: We assume that 'cap' represents a capability mask, 484*85732ac8SCy Schubert * not a discrete value. 485*85732ac8SCy Schubert */ 486*85732ac8SCy Schubert if ((hw & req_cap) != req_cap) { 487*85732ac8SCy Schubert wpa_printf(MSG_ERROR, 488*85732ac8SCy Schubert "Driver does not support configured VHT capability [%s]", 489*85732ac8SCy Schubert name); 490*85732ac8SCy Schubert return 0; 491*85732ac8SCy Schubert } 492*85732ac8SCy Schubert return 1; 493*85732ac8SCy Schubert } 494*85732ac8SCy Schubert 495*85732ac8SCy Schubert 496*85732ac8SCy Schubert static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, 497*85732ac8SCy Schubert unsigned int shift, 498*85732ac8SCy Schubert const char *name) 499*85732ac8SCy Schubert { 500*85732ac8SCy Schubert u32 hw_max = hw & mask; 501*85732ac8SCy Schubert u32 conf_val = conf & mask; 502*85732ac8SCy Schubert 503*85732ac8SCy Schubert if (conf_val > hw_max) { 504*85732ac8SCy Schubert wpa_printf(MSG_ERROR, 505*85732ac8SCy Schubert "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", 506*85732ac8SCy Schubert name, conf_val >> shift, hw_max >> shift); 507*85732ac8SCy Schubert return 0; 508*85732ac8SCy Schubert } 509*85732ac8SCy Schubert return 1; 510*85732ac8SCy Schubert } 511*85732ac8SCy Schubert 512*85732ac8SCy Schubert 513*85732ac8SCy Schubert int ieee80211ac_cap_check(u32 hw, u32 conf) 514*85732ac8SCy Schubert { 515*85732ac8SCy Schubert #define VHT_CAP_CHECK(cap) \ 516*85732ac8SCy Schubert do { \ 517*85732ac8SCy Schubert if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \ 518*85732ac8SCy Schubert return 0; \ 519*85732ac8SCy Schubert } while (0) 520*85732ac8SCy Schubert 521*85732ac8SCy Schubert #define VHT_CAP_CHECK_MAX(cap) \ 522*85732ac8SCy Schubert do { \ 523*85732ac8SCy Schubert if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ 524*85732ac8SCy Schubert #cap)) \ 525*85732ac8SCy Schubert return 0; \ 526*85732ac8SCy Schubert } while (0) 527*85732ac8SCy Schubert 528*85732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); 529*85732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK); 530*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_RXLDPC); 531*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); 532*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); 533*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_TXSTBC); 534*85732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); 535*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); 536*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); 537*85732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); 538*85732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); 539*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); 540*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); 541*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); 542*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_HTC_VHT); 543*85732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); 544*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); 545*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); 546*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); 547*85732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); 548*85732ac8SCy Schubert 549*85732ac8SCy Schubert #undef VHT_CAP_CHECK 550*85732ac8SCy Schubert #undef VHT_CAP_CHECK_MAX 551*85732ac8SCy Schubert 552*85732ac8SCy Schubert return 1; 553*85732ac8SCy Schubert } 554*85732ac8SCy Schubert 555*85732ac8SCy Schubert #endif /* CONFIG_IEEE80211AC */ 556