Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910// SPDX-License-Identifier: GPL-2.0/* * This file contains helper code to handle channel * settings and keeping track of what is possible at * any point in time. * * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2018-2026 Intel Corporation */
#include <linux/export.h>#include <linux/bitfield.h>#include <net/cfg80211.h>#include "core.h"#include "rdev-ops.h"
static bool cfg80211_valid_60g_freq(u32 freq){ return freq >= 58320 && freq <= 70200;}
void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, struct ieee80211_channel *chan, enum nl80211_channel_type chan_type){ if (WARN_ON(!chan)) return;
*chandef = (struct cfg80211_chan_def) { .chan = chan, };
WARN_ON(chan->band == NL80211_BAND_60GHZ || chan->band == NL80211_BAND_S1GHZ);
switch (chan_type) { case NL80211_CHAN_NO_HT: chandef->width = NL80211_CHAN_WIDTH_20_NOHT; chandef->center_freq1 = chan->center_freq; break; case NL80211_CHAN_HT20: chandef->width = NL80211_CHAN_WIDTH_20; chandef->center_freq1 = chan->center_freq; break; case NL80211_CHAN_HT40PLUS: chandef->width = NL80211_CHAN_WIDTH_40; chandef->center_freq1 = chan->center_freq + 10; break; case NL80211_CHAN_HT40MINUS: chandef->width = NL80211_CHAN_WIDTH_40; chandef->center_freq1 = chan->center_freq - 10; break; default: WARN_ON(1); }}EXPORT_SYMBOL(cfg80211_chandef_create);
static u32 cfg80211_get_start_freq(const struct cfg80211_chan_def *chandef, u32 cf){ u32 start_freq, center_freq, bandwidth;
center_freq = MHZ_TO_KHZ((cf == 1) ? chandef->center_freq1 : chandef->center_freq2); bandwidth = MHZ_TO_KHZ(cfg80211_chandef_get_width(chandef));
if (bandwidth <= MHZ_TO_KHZ(20)) start_freq = center_freq; else start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10);
return start_freq;}
static u32 cfg80211_get_end_freq(const struct cfg80211_chan_def *chandef, u32 cf){ u32 end_freq, center_freq, bandwidth;
center_freq = MHZ_TO_KHZ((cf == 1) ? chandef->center_freq1 : chandef->center_freq2); bandwidth = MHZ_TO_KHZ(cfg80211_chandef_get_width(chandef));
if (bandwidth <= MHZ_TO_KHZ(20)) end_freq = center_freq; else end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10);
return end_freq;}
#define for_each_subchan(chandef, freq, cf) \ for (u32 punctured = chandef->punctured, \ cf = 1, freq = cfg80211_get_start_freq(chandef, cf); \ freq <= cfg80211_get_end_freq(chandef, cf); \ freq += MHZ_TO_KHZ(20), \ ((cf == 1 && chandef->center_freq2 != 0 && \ freq > cfg80211_get_end_freq(chandef, cf)) ? \ (cf++, freq = cfg80211_get_start_freq(chandef, cf), \ punctured = 0) : (punctured >>= 1))) \ if (!(punctured & 1))
#define for_each_s1g_subchan(chandef, freq_khz) \ for (freq_khz = cfg80211_s1g_get_start_freq_khz(chandef); \ freq_khz <= cfg80211_s1g_get_end_freq_khz(chandef); \ freq_khz += MHZ_TO_KHZ(1))
struct cfg80211_per_bw_puncturing_values { u8 len; const u16 *valid_values;};
static const u16 puncturing_values_80mhz[] = { 0x8, 0x4, 0x2, 0x1};
static const u16 puncturing_values_160mhz[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3};
static const u16 puncturing_values_320mhz[] = { 0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00, 0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f, 0x300f, 0xc0f, 0x30f, 0xcf, 0x3f};
#define CFG80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \ { \ .len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \ .valid_values = puncturing_values_ ## _bw ## mhz \ }
static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = { CFG80211_PER_BW_VALID_PUNCTURING_VALUES(80), CFG80211_PER_BW_VALID_PUNCTURING_VALUES(160), CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320)};
static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef, u32 primary_center, u32 punctured){ u32 idx, i, start_freq;
switch (chandef->width) { case NL80211_CHAN_WIDTH_80: idx = 0; start_freq = chandef->center_freq1 - 40; break; case NL80211_CHAN_WIDTH_160: idx = 1; start_freq = chandef->center_freq1 - 80; break; case NL80211_CHAN_WIDTH_320: idx = 2; start_freq = chandef->center_freq1 - 160; break; default: return punctured == 0; }
if (!punctured) return true;
/* check if primary channel is punctured */ if (punctured & (u16)BIT((primary_center - start_freq) / 20)) return false;
for (i = 0; i < per_bw_puncturing[idx].len; i++) { if (per_bw_puncturing[idx].valid_values[i] == punctured) return true; }
return false;}
static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef){ int max_contiguous = 0; int num_of_enabled = 0; int contiguous = 0; int i;
if (!chandef->edmg.channels || !chandef->edmg.bw_config) return false;
if (!cfg80211_valid_60g_freq(chandef->chan->center_freq)) return false;
for (i = 0; i < 6; i++) { if (chandef->edmg.channels & BIT(i)) { contiguous++; num_of_enabled++; } else { contiguous = 0; }
max_contiguous = max(contiguous, max_contiguous); } /* basic verification of edmg configuration according to * IEEE P802.11ay/D4.0 section 9.4.2.251 */ /* check bw_config against contiguous edmg channels */ switch (chandef->edmg.bw_config) { case IEEE80211_EDMG_BW_CONFIG_4: case IEEE80211_EDMG_BW_CONFIG_8: case IEEE80211_EDMG_BW_CONFIG_12: if (max_contiguous < 1) return false; break; case IEEE80211_EDMG_BW_CONFIG_5: case IEEE80211_EDMG_BW_CONFIG_9: case IEEE80211_EDMG_BW_CONFIG_13: if (max_contiguous < 2) return false; break; case IEEE80211_EDMG_BW_CONFIG_6: case IEEE80211_EDMG_BW_CONFIG_10: case IEEE80211_EDMG_BW_CONFIG_14: if (max_contiguous < 3) return false; break; case IEEE80211_EDMG_BW_CONFIG_7: case IEEE80211_EDMG_BW_CONFIG_11: case IEEE80211_EDMG_BW_CONFIG_15: if (max_contiguous < 4) return false; break;
default: return false; }
/* check bw_config against aggregated (non contiguous) edmg channels */ switch (chandef->edmg.bw_config) { case IEEE80211_EDMG_BW_CONFIG_4: case IEEE80211_EDMG_BW_CONFIG_5: case IEEE80211_EDMG_BW_CONFIG_6: case IEEE80211_EDMG_BW_CONFIG_7: break; case IEEE80211_EDMG_BW_CONFIG_8: case IEEE80211_EDMG_BW_CONFIG_9: case IEEE80211_EDMG_BW_CONFIG_10: case IEEE80211_EDMG_BW_CONFIG_11: if (num_of_enabled < 2) return false; break; case IEEE80211_EDMG_BW_CONFIG_12: case IEEE80211_EDMG_BW_CONFIG_13: case IEEE80211_EDMG_BW_CONFIG_14: case IEEE80211_EDMG_BW_CONFIG_15: if (num_of_enabled < 4 || max_contiguous < 2) return false; break; default: return false; }
return true;}
int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width){ int mhz;
switch (chan_width) { case NL80211_CHAN_WIDTH_1: mhz = 1; break; case NL80211_CHAN_WIDTH_2: mhz = 2; break; case NL80211_CHAN_WIDTH_4: mhz = 4; break; case NL80211_CHAN_WIDTH_8: mhz = 8; break; case NL80211_CHAN_WIDTH_16: mhz = 16; break; case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: mhz = 20; break; case NL80211_CHAN_WIDTH_40: mhz = 40; break; case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_80: mhz = 80; break; case NL80211_CHAN_WIDTH_160: mhz = 160; break; case NL80211_CHAN_WIDTH_320: mhz = 320; break; default: WARN_ON_ONCE(1); return -1; } return mhz;}EXPORT_SYMBOL(nl80211_chan_width_to_mhz);
static bool cfg80211_valid_center_freq(u32 center, enum nl80211_chan_width width){ int bw; int step;
/* We only do strict verification on 6 GHz */ if (center < 5955 || center > 7215) return true;
bw = nl80211_chan_width_to_mhz(width); if (bw < 0) return false;
/* Validate that the channels bw is entirely within the 6 GHz band */ if (center - bw / 2 < 5945 || center + bw / 2 > 7225) return false;
/* With 320 MHz the permitted channels overlap */ if (bw == 320) step = 160; else step = bw;
/* * Valid channels are packed from lowest frequency towards higher ones. * So test that the lower frequency aligns with one of these steps. */ return (center - bw / 2 - 5945) % step == 0;}
static boolcfg80211_chandef_valid_control_freq(const struct cfg80211_chan_def *chandef, u32 control_freq){ switch (chandef->width) { case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_1: case NL80211_CHAN_WIDTH_2: case NL80211_CHAN_WIDTH_4: case NL80211_CHAN_WIDTH_8: case NL80211_CHAN_WIDTH_16: /* checked separately */ break; case NL80211_CHAN_WIDTH_320: if (chandef->center_freq1 == control_freq + 150 || chandef->center_freq1 == control_freq + 130 || chandef->center_freq1 == control_freq + 110 || chandef->center_freq1 == control_freq + 90 || chandef->center_freq1 == control_freq - 90 || chandef->center_freq1 == control_freq - 110 || chandef->center_freq1 == control_freq - 130 || chandef->center_freq1 == control_freq - 150) break; fallthrough; case NL80211_CHAN_WIDTH_160: if (chandef->center_freq1 == control_freq + 70 || chandef->center_freq1 == control_freq + 50 || chandef->center_freq1 == control_freq - 50 || chandef->center_freq1 == control_freq - 70) break; fallthrough; case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_80: if (chandef->center_freq1 == control_freq + 30 || chandef->center_freq1 == control_freq - 30) break; fallthrough; case NL80211_CHAN_WIDTH_40: if (chandef->center_freq1 == control_freq + 10 || chandef->center_freq1 == control_freq - 10) break; fallthrough; default: return false; }
return true;}
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef){ u32 control_freq, control_freq_khz, start_khz, end_khz;
if (!chandef->chan) return false;
if (chandef->freq1_offset >= 1000) return false;
control_freq = chandef->chan->center_freq;
if (cfg80211_chandef_is_s1g(chandef) && chandef->width != NL80211_CHAN_WIDTH_1 && chandef->width != NL80211_CHAN_WIDTH_2 && chandef->width != NL80211_CHAN_WIDTH_4 && chandef->width != NL80211_CHAN_WIDTH_8 && chandef->width != NL80211_CHAN_WIDTH_16) return false;
switch (chandef->width) { case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: if (ieee80211_chandef_to_khz(chandef) != ieee80211_channel_to_khz(chandef->chan)) return false; if (chandef->center_freq2) return false; break; case NL80211_CHAN_WIDTH_1: case NL80211_CHAN_WIDTH_2: case NL80211_CHAN_WIDTH_4: case NL80211_CHAN_WIDTH_8: case NL80211_CHAN_WIDTH_16: if (!cfg80211_chandef_is_s1g(chandef)) return false; if (chandef->center_freq2) return false;
control_freq_khz = ieee80211_channel_to_khz(chandef->chan); start_khz = cfg80211_s1g_get_start_freq_khz(chandef); end_khz = cfg80211_s1g_get_end_freq_khz(chandef);
if (control_freq_khz < start_khz || control_freq_khz > end_khz) return false; break; case NL80211_CHAN_WIDTH_80P80: if (!chandef->center_freq2) return false; /* adjacent is not allowed -- that's a 160 MHz channel */ if (chandef->center_freq1 - chandef->center_freq2 == 80 || chandef->center_freq2 - chandef->center_freq1 == 80) return false; break; default: if (chandef->center_freq2) return false; break; }
if (!cfg80211_chandef_valid_control_freq(chandef, control_freq)) return false;
if (chandef->npca_chan) { switch (chandef->width) { case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_160: case NL80211_CHAN_WIDTH_320: break; default: return false; } } else if (chandef->npca_punctured) { return false; }
if (!cfg80211_valid_center_freq(chandef->center_freq1, chandef->width)) return false;
if (chandef->width == NL80211_CHAN_WIDTH_80P80 && !cfg80211_valid_center_freq(chandef->center_freq2, chandef->width)) return false;
/* channel 14 is only for IEEE 802.11b */ if (chandef->center_freq1 == 2484 && chandef->width != NL80211_CHAN_WIDTH_20_NOHT) return false;
if (cfg80211_chandef_is_edmg(chandef) && !cfg80211_edmg_chandef_valid(chandef)) return false;
if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz) return false;
return valid_puncturing_bitmap(chandef, control_freq, chandef->punctured);}EXPORT_SYMBOL(cfg80211_chandef_valid);
int cfg80211_chandef_primary(const struct cfg80211_chan_def *c, enum nl80211_chan_width primary_chan_width, u16 *punctured){ int pri_width = nl80211_chan_width_to_mhz(primary_chan_width); int width = cfg80211_chandef_get_width(c); u32 control = c->chan->center_freq; u32 center = c->center_freq1; u16 _punct = 0;
if (WARN_ON_ONCE(pri_width < 0 || width < 0)) return -1;
/* not intended to be called this way, can't determine */ if (WARN_ON_ONCE(pri_width > width)) return -1;
if (!punctured) punctured = &_punct;
*punctured = c->punctured;
while (width > pri_width) { unsigned int bits_to_drop = width / 20 / 2;
if (control > center) { center += width / 4; *punctured >>= bits_to_drop; } else { center -= width / 4; *punctured &= (1 << bits_to_drop) - 1; } width /= 2; }
return center;}EXPORT_SYMBOL(cfg80211_chandef_primary);
bool cfg80211_chandef_npca_valid(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, const struct ieee80211_uhr_npca_info *npca){ struct cfg80211_chan_def tmp = *chandef; bool pri_upper, npca_upper; u32 cf1;
if (chandef->npca_chan || chandef->npca_punctured) return false;
if (!npca) return true;
if (cfg80211_chandef_add_npca(wiphy, &tmp, npca)) return false;
if (!cfg80211_chandef_valid_control_freq(&tmp, tmp.npca_chan->center_freq)) return false;
cf1 = tmp.center_freq1; pri_upper = tmp.chan->center_freq > cf1; npca_upper = tmp.npca_chan->center_freq > cf1;
if (pri_upper == npca_upper) return false;
if (!valid_puncturing_bitmap(&tmp, tmp.npca_chan->center_freq, tmp.npca_punctured) || (tmp.punctured & tmp.npca_punctured) != tmp.punctured) return false;
return true;}EXPORT_SYMBOL(cfg80211_chandef_npca_valid);
int cfg80211_chandef_add_npca(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, const struct ieee80211_uhr_npca_info *npca){ struct cfg80211_chan_def new_chandef = *chandef; u32 npca_freq; u8 chan;
if (chandef->npca_chan || chandef->npca_punctured) return -EINVAL;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return -EINVAL;
if (!npca) return 0;
switch (chandef->width) { case NL80211_CHAN_WIDTH_80: case NL80211_CHAN_WIDTH_160: case NL80211_CHAN_WIDTH_320: break; default: return -EINVAL; }
chan = le32_get_bits(npca->params, IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN);
npca_freq = ieee80211_channel_to_frequency(chan, chandef->chan->band); new_chandef.npca_chan = ieee80211_get_channel(wiphy, npca_freq); if (!new_chandef.npca_chan) return -EINVAL;
if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)) new_chandef.npca_punctured = le16_to_cpu(npca->dis_subch_bmap[0]);
if (!cfg80211_chandef_valid(&new_chandef)) return -EINVAL;
*chandef = new_chandef; return 0;}EXPORT_SYMBOL(cfg80211_chandef_add_npca);
int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef, const struct ieee80211_uhr_dbe_info *dbe){ struct cfg80211_chan_def new_chandef = *chandef; u16 starting_freq, bw_mhz, start_old, start_new; u8 bw, punct_shift; int offset, index;
if (!dbe) return 0;
if (!cfg80211_chandef_valid(chandef)) return -EINVAL;
if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT) return -EINVAL;
bw = u8_get_bits(dbe->params, IEEE80211_UHR_DBE_OPER_BANDWIDTH);
switch (chandef->chan->band) { case NL80211_BAND_5GHZ: if (bw > IEEE80211_UHR_DBE_OPER_BW_160) return -EINVAL; if (chandef->chan->center_freq < 5745) starting_freq = 5180; /* channel 36 */ else starting_freq = 5745; /* channel 149 */ break; case NL80211_BAND_6GHZ: starting_freq = 5955; /* channel 1 center */ break; default: return -EINVAL; }
switch (bw) { case IEEE80211_UHR_DBE_OPER_BW_320_2: case IEEE80211_UHR_DBE_OPER_BW_320_1: if (chandef->width == NL80211_CHAN_WIDTH_160) break; fallthrough; case IEEE80211_UHR_DBE_OPER_BW_160: if (chandef->width == NL80211_CHAN_WIDTH_80) break; fallthrough; case IEEE80211_UHR_DBE_OPER_BW_80: if (chandef->width == NL80211_CHAN_WIDTH_40) break; fallthrough; case IEEE80211_UHR_DBE_OPER_BW_40: if (chandef->width == NL80211_CHAN_WIDTH_20) break; fallthrough; default: return -EINVAL; }
switch (bw) { case IEEE80211_UHR_DBE_OPER_BW_320_2: /* 320-2 starts shifted by 160 */ starting_freq += 160; fallthrough; case IEEE80211_UHR_DBE_OPER_BW_320_1: new_chandef.width = NL80211_CHAN_WIDTH_320; bw_mhz = 320; break; case IEEE80211_UHR_DBE_OPER_BW_160: new_chandef.width = NL80211_CHAN_WIDTH_160; bw_mhz = 160; break; case IEEE80211_UHR_DBE_OPER_BW_80: new_chandef.width = NL80211_CHAN_WIDTH_80; bw_mhz = 80; break; case IEEE80211_UHR_DBE_OPER_BW_40: new_chandef.width = NL80211_CHAN_WIDTH_40; bw_mhz = 40; break; }
/* this should only happen for 320-2 and misconfigured AP */ if (chandef->chan->center_freq < starting_freq) return -EINVAL;
offset = chandef->chan->center_freq - starting_freq; index = offset / bw_mhz; start_new = starting_freq - 10 + index * bw_mhz; new_chandef.center_freq1 = start_new + bw_mhz / 2;
start_old = chandef->center_freq1 - cfg80211_chandef_get_width(chandef) / 2;
/* * If the DBE channel extends downward below the lower * edge of the BSS channel, we need to shift puncturing * bitmaps up to adjust for that. */ if (start_new < start_old) punct_shift = (start_old - start_new) / 20; else punct_shift = 0;
new_chandef.punctured <<= punct_shift; new_chandef.npca_punctured <<= punct_shift;
if (dbe->params & IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES) { u16 punct_mask = ((1 << (bw_mhz / 40)) - 1) << punct_shift; u16 punctured = le16_to_cpu(dbe->dis_subch_bmap[0]);
if ((punctured & punct_mask) != (new_chandef.punctured & punct_mask)) return -EINVAL;
new_chandef.punctured = punctured; }
if (!cfg80211_chandef_valid(&new_chandef)) return -EINVAL;
/* * If e.g. a 40 MHz BSS channel (erroneously) occupies the center of the * DBE 80 MHz channel, they would be incompatible; check and reject. */ if (!cfg80211_chandef_compatible(&new_chandef, chandef)) return -EINVAL;
*chandef = new_chandef; return 0;}EXPORT_SYMBOL(cfg80211_chandef_add_dbe);
static const struct cfg80211_chan_def *check_chandef_primary_compat(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2, enum nl80211_chan_width primary_chan_width){ u16 punct_c1 = 0, punct_c2 = 0;
/* check primary is compatible -> error if not */ if (cfg80211_chandef_primary(c1, primary_chan_width, &punct_c1) != cfg80211_chandef_primary(c2, primary_chan_width, &punct_c2)) return ERR_PTR(-EINVAL);
if (punct_c1 != punct_c2) return ERR_PTR(-EINVAL);
/* assumes c1 is smaller width, if that was just checked -> done */ if (c1->width == primary_chan_width) return c2;
/* otherwise continue checking the next width */ return NULL;}
static const struct cfg80211_chan_def *_cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2){ const struct cfg80211_chan_def *ret;
/* If they are identical, return */ if (cfg80211_chandef_identical(c1, c2)) return c2;
/* otherwise, must have same control channel */ if (c1->chan != c2->chan) return NULL;
/* * If they have the same width, but aren't identical, * then they can't be compatible. */ if (c1->width == c2->width) return NULL;
/* * We need NPCA to be compatible for some scenarios such as * multiple APs, but in this case userspace should configure * identical chandefs including NPCA, even if perhaps one of * the AP interfaces doesn't even advertise it. */ if (c1->npca_chan || c2->npca_chan) return NULL;
/* * can't be compatible if one of them is S1G * but they don't have the same width. */#define IS_S1G(width) ((width) == NL80211_CHAN_WIDTH_1 || \ (width) == NL80211_CHAN_WIDTH_2 || \ (width) == NL80211_CHAN_WIDTH_4 || \ (width) == NL80211_CHAN_WIDTH_8 || \ (width) == NL80211_CHAN_WIDTH_16)
if (IS_S1G(c1->width) || IS_S1G(c2->width)) return NULL;
/* * Make sure that c1 is always the narrower one, so that later * we either return NULL or c2 and don't have to check both * directions. */ if (c1->width > c2->width) swap(c1, c2);
/* * No further checks needed if the "narrower" one is only 20 MHz. * Here "narrower" includes being a 20 MHz non-HT channel vs. a * 20 MHz HT (or later) one. */ if (c1->width <= NL80211_CHAN_WIDTH_20) return c2;
ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_40); if (ret) return ret;
ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_80); if (ret) return ret;
/* * If c1 is 80+80, then c2 is 160 or higher, but that cannot * match. If c2 was also 80+80 it was already either accepted * or rejected above (identical or not, respectively.) */ if (c1->width == NL80211_CHAN_WIDTH_80P80) return NULL;
ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_160); if (ret) return ret;
/* * Getting here would mean they're both wider than 160, have the * same primary 160, but are not identical - this cannot happen * since they must be 320 (no wider chandefs exist, at least yet.) */ WARN_ON_ONCE(1);
return NULL;}
const struct cfg80211_chan_def *cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, const struct cfg80211_chan_def *c2){ const struct cfg80211_chan_def *ret;
ret = _cfg80211_chandef_compatible(c1, c2); if (IS_ERR(ret)) return NULL; return ret;}EXPORT_SYMBOL(cfg80211_chandef_compatible);
void cfg80211_set_dfs_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state){ struct ieee80211_channel *c; int width;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return;
width = cfg80211_chandef_get_width(chandef); if (width < 0) return;
for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c || !(c->flags & IEEE80211_CHAN_RADAR)) continue;
c->dfs_state = dfs_state; c->dfs_state_entered = jiffies; }}
void cfg80211_set_cac_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, bool cac_ongoing){ struct ieee80211_channel *c; int width; u64 cac_time;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return;
width = cfg80211_chandef_get_width(chandef); if (width < 0) return;
/* Get the same timestamp for all subchannels */ cac_time = cac_ongoing ? ktime_get_boottime_ns() : 0;
for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c) continue;
c->cac_start_time = cac_time; }}
static boolcfg80211_dfs_permissive_check_wdev(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, struct wireless_dev *wdev, struct ieee80211_channel *chan){ unsigned int link_id;
for_each_valid_link(wdev, link_id) { struct ieee80211_channel *other_chan = NULL; struct cfg80211_chan_def chandef = {}; int ret;
/* In order to avoid daisy chaining only allow BSS STA */ if (wdev->iftype != NL80211_IFTYPE_STATION || !wdev->links[link_id].client.current_bss) continue;
other_chan = wdev->links[link_id].client.current_bss->pub.channel;
if (!other_chan) continue;
if (chan == other_chan) return true;
/* continue if we can't get the channel */ ret = rdev_get_channel(rdev, wdev, link_id, &chandef); if (ret) continue;
if (cfg80211_is_sub_chan(&chandef, chan, false)) return true; }
return false;}
/* * Check if P2P GO is allowed to operate on a DFS channel */static bool cfg80211_dfs_permissive_chan(struct wiphy *wiphy, enum nl80211_iftype iftype, struct ieee80211_channel *chan){ struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
lockdep_assert_held(&rdev->wiphy.mtx);
if (!wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT) || !(chan->flags & IEEE80211_CHAN_DFS_CONCURRENT)) return false;
/* only valid for P2P GO */ if (iftype != NL80211_IFTYPE_P2P_GO) return false;
/* * Allow only if there's a concurrent BSS */ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { bool ret = cfg80211_dfs_permissive_check_wdev(rdev, iftype, wdev, chan); if (ret) return ret; }
return false;}
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_iftype iftype){ struct ieee80211_channel *c;
/* DFS is not required for S1G */ if (cfg80211_chandef_is_s1g(chandef)) return 0;
for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c) return -EINVAL;
if (c->flags & IEEE80211_CHAN_RADAR && !cfg80211_dfs_permissive_chan(wiphy, iftype, c)) return 1; }
return 0;}
int cfg80211_chandef_dfs_required(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_iftype iftype){ int width; int ret;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return -EINVAL;
switch (iftype) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_NAN: width = cfg80211_chandef_get_width(chandef); if (width < 0) return -EINVAL;
ret = cfg80211_get_chans_dfs_required(wiphy, chandef, iftype);
return (ret > 0) ? BIT(chandef->width) : ret; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN_DATA: case NL80211_IFTYPE_PD: break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: WARN_ON(1); }
return 0;}EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef){ struct ieee80211_channel *c; int width, count = 0;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return false;
width = cfg80211_chandef_get_width(chandef); if (width < 0) return false;
/* * Check entire range of channels for the bandwidth. * Check all channels are DFS channels (DFS_USABLE or * DFS_AVAILABLE). Return number of usable channels * (require CAC). Allow DFS and non-DFS channel mix. */ for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c) return false;
if (c->flags & IEEE80211_CHAN_DISABLED) return false;
if (c->flags & IEEE80211_CHAN_RADAR) { if (c->dfs_state == NL80211_DFS_UNAVAILABLE) return false;
if (c->dfs_state == NL80211_DFS_USABLE) count++; } }
return count > 0;}EXPORT_SYMBOL(cfg80211_chandef_dfs_usable);
/* * Checks if center frequency of chan falls with in the bandwidth * range of chandef. */bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, struct ieee80211_channel *chan, bool primary_only){ int width; u32 freq;
if (!chandef->chan) return false;
if (chandef->chan->center_freq == chan->center_freq) return true;
if (primary_only) return false;
width = cfg80211_chandef_get_width(chandef); if (width <= 20) return false;
for (freq = chandef->center_freq1 - width / 2 + 10; freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) { if (chan->center_freq == freq) return true; }
if (!chandef->center_freq2) return false;
for (freq = chandef->center_freq2 - width / 2 + 10; freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) { if (chan->center_freq == freq) return true; }
return false;}
bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev){ unsigned int link;
lockdep_assert_wiphy(wdev->wiphy);
switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: for_each_valid_link(wdev, link) { if (wdev->links[link].ap.beacon_interval) return true; } break; case NL80211_IFTYPE_ADHOC: if (wdev->u.ibss.ssid_len) return true; break; case NL80211_IFTYPE_MESH_POINT: if (wdev->u.mesh.id_len) return true; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: /* Can NAN type be considered as beaconing interface? */ case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_NAN_DATA: case NL80211_IFTYPE_PD: break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES: WARN_ON(1); }
return false;}
bool cfg80211_wdev_on_sub_chan(struct wireless_dev *wdev, struct ieee80211_channel *chan, bool primary_only){ unsigned int link;
switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: for_each_valid_link(wdev, link) { if (cfg80211_is_sub_chan(&wdev->links[link].ap.chandef, chan, primary_only)) return true; } break; case NL80211_IFTYPE_ADHOC: return cfg80211_is_sub_chan(&wdev->u.ibss.chandef, chan, primary_only); case NL80211_IFTYPE_MESH_POINT: return cfg80211_is_sub_chan(&wdev->u.mesh.chandef, chan, primary_only); default: break; }
return false;}
static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, struct ieee80211_channel *chan){ struct wireless_dev *wdev;
lockdep_assert_wiphy(wiphy);
list_for_each_entry(wdev, &wiphy->wdev_list, list) { if (!cfg80211_beaconing_iface_active(wdev)) continue;
if (cfg80211_wdev_on_sub_chan(wdev, chan, false)) return true; }
return false;}
static boolcfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev, struct ieee80211_channel *channel){ if (!rdev->background_radar_wdev) return false;
if (!cfg80211_chandef_valid(&rdev->background_radar_chandef)) return false;
return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel, false);}
bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, struct ieee80211_channel *chan){ struct cfg80211_registered_device *rdev;
lockdep_assert_held(&cfg80211_mutex);
if (!(chan->flags & IEEE80211_CHAN_RADAR)) return false;
for_each_rdev(rdev) { bool found;
if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) continue;
guard(wiphy)(&rdev->wiphy);
found = cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan) || cfg80211_offchan_chain_is_active(rdev, chan);
if (found) return true; }
return false;}
static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef){ struct ieee80211_channel *c; int width; bool dfs_offload;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return false;
width = cfg80211_chandef_get_width(chandef); if (width < 0) return false;
dfs_offload = wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD);
/* * Check entire range of channels for the bandwidth. * If any channel in between is disabled or has not * had gone through CAC return false */ for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c) return false;
if (c->flags & IEEE80211_CHAN_DISABLED) return false;
if ((c->flags & IEEE80211_CHAN_RADAR) && (c->dfs_state != NL80211_DFS_AVAILABLE) && !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload)) return false; }
return true;}
unsigned intcfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef){ struct ieee80211_channel *c; int width; unsigned int t1 = 0, t2 = 0;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return 0;
width = cfg80211_chandef_get_width(chandef); if (width < 0) return 0;
for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c || (c->flags & IEEE80211_CHAN_DISABLED)) { if (cf == 1) t1 = INT_MAX; else t2 = INT_MAX; continue; }
if (!(c->flags & IEEE80211_CHAN_RADAR)) continue;
if (cf == 1 && c->dfs_cac_ms > t1) t1 = c->dfs_cac_ms;
if (cf == 2 && c->dfs_cac_ms > t2) t2 = c->dfs_cac_ms; }
if (t1 == INT_MAX && t2 == INT_MAX) return 0;
if (t1 == INT_MAX) return t2;
if (t2 == INT_MAX) return t1;
return max(t1, t2);}EXPORT_SYMBOL(cfg80211_chandef_dfs_cac_time);
/* check if the operating channels are valid and supported */static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels, enum ieee80211_edmg_bw_config edmg_bw_config, int primary_channel, struct ieee80211_edmg *edmg_cap){ struct ieee80211_channel *chan; int i, freq; int channels_counter = 0;
if (!edmg_channels && !edmg_bw_config) return true;
if ((!edmg_channels && edmg_bw_config) || (edmg_channels && !edmg_bw_config)) return false;
if (!(edmg_channels & BIT(primary_channel - 1))) return false;
/* 60GHz channels 1..6 */ for (i = 0; i < 6; i++) { if (!(edmg_channels & BIT(i))) continue;
if (!(edmg_cap->channels & BIT(i))) return false;
channels_counter++;
freq = ieee80211_channel_to_frequency(i + 1, NL80211_BAND_60GHZ); chan = ieee80211_get_channel(wiphy, freq); if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) return false; }
/* IEEE802.11 allows max 4 channels */ if (channels_counter > 4) return false;
/* check bw_config is a subset of what driver supports * (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13) */ if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4)) return false;
if (edmg_bw_config > edmg_cap->bw_config) return false;
return true;}
static bool cfg80211_s1g_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef){ u32 freq_khz; const struct ieee80211_channel *chan; u32 pri_khz = ieee80211_channel_to_khz(chandef->chan); u32 end_khz = cfg80211_s1g_get_end_freq_khz(chandef); u32 start_khz = cfg80211_s1g_get_start_freq_khz(chandef); int width_mhz = cfg80211_chandef_get_width(chandef); u32 prohibited_flags = IEEE80211_CHAN_DISABLED;
if (width_mhz >= 16) prohibited_flags |= IEEE80211_CHAN_NO_16MHZ; if (width_mhz >= 8) prohibited_flags |= IEEE80211_CHAN_NO_8MHZ; if (width_mhz >= 4) prohibited_flags |= IEEE80211_CHAN_NO_4MHZ;
if (chandef->chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY) return false;
if (pri_khz < start_khz || pri_khz > end_khz) return false;
for_each_s1g_subchan(chandef, freq_khz) { chan = ieee80211_get_channel_khz(wiphy, freq_khz); if (!chan || (chan->flags & prohibited_flags)) return false; }
if (chandef->s1g_primary_2mhz) { u32 sib_khz; const struct ieee80211_channel *sibling;
sibling = cfg80211_s1g_get_primary_sibling(wiphy, chandef); if (!sibling) return false;
if (sibling->flags & IEEE80211_CHAN_S1G_NO_PRIMARY) return false;
sib_khz = ieee80211_channel_to_khz(sibling); if (sib_khz < start_khz || sib_khz > end_khz) return false; }
return true;}
bool _cfg80211_chandef_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, u32 prohibited_flags, u32 permitting_flags){ struct ieee80211_sta_ht_cap *ht_cap; struct ieee80211_sta_vht_cap *vht_cap; struct ieee80211_edmg *edmg_cap; u32 width, control_freq, cap; bool ext_nss_cap, support_80_80 = false, support_320 = false; const struct ieee80211_sband_iftype_data *iftd; struct ieee80211_supported_band *sband; struct ieee80211_channel *c; int i;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return false;
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap; ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) & IEEE80211_VHT_EXT_NSS_BW_CAPABLE;
if (cfg80211_chandef_is_s1g(chandef)) return cfg80211_s1g_usable(wiphy, chandef);
if (edmg_cap->channels && !cfg80211_edmg_usable(wiphy, chandef->edmg.channels, chandef->edmg.bw_config, chandef->chan->hw_value, edmg_cap)) return false;
control_freq = chandef->chan->center_freq;
switch (chandef->width) { case NL80211_CHAN_WIDTH_20: if (!ht_cap->ht_supported && chandef->chan->band != NL80211_BAND_6GHZ) return false; fallthrough; case NL80211_CHAN_WIDTH_20_NOHT: prohibited_flags |= IEEE80211_CHAN_NO_20MHZ; width = 20; break; case NL80211_CHAN_WIDTH_40: width = 40; if (chandef->chan->band == NL80211_BAND_6GHZ) break; if (!ht_cap->ht_supported) return false; if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) return false; if (chandef->center_freq1 < control_freq && chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) return false; if (chandef->center_freq1 > control_freq && chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) return false; break; case NL80211_CHAN_WIDTH_80P80: cap = vht_cap->cap; support_80_80 = (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) || (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) || (ext_nss_cap && u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1); if (chandef->chan->band != NL80211_BAND_6GHZ && !support_80_80) return false; fallthrough; case NL80211_CHAN_WIDTH_80: prohibited_flags |= IEEE80211_CHAN_NO_80MHZ; width = 80; if (chandef->chan->band == NL80211_BAND_6GHZ) break; if (!vht_cap->vht_supported) return false; break; case NL80211_CHAN_WIDTH_160: prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; width = 160; if (chandef->chan->band == NL80211_BAND_6GHZ) break; if (!vht_cap->vht_supported) return false; cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ && !(ext_nss_cap && (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK))) return false; break; case NL80211_CHAN_WIDTH_320: prohibited_flags |= IEEE80211_CHAN_NO_320MHZ; width = 320;
if (chandef->chan->band != NL80211_BAND_6GHZ) return false;
sband = wiphy->bands[NL80211_BAND_6GHZ]; if (!sband) return false;
for_each_sband_iftype_data(sband, i, iftd) { if (!iftd->eht_cap.has_eht) continue;
if (iftd->eht_cap.eht_cap_elem.phy_cap_info[0] & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) { support_320 = true; break; } }
if (!support_320) return false; break; default: WARN_ON_ONCE(1); return false; }
/* * TODO: What if there are only certain 80/160/80+80 MHz channels * allowed by the driver, or only certain combinations? * For 40 MHz the driver can set the NO_HT40 flags, but for * 80/160 MHz and in particular 80+80 MHz this isn't really * feasible and we only have NO_80MHZ/NO_160MHZ so far but * no way to cover 80+80 MHz or more complex restrictions. * Note that such restrictions also need to be advertised to * userspace, for example for P2P channel selection. */
if (width > 20) prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
/* 5 and 10 MHz are only defined for the OFDM PHY */ if (width < 20) prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
for_each_subchan(chandef, freq, cf) { c = ieee80211_get_channel_khz(wiphy, freq); if (!c) return false; if (c->flags & permitting_flags) continue; if (c->flags & prohibited_flags) return false; }
return true;}
bool cfg80211_chandef_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, u32 prohibited_flags){ return _cfg80211_chandef_usable(wiphy, chandef, prohibited_flags, 0);}EXPORT_SYMBOL(cfg80211_chandef_usable);
static bool cfg80211_ir_permissive_check_wdev(enum nl80211_iftype iftype, struct wireless_dev *wdev, struct ieee80211_channel *chan){ struct ieee80211_channel *other_chan = NULL; unsigned int link_id; int r1, r2;
for_each_valid_link(wdev, link_id) { if (wdev->iftype == NL80211_IFTYPE_STATION && wdev->links[link_id].client.current_bss) other_chan = wdev->links[link_id].client.current_bss->pub.channel;
/* * If a GO already operates on the same GO_CONCURRENT channel, * this one (maybe the same one) can beacon as well. We allow * the operation even if the station we relied on with * GO_CONCURRENT is disconnected now. But then we must make sure * we're not outdoor on an indoor-only channel. */ if (iftype == NL80211_IFTYPE_P2P_GO && wdev->iftype == NL80211_IFTYPE_P2P_GO && wdev->links[link_id].ap.beacon_interval && !(chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) other_chan = wdev->links[link_id].ap.chandef.chan;
if (!other_chan) continue;
if (chan == other_chan) return true;
if (chan->band != NL80211_BAND_5GHZ && chan->band != NL80211_BAND_6GHZ) continue;
r1 = cfg80211_get_unii(chan->center_freq); r2 = cfg80211_get_unii(other_chan->center_freq);
if (r1 != -EINVAL && r1 == r2) { /* * At some locations channels 149-165 are considered a * bundle, but at other locations, e.g., Indonesia, * channels 149-161 are considered a bundle while * channel 165 is left out and considered to be in a * different bundle. Thus, in case that there is a * station interface connected to an AP on channel 165, * it is assumed that channels 149-161 are allowed for * GO operations. However, having a station interface * connected to an AP on channels 149-161, does not * allow GO operation on channel 165. */ if (chan->center_freq == 5825 && other_chan->center_freq != 5825) continue; return true; } }
return false;}
/* * Check if the channel can be used under permissive conditions mandated by * some regulatory bodies, i.e., the channel is marked with * IEEE80211_CHAN_IR_CONCURRENT and there is an additional station interface * associated to an AP on the same channel or on the same UNII band * (assuming that the AP is an authorized master). * In addition allow operation on a channel on which indoor operation is * allowed, iff we are currently operating in an indoor environment. */static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy, enum nl80211_iftype iftype, struct ieee80211_channel *chan){ struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
lockdep_assert_held(&rdev->wiphy.mtx);
if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) || !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR)) return false;
/* only valid for GO and TDLS off-channel (station/p2p-CL) */ if (iftype != NL80211_IFTYPE_P2P_GO && iftype != NL80211_IFTYPE_STATION && iftype != NL80211_IFTYPE_P2P_CLIENT) return false;
if (regulatory_indoor_allowed() && (chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) return true;
if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT)) return false;
/* * Generally, it is possible to rely on another device/driver to allow * the IR concurrent relaxation, however, since the device can further * enforce the relaxation (by doing a similar verifications as this), * and thus fail the GO instantiation, consider only the interfaces of * the current registered device. */ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { bool ret;
ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan); if (ret) return ret; }
return false;}
static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, enum nl80211_iftype iftype, u32 prohibited_flags, u32 permitting_flags){ bool res, check_radar; int dfs_required;
trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype, prohibited_flags, permitting_flags);
if (!_cfg80211_chandef_usable(wiphy, chandef, IEEE80211_CHAN_DISABLED, 0)) return false;
dfs_required = cfg80211_chandef_dfs_required(wiphy, chandef, iftype); check_radar = dfs_required != 0;
if (dfs_required > 0 && cfg80211_chandef_dfs_available(wiphy, chandef)) { /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ prohibited_flags &= ~IEEE80211_CHAN_NO_IR; check_radar = false; }
if (check_radar && !_cfg80211_chandef_usable(wiphy, chandef, IEEE80211_CHAN_RADAR, 0)) return false;
res = _cfg80211_chandef_usable(wiphy, chandef, prohibited_flags, permitting_flags);
trace_cfg80211_return_bool(res); return res;}
bool cfg80211_reg_check_beaconing(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, struct cfg80211_beaconing_check_config *cfg){ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); u32 permitting_flags = 0; bool check_no_ir = true;
/* * Under certain conditions suggested by some regulatory bodies a * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag * only if such relaxations are not enabled and the conditions are not * met. */ if (cfg->relax) { lockdep_assert_held(&rdev->wiphy.mtx); check_no_ir = !cfg80211_ir_permissive_chan(wiphy, cfg->iftype, chandef->chan); }
if (cfg->reg_power == IEEE80211_REG_VLP_AP) permitting_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
if ((cfg->iftype == NL80211_IFTYPE_P2P_GO || cfg->iftype == NL80211_IFTYPE_AP) && (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || chandef->width == NL80211_CHAN_WIDTH_20)) permitting_flags |= IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY;
return _cfg80211_reg_can_beacon(wiphy, chandef, cfg->iftype, check_no_ir ? IEEE80211_CHAN_NO_IR : 0, permitting_flags);}EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
static bool cfg80211_can_set_monitor_channel(struct cfg80211_registered_device *rdev, struct cfg80211_chan_def *chandef){ struct wireless_dev *wdev; int radio_idx;
lockdep_assert_held(&rdev->wiphy.mtx);
if (rdev->num_running_monitor_ifaces < 1) return false;
if (cfg80211_has_monitors_only(rdev)) return true;
radio_idx = cfg80211_get_radio_idx_by_chan(&rdev->wiphy, chandef->chan); /* No defined radio covers this channel. Fall back on global behavior */ if (radio_idx < 0) return false;
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (wdev->iftype == NL80211_IFTYPE_MONITOR) continue; if (!wdev->netdev) continue; if (rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)) return false; }
return true;}
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_chan_def *chandef){ if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; if (!cfg80211_can_set_monitor_channel(rdev, chandef)) return -EBUSY;
return rdev_set_monitor_channel(rdev, dev, chandef);}
bool cfg80211_any_usable_channels(struct wiphy *wiphy, unsigned long sband_mask, u32 prohibited_flags){ int idx;
prohibited_flags |= IEEE80211_CHAN_DISABLED;
for_each_set_bit(idx, &sband_mask, NUM_NL80211_BANDS) { struct ieee80211_supported_band *sband = wiphy->bands[idx]; int chanidx;
if (!sband) continue;
for (chanidx = 0; chanidx < sband->n_channels; chanidx++) { struct ieee80211_channel *chan;
chan = &sband->channels[chanidx];
if (chan->flags & prohibited_flags) continue;
return true; } }
return false;}EXPORT_SYMBOL(cfg80211_any_usable_channels);
struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev, unsigned int link_id){ lockdep_assert_wiphy(wdev->wiphy);
WARN_ON(wdev->valid_links && !(wdev->valid_links & BIT(link_id))); WARN_ON(!wdev->valid_links && link_id > 0);
switch (wdev->iftype) { case NL80211_IFTYPE_MESH_POINT: return &wdev->u.mesh.chandef; case NL80211_IFTYPE_ADHOC: return &wdev->u.ibss.chandef; case NL80211_IFTYPE_OCB: return &wdev->u.ocb.chandef; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: return &wdev->links[link_id].ap.chandef; default: return NULL; }}EXPORT_SYMBOL(wdev_chandef);