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 { 904bc52338SCy Schubert int ok, first; 91325151a3SRui Paulo int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 9285732ac8SCy Schubert 149, 157, 165, 184, 192 }; 935b9c547cSRui Paulo size_t k; 944bc52338SCy Schubert struct hostapd_channel_data *p_chan, *s_chan; 954bc52338SCy Schubert const int ht40_plus = pri_chan < sec_chan; 965b9c547cSRui Paulo 974bc52338SCy Schubert p_chan = hw_get_channel_chan(mode, pri_chan, NULL); 984bc52338SCy Schubert if (!p_chan) 994bc52338SCy Schubert return 0; 1004bc52338SCy Schubert 1014bc52338SCy Schubert if (pri_chan == sec_chan || !sec_chan) { 1024bc52338SCy Schubert if (chan_pri_allowed(p_chan)) 1035b9c547cSRui Paulo return 1; /* HT40 not used */ 1045b9c547cSRui Paulo 1054bc52338SCy Schubert wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary", 1064bc52338SCy Schubert pri_chan); 1074bc52338SCy Schubert return 0; 1084bc52338SCy Schubert } 1094bc52338SCy Schubert 1104bc52338SCy Schubert s_chan = hw_get_channel_chan(mode, sec_chan, NULL); 1114bc52338SCy Schubert if (!s_chan) 1124bc52338SCy Schubert return 0; 1134bc52338SCy Schubert 1145b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 1155b9c547cSRui Paulo "HT40: control channel: %d secondary channel: %d", 1165b9c547cSRui Paulo pri_chan, sec_chan); 1175b9c547cSRui Paulo 1185b9c547cSRui Paulo /* Verify that HT40 secondary channel is an allowed 20 MHz 1195b9c547cSRui Paulo * channel */ 1204bc52338SCy Schubert if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) || 1214bc52338SCy Schubert (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) || 1224bc52338SCy Schubert (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) { 1235b9c547cSRui Paulo wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 1245b9c547cSRui Paulo sec_chan); 1255b9c547cSRui Paulo return 0; 1265b9c547cSRui Paulo } 1275b9c547cSRui Paulo 1285b9c547cSRui Paulo /* 1295b9c547cSRui Paulo * Verify that HT40 primary,secondary channel pair is allowed per 1305b9c547cSRui Paulo * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 1315b9c547cSRui Paulo * 2.4 GHz rules allow all cases where the secondary channel fits into 1325b9c547cSRui Paulo * the list of allowed channels (already checked above). 1335b9c547cSRui Paulo */ 1345b9c547cSRui Paulo if (mode->mode != HOSTAPD_MODE_IEEE80211A) 1355b9c547cSRui Paulo return 1; 1365b9c547cSRui Paulo 1375b9c547cSRui Paulo first = pri_chan < sec_chan ? pri_chan : sec_chan; 1385b9c547cSRui Paulo 1395b9c547cSRui Paulo ok = 0; 1405b9c547cSRui Paulo for (k = 0; k < ARRAY_SIZE(allowed); k++) { 1415b9c547cSRui Paulo if (first == allowed[k]) { 1425b9c547cSRui Paulo ok = 1; 1435b9c547cSRui Paulo break; 1445b9c547cSRui Paulo } 1455b9c547cSRui Paulo } 1465b9c547cSRui Paulo if (!ok) { 1475b9c547cSRui Paulo wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 1485b9c547cSRui Paulo pri_chan, sec_chan); 1495b9c547cSRui Paulo return 0; 1505b9c547cSRui Paulo } 1515b9c547cSRui Paulo 1525b9c547cSRui Paulo return 1; 1535b9c547cSRui Paulo } 1545b9c547cSRui Paulo 1555b9c547cSRui Paulo 1565b9c547cSRui Paulo void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) 1575b9c547cSRui Paulo { 1585b9c547cSRui Paulo struct ieee80211_ht_operation *oper; 1595b9c547cSRui Paulo struct ieee802_11_elems elems; 1605b9c547cSRui Paulo 1615b9c547cSRui Paulo *pri_chan = *sec_chan = 0; 1625b9c547cSRui Paulo 1635b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 164325151a3SRui Paulo if (elems.ht_operation) { 1655b9c547cSRui Paulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 1665b9c547cSRui Paulo *pri_chan = oper->primary_chan; 1675b9c547cSRui Paulo if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { 1685b9c547cSRui Paulo int sec = oper->ht_param & 1695b9c547cSRui Paulo HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; 1705b9c547cSRui Paulo if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 1715b9c547cSRui Paulo *sec_chan = *pri_chan + 4; 1725b9c547cSRui Paulo else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 1735b9c547cSRui Paulo *sec_chan = *pri_chan - 4; 1745b9c547cSRui Paulo } 1755b9c547cSRui Paulo } 1765b9c547cSRui Paulo } 1775b9c547cSRui Paulo 1785b9c547cSRui Paulo 1795b9c547cSRui Paulo int check_40mhz_5g(struct hostapd_hw_modes *mode, 1805b9c547cSRui Paulo struct wpa_scan_results *scan_res, int pri_chan, 1815b9c547cSRui Paulo int sec_chan) 1825b9c547cSRui Paulo { 1835b9c547cSRui Paulo int pri_freq, sec_freq, pri_bss, sec_bss; 1845b9c547cSRui Paulo int bss_pri_chan, bss_sec_chan; 1855b9c547cSRui Paulo size_t i; 1865b9c547cSRui Paulo int match; 1875b9c547cSRui Paulo 188325151a3SRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan || 189325151a3SRui Paulo pri_chan == sec_chan) 1905b9c547cSRui Paulo return 0; 1915b9c547cSRui Paulo 1925b9c547cSRui Paulo pri_freq = hw_get_freq(mode, pri_chan); 1935b9c547cSRui Paulo sec_freq = hw_get_freq(mode, sec_chan); 1945b9c547cSRui Paulo 1955b9c547cSRui Paulo /* 1965b9c547cSRui Paulo * Switch PRI/SEC channels if Beacons were detected on selected SEC 1975b9c547cSRui Paulo * channel, but not on selected PRI channel. 1985b9c547cSRui Paulo */ 1995b9c547cSRui Paulo pri_bss = sec_bss = 0; 2005b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2015b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2025b9c547cSRui Paulo if (bss->freq == pri_freq) 2035b9c547cSRui Paulo pri_bss++; 2045b9c547cSRui Paulo else if (bss->freq == sec_freq) 2055b9c547cSRui Paulo sec_bss++; 2065b9c547cSRui Paulo } 2075b9c547cSRui Paulo if (sec_bss && !pri_bss) { 2085b9c547cSRui Paulo wpa_printf(MSG_INFO, 2095b9c547cSRui Paulo "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes"); 2105b9c547cSRui Paulo return 2; 2115b9c547cSRui Paulo } 2125b9c547cSRui Paulo 2135b9c547cSRui Paulo /* 2145b9c547cSRui Paulo * Match PRI/SEC channel with any existing HT40 BSS on the same 2155b9c547cSRui Paulo * channels that we are about to use (if already mixed order in 2165b9c547cSRui Paulo * existing BSSes, use own preference). 2175b9c547cSRui Paulo */ 2185b9c547cSRui Paulo match = 0; 2195b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2205b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2215b9c547cSRui Paulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 2225b9c547cSRui Paulo if (pri_chan == bss_pri_chan && 2235b9c547cSRui Paulo sec_chan == bss_sec_chan) { 2245b9c547cSRui Paulo match = 1; 2255b9c547cSRui Paulo break; 2265b9c547cSRui Paulo } 2275b9c547cSRui Paulo } 2285b9c547cSRui Paulo if (!match) { 2295b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2305b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2315b9c547cSRui Paulo get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 2325b9c547cSRui Paulo if (pri_chan == bss_sec_chan && 2335b9c547cSRui Paulo sec_chan == bss_pri_chan) { 2345b9c547cSRui Paulo wpa_printf(MSG_INFO, "Switch own primary and " 2355b9c547cSRui Paulo "secondary channel due to BSS " 2365b9c547cSRui Paulo "overlap with " MACSTR, 2375b9c547cSRui Paulo MAC2STR(bss->bssid)); 2385b9c547cSRui Paulo return 2; 2395b9c547cSRui Paulo } 2405b9c547cSRui Paulo } 2415b9c547cSRui Paulo } 2425b9c547cSRui Paulo 2435b9c547cSRui Paulo return 1; 2445b9c547cSRui Paulo } 2455b9c547cSRui Paulo 2465b9c547cSRui Paulo 247325151a3SRui Paulo static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, 248325151a3SRui Paulo int end) 2495b9c547cSRui Paulo { 2505b9c547cSRui Paulo struct ieee802_11_elems elems; 2515b9c547cSRui Paulo struct ieee80211_ht_operation *oper; 2525b9c547cSRui Paulo 2535b9c547cSRui Paulo if (bss->freq < start || bss->freq > end || bss->freq == pri_freq) 2545b9c547cSRui Paulo return 0; 2555b9c547cSRui Paulo 2565b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 2575b9c547cSRui Paulo if (!elems.ht_capabilities) { 2585b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: " 2595b9c547cSRui Paulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 2605b9c547cSRui Paulo return 1; 2615b9c547cSRui Paulo } 2625b9c547cSRui Paulo 263325151a3SRui Paulo if (elems.ht_operation) { 2645b9c547cSRui Paulo oper = (struct ieee80211_ht_operation *) elems.ht_operation; 2655b9c547cSRui Paulo if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) 2665b9c547cSRui Paulo return 0; 2675b9c547cSRui Paulo 2685b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: " 2695b9c547cSRui Paulo MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 2705b9c547cSRui Paulo return 1; 2715b9c547cSRui Paulo } 2725b9c547cSRui Paulo return 0; 2735b9c547cSRui Paulo } 2745b9c547cSRui Paulo 2755b9c547cSRui Paulo 2765b9c547cSRui Paulo int check_40mhz_2g4(struct hostapd_hw_modes *mode, 2775b9c547cSRui Paulo struct wpa_scan_results *scan_res, int pri_chan, 2785b9c547cSRui Paulo int sec_chan) 2795b9c547cSRui Paulo { 2805b9c547cSRui Paulo int pri_freq, sec_freq; 2815b9c547cSRui Paulo int affected_start, affected_end; 2825b9c547cSRui Paulo size_t i; 2835b9c547cSRui Paulo 284325151a3SRui Paulo if (!mode || !scan_res || !pri_chan || !sec_chan || 285325151a3SRui Paulo pri_chan == sec_chan) 2865b9c547cSRui Paulo return 0; 2875b9c547cSRui Paulo 2885b9c547cSRui Paulo pri_freq = hw_get_freq(mode, pri_chan); 2895b9c547cSRui Paulo sec_freq = hw_get_freq(mode, sec_chan); 2905b9c547cSRui Paulo 2915b9c547cSRui Paulo affected_start = (pri_freq + sec_freq) / 2 - 25; 2925b9c547cSRui Paulo affected_end = (pri_freq + sec_freq) / 2 + 25; 2935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 2945b9c547cSRui Paulo affected_start, affected_end); 2955b9c547cSRui Paulo for (i = 0; i < scan_res->num; i++) { 2965b9c547cSRui Paulo struct wpa_scan_res *bss = scan_res->res[i]; 2975b9c547cSRui Paulo int pri = bss->freq; 2985b9c547cSRui Paulo int sec = pri; 2995b9c547cSRui Paulo struct ieee802_11_elems elems; 3005b9c547cSRui Paulo 3015b9c547cSRui Paulo /* Check for overlapping 20 MHz BSS */ 3025b9c547cSRui Paulo if (check_20mhz_bss(bss, pri_freq, affected_start, 3035b9c547cSRui Paulo affected_end)) { 3045b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 3055b9c547cSRui Paulo "Overlapping 20 MHz BSS is found"); 3065b9c547cSRui Paulo return 0; 3075b9c547cSRui Paulo } 3085b9c547cSRui Paulo 3095b9c547cSRui Paulo get_pri_sec_chan(bss, &pri_chan, &sec_chan); 3105b9c547cSRui Paulo 3115b9c547cSRui Paulo if (sec_chan) { 3125b9c547cSRui Paulo if (sec_chan < pri_chan) 3135b9c547cSRui Paulo sec = pri - 20; 3145b9c547cSRui Paulo else 3155b9c547cSRui Paulo sec = pri + 20; 3165b9c547cSRui Paulo } 3175b9c547cSRui Paulo 3185b9c547cSRui Paulo if ((pri < affected_start || pri > affected_end) && 3195b9c547cSRui Paulo (sec < affected_start || sec > affected_end)) 3205b9c547cSRui Paulo continue; /* not within affected channel range */ 3215b9c547cSRui Paulo 3225b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 3235b9c547cSRui Paulo " freq=%d pri=%d sec=%d", 3245b9c547cSRui Paulo MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 3255b9c547cSRui Paulo 3265b9c547cSRui Paulo if (sec_chan) { 3275b9c547cSRui Paulo if (pri_freq != pri || sec_freq != sec) { 3285b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 3295b9c547cSRui Paulo "40 MHz pri/sec mismatch with BSS " 3305b9c547cSRui Paulo MACSTR 3315b9c547cSRui Paulo " <%d,%d> (chan=%d%c) vs. <%d,%d>", 3325b9c547cSRui Paulo MAC2STR(bss->bssid), 3335b9c547cSRui Paulo pri, sec, pri_chan, 3345b9c547cSRui Paulo sec > pri ? '+' : '-', 3355b9c547cSRui Paulo pri_freq, sec_freq); 3365b9c547cSRui Paulo return 0; 3375b9c547cSRui Paulo } 3385b9c547cSRui Paulo } 3395b9c547cSRui Paulo 3405b9c547cSRui Paulo ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 3415b9c547cSRui Paulo 0); 342325151a3SRui Paulo if (elems.ht_capabilities) { 3435b9c547cSRui Paulo struct ieee80211_ht_capabilities *ht_cap = 3445b9c547cSRui Paulo (struct ieee80211_ht_capabilities *) 3455b9c547cSRui Paulo elems.ht_capabilities; 3465b9c547cSRui Paulo 3475b9c547cSRui Paulo if (le_to_host16(ht_cap->ht_capabilities_info) & 3485b9c547cSRui Paulo HT_CAP_INFO_40MHZ_INTOLERANT) { 3495b9c547cSRui Paulo wpa_printf(MSG_DEBUG, 3505b9c547cSRui Paulo "40 MHz Intolerant is set on channel %d in BSS " 3515b9c547cSRui Paulo MACSTR, pri, MAC2STR(bss->bssid)); 3525b9c547cSRui Paulo return 0; 3535b9c547cSRui Paulo } 3545b9c547cSRui Paulo } 3555b9c547cSRui Paulo } 3565b9c547cSRui Paulo 3575b9c547cSRui Paulo return 1; 3585b9c547cSRui Paulo } 3595b9c547cSRui Paulo 3605b9c547cSRui Paulo 3615b9c547cSRui Paulo int hostapd_set_freq_params(struct hostapd_freq_params *data, 3625b9c547cSRui Paulo enum hostapd_hw_mode mode, 3635b9c547cSRui Paulo int freq, int channel, int ht_enabled, 364*206b73d0SCy Schubert int vht_enabled, int he_enabled, 365*206b73d0SCy Schubert int sec_channel_offset, 366*206b73d0SCy Schubert int oper_chwidth, int center_segment0, 367*206b73d0SCy Schubert int center_segment1, u32 vht_caps, 368*206b73d0SCy Schubert struct he_capabilities *he_cap) 3695b9c547cSRui Paulo { 370*206b73d0SCy Schubert if (!he_cap) 371*206b73d0SCy Schubert he_enabled = 0; 3725b9c547cSRui Paulo os_memset(data, 0, sizeof(*data)); 3735b9c547cSRui Paulo data->mode = mode; 3745b9c547cSRui Paulo data->freq = freq; 3755b9c547cSRui Paulo data->channel = channel; 3765b9c547cSRui Paulo data->ht_enabled = ht_enabled; 3775b9c547cSRui Paulo data->vht_enabled = vht_enabled; 378*206b73d0SCy Schubert data->he_enabled = he_enabled; 3795b9c547cSRui Paulo data->sec_channel_offset = sec_channel_offset; 3805b9c547cSRui Paulo data->center_freq1 = freq + sec_channel_offset * 10; 3815b9c547cSRui Paulo data->center_freq2 = 0; 3825b9c547cSRui Paulo data->bandwidth = sec_channel_offset ? 40 : 20; 3835b9c547cSRui Paulo 384*206b73d0SCy Schubert if (data->vht_enabled) switch (oper_chwidth) { 385*206b73d0SCy Schubert case CHANWIDTH_USE_HT: 386325151a3SRui Paulo if (center_segment1 || 387325151a3SRui Paulo (center_segment0 != 0 && 3885b9c547cSRui Paulo 5000 + center_segment0 * 5 != data->center_freq1 && 389325151a3SRui Paulo 2407 + center_segment0 * 5 != data->center_freq1)) 3905b9c547cSRui Paulo return -1; 3915b9c547cSRui Paulo break; 392*206b73d0SCy Schubert case CHANWIDTH_80P80MHZ: 3935b9c547cSRui Paulo if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { 3945b9c547cSRui Paulo wpa_printf(MSG_ERROR, 3955b9c547cSRui Paulo "80+80 channel width is not supported!"); 3965b9c547cSRui Paulo return -1; 3975b9c547cSRui Paulo } 3985b9c547cSRui Paulo if (center_segment1 == center_segment0 + 4 || 3995b9c547cSRui Paulo center_segment1 == center_segment0 - 4) 4005b9c547cSRui Paulo return -1; 4015b9c547cSRui Paulo data->center_freq2 = 5000 + center_segment1 * 5; 4025b9c547cSRui Paulo /* fall through */ 403*206b73d0SCy Schubert case CHANWIDTH_80MHZ: 4045b9c547cSRui Paulo data->bandwidth = 80; 405*206b73d0SCy Schubert if ((oper_chwidth == CHANWIDTH_80MHZ && 40685732ac8SCy Schubert center_segment1) || 407*206b73d0SCy Schubert (oper_chwidth == CHANWIDTH_80P80MHZ && 40885732ac8SCy Schubert !center_segment1) || 409325151a3SRui Paulo !sec_channel_offset) 4105b9c547cSRui Paulo return -1; 411325151a3SRui Paulo if (!center_segment0) { 412325151a3SRui Paulo if (channel <= 48) 413325151a3SRui Paulo center_segment0 = 42; 414325151a3SRui Paulo else if (channel <= 64) 415325151a3SRui Paulo center_segment0 = 58; 416325151a3SRui Paulo else if (channel <= 112) 417325151a3SRui Paulo center_segment0 = 106; 418325151a3SRui Paulo else if (channel <= 128) 419325151a3SRui Paulo center_segment0 = 122; 420325151a3SRui Paulo else if (channel <= 144) 421325151a3SRui Paulo center_segment0 = 138; 422325151a3SRui Paulo else if (channel <= 161) 423325151a3SRui Paulo center_segment0 = 155; 4245b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 425325151a3SRui Paulo } else { 426325151a3SRui Paulo /* 427325151a3SRui Paulo * Note: HT/VHT config and params are coupled. Check if 428325151a3SRui Paulo * HT40 channel band is in VHT80 Pri channel band 429325151a3SRui Paulo * configuration. 430325151a3SRui Paulo */ 431325151a3SRui Paulo if (center_segment0 == channel + 6 || 432325151a3SRui Paulo center_segment0 == channel + 2 || 433325151a3SRui Paulo center_segment0 == channel - 2 || 434325151a3SRui Paulo center_segment0 == channel - 6) 435325151a3SRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 436325151a3SRui Paulo else 437325151a3SRui Paulo return -1; 438325151a3SRui Paulo } 4395b9c547cSRui Paulo break; 440*206b73d0SCy Schubert case CHANWIDTH_160MHZ: 4415b9c547cSRui Paulo data->bandwidth = 160; 4425b9c547cSRui Paulo if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 4435b9c547cSRui Paulo VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { 4445b9c547cSRui Paulo wpa_printf(MSG_ERROR, 4455b9c547cSRui Paulo "160MHZ channel width is not supported!"); 4465b9c547cSRui Paulo return -1; 4475b9c547cSRui Paulo } 4485b9c547cSRui Paulo if (center_segment1) 4495b9c547cSRui Paulo return -1; 4505b9c547cSRui Paulo if (!sec_channel_offset) 4515b9c547cSRui Paulo return -1; 452325151a3SRui Paulo /* 453325151a3SRui Paulo * Note: HT/VHT config and params are coupled. Check if 454325151a3SRui Paulo * HT40 channel band is in VHT160 channel band configuration. 455325151a3SRui Paulo */ 456325151a3SRui Paulo if (center_segment0 == channel + 14 || 457325151a3SRui Paulo center_segment0 == channel + 10 || 458325151a3SRui Paulo center_segment0 == channel + 6 || 459325151a3SRui Paulo center_segment0 == channel + 2 || 460325151a3SRui Paulo center_segment0 == channel - 2 || 461325151a3SRui Paulo center_segment0 == channel - 6 || 462325151a3SRui Paulo center_segment0 == channel - 10 || 463325151a3SRui Paulo center_segment0 == channel - 14) 4645b9c547cSRui Paulo data->center_freq1 = 5000 + center_segment0 * 5; 465325151a3SRui Paulo else 466325151a3SRui Paulo return -1; 4675b9c547cSRui Paulo break; 4685b9c547cSRui Paulo } 4695b9c547cSRui Paulo 4705b9c547cSRui Paulo return 0; 4715b9c547cSRui Paulo } 47285732ac8SCy Schubert 47385732ac8SCy Schubert 47485732ac8SCy Schubert void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, 47585732ac8SCy Schubert int disabled) 47685732ac8SCy Schubert { 47785732ac8SCy Schubert /* Masking these out disables HT40 */ 47885732ac8SCy Schubert le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | 47985732ac8SCy Schubert HT_CAP_INFO_SHORT_GI40MHZ); 48085732ac8SCy Schubert 48185732ac8SCy Schubert if (disabled) 48285732ac8SCy Schubert htcaps->ht_capabilities_info &= ~msk; 48385732ac8SCy Schubert else 48485732ac8SCy Schubert htcaps->ht_capabilities_info |= msk; 48585732ac8SCy Schubert } 48685732ac8SCy Schubert 48785732ac8SCy Schubert 48885732ac8SCy Schubert #ifdef CONFIG_IEEE80211AC 48985732ac8SCy Schubert 49085732ac8SCy Schubert static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, 49185732ac8SCy Schubert const char *name) 49285732ac8SCy Schubert { 49385732ac8SCy Schubert u32 req_cap = conf & cap; 49485732ac8SCy Schubert 49585732ac8SCy Schubert /* 49685732ac8SCy Schubert * Make sure we support all requested capabilities. 49785732ac8SCy Schubert * NOTE: We assume that 'cap' represents a capability mask, 49885732ac8SCy Schubert * not a discrete value. 49985732ac8SCy Schubert */ 50085732ac8SCy Schubert if ((hw & req_cap) != req_cap) { 50185732ac8SCy Schubert wpa_printf(MSG_ERROR, 50285732ac8SCy Schubert "Driver does not support configured VHT capability [%s]", 50385732ac8SCy Schubert name); 50485732ac8SCy Schubert return 0; 50585732ac8SCy Schubert } 50685732ac8SCy Schubert return 1; 50785732ac8SCy Schubert } 50885732ac8SCy Schubert 50985732ac8SCy Schubert 51085732ac8SCy Schubert static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, 51185732ac8SCy Schubert unsigned int shift, 51285732ac8SCy Schubert const char *name) 51385732ac8SCy Schubert { 51485732ac8SCy Schubert u32 hw_max = hw & mask; 51585732ac8SCy Schubert u32 conf_val = conf & mask; 51685732ac8SCy Schubert 51785732ac8SCy Schubert if (conf_val > hw_max) { 51885732ac8SCy Schubert wpa_printf(MSG_ERROR, 51985732ac8SCy Schubert "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", 52085732ac8SCy Schubert name, conf_val >> shift, hw_max >> shift); 52185732ac8SCy Schubert return 0; 52285732ac8SCy Schubert } 52385732ac8SCy Schubert return 1; 52485732ac8SCy Schubert } 52585732ac8SCy Schubert 52685732ac8SCy Schubert 52785732ac8SCy Schubert int ieee80211ac_cap_check(u32 hw, u32 conf) 52885732ac8SCy Schubert { 52985732ac8SCy Schubert #define VHT_CAP_CHECK(cap) \ 53085732ac8SCy Schubert do { \ 53185732ac8SCy Schubert if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \ 53285732ac8SCy Schubert return 0; \ 53385732ac8SCy Schubert } while (0) 53485732ac8SCy Schubert 53585732ac8SCy Schubert #define VHT_CAP_CHECK_MAX(cap) \ 53685732ac8SCy Schubert do { \ 53785732ac8SCy Schubert if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ 53885732ac8SCy Schubert #cap)) \ 53985732ac8SCy Schubert return 0; \ 54085732ac8SCy Schubert } while (0) 54185732ac8SCy Schubert 54285732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); 54385732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK); 54485732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_RXLDPC); 54585732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); 54685732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); 54785732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_TXSTBC); 54885732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); 54985732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); 55085732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); 55185732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); 55285732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); 55385732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); 55485732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); 55585732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); 55685732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_HTC_VHT); 55785732ac8SCy Schubert VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); 55885732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); 55985732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); 56085732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); 56185732ac8SCy Schubert VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); 56285732ac8SCy Schubert 56385732ac8SCy Schubert #undef VHT_CAP_CHECK 56485732ac8SCy Schubert #undef VHT_CAP_CHECK_MAX 56585732ac8SCy Schubert 56685732ac8SCy Schubert return 1; 56785732ac8SCy Schubert } 56885732ac8SCy Schubert 56985732ac8SCy Schubert #endif /* CONFIG_IEEE80211AC */ 5704bc52338SCy Schubert 5714bc52338SCy Schubert 5724bc52338SCy Schubert u32 num_chan_to_bw(int num_chans) 5734bc52338SCy Schubert { 5744bc52338SCy Schubert switch (num_chans) { 5754bc52338SCy Schubert case 2: 5764bc52338SCy Schubert case 4: 5774bc52338SCy Schubert case 8: 5784bc52338SCy Schubert return num_chans * 20; 5794bc52338SCy Schubert default: 5804bc52338SCy Schubert return 20; 5814bc52338SCy Schubert } 5824bc52338SCy Schubert } 5834bc52338SCy Schubert 5844bc52338SCy Schubert 5854bc52338SCy Schubert /* check if BW is applicable for channel */ 5864bc52338SCy Schubert int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, 5874bc52338SCy Schubert int ht40_plus, int pri) 5884bc52338SCy Schubert { 5894bc52338SCy Schubert u32 bw_mask; 5904bc52338SCy Schubert 5914bc52338SCy Schubert switch (bw) { 5924bc52338SCy Schubert case 20: 5934bc52338SCy Schubert bw_mask = HOSTAPD_CHAN_WIDTH_20; 5944bc52338SCy Schubert break; 5954bc52338SCy Schubert case 40: 5964bc52338SCy Schubert /* HT 40 MHz support declared only for primary channel, 5974bc52338SCy Schubert * just skip 40 MHz secondary checking */ 5984bc52338SCy Schubert if (pri && ht40_plus) 5994bc52338SCy Schubert bw_mask = HOSTAPD_CHAN_WIDTH_40P; 6004bc52338SCy Schubert else if (pri && !ht40_plus) 6014bc52338SCy Schubert bw_mask = HOSTAPD_CHAN_WIDTH_40M; 6024bc52338SCy Schubert else 6034bc52338SCy Schubert bw_mask = 0; 6044bc52338SCy Schubert break; 6054bc52338SCy Schubert case 80: 6064bc52338SCy Schubert bw_mask = HOSTAPD_CHAN_WIDTH_80; 6074bc52338SCy Schubert break; 6084bc52338SCy Schubert case 160: 6094bc52338SCy Schubert bw_mask = HOSTAPD_CHAN_WIDTH_160; 6104bc52338SCy Schubert break; 6114bc52338SCy Schubert default: 6124bc52338SCy Schubert bw_mask = 0; 6134bc52338SCy Schubert break; 6144bc52338SCy Schubert } 6154bc52338SCy Schubert 6164bc52338SCy Schubert return (chan->allowed_bw & bw_mask) == bw_mask; 6174bc52338SCy Schubert } 6184bc52338SCy Schubert 6194bc52338SCy Schubert 6204bc52338SCy Schubert /* check if channel is allowed to be used as primary */ 6214bc52338SCy Schubert int chan_pri_allowed(const struct hostapd_channel_data *chan) 6224bc52338SCy Schubert { 6234bc52338SCy Schubert return !(chan->flag & HOSTAPD_CHAN_DISABLED) && 6244bc52338SCy Schubert (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20); 6254bc52338SCy Schubert } 626