162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains helper code to handle channel 462306a36Sopenharmony_ci * settings and keeping track of what is possible at 562306a36Sopenharmony_ci * any point in time. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 862306a36Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 962306a36Sopenharmony_ci * Copyright 2018-2022 Intel Corporation 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/bitfield.h> 1462306a36Sopenharmony_ci#include <net/cfg80211.h> 1562306a36Sopenharmony_ci#include "core.h" 1662306a36Sopenharmony_ci#include "rdev-ops.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic bool cfg80211_valid_60g_freq(u32 freq) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return freq >= 58320 && freq <= 70200; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_civoid cfg80211_chandef_create(struct cfg80211_chan_def *chandef, 2462306a36Sopenharmony_ci struct ieee80211_channel *chan, 2562306a36Sopenharmony_ci enum nl80211_channel_type chan_type) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci if (WARN_ON(!chan)) 2862306a36Sopenharmony_ci return; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci chandef->chan = chan; 3162306a36Sopenharmony_ci chandef->freq1_offset = chan->freq_offset; 3262306a36Sopenharmony_ci chandef->center_freq2 = 0; 3362306a36Sopenharmony_ci chandef->edmg.bw_config = 0; 3462306a36Sopenharmony_ci chandef->edmg.channels = 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci switch (chan_type) { 3762306a36Sopenharmony_ci case NL80211_CHAN_NO_HT: 3862306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 3962306a36Sopenharmony_ci chandef->center_freq1 = chan->center_freq; 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case NL80211_CHAN_HT20: 4262306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_20; 4362306a36Sopenharmony_ci chandef->center_freq1 = chan->center_freq; 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci case NL80211_CHAN_HT40PLUS: 4662306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_40; 4762306a36Sopenharmony_ci chandef->center_freq1 = chan->center_freq + 10; 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci case NL80211_CHAN_HT40MINUS: 5062306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_40; 5162306a36Sopenharmony_ci chandef->center_freq1 = chan->center_freq - 10; 5262306a36Sopenharmony_ci break; 5362306a36Sopenharmony_ci default: 5462306a36Sopenharmony_ci WARN_ON(1); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_chandef_create); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci int max_contiguous = 0; 6262306a36Sopenharmony_ci int num_of_enabled = 0; 6362306a36Sopenharmony_ci int contiguous = 0; 6462306a36Sopenharmony_ci int i; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (!chandef->edmg.channels || !chandef->edmg.bw_config) 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!cfg80211_valid_60g_freq(chandef->chan->center_freq)) 7062306a36Sopenharmony_ci return false; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 7362306a36Sopenharmony_ci if (chandef->edmg.channels & BIT(i)) { 7462306a36Sopenharmony_ci contiguous++; 7562306a36Sopenharmony_ci num_of_enabled++; 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci contiguous = 0; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci max_contiguous = max(contiguous, max_contiguous); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci /* basic verification of edmg configuration according to 8362306a36Sopenharmony_ci * IEEE P802.11ay/D4.0 section 9.4.2.251 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci /* check bw_config against contiguous edmg channels */ 8662306a36Sopenharmony_ci switch (chandef->edmg.bw_config) { 8762306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_4: 8862306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_8: 8962306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_12: 9062306a36Sopenharmony_ci if (max_contiguous < 1) 9162306a36Sopenharmony_ci return false; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_5: 9462306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_9: 9562306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_13: 9662306a36Sopenharmony_ci if (max_contiguous < 2) 9762306a36Sopenharmony_ci return false; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_6: 10062306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_10: 10162306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_14: 10262306a36Sopenharmony_ci if (max_contiguous < 3) 10362306a36Sopenharmony_ci return false; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_7: 10662306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_11: 10762306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_15: 10862306a36Sopenharmony_ci if (max_contiguous < 4) 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci default: 11362306a36Sopenharmony_ci return false; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* check bw_config against aggregated (non contiguous) edmg channels */ 11762306a36Sopenharmony_ci switch (chandef->edmg.bw_config) { 11862306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_4: 11962306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_5: 12062306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_6: 12162306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_7: 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_8: 12462306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_9: 12562306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_10: 12662306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_11: 12762306a36Sopenharmony_ci if (num_of_enabled < 2) 12862306a36Sopenharmony_ci return false; 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_12: 13162306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_13: 13262306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_14: 13362306a36Sopenharmony_ci case IEEE80211_EDMG_BW_CONFIG_15: 13462306a36Sopenharmony_ci if (num_of_enabled < 4 || max_contiguous < 2) 13562306a36Sopenharmony_ci return false; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci return false; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return true; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int mhz; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci switch (chan_width) { 14962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_1: 15062306a36Sopenharmony_ci mhz = 1; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_2: 15362306a36Sopenharmony_ci mhz = 2; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_4: 15662306a36Sopenharmony_ci mhz = 4; 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_8: 15962306a36Sopenharmony_ci mhz = 8; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_16: 16262306a36Sopenharmony_ci mhz = 16; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 16562306a36Sopenharmony_ci mhz = 5; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 16862306a36Sopenharmony_ci mhz = 10; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 17162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 17262306a36Sopenharmony_ci mhz = 20; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 17562306a36Sopenharmony_ci mhz = 40; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 17862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 17962306a36Sopenharmony_ci mhz = 80; 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 18262306a36Sopenharmony_ci mhz = 160; 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 18562306a36Sopenharmony_ci mhz = 320; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci default: 18862306a36Sopenharmony_ci WARN_ON_ONCE(1); 18962306a36Sopenharmony_ci return -1; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci return mhz; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return nl80211_chan_width_to_mhz(c->width); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cibool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci u32 control_freq, oper_freq; 20262306a36Sopenharmony_ci int oper_width, control_width; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!chandef->chan) 20562306a36Sopenharmony_ci return false; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (chandef->freq1_offset >= 1000) 20862306a36Sopenharmony_ci return false; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci control_freq = chandef->chan->center_freq; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci switch (chandef->width) { 21362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 21462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 21562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 21662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 21762306a36Sopenharmony_ci if (ieee80211_chandef_to_khz(chandef) != 21862306a36Sopenharmony_ci ieee80211_channel_to_khz(chandef->chan)) 21962306a36Sopenharmony_ci return false; 22062306a36Sopenharmony_ci if (chandef->center_freq2) 22162306a36Sopenharmony_ci return false; 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_1: 22462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_2: 22562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_4: 22662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_8: 22762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_16: 22862306a36Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_S1GHZ) 22962306a36Sopenharmony_ci return false; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci control_freq = ieee80211_channel_to_khz(chandef->chan); 23262306a36Sopenharmony_ci oper_freq = ieee80211_chandef_to_khz(chandef); 23362306a36Sopenharmony_ci control_width = nl80211_chan_width_to_mhz( 23462306a36Sopenharmony_ci ieee80211_s1g_channel_width( 23562306a36Sopenharmony_ci chandef->chan)); 23662306a36Sopenharmony_ci oper_width = cfg80211_chandef_get_width(chandef); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (oper_width < 0 || control_width < 0) 23962306a36Sopenharmony_ci return false; 24062306a36Sopenharmony_ci if (chandef->center_freq2) 24162306a36Sopenharmony_ci return false; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (control_freq + MHZ_TO_KHZ(control_width) / 2 > 24462306a36Sopenharmony_ci oper_freq + MHZ_TO_KHZ(oper_width) / 2) 24562306a36Sopenharmony_ci return false; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (control_freq - MHZ_TO_KHZ(control_width) / 2 < 24862306a36Sopenharmony_ci oper_freq - MHZ_TO_KHZ(oper_width) / 2) 24962306a36Sopenharmony_ci return false; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 25262306a36Sopenharmony_ci if (!chandef->center_freq2) 25362306a36Sopenharmony_ci return false; 25462306a36Sopenharmony_ci /* adjacent is not allowed -- that's a 160 MHz channel */ 25562306a36Sopenharmony_ci if (chandef->center_freq1 - chandef->center_freq2 == 80 || 25662306a36Sopenharmony_ci chandef->center_freq2 - chandef->center_freq1 == 80) 25762306a36Sopenharmony_ci return false; 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci if (chandef->center_freq2) 26162306a36Sopenharmony_ci return false; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci switch (chandef->width) { 26662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 26762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 26862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 26962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 27062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_1: 27162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_2: 27262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_4: 27362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_8: 27462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_16: 27562306a36Sopenharmony_ci /* all checked above */ 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 27862306a36Sopenharmony_ci if (chandef->center_freq1 == control_freq + 150 || 27962306a36Sopenharmony_ci chandef->center_freq1 == control_freq + 130 || 28062306a36Sopenharmony_ci chandef->center_freq1 == control_freq + 110 || 28162306a36Sopenharmony_ci chandef->center_freq1 == control_freq + 90 || 28262306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 90 || 28362306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 110 || 28462306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 130 || 28562306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 150) 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci fallthrough; 28862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 28962306a36Sopenharmony_ci if (chandef->center_freq1 == control_freq + 70 || 29062306a36Sopenharmony_ci chandef->center_freq1 == control_freq + 50 || 29162306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 50 || 29262306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 70) 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci fallthrough; 29562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 29662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 29762306a36Sopenharmony_ci if (chandef->center_freq1 == control_freq + 30 || 29862306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 30) 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci fallthrough; 30162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 30262306a36Sopenharmony_ci if (chandef->center_freq1 == control_freq + 10 || 30362306a36Sopenharmony_ci chandef->center_freq1 == control_freq - 10) 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci fallthrough; 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci return false; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* channel 14 is only for IEEE 802.11b */ 31162306a36Sopenharmony_ci if (chandef->center_freq1 == 2484 && 31262306a36Sopenharmony_ci chandef->width != NL80211_CHAN_WIDTH_20_NOHT) 31362306a36Sopenharmony_ci return false; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (cfg80211_chandef_is_edmg(chandef) && 31662306a36Sopenharmony_ci !cfg80211_edmg_chandef_valid(chandef)) 31762306a36Sopenharmony_ci return false; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return true; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_chandef_valid); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void chandef_primary_freqs(const struct cfg80211_chan_def *c, 32462306a36Sopenharmony_ci u32 *pri40, u32 *pri80, u32 *pri160) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci int tmp; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci switch (c->width) { 32962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 33062306a36Sopenharmony_ci *pri40 = c->center_freq1; 33162306a36Sopenharmony_ci *pri80 = 0; 33262306a36Sopenharmony_ci *pri160 = 0; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 33562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 33662306a36Sopenharmony_ci *pri160 = 0; 33762306a36Sopenharmony_ci *pri80 = c->center_freq1; 33862306a36Sopenharmony_ci /* n_P20 */ 33962306a36Sopenharmony_ci tmp = (30 + c->chan->center_freq - c->center_freq1)/20; 34062306a36Sopenharmony_ci /* n_P40 */ 34162306a36Sopenharmony_ci tmp /= 2; 34262306a36Sopenharmony_ci /* freq_P40 */ 34362306a36Sopenharmony_ci *pri40 = c->center_freq1 - 20 + 40 * tmp; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 34662306a36Sopenharmony_ci *pri160 = c->center_freq1; 34762306a36Sopenharmony_ci /* n_P20 */ 34862306a36Sopenharmony_ci tmp = (70 + c->chan->center_freq - c->center_freq1)/20; 34962306a36Sopenharmony_ci /* n_P40 */ 35062306a36Sopenharmony_ci tmp /= 2; 35162306a36Sopenharmony_ci /* freq_P40 */ 35262306a36Sopenharmony_ci *pri40 = c->center_freq1 - 60 + 40 * tmp; 35362306a36Sopenharmony_ci /* n_P80 */ 35462306a36Sopenharmony_ci tmp /= 2; 35562306a36Sopenharmony_ci *pri80 = c->center_freq1 - 40 + 80 * tmp; 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 35862306a36Sopenharmony_ci /* n_P20 */ 35962306a36Sopenharmony_ci tmp = (150 + c->chan->center_freq - c->center_freq1) / 20; 36062306a36Sopenharmony_ci /* n_P40 */ 36162306a36Sopenharmony_ci tmp /= 2; 36262306a36Sopenharmony_ci /* freq_P40 */ 36362306a36Sopenharmony_ci *pri40 = c->center_freq1 - 140 + 40 * tmp; 36462306a36Sopenharmony_ci /* n_P80 */ 36562306a36Sopenharmony_ci tmp /= 2; 36662306a36Sopenharmony_ci *pri80 = c->center_freq1 - 120 + 80 * tmp; 36762306a36Sopenharmony_ci /* n_P160 */ 36862306a36Sopenharmony_ci tmp /= 2; 36962306a36Sopenharmony_ci *pri160 = c->center_freq1 - 80 + 160 * tmp; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci default: 37262306a36Sopenharmony_ci WARN_ON_ONCE(1); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciconst struct cfg80211_chan_def * 37762306a36Sopenharmony_cicfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, 37862306a36Sopenharmony_ci const struct cfg80211_chan_def *c2) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80, c1_pri160, c2_pri160; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* If they are identical, return */ 38362306a36Sopenharmony_ci if (cfg80211_chandef_identical(c1, c2)) 38462306a36Sopenharmony_ci return c1; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* otherwise, must have same control channel */ 38762306a36Sopenharmony_ci if (c1->chan != c2->chan) 38862306a36Sopenharmony_ci return NULL; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * If they have the same width, but aren't identical, 39262306a36Sopenharmony_ci * then they can't be compatible. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci if (c1->width == c2->width) 39562306a36Sopenharmony_ci return NULL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * can't be compatible if one of them is 5 or 10 MHz, 39962306a36Sopenharmony_ci * but they don't have the same width. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci if (c1->width == NL80211_CHAN_WIDTH_5 || 40262306a36Sopenharmony_ci c1->width == NL80211_CHAN_WIDTH_10 || 40362306a36Sopenharmony_ci c2->width == NL80211_CHAN_WIDTH_5 || 40462306a36Sopenharmony_ci c2->width == NL80211_CHAN_WIDTH_10) 40562306a36Sopenharmony_ci return NULL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || 40862306a36Sopenharmony_ci c1->width == NL80211_CHAN_WIDTH_20) 40962306a36Sopenharmony_ci return c2; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || 41262306a36Sopenharmony_ci c2->width == NL80211_CHAN_WIDTH_20) 41362306a36Sopenharmony_ci return c1; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci chandef_primary_freqs(c1, &c1_pri40, &c1_pri80, &c1_pri160); 41662306a36Sopenharmony_ci chandef_primary_freqs(c2, &c2_pri40, &c2_pri80, &c2_pri160); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (c1_pri40 != c2_pri40) 41962306a36Sopenharmony_ci return NULL; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (c1->width == NL80211_CHAN_WIDTH_40) 42262306a36Sopenharmony_ci return c2; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (c2->width == NL80211_CHAN_WIDTH_40) 42562306a36Sopenharmony_ci return c1; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (c1_pri80 != c2_pri80) 42862306a36Sopenharmony_ci return NULL; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (c1->width == NL80211_CHAN_WIDTH_80 && 43162306a36Sopenharmony_ci c2->width > NL80211_CHAN_WIDTH_80) 43262306a36Sopenharmony_ci return c2; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (c2->width == NL80211_CHAN_WIDTH_80 && 43562306a36Sopenharmony_ci c1->width > NL80211_CHAN_WIDTH_80) 43662306a36Sopenharmony_ci return c1; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci WARN_ON(!c1_pri160 && !c2_pri160); 43962306a36Sopenharmony_ci if (c1_pri160 && c2_pri160 && c1_pri160 != c2_pri160) 44062306a36Sopenharmony_ci return NULL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (c1->width > c2->width) 44362306a36Sopenharmony_ci return c1; 44462306a36Sopenharmony_ci return c2; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_chandef_compatible); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq, 44962306a36Sopenharmony_ci u32 bandwidth, 45062306a36Sopenharmony_ci enum nl80211_dfs_state dfs_state) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct ieee80211_channel *c; 45362306a36Sopenharmony_ci u32 freq; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci for (freq = center_freq - bandwidth/2 + 10; 45662306a36Sopenharmony_ci freq <= center_freq + bandwidth/2 - 10; 45762306a36Sopenharmony_ci freq += 20) { 45862306a36Sopenharmony_ci c = ieee80211_get_channel(wiphy, freq); 45962306a36Sopenharmony_ci if (!c || !(c->flags & IEEE80211_CHAN_RADAR)) 46062306a36Sopenharmony_ci continue; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci c->dfs_state = dfs_state; 46362306a36Sopenharmony_ci c->dfs_state_entered = jiffies; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_civoid cfg80211_set_dfs_state(struct wiphy *wiphy, 46862306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef, 46962306a36Sopenharmony_ci enum nl80211_dfs_state dfs_state) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci int width; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (WARN_ON(!cfg80211_chandef_valid(chandef))) 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci width = cfg80211_chandef_get_width(chandef); 47762306a36Sopenharmony_ci if (width < 0) 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1, 48162306a36Sopenharmony_ci width, dfs_state); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (!chandef->center_freq2) 48462306a36Sopenharmony_ci return; 48562306a36Sopenharmony_ci cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2, 48662306a36Sopenharmony_ci width, dfs_state); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic u32 cfg80211_get_start_freq(u32 center_freq, 49062306a36Sopenharmony_ci u32 bandwidth) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci u32 start_freq; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci bandwidth = MHZ_TO_KHZ(bandwidth); 49562306a36Sopenharmony_ci if (bandwidth <= MHZ_TO_KHZ(20)) 49662306a36Sopenharmony_ci start_freq = center_freq; 49762306a36Sopenharmony_ci else 49862306a36Sopenharmony_ci start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return start_freq; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic u32 cfg80211_get_end_freq(u32 center_freq, 50462306a36Sopenharmony_ci u32 bandwidth) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci u32 end_freq; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci bandwidth = MHZ_TO_KHZ(bandwidth); 50962306a36Sopenharmony_ci if (bandwidth <= MHZ_TO_KHZ(20)) 51062306a36Sopenharmony_ci end_freq = center_freq; 51162306a36Sopenharmony_ci else 51262306a36Sopenharmony_ci end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return end_freq; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, 51862306a36Sopenharmony_ci u32 center_freq, 51962306a36Sopenharmony_ci u32 bandwidth) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct ieee80211_channel *c; 52262306a36Sopenharmony_ci u32 freq, start_freq, end_freq; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci start_freq = cfg80211_get_start_freq(center_freq, bandwidth); 52562306a36Sopenharmony_ci end_freq = cfg80211_get_end_freq(center_freq, bandwidth); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { 52862306a36Sopenharmony_ci c = ieee80211_get_channel_khz(wiphy, freq); 52962306a36Sopenharmony_ci if (!c) 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (c->flags & IEEE80211_CHAN_RADAR) 53362306a36Sopenharmony_ci return 1; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciint cfg80211_chandef_dfs_required(struct wiphy *wiphy, 54062306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef, 54162306a36Sopenharmony_ci enum nl80211_iftype iftype) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci int width; 54462306a36Sopenharmony_ci int ret; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (WARN_ON(!cfg80211_chandef_valid(chandef))) 54762306a36Sopenharmony_ci return -EINVAL; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci switch (iftype) { 55062306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 55162306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 55262306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 55362306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 55462306a36Sopenharmony_ci width = cfg80211_chandef_get_width(chandef); 55562306a36Sopenharmony_ci if (width < 0) 55662306a36Sopenharmony_ci return -EINVAL; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci ret = cfg80211_get_chans_dfs_required(wiphy, 55962306a36Sopenharmony_ci ieee80211_chandef_to_khz(chandef), 56062306a36Sopenharmony_ci width); 56162306a36Sopenharmony_ci if (ret < 0) 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci else if (ret > 0) 56462306a36Sopenharmony_ci return BIT(chandef->width); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!chandef->center_freq2) 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = cfg80211_get_chans_dfs_required(wiphy, 57062306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq2), 57162306a36Sopenharmony_ci width); 57262306a36Sopenharmony_ci if (ret < 0) 57362306a36Sopenharmony_ci return ret; 57462306a36Sopenharmony_ci else if (ret > 0) 57562306a36Sopenharmony_ci return BIT(chandef->width); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 57962306a36Sopenharmony_ci case NL80211_IFTYPE_OCB: 58062306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 58162306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 58262306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 58362306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 58462306a36Sopenharmony_ci case NL80211_IFTYPE_NAN: 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 58762306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 58862306a36Sopenharmony_ci case NUM_NL80211_IFTYPES: 58962306a36Sopenharmony_ci WARN_ON(1); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_chandef_dfs_required); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy, 59762306a36Sopenharmony_ci u32 center_freq, 59862306a36Sopenharmony_ci u32 bandwidth) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct ieee80211_channel *c; 60162306a36Sopenharmony_ci u32 freq, start_freq, end_freq; 60262306a36Sopenharmony_ci int count = 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci start_freq = cfg80211_get_start_freq(center_freq, bandwidth); 60562306a36Sopenharmony_ci end_freq = cfg80211_get_end_freq(center_freq, bandwidth); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* 60862306a36Sopenharmony_ci * Check entire range of channels for the bandwidth. 60962306a36Sopenharmony_ci * Check all channels are DFS channels (DFS_USABLE or 61062306a36Sopenharmony_ci * DFS_AVAILABLE). Return number of usable channels 61162306a36Sopenharmony_ci * (require CAC). Allow DFS and non-DFS channel mix. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { 61462306a36Sopenharmony_ci c = ieee80211_get_channel_khz(wiphy, freq); 61562306a36Sopenharmony_ci if (!c) 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (c->flags & IEEE80211_CHAN_DISABLED) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (c->flags & IEEE80211_CHAN_RADAR) { 62262306a36Sopenharmony_ci if (c->dfs_state == NL80211_DFS_UNAVAILABLE) 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (c->dfs_state == NL80211_DFS_USABLE) 62662306a36Sopenharmony_ci count++; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return count; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cibool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, 63462306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci int width; 63762306a36Sopenharmony_ci int r1, r2 = 0; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (WARN_ON(!cfg80211_chandef_valid(chandef))) 64062306a36Sopenharmony_ci return false; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci width = cfg80211_chandef_get_width(chandef); 64362306a36Sopenharmony_ci if (width < 0) 64462306a36Sopenharmony_ci return false; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci r1 = cfg80211_get_chans_dfs_usable(wiphy, 64762306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq1), 64862306a36Sopenharmony_ci width); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (r1 < 0) 65162306a36Sopenharmony_ci return false; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci switch (chandef->width) { 65462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 65562306a36Sopenharmony_ci WARN_ON(!chandef->center_freq2); 65662306a36Sopenharmony_ci r2 = cfg80211_get_chans_dfs_usable(wiphy, 65762306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq2), 65862306a36Sopenharmony_ci width); 65962306a36Sopenharmony_ci if (r2 < 0) 66062306a36Sopenharmony_ci return false; 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci default: 66362306a36Sopenharmony_ci WARN_ON(chandef->center_freq2); 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return (r1 + r2 > 0); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/* 67162306a36Sopenharmony_ci * Checks if center frequency of chan falls with in the bandwidth 67262306a36Sopenharmony_ci * range of chandef. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_cibool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, 67562306a36Sopenharmony_ci struct ieee80211_channel *chan, 67662306a36Sopenharmony_ci bool primary_only) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci int width; 67962306a36Sopenharmony_ci u32 freq; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (!chandef->chan) 68262306a36Sopenharmony_ci return false; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (chandef->chan->center_freq == chan->center_freq) 68562306a36Sopenharmony_ci return true; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (primary_only) 68862306a36Sopenharmony_ci return false; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci width = cfg80211_chandef_get_width(chandef); 69162306a36Sopenharmony_ci if (width <= 20) 69262306a36Sopenharmony_ci return false; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci for (freq = chandef->center_freq1 - width / 2 + 10; 69562306a36Sopenharmony_ci freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) { 69662306a36Sopenharmony_ci if (chan->center_freq == freq) 69762306a36Sopenharmony_ci return true; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (!chandef->center_freq2) 70162306a36Sopenharmony_ci return false; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci for (freq = chandef->center_freq2 - width / 2 + 10; 70462306a36Sopenharmony_ci freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) { 70562306a36Sopenharmony_ci if (chan->center_freq == freq) 70662306a36Sopenharmony_ci return true; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return false; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cibool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci unsigned int link; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci switch (wdev->iftype) { 71962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 72062306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 72162306a36Sopenharmony_ci for_each_valid_link(wdev, link) { 72262306a36Sopenharmony_ci if (wdev->links[link].ap.beacon_interval) 72362306a36Sopenharmony_ci return true; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci break; 72662306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 72762306a36Sopenharmony_ci if (wdev->u.ibss.ssid_len) 72862306a36Sopenharmony_ci return true; 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 73162306a36Sopenharmony_ci if (wdev->u.mesh.id_len) 73262306a36Sopenharmony_ci return true; 73362306a36Sopenharmony_ci break; 73462306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 73562306a36Sopenharmony_ci case NL80211_IFTYPE_OCB: 73662306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 73762306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 73862306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 73962306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 74062306a36Sopenharmony_ci /* Can NAN type be considered as beaconing interface? */ 74162306a36Sopenharmony_ci case NL80211_IFTYPE_NAN: 74262306a36Sopenharmony_ci break; 74362306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 74462306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 74562306a36Sopenharmony_ci case NUM_NL80211_IFTYPES: 74662306a36Sopenharmony_ci WARN_ON(1); 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci return false; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cibool cfg80211_wdev_on_sub_chan(struct wireless_dev *wdev, 75362306a36Sopenharmony_ci struct ieee80211_channel *chan, 75462306a36Sopenharmony_ci bool primary_only) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci unsigned int link; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci switch (wdev->iftype) { 75962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 76062306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 76162306a36Sopenharmony_ci for_each_valid_link(wdev, link) { 76262306a36Sopenharmony_ci if (cfg80211_is_sub_chan(&wdev->links[link].ap.chandef, 76362306a36Sopenharmony_ci chan, primary_only)) 76462306a36Sopenharmony_ci return true; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 76862306a36Sopenharmony_ci return cfg80211_is_sub_chan(&wdev->u.ibss.chandef, chan, 76962306a36Sopenharmony_ci primary_only); 77062306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 77162306a36Sopenharmony_ci return cfg80211_is_sub_chan(&wdev->u.mesh.chandef, chan, 77262306a36Sopenharmony_ci primary_only); 77362306a36Sopenharmony_ci default: 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return false; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, 78162306a36Sopenharmony_ci struct ieee80211_channel *chan) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct wireless_dev *wdev; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci list_for_each_entry(wdev, &wiphy->wdev_list, list) { 78662306a36Sopenharmony_ci wdev_lock(wdev); 78762306a36Sopenharmony_ci if (!cfg80211_beaconing_iface_active(wdev)) { 78862306a36Sopenharmony_ci wdev_unlock(wdev); 78962306a36Sopenharmony_ci continue; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (cfg80211_wdev_on_sub_chan(wdev, chan, false)) { 79362306a36Sopenharmony_ci wdev_unlock(wdev); 79462306a36Sopenharmony_ci return true; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci wdev_unlock(wdev); 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return false; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic bool 80362306a36Sopenharmony_cicfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev, 80462306a36Sopenharmony_ci struct ieee80211_channel *channel) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci if (!rdev->background_radar_wdev) 80762306a36Sopenharmony_ci return false; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (!cfg80211_chandef_valid(&rdev->background_radar_chandef)) 81062306a36Sopenharmony_ci return false; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel, 81362306a36Sopenharmony_ci false); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cibool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, 81762306a36Sopenharmony_ci struct ieee80211_channel *chan) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ASSERT_RTNL(); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (!(chan->flags & IEEE80211_CHAN_RADAR)) 82462306a36Sopenharmony_ci return false; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 82762306a36Sopenharmony_ci if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) 82862306a36Sopenharmony_ci continue; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan)) 83162306a36Sopenharmony_ci return true; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (cfg80211_offchan_chain_is_active(rdev, chan)) 83462306a36Sopenharmony_ci return true; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci return false; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, 84162306a36Sopenharmony_ci u32 center_freq, 84262306a36Sopenharmony_ci u32 bandwidth) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct ieee80211_channel *c; 84562306a36Sopenharmony_ci u32 freq, start_freq, end_freq; 84662306a36Sopenharmony_ci bool dfs_offload; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci dfs_offload = wiphy_ext_feature_isset(wiphy, 84962306a36Sopenharmony_ci NL80211_EXT_FEATURE_DFS_OFFLOAD); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci start_freq = cfg80211_get_start_freq(center_freq, bandwidth); 85262306a36Sopenharmony_ci end_freq = cfg80211_get_end_freq(center_freq, bandwidth); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* 85562306a36Sopenharmony_ci * Check entire range of channels for the bandwidth. 85662306a36Sopenharmony_ci * If any channel in between is disabled or has not 85762306a36Sopenharmony_ci * had gone through CAC return false 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { 86062306a36Sopenharmony_ci c = ieee80211_get_channel_khz(wiphy, freq); 86162306a36Sopenharmony_ci if (!c) 86262306a36Sopenharmony_ci return false; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (c->flags & IEEE80211_CHAN_DISABLED) 86562306a36Sopenharmony_ci return false; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if ((c->flags & IEEE80211_CHAN_RADAR) && 86862306a36Sopenharmony_ci (c->dfs_state != NL80211_DFS_AVAILABLE) && 86962306a36Sopenharmony_ci !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload)) 87062306a36Sopenharmony_ci return false; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return true; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic bool cfg80211_chandef_dfs_available(struct wiphy *wiphy, 87762306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci int width; 88062306a36Sopenharmony_ci int r; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (WARN_ON(!cfg80211_chandef_valid(chandef))) 88362306a36Sopenharmony_ci return false; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci width = cfg80211_chandef_get_width(chandef); 88662306a36Sopenharmony_ci if (width < 0) 88762306a36Sopenharmony_ci return false; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci r = cfg80211_get_chans_dfs_available(wiphy, 89062306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq1), 89162306a36Sopenharmony_ci width); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* If any of channels unavailable for cf1 just return */ 89462306a36Sopenharmony_ci if (!r) 89562306a36Sopenharmony_ci return r; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci switch (chandef->width) { 89862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 89962306a36Sopenharmony_ci WARN_ON(!chandef->center_freq2); 90062306a36Sopenharmony_ci r = cfg80211_get_chans_dfs_available(wiphy, 90162306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq2), 90262306a36Sopenharmony_ci width); 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci default: 90562306a36Sopenharmony_ci WARN_ON(chandef->center_freq2); 90662306a36Sopenharmony_ci break; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci return r; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy, 91362306a36Sopenharmony_ci u32 center_freq, 91462306a36Sopenharmony_ci u32 bandwidth) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct ieee80211_channel *c; 91762306a36Sopenharmony_ci u32 start_freq, end_freq, freq; 91862306a36Sopenharmony_ci unsigned int dfs_cac_ms = 0; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci start_freq = cfg80211_get_start_freq(center_freq, bandwidth); 92162306a36Sopenharmony_ci end_freq = cfg80211_get_end_freq(center_freq, bandwidth); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { 92462306a36Sopenharmony_ci c = ieee80211_get_channel_khz(wiphy, freq); 92562306a36Sopenharmony_ci if (!c) 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (c->flags & IEEE80211_CHAN_DISABLED) 92962306a36Sopenharmony_ci return 0; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (!(c->flags & IEEE80211_CHAN_RADAR)) 93262306a36Sopenharmony_ci continue; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (c->dfs_cac_ms > dfs_cac_ms) 93562306a36Sopenharmony_ci dfs_cac_ms = c->dfs_cac_ms; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return dfs_cac_ms; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ciunsigned int 94262306a36Sopenharmony_cicfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, 94362306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci int width; 94662306a36Sopenharmony_ci unsigned int t1 = 0, t2 = 0; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (WARN_ON(!cfg80211_chandef_valid(chandef))) 94962306a36Sopenharmony_ci return 0; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci width = cfg80211_chandef_get_width(chandef); 95262306a36Sopenharmony_ci if (width < 0) 95362306a36Sopenharmony_ci return 0; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci t1 = cfg80211_get_chans_dfs_cac_time(wiphy, 95662306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq1), 95762306a36Sopenharmony_ci width); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (!chandef->center_freq2) 96062306a36Sopenharmony_ci return t1; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci t2 = cfg80211_get_chans_dfs_cac_time(wiphy, 96362306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq2), 96462306a36Sopenharmony_ci width); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return max(t1, t2); 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, 97062306a36Sopenharmony_ci u32 center_freq, u32 bandwidth, 97162306a36Sopenharmony_ci u32 prohibited_flags) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct ieee80211_channel *c; 97462306a36Sopenharmony_ci u32 freq, start_freq, end_freq; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci start_freq = cfg80211_get_start_freq(center_freq, bandwidth); 97762306a36Sopenharmony_ci end_freq = cfg80211_get_end_freq(center_freq, bandwidth); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { 98062306a36Sopenharmony_ci c = ieee80211_get_channel_khz(wiphy, freq); 98162306a36Sopenharmony_ci if (!c || c->flags & prohibited_flags) 98262306a36Sopenharmony_ci return false; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return true; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci/* check if the operating channels are valid and supported */ 98962306a36Sopenharmony_cistatic bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels, 99062306a36Sopenharmony_ci enum ieee80211_edmg_bw_config edmg_bw_config, 99162306a36Sopenharmony_ci int primary_channel, 99262306a36Sopenharmony_ci struct ieee80211_edmg *edmg_cap) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct ieee80211_channel *chan; 99562306a36Sopenharmony_ci int i, freq; 99662306a36Sopenharmony_ci int channels_counter = 0; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (!edmg_channels && !edmg_bw_config) 99962306a36Sopenharmony_ci return true; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if ((!edmg_channels && edmg_bw_config) || 100262306a36Sopenharmony_ci (edmg_channels && !edmg_bw_config)) 100362306a36Sopenharmony_ci return false; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (!(edmg_channels & BIT(primary_channel - 1))) 100662306a36Sopenharmony_ci return false; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci /* 60GHz channels 1..6 */ 100962306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 101062306a36Sopenharmony_ci if (!(edmg_channels & BIT(i))) 101162306a36Sopenharmony_ci continue; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (!(edmg_cap->channels & BIT(i))) 101462306a36Sopenharmony_ci return false; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci channels_counter++; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(i + 1, 101962306a36Sopenharmony_ci NL80211_BAND_60GHZ); 102062306a36Sopenharmony_ci chan = ieee80211_get_channel(wiphy, freq); 102162306a36Sopenharmony_ci if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) 102262306a36Sopenharmony_ci return false; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* IEEE802.11 allows max 4 channels */ 102662306a36Sopenharmony_ci if (channels_counter > 4) 102762306a36Sopenharmony_ci return false; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* check bw_config is a subset of what driver supports 103062306a36Sopenharmony_ci * (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13) 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4)) 103362306a36Sopenharmony_ci return false; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (edmg_bw_config > edmg_cap->bw_config) 103662306a36Sopenharmony_ci return false; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return true; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cibool cfg80211_chandef_usable(struct wiphy *wiphy, 104262306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef, 104362306a36Sopenharmony_ci u32 prohibited_flags) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap; 104662306a36Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap; 104762306a36Sopenharmony_ci struct ieee80211_edmg *edmg_cap; 104862306a36Sopenharmony_ci u32 width, control_freq, cap; 104962306a36Sopenharmony_ci bool ext_nss_cap, support_80_80 = false, support_320 = false; 105062306a36Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 105162306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 105262306a36Sopenharmony_ci int i; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (WARN_ON(!cfg80211_chandef_valid(chandef))) 105562306a36Sopenharmony_ci return false; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; 105862306a36Sopenharmony_ci vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; 105962306a36Sopenharmony_ci edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap; 106062306a36Sopenharmony_ci ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) & 106162306a36Sopenharmony_ci IEEE80211_VHT_EXT_NSS_BW_CAPABLE; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (edmg_cap->channels && 106462306a36Sopenharmony_ci !cfg80211_edmg_usable(wiphy, 106562306a36Sopenharmony_ci chandef->edmg.channels, 106662306a36Sopenharmony_ci chandef->edmg.bw_config, 106762306a36Sopenharmony_ci chandef->chan->hw_value, 106862306a36Sopenharmony_ci edmg_cap)) 106962306a36Sopenharmony_ci return false; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci control_freq = chandef->chan->center_freq; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci switch (chandef->width) { 107462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_1: 107562306a36Sopenharmony_ci width = 1; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_2: 107862306a36Sopenharmony_ci width = 2; 107962306a36Sopenharmony_ci break; 108062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_4: 108162306a36Sopenharmony_ci width = 4; 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_8: 108462306a36Sopenharmony_ci width = 8; 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_16: 108762306a36Sopenharmony_ci width = 16; 108862306a36Sopenharmony_ci break; 108962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 109062306a36Sopenharmony_ci width = 5; 109162306a36Sopenharmony_ci break; 109262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 109362306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_10MHZ; 109462306a36Sopenharmony_ci width = 10; 109562306a36Sopenharmony_ci break; 109662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 109762306a36Sopenharmony_ci if (!ht_cap->ht_supported && 109862306a36Sopenharmony_ci chandef->chan->band != NL80211_BAND_6GHZ) 109962306a36Sopenharmony_ci return false; 110062306a36Sopenharmony_ci fallthrough; 110162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 110262306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_20MHZ; 110362306a36Sopenharmony_ci width = 20; 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 110662306a36Sopenharmony_ci width = 40; 110762306a36Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 110862306a36Sopenharmony_ci break; 110962306a36Sopenharmony_ci if (!ht_cap->ht_supported) 111062306a36Sopenharmony_ci return false; 111162306a36Sopenharmony_ci if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || 111262306a36Sopenharmony_ci ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) 111362306a36Sopenharmony_ci return false; 111462306a36Sopenharmony_ci if (chandef->center_freq1 < control_freq && 111562306a36Sopenharmony_ci chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) 111662306a36Sopenharmony_ci return false; 111762306a36Sopenharmony_ci if (chandef->center_freq1 > control_freq && 111862306a36Sopenharmony_ci chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) 111962306a36Sopenharmony_ci return false; 112062306a36Sopenharmony_ci break; 112162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 112262306a36Sopenharmony_ci cap = vht_cap->cap; 112362306a36Sopenharmony_ci support_80_80 = 112462306a36Sopenharmony_ci (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) || 112562306a36Sopenharmony_ci (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && 112662306a36Sopenharmony_ci cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) || 112762306a36Sopenharmony_ci (ext_nss_cap && 112862306a36Sopenharmony_ci u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1); 112962306a36Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_6GHZ && !support_80_80) 113062306a36Sopenharmony_ci return false; 113162306a36Sopenharmony_ci fallthrough; 113262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 113362306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_80MHZ; 113462306a36Sopenharmony_ci width = 80; 113562306a36Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci if (!vht_cap->vht_supported) 113862306a36Sopenharmony_ci return false; 113962306a36Sopenharmony_ci break; 114062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 114162306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; 114262306a36Sopenharmony_ci width = 160; 114362306a36Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 114462306a36Sopenharmony_ci break; 114562306a36Sopenharmony_ci if (!vht_cap->vht_supported) 114662306a36Sopenharmony_ci return false; 114762306a36Sopenharmony_ci cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 114862306a36Sopenharmony_ci if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && 114962306a36Sopenharmony_ci cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ && 115062306a36Sopenharmony_ci !(ext_nss_cap && 115162306a36Sopenharmony_ci (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK))) 115262306a36Sopenharmony_ci return false; 115362306a36Sopenharmony_ci break; 115462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 115562306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_320MHZ; 115662306a36Sopenharmony_ci width = 320; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_6GHZ) 115962306a36Sopenharmony_ci return false; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci sband = wiphy->bands[NL80211_BAND_6GHZ]; 116262306a36Sopenharmony_ci if (!sband) 116362306a36Sopenharmony_ci return false; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci for (i = 0; i < sband->n_iftype_data; i++) { 116662306a36Sopenharmony_ci iftd = &sband->iftype_data[i]; 116762306a36Sopenharmony_ci if (!iftd->eht_cap.has_eht) 116862306a36Sopenharmony_ci continue; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (iftd->eht_cap.eht_cap_elem.phy_cap_info[0] & 117162306a36Sopenharmony_ci IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) { 117262306a36Sopenharmony_ci support_320 = true; 117362306a36Sopenharmony_ci break; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (!support_320) 117862306a36Sopenharmony_ci return false; 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci default: 118162306a36Sopenharmony_ci WARN_ON_ONCE(1); 118262306a36Sopenharmony_ci return false; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* 118662306a36Sopenharmony_ci * TODO: What if there are only certain 80/160/80+80 MHz channels 118762306a36Sopenharmony_ci * allowed by the driver, or only certain combinations? 118862306a36Sopenharmony_ci * For 40 MHz the driver can set the NO_HT40 flags, but for 118962306a36Sopenharmony_ci * 80/160 MHz and in particular 80+80 MHz this isn't really 119062306a36Sopenharmony_ci * feasible and we only have NO_80MHZ/NO_160MHZ so far but 119162306a36Sopenharmony_ci * no way to cover 80+80 MHz or more complex restrictions. 119262306a36Sopenharmony_ci * Note that such restrictions also need to be advertised to 119362306a36Sopenharmony_ci * userspace, for example for P2P channel selection. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (width > 20) 119762306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_OFDM; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci /* 5 and 10 MHz are only defined for the OFDM PHY */ 120062306a36Sopenharmony_ci if (width < 20) 120162306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_OFDM; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (!cfg80211_secondary_chans_ok(wiphy, 120562306a36Sopenharmony_ci ieee80211_chandef_to_khz(chandef), 120662306a36Sopenharmony_ci width, prohibited_flags)) 120762306a36Sopenharmony_ci return false; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (!chandef->center_freq2) 121062306a36Sopenharmony_ci return true; 121162306a36Sopenharmony_ci return cfg80211_secondary_chans_ok(wiphy, 121262306a36Sopenharmony_ci MHZ_TO_KHZ(chandef->center_freq2), 121362306a36Sopenharmony_ci width, prohibited_flags); 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_chandef_usable); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistatic bool cfg80211_ir_permissive_check_wdev(enum nl80211_iftype iftype, 121862306a36Sopenharmony_ci struct wireless_dev *wdev, 121962306a36Sopenharmony_ci struct ieee80211_channel *chan) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci struct ieee80211_channel *other_chan = NULL; 122262306a36Sopenharmony_ci unsigned int link_id; 122362306a36Sopenharmony_ci int r1, r2; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci for_each_valid_link(wdev, link_id) { 122662306a36Sopenharmony_ci if (wdev->iftype == NL80211_IFTYPE_STATION && 122762306a36Sopenharmony_ci wdev->links[link_id].client.current_bss) 122862306a36Sopenharmony_ci other_chan = wdev->links[link_id].client.current_bss->pub.channel; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* 123162306a36Sopenharmony_ci * If a GO already operates on the same GO_CONCURRENT channel, 123262306a36Sopenharmony_ci * this one (maybe the same one) can beacon as well. We allow 123362306a36Sopenharmony_ci * the operation even if the station we relied on with 123462306a36Sopenharmony_ci * GO_CONCURRENT is disconnected now. But then we must make sure 123562306a36Sopenharmony_ci * we're not outdoor on an indoor-only channel. 123662306a36Sopenharmony_ci */ 123762306a36Sopenharmony_ci if (iftype == NL80211_IFTYPE_P2P_GO && 123862306a36Sopenharmony_ci wdev->iftype == NL80211_IFTYPE_P2P_GO && 123962306a36Sopenharmony_ci wdev->links[link_id].ap.beacon_interval && 124062306a36Sopenharmony_ci !(chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) 124162306a36Sopenharmony_ci other_chan = wdev->links[link_id].ap.chandef.chan; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (!other_chan) 124462306a36Sopenharmony_ci continue; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (chan == other_chan) 124762306a36Sopenharmony_ci return true; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (chan->band != NL80211_BAND_5GHZ && 125062306a36Sopenharmony_ci chan->band != NL80211_BAND_6GHZ) 125162306a36Sopenharmony_ci continue; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci r1 = cfg80211_get_unii(chan->center_freq); 125462306a36Sopenharmony_ci r2 = cfg80211_get_unii(other_chan->center_freq); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (r1 != -EINVAL && r1 == r2) { 125762306a36Sopenharmony_ci /* 125862306a36Sopenharmony_ci * At some locations channels 149-165 are considered a 125962306a36Sopenharmony_ci * bundle, but at other locations, e.g., Indonesia, 126062306a36Sopenharmony_ci * channels 149-161 are considered a bundle while 126162306a36Sopenharmony_ci * channel 165 is left out and considered to be in a 126262306a36Sopenharmony_ci * different bundle. Thus, in case that there is a 126362306a36Sopenharmony_ci * station interface connected to an AP on channel 165, 126462306a36Sopenharmony_ci * it is assumed that channels 149-161 are allowed for 126562306a36Sopenharmony_ci * GO operations. However, having a station interface 126662306a36Sopenharmony_ci * connected to an AP on channels 149-161, does not 126762306a36Sopenharmony_ci * allow GO operation on channel 165. 126862306a36Sopenharmony_ci */ 126962306a36Sopenharmony_ci if (chan->center_freq == 5825 && 127062306a36Sopenharmony_ci other_chan->center_freq != 5825) 127162306a36Sopenharmony_ci continue; 127262306a36Sopenharmony_ci return true; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci return false; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci/* 128062306a36Sopenharmony_ci * Check if the channel can be used under permissive conditions mandated by 128162306a36Sopenharmony_ci * some regulatory bodies, i.e., the channel is marked with 128262306a36Sopenharmony_ci * IEEE80211_CHAN_IR_CONCURRENT and there is an additional station interface 128362306a36Sopenharmony_ci * associated to an AP on the same channel or on the same UNII band 128462306a36Sopenharmony_ci * (assuming that the AP is an authorized master). 128562306a36Sopenharmony_ci * In addition allow operation on a channel on which indoor operation is 128662306a36Sopenharmony_ci * allowed, iff we are currently operating in an indoor environment. 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_cistatic bool cfg80211_ir_permissive_chan(struct wiphy *wiphy, 128962306a36Sopenharmony_ci enum nl80211_iftype iftype, 129062306a36Sopenharmony_ci struct ieee80211_channel *chan) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct wireless_dev *wdev; 129362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) || 129862306a36Sopenharmony_ci !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR)) 129962306a36Sopenharmony_ci return false; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* only valid for GO and TDLS off-channel (station/p2p-CL) */ 130262306a36Sopenharmony_ci if (iftype != NL80211_IFTYPE_P2P_GO && 130362306a36Sopenharmony_ci iftype != NL80211_IFTYPE_STATION && 130462306a36Sopenharmony_ci iftype != NL80211_IFTYPE_P2P_CLIENT) 130562306a36Sopenharmony_ci return false; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (regulatory_indoor_allowed() && 130862306a36Sopenharmony_ci (chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) 130962306a36Sopenharmony_ci return true; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT)) 131262306a36Sopenharmony_ci return false; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* 131562306a36Sopenharmony_ci * Generally, it is possible to rely on another device/driver to allow 131662306a36Sopenharmony_ci * the IR concurrent relaxation, however, since the device can further 131762306a36Sopenharmony_ci * enforce the relaxation (by doing a similar verifications as this), 131862306a36Sopenharmony_ci * and thus fail the GO instantiation, consider only the interfaces of 131962306a36Sopenharmony_ci * the current registered device. 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 132262306a36Sopenharmony_ci bool ret; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci wdev_lock(wdev); 132562306a36Sopenharmony_ci ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan); 132662306a36Sopenharmony_ci wdev_unlock(wdev); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (ret) 132962306a36Sopenharmony_ci return ret; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci return false; 133362306a36Sopenharmony_ci} 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic bool _cfg80211_reg_can_beacon(struct wiphy *wiphy, 133662306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, 133762306a36Sopenharmony_ci enum nl80211_iftype iftype, 133862306a36Sopenharmony_ci bool check_no_ir) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci bool res; 134162306a36Sopenharmony_ci u32 prohibited_flags = IEEE80211_CHAN_DISABLED | 134262306a36Sopenharmony_ci IEEE80211_CHAN_RADAR; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (check_no_ir) 134762306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_NO_IR; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 && 135062306a36Sopenharmony_ci cfg80211_chandef_dfs_available(wiphy, chandef)) { 135162306a36Sopenharmony_ci /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ 135262306a36Sopenharmony_ci prohibited_flags = IEEE80211_CHAN_DISABLED; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci trace_cfg80211_return_bool(res); 135862306a36Sopenharmony_ci return res; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cibool cfg80211_reg_can_beacon(struct wiphy *wiphy, 136262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, 136362306a36Sopenharmony_ci enum nl80211_iftype iftype) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, true); 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_reg_can_beacon); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cibool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy, 137062306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, 137162306a36Sopenharmony_ci enum nl80211_iftype iftype) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 137462306a36Sopenharmony_ci bool check_no_ir; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci lockdep_assert_held(&rdev->wiphy.mtx); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* 137962306a36Sopenharmony_ci * Under certain conditions suggested by some regulatory bodies a 138062306a36Sopenharmony_ci * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag 138162306a36Sopenharmony_ci * only if such relaxations are not enabled and the conditions are not 138262306a36Sopenharmony_ci * met. 138362306a36Sopenharmony_ci */ 138462306a36Sopenharmony_ci check_no_ir = !cfg80211_ir_permissive_chan(wiphy, iftype, 138562306a36Sopenharmony_ci chandef->chan); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci return _cfg80211_reg_can_beacon(wiphy, chandef, iftype, check_no_ir); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_reg_can_beacon_relax); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ciint cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, 139262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 139362306a36Sopenharmony_ci{ 139462306a36Sopenharmony_ci if (!rdev->ops->set_monitor_channel) 139562306a36Sopenharmony_ci return -EOPNOTSUPP; 139662306a36Sopenharmony_ci if (!cfg80211_has_monitors_only(rdev)) 139762306a36Sopenharmony_ci return -EBUSY; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci return rdev_set_monitor_channel(rdev, chandef); 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cibool cfg80211_any_usable_channels(struct wiphy *wiphy, 140362306a36Sopenharmony_ci unsigned long sband_mask, 140462306a36Sopenharmony_ci u32 prohibited_flags) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci int idx; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci prohibited_flags |= IEEE80211_CHAN_DISABLED; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci for_each_set_bit(idx, &sband_mask, NUM_NL80211_BANDS) { 141162306a36Sopenharmony_ci struct ieee80211_supported_band *sband = wiphy->bands[idx]; 141262306a36Sopenharmony_ci int chanidx; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (!sband) 141562306a36Sopenharmony_ci continue; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci for (chanidx = 0; chanidx < sband->n_channels; chanidx++) { 141862306a36Sopenharmony_ci struct ieee80211_channel *chan; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci chan = &sband->channels[chanidx]; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (chan->flags & prohibited_flags) 142362306a36Sopenharmony_ci continue; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return true; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci return false; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_any_usable_channels); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistruct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev, 143462306a36Sopenharmony_ci unsigned int link_id) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci /* 143762306a36Sopenharmony_ci * We need to sort out the locking here - in some cases 143862306a36Sopenharmony_ci * where we get here we really just don't care (yet) 143962306a36Sopenharmony_ci * about the valid links, but in others we do. But we 144062306a36Sopenharmony_ci * get here with various driver cases, so we cannot 144162306a36Sopenharmony_ci * easily require the wdev mutex. 144262306a36Sopenharmony_ci */ 144362306a36Sopenharmony_ci if (link_id || wdev->valid_links & BIT(0)) { 144462306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 144562306a36Sopenharmony_ci WARN_ON(!(wdev->valid_links & BIT(link_id))); 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci switch (wdev->iftype) { 144962306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 145062306a36Sopenharmony_ci return &wdev->u.mesh.chandef; 145162306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 145262306a36Sopenharmony_ci return &wdev->u.ibss.chandef; 145362306a36Sopenharmony_ci case NL80211_IFTYPE_OCB: 145462306a36Sopenharmony_ci return &wdev->u.ocb.chandef; 145562306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 145662306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 145762306a36Sopenharmony_ci return &wdev->links[link_id].ap.chandef; 145862306a36Sopenharmony_ci default: 145962306a36Sopenharmony_ci return NULL; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ciEXPORT_SYMBOL(wdev_chandef); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistruct cfg80211_per_bw_puncturing_values { 146562306a36Sopenharmony_ci u8 len; 146662306a36Sopenharmony_ci const u16 *valid_values; 146762306a36Sopenharmony_ci}; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cistatic const u16 puncturing_values_80mhz[] = { 147062306a36Sopenharmony_ci 0x8, 0x4, 0x2, 0x1 147162306a36Sopenharmony_ci}; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic const u16 puncturing_values_160mhz[] = { 147462306a36Sopenharmony_ci 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3 147562306a36Sopenharmony_ci}; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic const u16 puncturing_values_320mhz[] = { 147862306a36Sopenharmony_ci 0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00, 147962306a36Sopenharmony_ci 0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f, 148062306a36Sopenharmony_ci 0x300f, 0xc0f, 0x30f, 0xcf, 0x3f 148162306a36Sopenharmony_ci}; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci#define CFG80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \ 148462306a36Sopenharmony_ci { \ 148562306a36Sopenharmony_ci .len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \ 148662306a36Sopenharmony_ci .valid_values = puncturing_values_ ## _bw ## mhz \ 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_cistatic const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = { 149062306a36Sopenharmony_ci CFG80211_PER_BW_VALID_PUNCTURING_VALUES(80), 149162306a36Sopenharmony_ci CFG80211_PER_BW_VALID_PUNCTURING_VALUES(160), 149262306a36Sopenharmony_ci CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320) 149362306a36Sopenharmony_ci}; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_cibool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap, 149662306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci u32 idx, i, start_freq; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci switch (chandef->width) { 150162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 150262306a36Sopenharmony_ci idx = 0; 150362306a36Sopenharmony_ci start_freq = chandef->center_freq1 - 40; 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 150662306a36Sopenharmony_ci idx = 1; 150762306a36Sopenharmony_ci start_freq = chandef->center_freq1 - 80; 150862306a36Sopenharmony_ci break; 150962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 151062306a36Sopenharmony_ci idx = 2; 151162306a36Sopenharmony_ci start_freq = chandef->center_freq1 - 160; 151262306a36Sopenharmony_ci break; 151362306a36Sopenharmony_ci default: 151462306a36Sopenharmony_ci *bitmap = 0; 151562306a36Sopenharmony_ci break; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (!*bitmap) 151962306a36Sopenharmony_ci return true; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* check if primary channel is punctured */ 152262306a36Sopenharmony_ci if (*bitmap & (u16)BIT((chandef->chan->center_freq - start_freq) / 20)) 152362306a36Sopenharmony_ci return false; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci for (i = 0; i < per_bw_puncturing[idx].len; i++) 152662306a36Sopenharmony_ci if (per_bw_puncturing[idx].valid_values[i] == *bitmap) 152762306a36Sopenharmony_ci return true; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci return false; 153062306a36Sopenharmony_ci} 153162306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_valid_disable_subchannel_bitmap); 1532